Skip to content

Commit 70bf5e5

Browse files
authored
[BOLT][AArch64] Symbolize ADRP after relaxation (#131414)
When the linker relaxes a GOT load, it changes ADRP+LDR instruction pair into ADRP+ADD. It is relatively straightforward to detect and symbolize the second instruction in the disassembler. However, it is not always possible to properly symbolize the ADRP instruction without looking at the second instruction. Hence, we have the FixRelaxationPass that adjust the operand of ADRP by looking at the corresponding ADD. This PR tries to properly symbolize ADRP earlier in the pipeline, i.e. in AArch64MCSymbolizer. This change makes it easier to adjust the instruction once we add AArch64 support in `scanExternalRefs()`. Additionally, we get a benefit of looking at proper operands while observing the function state prior to running FixRelaxationPass. To disambiguate the operand of ADRP that has a GOT relocation against it, we look at the contents/value of the operand. If it contains an address of a page that is valid for GOT, we assume that the operand wasn't modified by the linker and leave it up to FixRelaxationPass to do a proper adjustment. If the page referenced by ADRP cannot point to GOT, then it's an indication that the linker has modified the operand and we substitute the operand with a non-GOT reference to the symbol.
1 parent bc646f4 commit 70bf5e5

File tree

3 files changed

+131
-4
lines changed

3 files changed

+131
-4
lines changed

bolt/lib/Target/AArch64/AArch64MCSymbolizer.cpp

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,15 +125,39 @@ AArch64MCSymbolizer::adjustRelocation(const Relocation &Rel,
125125
// instruction pairs and will perform necessary adjustments.
126126
ErrorOr<uint64_t> SymbolValue = BC.getSymbolValue(*Rel.Symbol);
127127
assert(SymbolValue && "Symbol value should be set");
128-
(void)SymbolValue;
129-
130-
AdjustedRel.Symbol = BC.registerNameAtAddress("__BOLT_got_zero", 0, 0, 0);
131-
AdjustedRel.Addend = Rel.Value;
128+
const uint64_t SymbolPageAddr = *SymbolValue & ~0xfffULL;
129+
130+
// Check if defined symbol and GOT are on the same page. If they are not,
131+
// disambiguate the operand.
132+
if (BC.MIB->isADRP(Inst) && Rel.Addend == 0 &&
133+
SymbolPageAddr == Rel.Value &&
134+
!isPageAddressValidForGOT(SymbolPageAddr)) {
135+
AdjustedRel.Type = ELF::R_AARCH64_ADR_PREL_PG_HI21;
136+
} else {
137+
AdjustedRel.Symbol = BC.registerNameAtAddress("__BOLT_got_zero", 0, 0, 0);
138+
AdjustedRel.Addend = Rel.Value;
139+
}
132140
}
133141

134142
return AdjustedRel;
135143
}
136144

145+
bool AArch64MCSymbolizer::isPageAddressValidForGOT(uint64_t PageAddress) const {
146+
assert(!(PageAddress & 0xfffULL) && "Page address not aligned at 4KB");
147+
148+
ErrorOr<BinarySection &> GOT =
149+
Function.getBinaryContext().getUniqueSectionByName(".got");
150+
if (!GOT || !GOT->getSize())
151+
return false;
152+
153+
const uint64_t GOTFirstPageAddress = GOT->getAddress() & ~0xfffULL;
154+
const uint64_t GOTLastPageAddress =
155+
(GOT->getAddress() + GOT->getSize() - 1) & ~0xfffULL;
156+
157+
return PageAddress >= GOTFirstPageAddress &&
158+
PageAddress <= GOTLastPageAddress;
159+
}
160+
137161
void AArch64MCSymbolizer::tryAddingPcLoadReferenceComment(raw_ostream &CStream,
138162
int64_t Value,
139163
uint64_t Address) {}

