Skip to content

Commit 2516b0a

Browse files
rafaelaulermemfrob
authored andcommitted
[BOLT-AArch64] Support relocation mode for bzip2
Summary: As we deal with incomplete addresses in address-computing sequences of code in AArch64, we found it is easier to handle them in relocation mode in the presence of relocations. Incomplete addresses may mislead BOLT into thinking there are instructions referring to a basic block when, in fact, this may be the base address of a data reference. If the relocation is present, we can easily spot such cases. This diff contains extensions in relocation mode to understand and deal with AArch64 relocations. It also adds code to process data inside functions as marked by AArch64 ABI (symbol table entries named "$d"). In our code, this is called constant islands handling. Last, it extends bughunter with a "cross" mode, in which the host generates the binaries and the user test them (uploading to the target), useful when debugging in AArch64. (cherry picked from FBD6024570)
1 parent 8d18278 commit 2516b0a

File tree

8 files changed

+488
-66
lines changed

8 files changed

+488
-66
lines changed

bolt/BinaryContext.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,13 +489,71 @@ size_t Relocation::getSizeForType(uint64_t Type) {
489489
case ELF::R_X86_64_TPOFF32:
490490
case ELF::R_X86_64_GOTPCRELX:
491491
case ELF::R_X86_64_REX_GOTPCRELX:
492+
case ELF::R_AARCH64_CALL26:
493+
case ELF::R_AARCH64_ADR_PREL_PG_HI21:
494+
case ELF::R_AARCH64_LDST64_ABS_LO12_NC:
495+
case ELF::R_AARCH64_ADD_ABS_LO12_NC:
496+
case ELF::R_AARCH64_LDST32_ABS_LO12_NC:
497+
case ELF::R_AARCH64_LDST8_ABS_LO12_NC:
498+
case ELF::R_AARCH64_ADR_GOT_PAGE:
499+
case ELF::R_AARCH64_LD64_GOT_LO12_NC:
500+
case ELF::R_AARCH64_JUMP26:
492501
return 4;
493502
case ELF::R_X86_64_PC64:
494503
case ELF::R_X86_64_64:
504+
case ELF::R_AARCH64_ABS64:
495505
return 8;
496506
}
497507
}
498508

