Skip to content

Commit f900dc7

Browse files
committed
Merge branch 'main' into fix_mtp
2 parents 0db9830 + f09e245 commit f900dc7

File tree

1,013 files changed

+112098
-14253
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,013 files changed

+112098
-14253
lines changed

bolt/include/bolt/Rewrite/RewriteInstance.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,9 @@ class RewriteInstance {
505505
/// Number of local symbols in newly written symbol table.
506506
uint64_t NumLocalSymbols{0};
507507

508+
/// Flag indicating runtime library linking just started.
509+
bool StartLinkingRuntimeLib{false};
510+
508511
/// Information on special Procedure Linkage Table sections. There are
509512
/// multiple variants generated by different linkers.
510513
struct PLTSectionInfo {

bolt/lib/Passes/Instrumentation.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,7 @@ Error Instrumentation::runOnFunctions(BinaryContext &BC) {
604604
/*IsText=*/false,
605605
/*IsAllocatable=*/true);
606606
BC.registerOrUpdateSection(".bolt.instr.counters", ELF::SHT_PROGBITS, Flags,
607-
nullptr, 0, 1);
607+
nullptr, 0, BC.RegularPageSize);
608608

609609
BC.registerOrUpdateNoteSection(".bolt.instr.tables", nullptr, 0,
610610
/*Alignment=*/1,

bolt/lib/Rewrite/RewriteInstance.cpp

Lines changed: 91 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,11 @@ Error RewriteInstance::discoverStorage() {
628628
unsigned Phnum = Obj.getHeader().e_phnum;
629629
Phnum += 3;
630630

631+
// Reserve two more pheaders to avoid having writeable and executable
632+
// segment in instrumented binary.
633+
if (opts::Instrument)
634+
Phnum += 2;
635+
631636
NextAvailableAddress += Phnum * sizeof(ELF64LEPhdrTy);
632637
NextAvailableOffset += Phnum * sizeof(ELF64LEPhdrTy);
633638
}
@@ -2083,6 +2088,13 @@ void RewriteInstance::adjustCommandLineOptions() {
20832088
opts::HotText = false;
20842089
}
20852090

2091+
if (opts::Instrument && opts::UseGnuStack) {
2092+
BC->errs() << "BOLT-ERROR: cannot avoid having writeable and executable "
2093+
"segment in instrumented binary if program headers will be "
2094+
"updated in place\n";
2095+
exit(1);
2096+
}
2097+
20862098
if (opts::HotText && opts::HotTextMoveSections.getNumOccurrences() == 0) {
20872099
opts::HotTextMoveSections.addValue(".stub");
20882100
opts::HotTextMoveSections.addValue(".mover");
@@ -3612,11 +3624,13 @@ void RewriteInstance::emitAndLink() {
36123624
static_cast<MCObjectStreamer &>(*Streamer).getAssembler());
36133625
}
36143626

3615-
if (RuntimeLibrary *RtLibrary = BC->getRuntimeLibrary())
3627+
if (RuntimeLibrary *RtLibrary = BC->getRuntimeLibrary()) {
3628+
StartLinkingRuntimeLib = true;
36163629
RtLibrary->link(*BC, ToolPath, *Linker, [this](auto MapSection) {
36173630
// Map newly registered sections.
36183631
this->mapAllocatableSections(MapSection);
36193632
});
3633+
}
36203634

36213635
// Once the code is emitted, we can rename function sections to actual
36223636
// output sections and de-register sections used for emission.
@@ -4011,12 +4025,17 @@ void RewriteInstance::mapAllocatableSections(
40114025
Section.setOutputFileOffset(Section.getInputFileOffset());
40124026
MapSection(Section, Section.getAddress());
40134027
} else {
4014-
NextAvailableAddress =
4015-
alignTo(NextAvailableAddress, Section.getAlignment());
4028+
uint64_t Alignment = Section.getAlignment();
4029+
if (opts::Instrument && StartLinkingRuntimeLib) {
4030+
Alignment = BC->RegularPageSize;
4031+
StartLinkingRuntimeLib = false;
4032+
}
4033+
NextAvailableAddress = alignTo(NextAvailableAddress, Alignment);
4034+
40164035
LLVM_DEBUG({
4017-
dbgs() << "BOLT: mapping section " << Section.getName() << " (0x"
4018-
<< Twine::utohexstr(Section.getAllocAddress()) << ") to 0x"
4019-
<< Twine::utohexstr(NextAvailableAddress) << ":0x"
4036+
dbgs() << "BOLT-DEBUG: mapping section " << Section.getName()
4037+
<< " (0x" << Twine::utohexstr(Section.getAllocAddress())
4038+
<< ") to 0x" << Twine::utohexstr(NextAvailableAddress) << ":0x"
40204039
<< Twine::utohexstr(NextAvailableAddress +
40214040
Section.getOutputSize())
40224041
<< '\n';
@@ -4079,6 +4098,9 @@ void RewriteInstance::patchELFPHDRTable() {
40794098
}
40804099
}
40814100

4101+
if (opts::Instrument)
4102+
Phnum += 2;
4103+
40824104
// NOTE Currently .eh_frame_hdr appends to the last segment, recalculate
40834105
// last segments size based on the NextAvailableAddress variable.
40844106
if (!NewWritableSegmentSize) {
@@ -4093,7 +4115,8 @@ void RewriteInstance::patchELFPHDRTable() {
40934115
const uint64_t SavedPos = OS.tell();
40944116
OS.seek(PHDRTableOffset);
40954117

4096-
auto createNewTextPhdr = [&]() {
4118+
auto createNewPhdrs = [&]() {
4119+
SmallVector<ELF64LEPhdrTy, 3> NewPhdrs;
40974120
ELF64LEPhdrTy NewPhdr;
40984121
NewPhdr.p_type = ELF::PT_LOAD;
40994122
if (PHDRTableAddress) {
@@ -4108,20 +4131,67 @@ void RewriteInstance::patchELFPHDRTable() {
41084131
NewPhdr.p_filesz = NewTextSegmentSize;
41094132
NewPhdr.p_memsz = NewTextSegmentSize;
41104133
NewPhdr.p_flags = ELF::PF_X | ELF::PF_R;
4111-
if (opts::Instrument) {
4112-
// FIXME: Currently instrumentation is experimental and the runtime data
4113-
// is emitted with code, thus everything needs to be writable.
4114-
NewPhdr.p_flags |= ELF::PF_W;
4115-
}
41164134
NewPhdr.p_align = BC->PageAlign;
41174135

4118-
return NewPhdr;
4136+
if (!opts::Instrument) {
4137+
NewPhdrs.push_back(NewPhdr);
4138+
} else {
4139+
ErrorOr<BinarySection &> Sec =
4140+
BC->getUniqueSectionByName(".bolt.instr.counters");
4141+
assert(Sec && "expected one and only one `.bolt.instr.counters` section");
4142+
const uint64_t Addr = Sec->getOutputAddress();
4143+
const uint64_t Offset = Sec->getOutputFileOffset();
4144+
const uint64_t Size = Sec->getOutputSize();
4145+
assert(Addr > NewPhdr.p_vaddr &&
4146+
Addr + Size < NewPhdr.p_vaddr + NewPhdr.p_memsz &&
4147+
"`.bolt.instr.counters` section is expected to be included in the "
4148+
"new text sgement");
4149+
4150+
// Set correct size for the previous header since we are breaking the
4151+
// new text segment into three segments.
4152+
uint64_t Delta = Addr - NewPhdr.p_vaddr;
4153+
NewPhdr.p_filesz = Delta;
4154+
NewPhdr.p_memsz = Delta;
4155+
NewPhdrs.push_back(NewPhdr);
4156+
4157+
// Create a program header for a RW segment that includes the
4158+
// `.bolt.instr.counters` section only.
4159+
ELF64LEPhdrTy NewPhdrRWSegment;
4160+
NewPhdrRWSegment.p_type = ELF::PT_LOAD;
4161+
NewPhdrRWSegment.p_offset = Offset;
4162+
NewPhdrRWSegment.p_vaddr = Addr;
4163+
NewPhdrRWSegment.p_paddr = Addr;
4164+
NewPhdrRWSegment.p_filesz = Size;
4165+
NewPhdrRWSegment.p_memsz = Size;
4166+
NewPhdrRWSegment.p_flags = ELF::PF_R | ELF::PF_W;
4167+
NewPhdrRWSegment.p_align = BC->RegularPageSize;
4168+
NewPhdrs.push_back(NewPhdrRWSegment);
4169+
4170+
// Create a program header for a RX segment that includes all the RX
4171+
// sections from runtime library.
4172+
ELF64LEPhdrTy NewPhdrRXSegment;
4173+
NewPhdrRXSegment.p_type = ELF::PT_LOAD;
4174+
const uint64_t AddrRX = alignTo(Addr + Size, BC->RegularPageSize);
4175+
const uint64_t OffsetRX = alignTo(Offset + Size, BC->RegularPageSize);
4176+
const uint64_t SizeRX = NewTextSegmentSize - (AddrRX - NewPhdr.p_paddr);
4177+
NewPhdrRXSegment.p_offset = OffsetRX;
4178+
NewPhdrRXSegment.p_vaddr = AddrRX;
4179+
NewPhdrRXSegment.p_paddr = AddrRX;
4180+
NewPhdrRXSegment.p_filesz = SizeRX;
4181+
NewPhdrRXSegment.p_memsz = SizeRX;
4182+
NewPhdrRXSegment.p_flags = ELF::PF_X | ELF::PF_R;
4183+
NewPhdrRXSegment.p_align = BC->RegularPageSize;
4184+
NewPhdrs.push_back(NewPhdrRXSegment);
4185+
}
4186+
4187+
return NewPhdrs;
41194188
};
41204189

41214190
auto writeNewSegmentPhdrs = [&]() {
41224191
if (PHDRTableAddress || NewTextSegmentSize) {
4123-
ELF64LE::Phdr NewPhdr = createNewTextPhdr();
4124-
OS.write(reinterpret_cast<const char *>(&NewPhdr), sizeof(NewPhdr));
4192+
SmallVector<ELF64LE::Phdr, 3> NewPhdrs = createNewPhdrs();
4193+
OS.write(reinterpret_cast<const char *>(NewPhdrs.data()),
4194+
sizeof(ELF64LE::Phdr) * NewPhdrs.size());
41254195
}
41264196

41274197
if (NewWritableSegmentSize) {
@@ -4169,8 +4239,12 @@ void RewriteInstance::patchELFPHDRTable() {
41694239
}
41704240
case ELF::PT_GNU_STACK:
41714241
if (opts::UseGnuStack) {
4172-
// Overwrite the header with the new text segment header.
4173-
NewPhdr = createNewTextPhdr();
4242+
// Overwrite the header with the new segment header.
4243+
assert(!opts::Instrument);
4244+
SmallVector<ELF64LE::Phdr, 3> NewPhdrs = createNewPhdrs();
4245+
assert(NewPhdrs.size() == 1 &&
4246+
"expect exactly one program header was created");
4247+
NewPhdr = NewPhdrs[0];
41744248
ModdedGnuStack = true;
41754249
}
41764250
break;

bolt/test/avoid-wx-segment.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Test bolt instrumentation won't generate a binary with any segment that
2+
// is writable and executable. Basically we want to put `.bolt.instr.counters`
3+
// section into its own segment, separated from its surrounding RX sections.
4+
5+
// REQUIRES: system-linux
6+
7+
void foo() {}
8+
void bar() { foo(); }
9+
10+
// RUN: %clang %cflags -c %s -o %t.o
11+
// RUN: ld.lld -q -o %t.so %t.o -shared --init=foo --fini=foo
12+
// RUN: llvm-bolt --instrument %t.so -o %tt.so
13+
// RUN: llvm-readelf -l %tt.so | FileCheck %s
14+
// CHECK-NOT: RWE
15+
// CHECK: {{[0-9]*}} .bolt.instr.counters {{$}}

clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
#include "UndelegatedConstructorCheck.h"
9191
#include "UnhandledExceptionAtNewCheck.h"
9292
#include "UnhandledSelfAssignmentCheck.h"
93+
#include "UnintendedCharOstreamOutputCheck.h"
9394
#include "UniquePtrArrayMismatchCheck.h"
9495
#include "UnsafeFunctionsCheck.h"
9596
#include "UnusedLocalNonTrivialVariableCheck.h"
@@ -147,6 +148,8 @@ class BugproneModule : public ClangTidyModule {
147148
"bugprone-incorrect-enable-if");
148149
CheckFactories.registerCheck<IncorrectEnableSharedFromThisCheck>(
149150
"bugprone-incorrect-enable-shared-from-this");
151+
CheckFactories.registerCheck<UnintendedCharOstreamOutputCheck>(
152+
"bugprone-unintended-char-ostream-output");
150153
CheckFactories.registerCheck<ReturnConstRefFromParameterCheck>(
151154
"bugprone-return-const-ref-from-parameter");
152155
CheckFactories.registerCheck<SwitchMissingDefaultCaseCheck>(

clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ add_clang_library(clangTidyBugproneModule STATIC
2929
InaccurateEraseCheck.cpp
3030
IncorrectEnableIfCheck.cpp
3131
IncorrectEnableSharedFromThisCheck.cpp
32+
UnintendedCharOstreamOutputCheck.cpp
3233
ReturnConstRefFromParameterCheck.cpp
3334
SuspiciousStringviewDataUsageCheck.cpp
3435
SwitchMissingDefaultCaseCheck.cpp
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
//===--- UnintendedCharOstreamOutputCheck.cpp - clang-tidy ----------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "UnintendedCharOstreamOutputCheck.h"
10+
#include "clang/AST/Type.h"
11+
#include "clang/ASTMatchers/ASTMatchFinder.h"
12+
#include "clang/ASTMatchers/ASTMatchers.h"
13+
#include "clang/Basic/Diagnostic.h"
14+
#include "clang/Tooling/FixIt.h"
15+
16+
using namespace clang::ast_matchers;
17+
18+
namespace clang::tidy::bugprone {
19+
20+
namespace {
21+
22+
// check if the type is unsigned char or signed char
23+
AST_MATCHER(Type, isNumericChar) {
24+
return Node.isSpecificBuiltinType(BuiltinType::SChar) ||
25+
Node.isSpecificBuiltinType(BuiltinType::UChar);
26+
}
27+
28+
// check if the type is char
29+
AST_MATCHER(Type, isChar) {
30+
return Node.isSpecificBuiltinType(BuiltinType::Char_S) ||
31+
Node.isSpecificBuiltinType(BuiltinType::Char_U);
32+
}
33+
34+
} // namespace
35+
36+
UnintendedCharOstreamOutputCheck::UnintendedCharOstreamOutputCheck(
37+
StringRef Name, ClangTidyContext *Context)
38+
: ClangTidyCheck(Name, Context), CastTypeName(Options.get("CastTypeName")) {
39+
}
40+
void UnintendedCharOstreamOutputCheck::storeOptions(
41+
ClangTidyOptions::OptionMap &Opts) {
42+
if (CastTypeName.has_value())
43+
Options.store(Opts, "CastTypeName", CastTypeName.value());
44+
}
45+
46+
void UnintendedCharOstreamOutputCheck::registerMatchers(MatchFinder *Finder) {
47+
auto BasicOstream =
48+
cxxRecordDecl(hasName("::std::basic_ostream"),
49+
// only basic_ostream<char, Traits> has overload operator<<
50+
// with char / unsigned char / signed char
51+
classTemplateSpecializationDecl(
52+
hasTemplateArgument(0, refersToType(isChar()))));
53+
Finder->addMatcher(
54+
cxxOperatorCallExpr(
55+
hasOverloadedOperatorName("<<"),
56+
hasLHS(hasType(hasUnqualifiedDesugaredType(
57+
recordType(hasDeclaration(cxxRecordDecl(
58+
anyOf(BasicOstream, isDerivedFrom(BasicOstream)))))))),
59+
hasRHS(hasType(hasUnqualifiedDesugaredType(isNumericChar()))))
60+
.bind("x"),
61+
this);
62+
}
63+
64+
void UnintendedCharOstreamOutputCheck::check(
65+
const MatchFinder::MatchResult &Result) {
66+
const auto *Call = Result.Nodes.getNodeAs<CXXOperatorCallExpr>("x");
67+
const Expr *Value = Call->getArg(1);
68+
const SourceRange SourceRange = Value->getSourceRange();
69+
70+
DiagnosticBuilder Builder =
71+
diag(Call->getOperatorLoc(),
72+
"%0 passed to 'operator<<' outputs as character instead of integer. "
73+
"cast to 'unsigned int' to print numeric value or cast to 'char' to "
74+
"print as character")
75+
<< Value->getType() << SourceRange;
76+
77+
QualType T = Value->getType();
78+
const Type *UnqualifiedDesugaredType = T->getUnqualifiedDesugaredType();
79+
80+
llvm::StringRef CastType = CastTypeName.value_or(
81+
UnqualifiedDesugaredType->isSpecificBuiltinType(BuiltinType::SChar)
82+
? "int"
83+
: "unsigned int");
84+
85+
Builder << FixItHint::CreateReplacement(
86+
SourceRange, ("static_cast<" + CastType + ">(" +
87+
tooling::fixit::getText(*Value, *Result.Context) + ")")
88+
.str());
89+
}
90+
91+
} // namespace clang::tidy::bugprone
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//===--- UnintendedCharOstreamOutputCheck.h - clang-tidy --------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNINTENDEDCHAROSTREAMOUTPUTCHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNINTENDEDCHAROSTREAMOUTPUTCHECK_H
11+
12+
#include "../ClangTidyCheck.h"
13+
#include <optional>
14+
15+
namespace clang::tidy::bugprone {
16+
17+
/// Finds unintended character output from `unsigned char` and `signed char` to
18+
/// an ostream.
19+
///
20+
/// For the user-facing documentation see:
21+
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/unintended-char-ostream-output.html
22+
class UnintendedCharOstreamOutputCheck : public ClangTidyCheck {
23+
public:
24+
UnintendedCharOstreamOutputCheck(StringRef Name, ClangTidyContext *Context);
25+
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
26+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
27+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
28+
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
29+
return LangOpts.CPlusPlus;
30+
}
31+
32+
private:
33+
const std::optional<StringRef> CastTypeName;
34+
};
35+
36+
} // namespace clang::tidy::bugprone
37+
38+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNINTENDEDCHAROSTREAMOUTPUTCHECK_H

clang-tools-extra/clang-tidy/performance/MoveConstArgCheck.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ void MoveConstArgCheck::registerMatchers(MatchFinder *Finder) {
4444
unless(isInTemplateInstantiation()))
4545
.bind("call-move");
4646

47+
// Match ternary expressions where either branch contains std::move
48+
auto TernaryWithMoveMatcher =
49+
conditionalOperator(hasDescendant(MoveCallMatcher));
50+
4751
Finder->addMatcher(
4852
expr(anyOf(
4953
castExpr(hasSourceExpression(MoveCallMatcher)),
@@ -58,13 +62,15 @@ void MoveConstArgCheck::registerMatchers(MatchFinder *Finder) {
5862
qualType(rValueReferenceType()).bind("invocation-parm-type");
5963
// Matches respective ParmVarDecl for a CallExpr or CXXConstructExpr.
6064
auto ArgumentWithParamMatcher = forEachArgumentWithParam(
61-
MoveCallMatcher, parmVarDecl(anyOf(hasType(ConstTypeParmMatcher),
62-
hasType(RValueTypeParmMatcher)))
63-
.bind("invocation-parm"));
65+
anyOf(MoveCallMatcher, TernaryWithMoveMatcher),
66+
parmVarDecl(
67+
anyOf(hasType(ConstTypeParmMatcher), hasType(RValueTypeParmMatcher)))
68+
.bind("invocation-parm"));
6469
// Matches respective types of arguments for a CallExpr or CXXConstructExpr
6570
// and it works on calls through function pointers as well.
6671
auto ArgumentWithParamTypeMatcher = forEachArgumentWithParamType(
67-
MoveCallMatcher, anyOf(ConstTypeParmMatcher, RValueTypeParmMatcher));
72+
anyOf(MoveCallMatcher, TernaryWithMoveMatcher),
73+
anyOf(ConstTypeParmMatcher, RValueTypeParmMatcher));
6874

6975
Finder->addMatcher(
7076
invocation(anyOf(ArgumentWithParamMatcher, ArgumentWithParamTypeMatcher))

0 commit comments

Comments
 (0)