Skip to content

Commit be89e79

Browse files
authored
[BOLT][AArch64] Add support for long absolute LLD thunks/veneers (#113408)
Absolute thunks generated by LLD reference function addresses recorded as data in code. Since they are generated by the linker, they don't have relocations associated with them and thus the addresses are left undetected. Use pattern matching to detect such thunks and handle them in VeneerElimination pass.
1 parent 3ab5927 commit be89e79

File tree

4 files changed

+155
-16
lines changed

4 files changed

+155
-16
lines changed

bolt/include/bolt/Core/MCPlusBuilder.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1536,6 +1536,14 @@ class MCPlusBuilder {
15361536
llvm_unreachable("not implemented");
15371537
}
15381538

1539+
/// Match function \p BF to a long veneer for absolute code. Return true if
1540+
/// the match was successful and populate \p TargetAddress with an address of
1541+
/// the function veneer jumps to.
1542+
virtual bool matchAbsLongVeneer(const BinaryFunction &BF,
1543+
uint64_t &TargetAddress) const {
1544+
llvm_unreachable("not implemented");
1545+
}
1546+
15391547
virtual bool matchAdrpAddPair(const MCInst &Adrp, const MCInst &Add) const {
15401548
llvm_unreachable("not implemented");
15411549
return false;

bolt/lib/Passes/VeneerElimination.cpp

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,30 +29,47 @@ static llvm::cl::opt<bool>
2929
namespace llvm {
3030
namespace bolt {
3131

32+
static bool isPossibleVeneer(const BinaryFunction &BF) {
33+
return BF.isAArch64Veneer() || BF.getOneName().starts_with("__AArch64");
34+
}
35+
3236
Error VeneerElimination::runOnFunctions(BinaryContext &BC) {
3337
if (!opts::EliminateVeneers || !BC.isAArch64())
3438
return Error::success();
3539

36-
std::map<uint64_t, BinaryFunction> &BFs = BC.getBinaryFunctions();
3740
std::unordered_map<const MCSymbol *, const MCSymbol *> VeneerDestinations;
38-
uint64_t VeneersCount = 0;
39-
for (auto &It : BFs) {
40-
BinaryFunction &VeneerFunction = It.second;
41-
if (!VeneerFunction.isAArch64Veneer())
41+
uint64_t NumEliminatedVeneers = 0;
42+
for (BinaryFunction &BF : llvm::make_second_range(BC.getBinaryFunctions())) {
43+
if (!isPossibleVeneer(BF))
4244
continue;
4345

44-
VeneersCount++;
45-
VeneerFunction.setPseudo(true);
46-
MCInst &FirstInstruction = *(VeneerFunction.begin()->begin());
47-
const MCSymbol *VeneerTargetSymbol =
48-
BC.MIB->getTargetSymbol(FirstInstruction, 1);
49-
assert(VeneerTargetSymbol && "Expecting target symbol for instruction");
50-
for (const MCSymbol *Symbol : VeneerFunction.getSymbols())
46+
if (BF.isIgnored())
47+
continue;
48+
49+
const MCSymbol *VeneerTargetSymbol = 0;
50+
uint64_t TargetAddress;
51+
if (BC.MIB->matchAbsLongVeneer(BF, TargetAddress)) {
52+
if (BinaryFunction *TargetBF =
53+
BC.getBinaryFunctionAtAddress(TargetAddress))
54+
VeneerTargetSymbol = TargetBF->getSymbol();
55+
} else {
56+
MCInst &FirstInstruction = *(BF.begin()->begin());
57+
if (BC.MIB->hasAnnotation(FirstInstruction, "AArch64Veneer"))
58+
VeneerTargetSymbol = BC.MIB->getTargetSymbol(FirstInstruction, 1);
59+
}
60+
61+
if (!VeneerTargetSymbol)
62+
continue;
63+
64+
for (const MCSymbol *Symbol : BF.getSymbols())
5165
VeneerDestinations[Symbol] = VeneerTargetSymbol;
66+
67+
NumEliminatedVeneers++;
68+
BF.setPseudo(true);
5269
}
5370

5471
BC.outs() << "BOLT-INFO: number of removed linker-inserted veneers: "
55-
<< VeneersCount << "\n";
72+
<< NumEliminatedVeneers << '\n';
5673

5774
// Handle veneers to veneers in case they occur
5875
for (auto &Entry : VeneerDestinations) {
@@ -65,9 +82,8 @@ Error VeneerElimination::runOnFunctions(BinaryContext &BC) {
6582
}
6683

6784
uint64_t VeneerCallers = 0;
68-
for (auto &It : BFs) {
69-
BinaryFunction &Function = It.second;
70-
for (BinaryBasicBlock &BB : Function) {
85+
for (BinaryFunction &BF : llvm::make_second_range(BC.getBinaryFunctions())) {
86+
for (BinaryBasicBlock &BB : BF) {
7187
for (MCInst &Instr : BB) {
7288
if (!BC.MIB->isCall(Instr) || BC.MIB->isIndirectCall(Instr))
7389
continue;

bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,16 @@
1515
#include "MCTargetDesc/AArch64MCExpr.h"
1616
#include "MCTargetDesc/AArch64MCTargetDesc.h"
1717
#include "Utils/AArch64BaseInfo.h"
18+
#include "bolt/Core/BinaryBasicBlock.h"
19+
#include "bolt/Core/BinaryFunction.h"
1820
#include "bolt/Core/MCPlusBuilder.h"
1921
#include "llvm/BinaryFormat/ELF.h"
2022
#include "llvm/MC/MCContext.h"
2123
#include "llvm/MC/MCFixupKindInfo.h"
2224
#include "llvm/MC/MCInstBuilder.h"
2325
#include "llvm/MC/MCInstrInfo.h"
2426
#include "llvm/MC/MCRegisterInfo.h"
27+
#include "llvm/Support/DataExtractor.h"
2528
#include "llvm/Support/Debug.h"
2629
#include "llvm/Support/ErrorHandling.h"
2730

@@ -1320,6 +1323,67 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
13201323
return 3;
13211324
}
13221325

1326+
/// Match the following pattern:
1327+
///
1328+
/// LDR x16, .L1
1329+
/// BR x16
1330+
/// L1:
1331+
/// .quad Target
1332+
///
1333+
/// Populate \p TargetAddress with the Target value on successful match.
1334+
bool matchAbsLongVeneer(const BinaryFunction &BF,
1335+
uint64_t &TargetAddress) const override {
1336+
if (BF.size() != 1 || BF.getMaxSize() < 16)
1337+
return false;
1338+
1339+
if (!BF.hasConstantIsland())
1340+
return false;
1341+
1342+
const BinaryBasicBlock &BB = BF.front();
1343+
if (BB.size() != 2)
1344+
return false;
1345+
1346+
const MCInst &LDRInst = BB.getInstructionAtIndex(0);
1347+
if (LDRInst.getOpcode() != AArch64::LDRXl)
1348+
return false;
1349+
1350+
if (!LDRInst.getOperand(0).isReg() ||
1351+
LDRInst.getOperand(0).getReg() != AArch64::X16)
1352+
return false;
1353+
1354+
const MCSymbol *TargetSym = getTargetSymbol(LDRInst, 1);
1355+
if (!TargetSym)
1356+
return false;
1357+
1358+
const MCInst &BRInst = BB.getInstructionAtIndex(1);
1359+
if (BRInst.getOpcode() != AArch64::BR)
1360+
return false;
1361+
if (!BRInst.getOperand(0).isReg() ||
1362+
BRInst.getOperand(0).getReg() != AArch64::X16)
1363+
return false;
1364+
1365+
const BinaryFunction::IslandInfo &IInfo = BF.getIslandInfo();
1366+
if (IInfo.HasDynamicRelocations)
1367+
return false;
1368+
1369+
auto Iter = IInfo.Offsets.find(8);
1370+
if (Iter == IInfo.Offsets.end() || Iter->second != TargetSym)
1371+
return false;
1372+
1373+
// Extract the absolute value stored inside the island.
1374+
StringRef SectionContents = BF.getOriginSection()->getContents();
1375+
StringRef FunctionContents = SectionContents.substr(
1376+
BF.getAddress() - BF.getOriginSection()->getAddress(), BF.getMaxSize());
1377+
1378+
const BinaryContext &BC = BF.getBinaryContext();
1379+
DataExtractor DE(FunctionContents, BC.AsmInfo->isLittleEndian(),
1380+
BC.AsmInfo->getCodePointerSize());
1381+
uint64_t Offset = 8;
1382+
TargetAddress = DE.getAddress(&Offset);
1383+
1384+
return true;
1385+
}
1386+
13231387
bool matchAdrpAddPair(const MCInst &Adrp, const MCInst &Add) const override {
13241388
if (!isADRP(Adrp) || !isAddXri(Add))
13251389
return false;

bolt/test/AArch64/veneer-lld-abs.s

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
## Check that llvm-bolt correctly recognizes long absolute thunks generated
2+
## by LLD.
3+
4+
# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %s -o %t.o
5+
# RUN: %clang %cflags -fno-PIC -no-pie %t.o -o %t.exe -nostdlib \
6+
# RUN: -fuse-ld=lld -Wl,-q
7+
# RUN: llvm-objdump -d %t.exe | FileCheck --check-prefix=CHECK-INPUT %s
8+
# RUN: llvm-objcopy --remove-section .rela.mytext %t.exe
9+
# RUN: llvm-bolt %t.exe -o %t.bolt --elim-link-veneers=true --lite=0
10+
# RUN: llvm-objdump -d -j .text %t.bolt | \
11+
# RUN: FileCheck --check-prefix=CHECK-OUTPUT %s
12+
13+
.text
14+
.balign 4
15+
.global foo
16+
.type foo, %function
17+
foo:
18+
adrp x1, foo
19+
ret
20+
.size foo, .-foo
21+
22+
.section ".mytext", "ax"
23+
.balign 4
24+
25+
.global __AArch64AbsLongThunk_foo
26+
.type __AArch64AbsLongThunk_foo, %function
27+
__AArch64AbsLongThunk_foo:
28+
ldr x16, .L1
29+
br x16
30+
# CHECK-INPUT-LABEL: <__AArch64AbsLongThunk_foo>:
31+
# CHECK-INPUT-NEXT: ldr
32+
# CHECK-INPUT-NEXT: br
33+
.L1:
34+
.quad foo
35+
.size __AArch64AbsLongThunk_foo, .-__AArch64AbsLongThunk_foo
36+
37+
## Check that the thunk was removed from .text and _start() calls foo()
38+
## directly.
39+
40+
# CHECK-OUTPUT-NOT: __AArch64AbsLongThunk_foo
41+
42+
.global _start
43+
.type _start, %function
44+
_start:
45+
# CHECK-INPUT-LABEL: <_start>:
46+
# CHECK-OUTPUT-LABEL: <_start>:
47+
bl __AArch64AbsLongThunk_foo
48+
# CHECK-INPUT-NEXT: bl {{.*}} <__AArch64AbsLongThunk_foo>
49+
# CHECK-OUTPUT-NEXT: bl {{.*}} <foo>
50+
ret
51+
.size _start, .-_start

0 commit comments

Comments
 (0)