509+
uint64_t Relocation::extractValue(uint64_t Type, uint64_t Contents) {
510+
switch (Type) {
511+
default:
512+
llvm_unreachable("unsupported relocation type");
513+
case ELF::R_AARCH64_ABS64:
514+
return Contents;
515+
case ELF::R_AARCH64_JUMP26:
516+
case ELF::R_AARCH64_CALL26:
517+
// Immediate goes in bits 25:0 of B and BL.
518+
Contents &= ~0xfffffffffc000000ULL;
519+
return SignExtend64<28>(Contents << 2);
520+
case ELF::R_AARCH64_ADR_GOT_PAGE:
521+
case ELF::R_AARCH64_ADR_PREL_PG_HI21: {
522+
// Bits 32:12 of Symbol address goes in bits 30:29 + 23:5 of ADRP
523+
// instruction
524+
Contents &= ~0xffffffff9f00001fU;
525+
auto LowBits = (Contents >> 29) & 0x3;
526+
auto HighBits = (Contents >> 5) & 0x7ffff;
527+
Contents = LowBits | (HighBits << 2);
528+
return SignExtend64<32>(Contents << 12);
529+
}
530+
case ELF::R_AARCH64_LD64_GOT_LO12_NC:
531+
case ELF::R_AARCH64_LDST64_ABS_LO12_NC: {
532+
// Immediate goes in bits 21:10 of LD/ST instruction, taken
533+
// from bits 11:3 of Symbol address
534+
Contents &= ~0xffffffffffc003ffU;
535+
return Contents >> (10 - 3);
536+
}
537+
case ELF::R_AARCH64_ADD_ABS_LO12_NC: {
538+
// Immediate goes in bits 21:10 of ADD instruction
539+
Contents &= ~0xffffffffffc003ffU;
540+
return Contents >> (10 - 0);
541+
}
542+
case ELF::R_AARCH64_LDST32_ABS_LO12_NC: {
543+
// Immediate goes in bits 21:10 of ADD instruction, taken
544+
// from bits 11:2 of Symbol address
545+
Contents &= ~0xffffffffffc003ffU;
546+
return Contents >> (10 - 2);
547+
}
548+
case ELF::R_AARCH64_LDST8_ABS_LO12_NC: {
549+
// Immediate goes in bits 21:10 of ADD instruction, taken
550+
// from bits 11:0 of Symbol address
551+
Contents &= ~0xffffffffffc003ffU;
552+
return Contents >> (10 - 0);
553+
}
554+
}
555+
}
556+
499557
bool Relocation::isPCRelative(uint64_t Type) {
500558
switch (Type) {
501559
default:
@@ -505,6 +563,12 @@ bool Relocation::isPCRelative(uint64_t Type) {
505563
case ELF::R_X86_64_32:
506564
case ELF::R_X86_64_32S:
507565
case ELF::R_X86_64_TPOFF32:
566+
case ELF::R_AARCH64_ABS64:
567+
case ELF::R_AARCH64_LDST64_ABS_LO12_NC:
568+
case ELF::R_AARCH64_ADD_ABS_LO12_NC:
569+
case ELF::R_AARCH64_LDST32_ABS_LO12_NC:
570+
case ELF::R_AARCH64_LDST8_ABS_LO12_NC:
571+
case ELF::R_AARCH64_LD64_GOT_LO12_NC:
508572
return false;
509573

510574
case ELF::R_X86_64_PC8:
@@ -514,6 +578,10 @@ bool Relocation::isPCRelative(uint64_t Type) {
514578
case ELF::R_X86_64_GOTTPOFF:
515579
case ELF::R_X86_64_GOTPCRELX:
516580
case ELF::R_X86_64_REX_GOTPCRELX:
581+
case ELF::R_AARCH64_CALL26:
582+
case ELF::R_AARCH64_ADR_PREL_PG_HI21:
583+
case ELF::R_AARCH64_ADR_GOT_PAGE:
584+
case ELF::R_AARCH64_JUMP26:
517585
return true;
518586
}
519587
}

bolt/BinaryContext.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ struct Relocation {
6464
/// Return size of the given relocation \p Type.
6565
static size_t getSizeForType(uint64_t Type);
6666

67+
/// Extract current relocated value from binary contents. This is used for
68+
/// RISC architectures where values are encoded in specific bits depending
69+
/// on the relocation value.
70+
static uint64_t extractValue(uint64_t Type, uint64_t Contents);
71+
6772
/// Return true if relocation type is PC-relative. Return false otherwise.
6873
static bool isPCRelative(uint64_t Type);
6974

@@ -154,6 +159,11 @@ class BinaryContext {
154159
/// final addresses functions will have.
155160
uint64_t LayoutStartAddress{0};
156161

162+
/// Old .text info.
163+
uint64_t OldTextSectionAddress{0};
164+
uint64_t OldTextSectionOffset{0};
165+
uint64_t OldTextSectionSize{0};
166+
157167
/// True if the binary requires immediate relocation processing.
158168
bool RequiresZNow{false};
159169

bolt/BinaryFunction.cpp

Lines changed: 122 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -795,10 +795,21 @@ void BinaryFunction::disassemble(ArrayRef<uint8_t> FunctionData) {
795795
}
796796
}
797797

798+
if (BC.TheTriple->getArch() == llvm::Triple::aarch64 &&
799+
isInConstantIsland(TargetAddress)) {
800+
TargetSymbol = BC.getOrCreateGlobalSymbol(TargetAddress, "ISLANDat");
801+
IslandSymbols[TargetAddress - getAddress()] = TargetSymbol;
802+
}
803+
798804
// Note that the address does not necessarily have to reside inside
799805
// a section, it could be an absolute address too.
800806
auto Section = BC.getSectionForAddress(TargetAddress);
801-
if (Section && Section->isText()) {
807+
// Assume AArch64's ADRP never references code - it does, but this is fixed
808+
// after reading relocations. ADRP contents now are not really meaningful
809+
// without its supporting relocation.
810+
if (!TargetSymbol && Section && Section->isText() &&
811+
(BC.TheTriple->getArch() != llvm::Triple::aarch64 ||
812+
!BC.MIA->isADRP(Instruction))) {
802813
if (containsAddress(TargetAddress)) {
803814
if (TargetAddress != getAddress()) {
804815
// The address could potentially escape. Mark it as another entry
@@ -829,6 +840,16 @@ void BinaryFunction::disassemble(ArrayRef<uint8_t> FunctionData) {
829840
MCInst Instruction;
830841
const uint64_t AbsoluteInstrAddr = getAddress() + Offset;
831842

843+
// Check for data inside code and ignore it
844+
if (DataOffsets.find(Offset) != DataOffsets.end()) {
845+
auto Iter = CodeOffsets.upper_bound(Offset);
846+
if (Iter != CodeOffsets.end()) {
847+
Size = *Iter - Offset;
848+
continue;
849+
}
850+
break;
851+
}
852+
832853
if (!BC.DisAsm->getInstruction(Instruction,
833854
Size,
834855
FunctionData.slice(Offset),
@@ -985,10 +1006,16 @@ void BinaryFunction::disassemble(ArrayRef<uint8_t> FunctionData) {
9851006
// code without re-assembly.
9861007
size_t RelSize = (Size < 5) ? 1 : 4;
9871008
auto RelOffset = Offset + Size - RelSize;
1009+
if (BC.TheTriple->getArch() == llvm::Triple::aarch64) {
1010+
RelSize = 0;
1011+
RelOffset = Offset;
1012+
}
9881013
auto RI = MoveRelocations.find(RelOffset);
9891014
if (RI == MoveRelocations.end()) {
9901015
uint64_t RelType = (RelSize == 1) ? ELF::R_X86_64_PC8
9911016
: ELF::R_X86_64_PC32;
1017+
if (BC.TheTriple->getArch() == llvm::Triple::aarch64)
1018+
RelType = ELF::R_AARCH64_CALL26;
9921019
DEBUG(dbgs() << "BOLT-DEBUG: creating relocation for static"
9931020
<< " function call to " << TargetSymbol->getName()
9941021
<< " at offset 0x"
@@ -2485,6 +2512,9 @@ void BinaryFunction::emitBody(MCStreamer &Streamer, bool EmitColdPart) {
24852512
LastIsPrefix = BC.MIA->isPrefix(Instr);
24862513
}
24872514
}
2515+
2516+
if (!EmitColdPart)
2517+
emitConstantIslands(Streamer);
24882518
}
24892519

24902520
void BinaryFunction::emitBodyRaw(MCStreamer *Streamer) {
@@ -2545,6 +2575,70 @@ void BinaryFunction::emitBodyRaw(MCStreamer *Streamer) {
25452575
}
25462576
}
25472577

2578+
void BinaryFunction::emitConstantIslands(MCStreamer &Streamer) {
2579+
if (DataOffsets.empty())
2580+
return;
2581+
2582+
Streamer.EmitLabel(getFunctionConstantIslandLabel());
2583+
// Raw contents of the function.
2584+
StringRef SectionContents;
2585+
Section.getContents(SectionContents);
2586+
2587+
// Raw contents of the function.
2588+
StringRef FunctionContents =
2589+
SectionContents.substr(getAddress() - Section.getAddress(),
2590+
getMaxSize());
2591+
2592+
if (opts::Verbosity)
2593+
outs() << "BOLT-INFO: emitting constant island for function " << *this
2594+
<< "\n";
2595+
2596+
auto IS = IslandSymbols.begin();
2597+
2598+
// We split the island into smaller blocks and output labels between them.
2599+
for (auto DataIter = DataOffsets.begin(); DataIter != DataOffsets.end();
2600+
++DataIter) {
2601+
uint64_t FunctionOffset = *DataIter;
2602+
uint64_t EndOffset = 0ULL;
2603+
2604+
// Determine size of this data chunk
2605+
auto NextData = std::next(DataIter);
2606+
auto CodeIter = CodeOffsets.lower_bound(*DataIter);
2607+
if (CodeIter == CodeOffsets.end() && NextData == DataOffsets.end()) {
2608+
EndOffset = getMaxSize();
2609+
} else if (CodeIter == CodeOffsets.end()) {
2610+
EndOffset = *NextData;
2611+
} else if (NextData == DataOffsets.end()) {
2612+
EndOffset = *CodeIter;
2613+
} else {
2614+
EndOffset = (*CodeIter > *NextData) ? *NextData : *CodeIter;
2615+
}
2616+
2617+
if (FunctionOffset == EndOffset)
2618+
continue; // Size is zero, nothing to emit
2619+
2620+
// Emit labels and data
2621+
while (IS != IslandSymbols.end() && IS->first < EndOffset) {
2622+
auto NextStop = IS->first;
2623+
assert(NextStop <= EndOffset && "internal overflow error");
2624+
if (FunctionOffset < NextStop) {
2625+
Streamer.EmitBytes(FunctionContents.slice(FunctionOffset, NextStop));
2626+
FunctionOffset = NextStop;
2627+
}
2628+
DEBUG(dbgs() << "BOLT-DEBUG: emitted label " << IS->second->getName()
2629+
<< " at offset 0x" << Twine::utohexstr(IS->first) << '\n');
2630+
Streamer.EmitLabel(IS->second);
2631+
++IS;
2632+
}
2633+
assert(FunctionOffset <= EndOffset && "overflow error");
2634+
if (FunctionOffset < EndOffset) {
2635+
Streamer.EmitBytes(FunctionContents.slice(FunctionOffset, EndOffset));
2636+
}
2637+
}
2638+
2639+
assert(IS == IslandSymbols.end() && "some symbols were not emitted!");
2640+
}
2641+
25482642
namespace {
25492643

25502644
#ifndef MAX_PATH
@@ -3334,10 +3428,37 @@ BinaryBasicBlock *BinaryFunction::splitEdge(BinaryBasicBlock *From,
33343428
return NewBBPtr;
33353429
}
33363430

3431+
bool BinaryFunction::isDataMarker(const SymbolRef &Symbol,
3432+
uint64_t SymbolSize) const {
3433+
// For aarch64, the ABI defines mapping symbols so we identify data in the
3434+
// code section (see IHI0056B). $d identifies a symbol starting data contents.
3435+
if (BC.TheTriple->getArch() == llvm::Triple::aarch64 &&
3436+
Symbol.getType() == SymbolRef::ST_Unknown &&
3437+
SymbolSize == 0 &&
3438+
(!Symbol.getName().getError() && *Symbol.getName() == "$d"))
3439+
return true;
3440+
return false;
3441+
}
3442+
3443+
bool BinaryFunction::isCodeMarker(const SymbolRef &Symbol,
3444+
uint64_t SymbolSize) const {
3445+
// For aarch64, the ABI defines mapping symbols so we identify data in the
3446+
// code section (see IHI0056B). $x identifies a symbol starting code or the
3447+
// end of a data chunk inside code.
3448+
if (BC.TheTriple->getArch() == llvm::Triple::aarch64 &&
3449+
Symbol.getType() == SymbolRef::ST_Unknown &&
3450+
SymbolSize == 0 &&
3451+
(!Symbol.getName().getError() && *Symbol.getName() == "$x"))
3452+
return true;
3453+
return false;
3454+
}
3455+
33373456
bool BinaryFunction::isSymbolValidInScope(const SymbolRef &Symbol,
33383457
uint64_t SymbolSize) const {
33393458
// Some symbols are tolerated inside function bodies, others are not.
33403459
// The real function boundaries may not be known at this point.
3460+
if (isDataMarker(Symbol, SymbolSize) || isCodeMarker(Symbol, SymbolSize))
3461+
return true;
33413462

33423463
// It's okay to have a zero-sized symbol in the middle of non-zero-sized
33433464
// function.

0 commit comments

Comments
 (0)