Skip to content

Commit 9d6ec28

Browse files
committed
[lld/ELF] Don't relax R_X86_64_(REX_)GOTPCRELX when offset is too far
For each R_X86_64_(REX_)GOTPCRELX relocation, check that the offset to the symbol is representable with 2^32 signed offset. If not, add a GOT entry for it and set its expr to R_GOT_PC so that we emit the GOT load instead of the relaxed lea. Do this in finalizeAddressDependentContent() where we iteratively attempt this (e.g. RISCV uses this for relaxation, ARM uses this to insert thunks). Decided not to do the opposite of inserting GOT entries initially and removing them when relaxable because removing GOT entries isn't simple. One drawback of this approach is that if we see any GOTPCRELX relocation, we'll create an empty .got even if it's not required in the end. Reviewed By: MaskRay Differential Revision: https://reviews.llvm.org/D157020
1 parent bfcfc2a commit 9d6ec28

File tree

7 files changed

+129
-53
lines changed

7 files changed

+129
-53
lines changed

lld/ELF/Arch/X86_64.cpp

+42-1
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "OutputSections.h"
10+
#include "Relocations.h"
1011
#include "Symbols.h"
1112
#include "SyntheticSections.h"
1213
#include "Target.h"
1314
#include "lld/Common/ErrorHandler.h"
1415
#include "llvm/BinaryFormat/ELF.h"
1516
#include "llvm/Support/Endian.h"
17+
#include "llvm/Support/MathExtras.h"
1618

1719
using namespace llvm;
1820
using namespace llvm::object;
@@ -47,6 +49,7 @@ class X86_64 : public TargetInfo {
4749
uint8_t stOther) const override;
4850
bool deleteFallThruJmpInsn(InputSection &is, InputFile *file,
4951
InputSection *nextIS) const override;
52+
bool relaxOnce(int pass) const override;
5053
};
5154
} // namespace
5255

@@ -305,6 +308,43 @@ bool X86_64::deleteFallThruJmpInsn(InputSection &is, InputFile *file,
305308
return true;
306309
}
307310

311+
bool X86_64::relaxOnce(int pass) const {
312+
uint64_t minVA = UINT64_MAX, maxVA = 0;
313+
for (OutputSection *osec : outputSections) {
314+
minVA = std::min(minVA, osec->addr);
315+
maxVA = std::max(maxVA, osec->addr + osec->size);
316+
}
317+
// If the max VA difference is under 2^31, GOT-generating relocations with a 32-bit range cannot overflow.
318+
if (isUInt<31>(maxVA - minVA))
319+
return false;
320+
321+
SmallVector<InputSection *, 0> storage;
322+
bool changed = false;
323+
for (OutputSection *osec : outputSections) {
324+
if (!(osec->flags & SHF_EXECINSTR))
325+
continue;
326+
for (InputSection *sec : getInputSections(*osec, storage)) {
327+
for (Relocation &rel : sec->relocs()) {
328+
if (rel.expr != R_RELAX_GOT_PC)
329+
continue;
330+
331+
uint64_t v = sec->getRelocTargetVA(
332+
sec->file, rel.type, rel.addend,
333+
sec->getOutputSection()->addr + rel.offset, *rel.sym, rel.expr);
334+
if (isInt<32>(v))
335+
continue;
336+
if (rel.sym->auxIdx == 0) {
337+
rel.sym->allocateAux();
338+
addGotEntry(*rel.sym);
339+
changed = true;
340+
}
341+
rel.expr = R_GOT_PC;
342+
}
343+
}
344+
}
345+
return changed;
346+
}
347+
308348
RelExpr X86_64::getRelExpr(RelType type, const Symbol &s,
309349
const uint8_t *loc) const {
310350
switch (type) {
@@ -912,7 +952,8 @@ static void relaxGotNoPic(uint8_t *loc, uint64_t val, uint8_t op,
912952
}
913953

914954
static void relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) {
915-
checkInt(loc, val, 32, rel);
955+
assert(isInt<32>(val) &&
956+
"GOTPCRELX should not have been relaxed if it overflows");
916957
const uint8_t op = loc[-2];
917958
const uint8_t modRm = loc[-1];
918959

lld/ELF/Relocations.cpp

+5-1
Original file line numberDiff line numberDiff line change
@@ -904,7 +904,7 @@ static void addPltEntry(PltSection &plt, GotPltSection &gotPlt,
904904
sym, 0, R_ABS});
905905
}
906906

907-
static void addGotEntry(Symbol &sym) {
907+
void elf::addGotEntry(Symbol &sym) {
908908
in.got->addEntry(sym);
909909
uint64_t off = sym.getGotOffset();
910910

@@ -1055,6 +1055,10 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset,
10551055
} else if (!isAbsoluteValue(sym)) {
10561056
expr =
10571057
target->adjustGotPcExpr(type, addend, sec->content().data() + offset);
1058+
// If the target adjusted the expression to R_RELAX_GOT_PC, we may end up
1059+
// needing the GOT if we can't relax everything.
1060+
if (expr == R_RELAX_GOT_PC)
1061+
in.got->hasGotOffRel.store(true, std::memory_order_relaxed);
10581062
}
10591063
}
10601064

