Skip to content

Commit d49a2b5

Browse files
committed
[𝘀𝗽𝗿] initial version
Created using spr 1.3.4
2 parents 9925359 + fdde8ef commit d49a2b5

17 files changed

+509
-98
lines changed

bolt/include/bolt/Core/BinaryContext.h

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,18 @@ class BinaryContext {
435435

436436
/// Return size of an entry for the given jump table \p Type.
437437
uint64_t getJumpTableEntrySize(JumpTable::JumpTableType Type) const {
438-
return Type == JumpTable::JTT_PIC ? 4 : AsmInfo->getCodePointerSize();
438+
switch (Type) {
439+
case JumpTable::JTT_X86_64_PIC4:
440+
return 4;
441+
case JumpTable::JTT_X86_64_ABS:
442+
return AsmInfo->getCodePointerSize();
443+
case JumpTable::JTT_AARCH64_REL1:
444+
return 1;
445+
case JumpTable::JTT_AARCH64_REL2:
446+
return 2;
447+
case JumpTable::JTT_AARCH64_REL4:
448+
return 4;
449+
}
439450
}
440451

441452
/// Return JumpTable containing a given \p Address.
@@ -573,14 +584,13 @@ class BinaryContext {
573584
/// If \p NextJTAddress is different from zero, it is used as an upper
574585
/// bound for jump table memory layout.
575586
///
576-
/// Optionally, populate \p Address from jump table entries. The entries
577-
/// could be partially populated if the jump table detection fails.
587+
/// If \p JT is set, populate it with jump table entries. The entries could be
588+
/// partially populated if the jump table detection fails.
578589
bool analyzeJumpTable(const uint64_t Address,
579590
const JumpTable::JumpTableType Type,
580591
const BinaryFunction &BF,
581592
const uint64_t NextJTAddress = 0,
582-
JumpTable::AddressesType *EntriesAsAddress = nullptr,
583-
bool *HasEntryInFragment = nullptr) const;
593+
JumpTable *JT = nullptr) const;
584594

585595
/// After jump table locations are established, this function will populate
586596
/// their EntriesAsAddress based on memory contents.

bolt/include/bolt/Core/JumpTable.h

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,42 @@ class BinaryFunction;
4040
/// a different label at a different offset in this jump table.
4141
class JumpTable : public BinaryData {
4242
friend class BinaryContext;
43+
friend class JumpTableInfoReader;
4344

4445
JumpTable() = delete;
4546
JumpTable(const JumpTable &) = delete;
4647
JumpTable &operator=(const JumpTable &) = delete;
4748

4849
public:
4950
enum JumpTableType : char {
50-
JTT_NORMAL,
51-
JTT_PIC,
51+
JTT_X86_64_FIRST = 0,
52+
JTT_X86_64_ABS = JTT_X86_64_FIRST,
53+
JTT_X86_64_PIC4,
54+
JTT_X86_64_LAST = JTT_X86_64_PIC4,
55+
JTT_AARCH64_FIRST,
56+
JTT_AARCH64_REL1 = JTT_AARCH64_FIRST,
57+
JTT_AARCH64_REL2,
58+
JTT_AARCH64_REL4,
59+
JTT_AARCH64_LAST = JTT_AARCH64_REL4
5260
};
5361

62+
static StringRef getTypeStr(JumpTableType Type) {
63+
switch (Type) {
64+
case JTT_X86_64_ABS:
65+
return "X86_64_ABS";
66+
case JTT_X86_64_PIC4:
67+
return "X86_64_PIC4";
68+
case JTT_AARCH64_REL1:
69+
return "AARCH64_REL1";
70+
case JTT_AARCH64_REL2:
71+
return "AARCH64_REL2";
72+
case JTT_AARCH64_REL4:
73+
return "AARCH64_REL4";
74+
}
75+
}
76+
77+
const StringRef getTypeStr() { return getTypeStr(Type); }
78+
5479
/// Branch statistics for jump table entries.
5580
struct JumpInfo {
5681
uint64_t Mispreds{0};
@@ -92,6 +117,16 @@ class JumpTable : public BinaryData {
92117
/// BinaryFunction this jump tables belongs to.
93118
SmallVector<BinaryFunction *, 1> Parents;
94119

120+
///
121+
/// AArch64-specific fields
122+
///
123+
124+
/// Entries are offsets relative to an arbitrary function location.
125+
uint64_t BaseAddress{0};
126+
127+
/// Address of the instruction referencing the jump table (MemLocInstr).
128+
uint64_t MemLocInstrAddress{0};
129+
95130
private:
96131
/// Constructor should only be called by a BinaryContext.
97132
JumpTable(MCSymbol &Symbol, uint64_t Address, size_t EntrySize,

bolt/include/bolt/Rewrite/MetadataManager.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ class MetadataManager {
3131
/// Run initializers after sections are discovered.
3232
void runSectionInitializers();
3333

34+
/// Execute metadata initializers when functions are discovered but not yet
35+
/// disassembled.
36+
void runInitializersPreDisasm();
37+
3438
/// Execute initialization of rewriters while functions are disassembled, but
3539
/// CFG is not yet built.
3640
void runInitializersPreCFG();

bolt/include/bolt/Rewrite/MetadataRewriter.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ class MetadataRewriter {
4949
/// but before functions are discovered.
5050
virtual Error sectionInitializer() { return Error::success(); }
5151

52+
/// Run initialization after the functions are identified but not yet
53+
/// disassembled.
54+
virtual Error preDisasmInitializer() { return Error::success(); }
55+
5256
/// Interface for modifying/annotating functions in the binary based on the
5357
/// contents of the section. Functions are in pre-cfg state.
5458
virtual Error preCFGInitializer() { return Error::success(); }

bolt/include/bolt/Rewrite/MetadataRewriters.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ std::unique_ptr<MetadataRewriter> createPseudoProbeRewriter(BinaryContext &);
2727

2828
std::unique_ptr<MetadataRewriter> createSDTRewriter(BinaryContext &);
2929

30+
std::unique_ptr<MetadataRewriter> createJumpTableInfoReader(BinaryContext &);
31+
3032
} // namespace bolt
3133
} // namespace llvm
3234

bolt/include/bolt/Rewrite/RewriteInstance.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,9 @@ class RewriteInstance {
181181
/// Process metadata in sections before functions are discovered.
182182
void processSectionMetadata();
183183

184+
/// Process metadata in special sections before functions are disassembled.
185+
void processMetadataPreDisasm();
186+
184187
/// Process metadata in special sections before CFG is built for functions.
185188
void processMetadataPreCFG();
186189

bolt/lib/Core/BinaryContext.cpp

Lines changed: 74 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ BinaryContext::handleAddressRef(uint64_t Address, BinaryFunction &BF,
496496
const MemoryContentsType MemType = analyzeMemoryAt(Address, BF);
497497
if (MemType == MemoryContentsType::POSSIBLE_PIC_JUMP_TABLE && IsPCRel) {
498498
const MCSymbol *Symbol =
499-
getOrCreateJumpTable(BF, Address, JumpTable::JTT_PIC);
499+
getOrCreateJumpTable(BF, Address, JumpTable::JTT_X86_64_PIC4);
500500

501501
return std::make_pair(Symbol, 0);
502502
}
@@ -540,10 +540,10 @@ MemoryContentsType BinaryContext::analyzeMemoryAt(uint64_t Address,
540540

541541
// Start with checking for PIC jump table. We expect non-PIC jump tables
542542
// to have high 32 bits set to 0.
543-
if (analyzeJumpTable(Address, JumpTable::JTT_PIC, BF))
543+
if (analyzeJumpTable(Address, JumpTable::JTT_X86_64_PIC4, BF))
544544
return MemoryContentsType::POSSIBLE_PIC_JUMP_TABLE;
545545

546-
if (analyzeJumpTable(Address, JumpTable::JTT_NORMAL, BF))
546+
if (analyzeJumpTable(Address, JumpTable::JTT_X86_64_ABS, BF))
547547
return MemoryContentsType::POSSIBLE_JUMP_TABLE;
548548

549549
return MemoryContentsType::UNKNOWN;
@@ -553,8 +553,7 @@ bool BinaryContext::analyzeJumpTable(const uint64_t Address,
553553
const JumpTable::JumpTableType Type,
554554
const BinaryFunction &BF,
555555
const uint64_t NextJTAddress,
556-
JumpTable::AddressesType *EntriesAsAddress,
557-
bool *HasEntryInFragment) const {
556+
JumpTable *JT) const {
558557
// Target address of __builtin_unreachable.
559558
const uint64_t UnreachableAddress = BF.getAddress() + BF.getSize();
560559

@@ -571,11 +570,11 @@ bool BinaryContext::analyzeJumpTable(const uint64_t Address,
571570
size_t TrimmedSize = 0;
572571

573572
auto addEntryAddress = [&](uint64_t EntryAddress, bool Unreachable = false) {
574-
if (!EntriesAsAddress)
573+
if (!JT)
575574
return;
576-
EntriesAsAddress->emplace_back(EntryAddress);
575+
JT->EntriesAsAddress.emplace_back(EntryAddress);
577576
if (!Unreachable)
578-
TrimmedSize = EntriesAsAddress->size();
577+
TrimmedSize = JT->EntriesAsAddress.size();
579578
};
580579

581580
ErrorOr<const BinarySection &> Section = getSectionForAddress(Address);
@@ -594,37 +593,48 @@ bool BinaryContext::analyzeJumpTable(const uint64_t Address,
594593
if (NextJTAddress)
595594
UpperBound = std::min(NextJTAddress, UpperBound);
596595

597-
LLVM_DEBUG({
598-
using JTT = JumpTable::JumpTableType;
599-
dbgs() << formatv("BOLT-DEBUG: analyzeJumpTable @{0:x} in {1}, JTT={2}\n",
600-
Address, BF.getPrintName(),
601-
Type == JTT::JTT_PIC ? "PIC" : "Normal");
602-
});
596+
LLVM_DEBUG(
597+
dbgs() << formatv("BOLT-DEBUG: analyzeJumpTable @{0:x} in {1}, JTT={2}\n",
598+
Address, BF, JumpTable::getTypeStr(Type)));
603599
const uint64_t EntrySize = getJumpTableEntrySize(Type);
604600
for (uint64_t EntryAddress = Address; EntryAddress <= UpperBound - EntrySize;
605601
EntryAddress += EntrySize) {
606602
LLVM_DEBUG(dbgs() << " * Checking 0x" << Twine::utohexstr(EntryAddress)
607603
<< " -> ");
608604
// Check if there's a proper relocation against the jump table entry.
609605
if (HasRelocations) {
610-
if (Type == JumpTable::JTT_PIC &&
606+
if (Type == JumpTable::JTT_X86_64_PIC4 &&
611607
!DataPCRelocations.count(EntryAddress)) {
612608
LLVM_DEBUG(
613609
dbgs() << "FAIL: JTT_PIC table, no relocation for this address\n");
614610
break;
615611
}
616-
if (Type == JumpTable::JTT_NORMAL && !getRelocationAt(EntryAddress)) {
612+
if (Type == JumpTable::JTT_X86_64_ABS && !getRelocationAt(EntryAddress)) {
617613
LLVM_DEBUG(
618614
dbgs()
619615
<< "FAIL: JTT_NORMAL table, no relocation for this address\n");
620616
break;
621617
}
622618
}
623619

624-
const uint64_t Value =
625-
(Type == JumpTable::JTT_PIC)
626-
? Address + *getSignedValueAtAddress(EntryAddress, EntrySize)
627-
: *getPointerAtAddress(EntryAddress);
620+
uint64_t Value = 0;
621+
switch (Type) {
622+
case JumpTable::JTT_X86_64_PIC4:
623+
Value = Address + *getSignedValueAtAddress(EntryAddress, EntrySize);
624+
break;
625+
case JumpTable::JTT_X86_64_ABS:
626+
Value = *getPointerAtAddress(EntryAddress);
627+
break;
628+
case JumpTable::JTT_AARCH64_REL1:
629+
case JumpTable::JTT_AARCH64_REL2:
630+
case JumpTable::JTT_AARCH64_REL4:
631+
unsigned ShiftAmt = Type == JumpTable::JTT_AARCH64_REL4 ? 0 : 2;
632+
assert(JT &&
633+
"jump table must be non-null for AArch64 in analyzeJumpTable");
634+
Value = JT->BaseAddress +
635+
(*getUnsignedValueAtAddress(EntryAddress, EntrySize) << ShiftAmt);
636+
break;
637+
}
628638

629639
// __builtin_unreachable() case.
630640
if (Value == UnreachableAddress) {
@@ -645,24 +655,19 @@ bool BinaryContext::analyzeJumpTable(const uint64_t Address,
645655

646656
// Function or one of its fragments.
647657
const BinaryFunction *TargetBF = getBinaryFunctionContainingAddress(Value);
648-
const bool DoesBelongToFunction =
649-
BF.containsAddress(Value) ||
650-
(TargetBF && areRelatedFragments(TargetBF, &BF));
651-
if (!DoesBelongToFunction) {
658+
if (!TargetBF || !areRelatedFragments(TargetBF, &BF)) {
652659
LLVM_DEBUG({
653-
if (!BF.containsAddress(Value)) {
654-
dbgs() << "FAIL: function doesn't contain this address\n";
655-
if (TargetBF) {
656-
dbgs() << " ! function containing this address: "
657-
<< TargetBF->getPrintName() << '\n';
658-
if (TargetBF->isFragment()) {
659-
dbgs() << " ! is a fragment";
660-
for (BinaryFunction *Parent : TargetBF->ParentFragments)
661-
dbgs() << ", parent: " << Parent->getPrintName();
662-
dbgs() << '\n';
663-
}
664-
}
665-
}
660+
dbgs() << "FAIL: function doesn't contain this address\n";
661+
if (!TargetBF)
662+
break;
663+
dbgs() << " ! function containing this address: " << *TargetBF << '\n';
664+
if (!TargetBF->isFragment())
665+
break;
666+
dbgs() << " ! is a fragment with parents: ";
667+
ListSeparator LS;
668+
for (BinaryFunction *Parent : TargetBF->ParentFragments)
669+
dbgs() << LS << *Parent;
670+
dbgs() << '\n';
666671
});
667672
break;
668673
}
@@ -677,17 +682,17 @@ bool BinaryContext::analyzeJumpTable(const uint64_t Address,
677682
++NumRealEntries;
678683
LLVM_DEBUG(dbgs() << formatv("OK: {0:x} real entry\n", Value));
679684

680-
if (TargetBF != &BF && HasEntryInFragment)
681-
*HasEntryInFragment = true;
685+
if (TargetBF != &BF && JT)
686+
JT->IsSplit = true;
682687
addEntryAddress(Value);
683688
}
684689

685690
// Trim direct/normal jump table to exclude trailing unreachable entries that
686691
// can collide with a function address.
687-
if (Type == JumpTable::JTT_NORMAL && EntriesAsAddress &&
688-
TrimmedSize != EntriesAsAddress->size() &&
692+
if (Type == JumpTable::JTT_X86_64_ABS && JT &&
693+
TrimmedSize != JT->EntriesAsAddress.size() &&
689694
getBinaryFunctionAtAddress(UnreachableAddress))
690-
EntriesAsAddress->resize(TrimmedSize);
695+
JT->EntriesAsAddress.resize(TrimmedSize);
691696

692697
// It's a jump table if the number of real entries is more than 1, or there's
693698
// one real entry and one or more special targets. If there are only multiple
@@ -702,20 +707,20 @@ void BinaryContext::populateJumpTables() {
702707
++JTI) {
703708
JumpTable *JT = JTI->second;
704709

705-
bool NonSimpleParent = false;
706-
for (BinaryFunction *BF : JT->Parents)
707-
NonSimpleParent |= !BF->isSimple();
708-
if (NonSimpleParent)
710+
auto isSimple = std::bind(&BinaryFunction::isSimple, std::placeholders::_1);
711+
if (!llvm::all_of(JT->Parents, isSimple))
709712
continue;
710713

711714
uint64_t NextJTAddress = 0;
712715
auto NextJTI = std::next(JTI);
713-
if (NextJTI != JTE)
716+
if (isAArch64()) {
717+
NextJTAddress = JT->getAddress() + JT->getSize();
718+
JT->Entries.clear();
719+
} else if (NextJTI != JTE)
714720
NextJTAddress = NextJTI->second->getAddress();
715721

716-
const bool Success =
717-
analyzeJumpTable(JT->getAddress(), JT->Type, *(JT->Parents[0]),
718-
NextJTAddress, &JT->EntriesAsAddress, &JT->IsSplit);
722+
const bool Success = analyzeJumpTable(
723+
JT->getAddress(), JT->Type, *JT->Parents.front(), NextJTAddress, JT);
719724
if (!Success) {
720725
LLVM_DEBUG({
721726
dbgs() << "failed to analyze ";
@@ -743,7 +748,7 @@ void BinaryContext::populateJumpTables() {
743748

744749
// In strict mode, erase PC-relative relocation record. Later we check that
745750
// all such records are erased and thus have been accounted for.
746-
if (opts::StrictMode && JT->Type == JumpTable::JTT_PIC) {
751+
if (opts::StrictMode && JT->Type == JumpTable::JTT_X86_64_PIC4) {
747752
for (uint64_t Address = JT->getAddress();
748753
Address < JT->getAddress() + JT->getSize();
749754
Address += JT->EntrySize) {
@@ -839,33 +844,26 @@ BinaryContext::getOrCreateJumpTable(BinaryFunction &Function, uint64_t Address,
839844
assert(JT->Type == Type && "jump table types have to match");
840845
assert(Address == JT->getAddress() && "unexpected non-empty jump table");
841846

842-
// Prevent associating a jump table to a specific fragment twice.
843-
if (!llvm::is_contained(JT->Parents, &Function)) {
844-
assert(llvm::all_of(JT->Parents,
845-
[&](const BinaryFunction *BF) {
846-
return areRelatedFragments(&Function, BF);
847-
}) &&
848-
"cannot re-use jump table of a different function");
849-
// Duplicate the entry for the parent function for easy access
850-
JT->Parents.push_back(&Function);
851-
if (opts::Verbosity > 2) {
852-
this->outs() << "BOLT-INFO: Multiple fragments access same jump table: "
853-
<< JT->Parents[0]->getPrintName() << "; "
854-
<< Function.getPrintName() << "\n";
855-
JT->print(this->outs());
856-
}
857-
Function.JumpTables.emplace(Address, JT);
858-
for (BinaryFunction *Parent : JT->Parents)
859-
Parent->setHasIndirectTargetToSplitFragment(true);
860-
}
847+
if (llvm::is_contained(JT->Parents, &Function))
848+
return JT->getFirstLabel();
861849

862-
bool IsJumpTableParent = false;
863-
(void)IsJumpTableParent;
864-
for (BinaryFunction *Frag : JT->Parents)
865-
if (Frag == &Function)
866-
IsJumpTableParent = true;
867-
assert(IsJumpTableParent &&
850+
// Prevent associating a jump table to a specific fragment twice.
851+
auto isSibling = std::bind(&BinaryContext::areRelatedFragments, this,
852+
&Function, std::placeholders::_1);
853+
assert(llvm::all_of(JT->Parents, isSibling) &&
868854
"cannot re-use jump table of a different function");
855+
if (opts::Verbosity > 2) {
856+
this->outs() << "BOLT-INFO: Multiple fragments access same jump table: "
857+
<< JT->Parents[0]->getPrintName() << "; "
858+
<< Function.getPrintName() << "\n";
859+
JT->print(this->outs());
860+
}
861+
if (JT->Parents.size() == 1)
862+
JT->Parents.front()->setHasIndirectTargetToSplitFragment(true);
863+
Function.setHasIndirectTargetToSplitFragment(true);
864+
// Duplicate the entry for the parent function for easy access
865+
JT->Parents.push_back(&Function);
866+
Function.JumpTables.emplace(Address, JT);
869867
return JT->getFirstLabel();
870868
}
871869

0 commit comments

Comments
 (0)