bolt/lib/Target/AArch64/AArch64MCSymbolizer.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ class AArch64MCSymbolizer : public MCSymbolizer {
2828
std::optional<Relocation> adjustRelocation(const Relocation &Rel,
2929
const MCInst &Inst) const;
3030

31+
/// Return true if \p PageAddress is a valid page address for .got section.
32+
bool isPageAddressValidForGOT(uint64_t PageAddress) const;
33+
3134
public:
3235
AArch64MCSymbolizer(BinaryFunction &Function, bool CreateNewSymbols = true)
3336
: MCSymbolizer(*Function.getBinaryContext().Ctx.get(), nullptr),
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
## Check that BOLT symbolizer properly handles loads from GOT, including
2+
## instruction sequences changed/relaxed by the linker.
3+
4+
# RUN: split-file %s %t
5+
6+
# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %t/main.s \
7+
# RUN: -o %t/main.o
8+
# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %t/near.s \
9+
# RUN: -o %t/near.o
10+
# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %t/far.s \
11+
# RUN: -o %t/far.o
12+
# RUN: %clang %cflags %t/main.o %t/near.o %t/far.o -o %t/main.exe -Wl,-q -static
13+
# RUN: llvm-bolt %t/main.exe -o %t/main.bolt --keep-nops --print-disasm \
14+
# RUN: --print-only=_start | FileCheck %s
15+
16+
#--- main.s
17+
18+
.text
19+
.globl _start
20+
.p2align 2
21+
.type _start, @function
22+
# CHECK-LABEL: _start
23+
_start:
24+
25+
## Function address load relaxable into nop+adr.
26+
# CHECK: nop
27+
# CHECK-NEXT: adr x0, near
28+
adrp x0, :got:near
29+
ldr x0, [x0, :got_lo12:near]
30+
31+
## Function address load relaxable into adrp+add.
32+
# CHECK-NEXT: adrp x1, far
33+
# CHECK-NEXT: add x1, x1, :lo12:far
34+
adrp x1, :got:far
35+
ldr x1, [x1, :got_lo12:far]
36+
37+
## Non-relaxable due to the instruction in-between.
38+
# CHECK-NEXT: adrp x2, __BOLT_got_zero
39+
# CHECK-NEXT: nop
40+
# CHECK-NEXT: ldr x2, [x2, :lo12:__BOLT_got_zero{{.*}}]
41+
adrp x2, :got:near
42+
nop
43+
ldr x2, [x2, :got_lo12:near]
44+
45+
## Load data object with local visibility. Relaxable into adrp+add.
46+
# CHECK-NEXT: adrp x3, "local_far_data/1"
47+
# CHECK-NEXT: add x3, x3, :lo12:"local_far_data/1"
48+
adrp x3, :got:local_far_data
49+
ldr x3, [x3, :got_lo12:local_far_data]
50+
51+
## Global data reference relaxable into adrp+add.
52+
# CHECK-NEXT: adrp x4, far_data
53+
# CHECK-NEXT: add x4, x4, :lo12:far_data
54+
adrp x4, :got:far_data
55+
ldr x4, [x4, :got_lo12:far_data]
56+
57+
ret
58+
.size _start, .-_start
59+
60+
.weak near
61+
.weak far
62+
.weak far_data
63+
64+
## Data object separated by more than 1MB from _start.
65+
.data
66+
.type local_far_data, @object
67+
local_far_data:
68+
.xword 0
69+
.size local_far_data, .-local_far_data
70+
71+
#--- near.s
72+
73+
.text
74+
.globl near
75+
.type near, @function
76+
near:
77+
ret
78+
.size near, .-near
79+
80+
#--- far.s
81+
82+
.text
83+
84+
## Insert 1MB of empty space to make objects after it unreachable by adr
85+
## instructions in _start.
86+
.space 0x100000
87+
88+
.globl far
89+
.type far, @function
90+
far:
91+
ret
92+
.size far, .-far
93+
94+
.data
95+
.globl far_data
96+
.type far_data, @object
97+
far_data:
98+
.xword 0
99+
.size far_data, .-far_data
100+

0 commit comments

Comments
 (0)