lld/ELF/Relocations.h

+1
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ struct JumpInstrMod {
137137
template <class ELFT> void scanRelocations();
138138
void reportUndefinedSymbols();
139139
void postScanRelocations();
140+
void addGotEntry(Symbol &sym);
140141

141142
void hexagonTLSSymbolUpdate(ArrayRef<OutputSection *> outputSections);
142143
bool hexagonNeedsTLSSymbol(ArrayRef<OutputSection *> outputSections);

lld/ELF/Writer.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -1672,6 +1672,7 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
16721672
changed |= a32p.createFixes();
16731673
}
16741674

1675+
finalizeSynthetic(in.got.get());
16751676
if (in.mipsGot)
16761677
in.mipsGot->updateAllocSize();
16771678

lld/test/ELF/x86-64-gotpc-err.s

-26
This file was deleted.
+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# REQUIRES: x86
2+
# RUN: split-file %s %t
3+
# RUN: llvm-mc -filetype=obj -triple=x86_64 %t/a.s -o %t/a.o
4+
# RUN: ld.lld -T %t/lds1 %t/a.o -o %t/bin
5+
# RUN: llvm-objdump --no-print-imm-hex -d %t/bin | FileCheck --check-prefix=DISASM %s
6+
# RUN: llvm-readelf -S %t/bin | FileCheck --check-prefixes=GOT %s
7+
# RUN: ld.lld -T %t/lds2 %t/a.o -o %t/bin2
8+
# RUN: llvm-readelf -S %t/bin2 | FileCheck --check-prefixes=UNNECESSARY-GOT %s
9+
10+
# DISASM: <_foo>:
11+
# DISASM-NEXT: movl 2097146(%rip), %eax
12+
# DISASM: <_start>:
13+
# DISASM-NEXT: movl 1048578(%rip), %eax
14+
# DISASM-NEXT: movq 1048571(%rip), %rax
15+
# DISASM-NEXT: leaq 2147483641(%rip), %rax
16+
# DISASM-NEXT: leal 2147483635(%rip), %eax
17+
18+
# In our implementation, .got is retained even if all GOT-generating relocations are optimized.
19+
# Make sure .got still exists with the right size.
20+
# UNNECESSARY-GOT: .got PROGBITS 0000000000300000 101020 000000 00 WA 0 0 8
21+
# GOT: .got PROGBITS 0000000000300000 102000 000010 00 WA 0 0 8
22+
23+
#--- a.s
24+
.section .text.foo,"ax"
25+
.globl _foo
26+
.type _foo, @function
27+
_foo:
28+
movl __start_data@GOTPCREL(%rip), %eax # out of range
29+
30+
.section .text,"ax"
31+
.globl _start
32+
.type _start, @function
33+
_start:
34+
movl __stop_data@GOTPCREL(%rip), %eax # out of range
35+
movq __stop_data@GOTPCREL(%rip), %rax # out of range
36+
movq __stop_data@GOTPCREL(%rip), %rax # in range
37+
movl __stop_data@GOTPCREL(%rip), %eax # in range
38+
39+
.section data,"aw",@progbits
40+
.space 13
41+
42+
#--- lds1
43+
SECTIONS {
44+
.text.foo 0x100000 : { *(.text.foo) }
45+
.text 0x200000 : { *(.text) }
46+
.got 0x300000 : { *(.got) }
47+
data 0x80200000 : { *(data) }
48+
}
49+
#--- lds2
50+
SECTIONS {
51+
.text.foo 0x100000 : { *(.text.foo) }
52+
.text 0x200000 : { *(.text) }
53+
.got 0x300000 : { *(.got) }
54+
data 0x400000 : { *(data) }
55+
}

lld/test/ELF/x86-64-gotpc-relax.s

+25-25
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,18 @@
1313
# RUN: ld.lld --no-relax %t.o -o %t2
1414
# RUN: llvm-objdump --no-print-imm-hex -d %t2 | FileCheck --check-prefix=NORELAX %s
1515

16-
## .got is removed as all GOT-generating relocations are optimized.
16+
## In our implementation, .got is retained even if all GOT-generating relocations are optimized.
1717
# CHECK: Name Type Address Off Size ES Flg Lk Inf Al
18-
# CHECK: .iplt PROGBITS 0000000000201210 000210 000010 00 AX 0 0 16
19-
# CHECK-NEXT: .got.plt PROGBITS 0000000000202220 000220 000008 00 WA 0 0 8
18+
# CHECK: .iplt PROGBITS 0000000000201280 000280 000010 00 AX 0 0 16
19+
# CHECK-NEXT: .got PROGBITS 0000000000202290 000290 000000 00 WA 0 0 8
2020

