Skip to content

Commit 6eea20e

Browse files
committed
[𝘀𝗽𝗿] initial version
Created using spr 1.3.4
2 parents 9925359 + 99952ea commit 6eea20e

File tree

8 files changed

+119
-96
lines changed

8 files changed

+119
-96
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: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,34 @@ class JumpTable : public BinaryData {
4747

4848
public:
4949
enum JumpTableType : char {
50-
JTT_NORMAL,
51-
JTT_PIC,
50+
JTT_X86_64_FIRST = 0,
51+
JTT_X86_64_ABS = JTT_X86_64_FIRST,
52+
JTT_X86_64_PIC4,
53+
JTT_X86_64_LAST = JTT_X86_64_PIC4,
54+
JTT_AARCH64_FIRST,
55+
JTT_AARCH64_REL1 = JTT_AARCH64_FIRST,
56+
JTT_AARCH64_REL2,
57+
JTT_AARCH64_REL4,
58+
JTT_AARCH64_LAST = JTT_AARCH64_REL4
5259
};
5360

61+
static StringRef getTypeStr(JumpTableType Type) {
62+
switch (Type) {
63+
case JTT_X86_64_ABS:
64+
return "X86_64_ABS";
65+
case JTT_X86_64_PIC4:
66+
return "X86_64_PIC4";
67+
case JTT_AARCH64_REL1:
68+
return "AARCH64_REL1";
69+
case JTT_AARCH64_REL2:
70+
return "AARCH64_REL2";
71+
case JTT_AARCH64_REL4:
72+
return "AARCH64_REL4";
73+
}
74+
}
75+
76+
const StringRef getTypeStr() { return getTypeStr(Type); }
77+
5478
/// Branch statistics for jump table entries.
5579
struct JumpInfo {
5680
uint64_t Mispreds{0};

bolt/lib/Core/BinaryContext.cpp

Lines changed: 61 additions & 75 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,39 @@ 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+
}
628629

629630
// __builtin_unreachable() case.
630631
if (Value == UnreachableAddress) {
@@ -645,24 +646,19 @@ bool BinaryContext::analyzeJumpTable(const uint64_t Address,
645646

646647
// Function or one of its fragments.
647648
const BinaryFunction *TargetBF = getBinaryFunctionContainingAddress(Value);
648-
const bool DoesBelongToFunction =
649-
BF.containsAddress(Value) ||
650-
(TargetBF && areRelatedFragments(TargetBF, &BF));
651-
if (!DoesBelongToFunction) {
649+
if (!TargetBF || !areRelatedFragments(TargetBF, &BF)) {
652650
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-
}
651+
dbgs() << "FAIL: function doesn't contain this address\n";
652+
if (!TargetBF)
653+
break;
654+
dbgs() << " ! function containing this address: " << *TargetBF << '\n';
655+
if (!TargetBF->isFragment())
656+
break;
657+
dbgs() << " ! is a fragment with parents: ";
658+
ListSeparator LS;
659+
for (BinaryFunction *Parent : TargetBF->ParentFragments)
660+
dbgs() << LS << *Parent;
661+
dbgs() << '\n';
666662
});
667663
break;
668664
}
@@ -677,17 +673,17 @@ bool BinaryContext::analyzeJumpTable(const uint64_t Address,
677673
++NumRealEntries;
678674
LLVM_DEBUG(dbgs() << formatv("OK: {0:x} real entry\n", Value));
679675

680-
if (TargetBF != &BF && HasEntryInFragment)
681-
*HasEntryInFragment = true;
676+
if (TargetBF != &BF && JT)
677+
JT->IsSplit = true;
682678
addEntryAddress(Value);
683679
}
684680

685681
// Trim direct/normal jump table to exclude trailing unreachable entries that
686682
// can collide with a function address.
687-
if (Type == JumpTable::JTT_NORMAL && EntriesAsAddress &&
688-
TrimmedSize != EntriesAsAddress->size() &&
683+
if (Type == JumpTable::JTT_X86_64_ABS && JT &&
684+
TrimmedSize != JT->EntriesAsAddress.size() &&
689685
getBinaryFunctionAtAddress(UnreachableAddress))
690-
EntriesAsAddress->resize(TrimmedSize);
686+
JT->EntriesAsAddress.resize(TrimmedSize);
691687

692688
// It's a jump table if the number of real entries is more than 1, or there's
693689
// one real entry and one or more special targets. If there are only multiple
@@ -702,20 +698,17 @@ void BinaryContext::populateJumpTables() {
702698
++JTI) {
703699
JumpTable *JT = JTI->second;
704700

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

711705
uint64_t NextJTAddress = 0;
712706
auto NextJTI = std::next(JTI);
713707
if (NextJTI != JTE)
714708
NextJTAddress = NextJTI->second->getAddress();
715709

716-
const bool Success =
717-
analyzeJumpTable(JT->getAddress(), JT->Type, *(JT->Parents[0]),
718-
NextJTAddress, &JT->EntriesAsAddress, &JT->IsSplit);
710+
const bool Success = analyzeJumpTable(
711+
JT->getAddress(), JT->Type, *JT->Parents.front(), NextJTAddress, JT);
719712
if (!Success) {
720713
LLVM_DEBUG({
721714
dbgs() << "failed to analyze ";
@@ -743,7 +736,7 @@ void BinaryContext::populateJumpTables() {
743736

744737
// In strict mode, erase PC-relative relocation record. Later we check that
745738
// all such records are erased and thus have been accounted for.
746-
if (opts::StrictMode && JT->Type == JumpTable::JTT_PIC) {
739+
if (opts::StrictMode && JT->Type == JumpTable::JTT_X86_64_PIC4) {
747740
for (uint64_t Address = JT->getAddress();
748741
Address < JT->getAddress() + JT->getSize();
749742
Address += JT->EntrySize) {
@@ -839,33 +832,26 @@ BinaryContext::getOrCreateJumpTable(BinaryFunction &Function, uint64_t Address,
839832
assert(JT->Type == Type && "jump table types have to match");
840833
assert(Address == JT->getAddress() && "unexpected non-empty jump table");
841834

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-
}
835+
if (llvm::is_contained(JT->Parents, &Function))
836+
return JT->getFirstLabel();
861837

862-
bool IsJumpTableParent = false;
863-
(void)IsJumpTableParent;
864-
for (BinaryFunction *Frag : JT->Parents)
865-
if (Frag == &Function)
866-
IsJumpTableParent = true;
867-
assert(IsJumpTableParent &&
838+
// Prevent associating a jump table to a specific fragment twice.
839+
auto isSibling = std::bind(&BinaryContext::areRelatedFragments, this,
840+
&Function, std::placeholders::_1);
841+
assert(llvm::all_of(JT->Parents, isSibling) &&
868842
"cannot re-use jump table of a different function");
843+
if (opts::Verbosity > 2) {
844+
this->outs() << "BOLT-INFO: Multiple fragments access same jump table: "
845+
<< JT->Parents[0]->getPrintName() << "; "
846+
<< Function.getPrintName() << "\n";
847+
JT->print(this->outs());
848+
}
849+
if (JT->Parents.size() == 1)
850+
JT->Parents.front()->setHasIndirectTargetToSplitFragment(true);
851+
Function.setHasIndirectTargetToSplitFragment(true);
852+
// Duplicate the entry for the parent function for easy access
853+
JT->Parents.push_back(&Function);
854+
Function.JumpTables.emplace(Address, JT);
869855
return JT->getFirstLabel();
870856
}
871857

bolt/lib/Core/BinaryEmitter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -850,7 +850,7 @@ void BinaryEmitter::emitJumpTable(const JumpTable &JT, MCSection *HotSection,
850850
}
851851
LastLabel = LI->second;
852852
}
853-
if (JT.Type == JumpTable::JTT_NORMAL) {
853+
if (JT.Type == JumpTable::JTT_X86_64_ABS) {
854854
Streamer.emitSymbolValue(Entry, JT.OutputEntrySize);
855855
} else { // JTT_PIC
856856
const MCSymbolRefExpr *JTExpr =

bolt/lib/Core/BinaryFunction.cpp

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -912,7 +912,7 @@ BinaryFunction::processIndirectBranch(MCInst &Instruction, unsigned Size,
912912
"Invalid memory instruction");
913913
const MCExpr *FixedEntryDispExpr = FixedEntryDispOperand->getExpr();
914914
const uint64_t EntryAddress = getExprValue(FixedEntryDispExpr);
915-
uint64_t EntrySize = BC.getJumpTableEntrySize(JumpTable::JTT_PIC);
915+
uint64_t EntrySize = BC.getJumpTableEntrySize(JumpTable::JTT_X86_64_PIC4);
916916
ErrorOr<int64_t> Value =
917917
BC.getSignedValueAtAddress(EntryAddress, EntrySize);
918918
if (!Value)
@@ -982,12 +982,14 @@ BinaryFunction::processIndirectBranch(MCInst &Instruction, unsigned Size,
982982
MemoryContentsType MemType;
983983
if (JumpTable *JT = BC.getJumpTableContainingAddress(ArrayStart)) {
984984
switch (JT->Type) {
985-
case JumpTable::JTT_NORMAL:
985+
case JumpTable::JTT_X86_64_ABS:
986986
MemType = MemoryContentsType::POSSIBLE_JUMP_TABLE;
987987
break;
988-
case JumpTable::JTT_PIC:
988+
case JumpTable::JTT_X86_64_PIC4:
989989
MemType = MemoryContentsType::POSSIBLE_PIC_JUMP_TABLE;
990990
break;
991+
default:
992+
llvm_unreachable("Unhandled jump table type");
991993
}
992994
} else {
993995
MemType = BC.analyzeMemoryAt(ArrayStart, *this);
@@ -998,7 +1000,7 @@ BinaryFunction::processIndirectBranch(MCInst &Instruction, unsigned Size,
9981000
if (BranchType == IndirectBranchType::POSSIBLE_PIC_JUMP_TABLE) {
9991001
if (MemType != MemoryContentsType::POSSIBLE_PIC_JUMP_TABLE)
10001002
return IndirectBranchType::UNKNOWN;
1001-
JTType = JumpTable::JTT_PIC;
1003+
JTType = JumpTable::JTT_X86_64_PIC4;
10021004
} else {
10031005
if (MemType == MemoryContentsType::POSSIBLE_PIC_JUMP_TABLE)
10041006
return IndirectBranchType::UNKNOWN;
@@ -1007,7 +1009,7 @@ BinaryFunction::processIndirectBranch(MCInst &Instruction, unsigned Size,
10071009
return IndirectBranchType::POSSIBLE_TAIL_CALL;
10081010

10091011
BranchType = IndirectBranchType::POSSIBLE_JUMP_TABLE;
1010-
JTType = JumpTable::JTT_NORMAL;
1012+
JTType = JumpTable::JTT_X86_64_ABS;
10111013
}
10121014

10131015
// Convert the instruction into jump table branch.
@@ -1779,7 +1781,8 @@ void BinaryFunction::postProcessJumpTables() {
17791781
// Create labels for all entries.
17801782
for (auto &JTI : JumpTables) {
17811783
JumpTable &JT = *JTI.second;
1782-
if (JT.Type == JumpTable::JTT_PIC && opts::JumpTables == JTS_BASIC) {
1784+
if (JT.Type == JumpTable::JTT_X86_64_PIC4 &&
1785+
opts::JumpTables == JTS_BASIC) {
17831786
opts::JumpTables = JTS_MOVE;
17841787
BC.outs() << "BOLT-INFO: forcing -jump-tables=move as PIC jump table was "
17851788
"detected in function "
@@ -1974,7 +1977,7 @@ bool BinaryFunction::postProcessIndirectBranches(
19741977
BC.MIB->unsetJumpTable(Instr);
19751978

19761979
JumpTable *JT = BC.getJumpTableContainingAddress(LastJT);
1977-
if (JT->Type == JumpTable::JTT_NORMAL) {
1980+
if (JT->Type == JumpTable::JTT_X86_64_ABS) {
19781981
// Invalidating the jump table may also invalidate other jump table
19791982
// boundaries. Until we have/need a support for this, mark the
19801983
// function as non-simple.

0 commit comments

Comments
 (0)