Skip to content

Commit 5cf5322

Browse files
committed
[BOLT][AArch64] Add support for long absolute LLD thunks
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 10f0c1a commit 5cf5322

File tree

4 files changed

+151
-12
lines changed

4 files changed

+151
-12
lines changed

bolt/include/bolt/Core/MCPlusBuilder.h

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

1539+
virtual bool matchAbsLongVeneer(const BinaryFunction &BF,
1540+
uint64_t &TargetAddress) const {
1541+
llvm_unreachable("not implemented");
1542+
}
1543+
15391544
virtual bool matchAdrpAddPair(const MCInst &Adrp, const MCInst &Add) const {
15401545
llvm_unreachable("not implemented");
15411546
return false;

bolt/lib/Passes/VeneerElimination.cpp

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,30 +29,49 @@ 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

3640
std::map<uint64_t, BinaryFunction> &BFs = BC.getBinaryFunctions();
3741
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())
42+
uint64_t NumEliminatedVeneers = 0;
43+
for (BinaryFunction &BF : llvm::make_second_range(BC.getBinaryFunctions())) {
44+
if (!isPossibleVeneer(BF))
45+
continue;
46+
47+
if (BF.isIgnored())
4248
continue;
4349

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())
50+
MCInst &FirstInstruction = *(BF.begin()->begin());
51+
const MCSymbol *VeneerTargetSymbol = 0;
52+
uint64_t TargetAddress;
53+
if (BC.MIB->matchAbsLongVeneer(BF, TargetAddress)) {
54+
if (BinaryFunction *TargetBF =
55+
BC.getBinaryFunctionAtAddress(TargetAddress))
56+
VeneerTargetSymbol = TargetBF->getSymbol();
57+
} else {
58+
if (!BC.MIB->hasAnnotation(FirstInstruction, "AArch64Veneer"))
59+
continue;
60+
VeneerTargetSymbol = BC.MIB->getTargetSymbol(FirstInstruction, 1);
61+
}
62+
63+
if (!VeneerTargetSymbol)
64+
continue;
65+
66+
for (const MCSymbol *Symbol : BF.getSymbols())
5167
VeneerDestinations[Symbol] = VeneerTargetSymbol;
68+
69+
NumEliminatedVeneers++;
70+
BF.setPseudo(true);
5271
}
5372

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

5776
// Handle veneers to veneers in case they occur
5877
for (auto &Entry : VeneerDestinations) {

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+
/// ADR 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)