2121
## There is one R_X86_64_IRELATIVE relocations.
2222
# RELOC-LABEL: Relocation section '.rela.dyn' at offset {{.*}} contains 1 entry:
2323
# CHECK: Offset Info Type Symbol's Value Symbol's Name + Addend
24-
# CHECK: 0000000000202220 0000000000000025 R_X86_64_IRELATIVE 201172
24+
# CHECK: 0000000000203290 0000000000000025 R_X86_64_IRELATIVE 2011e2
2525
# CHECK-LABEL: Hex dump of section '.got.plt':
26-
# NOAPPLY-NEXT: 0x00202220 00000000 00000000
27-
# APPLY-NEXT: 0x00202220 72112000 00000000
26+
# NOAPPLY-NEXT: 0x00203290 00000000 00000000
27+
# APPLY-NEXT: 0x00203290 e2112000 00000000
2828

2929
# 0x201173 + 7 - 10 = 0x201170
3030
# 0x20117a + 7 - 17 = 0x201170
@@ -33,40 +33,40 @@
3333
# DISASM: Disassembly of section .text:
3434
# DISASM-EMPTY:
3535
# DISASM-NEXT: <foo>:
36-
# DISASM-NEXT: 201170: 90 nop
36+
# DISASM-NEXT: 2011e0: 90 nop
3737
# DISASM: <hid>:
38-
# DISASM-NEXT: 201171: 90 nop
38+
# DISASM-NEXT: 2011e1: 90 nop
3939
# DISASM: <ifunc>:
40-
# DISASM-NEXT: 201172: c3 retq
40+
# DISASM-NEXT: 2011e2: c3 retq
4141
# DISASM: <_start>:
4242
# DISASM-NEXT: leaq -10(%rip), %rax
4343
# DISASM-NEXT: leaq -17(%rip), %rax
4444
# DISASM-NEXT: leaq -23(%rip), %rax
4545
# DISASM-NEXT: leaq -30(%rip), %rax
46-
# DISASM-NEXT: movq 4234(%rip), %rax
47-
# DISASM-NEXT: movq 4227(%rip), %rax
46+
# DISASM-NEXT: movq 8330(%rip), %rax
47+
# DISASM-NEXT: movq 8323(%rip), %rax
4848
# DISASM-NEXT: leaq -52(%rip), %rax
4949
# DISASM-NEXT: leaq -59(%rip), %rax
5050
# DISASM-NEXT: leaq -65(%rip), %rax
5151
# DISASM-NEXT: leaq -72(%rip), %rax
52-
# DISASM-NEXT: movq 4192(%rip), %rax
53-
# DISASM-NEXT: movq 4185(%rip), %rax
54-
# DISASM-NEXT: callq 0x201170 <foo>
55-
# DISASM-NEXT: callq 0x201170 <foo>
56-
# DISASM-NEXT: callq 0x201171 <hid>
57-
# DISASM-NEXT: callq 0x201171 <hid>
58-
# DISASM-NEXT: callq *4155(%rip)
59-
# DISASM-NEXT: callq *4149(%rip)
60-
# DISASM-NEXT: jmp 0x201170 <foo>
52+
# DISASM-NEXT: movq 8288(%rip), %rax
53+
# DISASM-NEXT: movq 8281(%rip), %rax
54+
# DISASM-NEXT: callq 0x2011e0 <foo>
55+
# DISASM-NEXT: callq 0x2011e0 <foo>
56+
# DISASM-NEXT: callq 0x2011e1 <hid>
57+
# DISASM-NEXT: callq 0x2011e1 <hid>
58+
# DISASM-NEXT: callq *8251(%rip)
59+
# DISASM-NEXT: callq *8245(%rip)
60+
# DISASM-NEXT: jmp 0x2011e0 <foo>
6161
# DISASM-NEXT: nop
62-
# DISASM-NEXT: jmp 0x201170 <foo>
62+
# DISASM-NEXT: jmp 0x2011e0 <foo>
6363
# DISASM-NEXT: nop
64-
# DISASM-NEXT: jmp 0x201171 <hid>
64+
# DISASM-NEXT: jmp 0x2011e1 <hid>
6565
# DISASM-NEXT: nop
66-
# DISASM-NEXT: jmp 0x201171 <hid>
66+
# DISASM-NEXT: jmp 0x2011e1 <hid>
6767
# DISASM-NEXT: nop
68-
# DISASM-NEXT: jmpq *4119(%rip)
69-
# DISASM-NEXT: jmpq *4113(%rip)
68+
# DISASM-NEXT: jmpq *8215(%rip)
69+
# DISASM-NEXT: jmpq *8209(%rip)
7070

7171
# NORELAX-LABEL: <_start>:
7272
# NORELAX-COUNT-12: movq

0 commit comments

Comments
 (0)