diff --git a/lld/ELF/Arch/X86_64.cpp b/lld/ELF/Arch/X86_64.cpp --- a/lld/ELF/Arch/X86_64.cpp +++ b/lld/ELF/Arch/X86_64.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "OutputSections.h" +#include "Relocations.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" @@ -47,6 +48,7 @@ uint8_t stOther) const override; bool deleteFallThruJmpInsn(InputSection &is, InputFile *file, InputSection *nextIS) const override; + bool relaxOnce(int pass) const override; }; } // namespace @@ -305,6 +307,37 @@ return true; } +bool X86_64::relaxOnce(int pass) const { + SmallVector storage; + bool changed = false; + for (OutputSection *osec : outputSections) { + if (!(osec->flags & SHF_EXECINSTR)) + continue; + for (InputSection *sec : getInputSections(*osec, storage)) { + for (Relocation &rel : sec->relocs()) { + if (rel.type != R_X86_64_GOTPCRELX && + rel.type != R_X86_64_REX_GOTPCRELX) + continue; + if (rel.expr != R_RELAX_GOT_PC) + continue; + + uint64_t v = sec->getRelocTargetVA( + sec->file, rel.type, rel.addend, + sec->getOutputSection()->addr + rel.offset, *rel.sym, rel.expr); + if (static_cast(v) == llvm::SignExtend64(v, 32)) + continue; + if (rel.sym->auxIdx == 0) { + rel.sym->allocateAux(); + addGotEntry(*rel.sym); + changed = true; + } + rel.expr = R_GOT_PC; + } + } + } + return changed; +} + RelExpr X86_64::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const { switch (type) { diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h --- a/lld/ELF/Relocations.h +++ b/lld/ELF/Relocations.h @@ -137,6 +137,7 @@ template void scanRelocations(); void reportUndefinedSymbols(); void postScanRelocations(); +void addGotEntry(Symbol &sym); void hexagonTLSSymbolUpdate(ArrayRef outputSections); bool hexagonNeedsTLSSymbol(ArrayRef outputSections); diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -904,7 +904,8 @@ sym, 0, R_ABS}); } -static void addGotEntry(Symbol &sym) { +namespace lld::elf { +void addGotEntry(Symbol &sym) { in.got->addEntry(sym); uint64_t off = sym.getGotOffset(); @@ -922,6 +923,7 @@ else addRelativeReloc(*in.got, off, sym, 0, R_ABS, target->symbolicRel); } +} // namespace lld::elf static void addTpOffsetGotEntry(Symbol &sym) { in.got->addEntry(sym); diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -1650,6 +1650,8 @@ changed |= a32p.createFixes(); } + if (in.got) + in.got->updateAllocSize(); if (in.mipsGot) in.mipsGot->updateAllocSize(); @@ -2149,6 +2151,8 @@ // finalizeAddressDependentContent may have added local symbols to the // static symbol table. finalizeSynthetic(in.symTab.get()); + // finalizeAddressDependentContent may have added GOT entries. + finalizeSynthetic(in.got.get()); finalizeSynthetic(in.ppc64LongBranchTarget.get()); finalizeSynthetic(in.armCmseSGSection.get()); } diff --git a/lld/test/ELF/x86-64-gotpc-err.s b/lld/test/ELF/x86-64-gotpc-err.s deleted file mode 100644 --- a/lld/test/ELF/x86-64-gotpc-err.s +++ /dev/null @@ -1,26 +0,0 @@ -# REQUIRES: x86 -# RUN: split-file %s %t -# RUN: llvm-mc -filetype=obj -triple=x86_64 %t/a.s -o %t/a.o -# RUN: not ld.lld -T %t/lds %t/a.o -o /dev/null 2>&1 | FileCheck %s - -## Test diagnostics for GOTPCRELX overflows. In addition, test that there is no -## `>>> defined in` for linker synthesized __stop_* symbols (there is no -## associated file or linker script line number). - -# CHECK: error: {{.*}}:(.text+0x2): relocation R_X86_64_GOTPCRELX out of range: 2147483655 is not in [-2147483648, 2147483647]; references '__stop_data' -# CHECK-NEXT: error: {{.*}}:(.text+0x9): relocation R_X86_64_REX_GOTPCRELX out of range: 2147483648 is not in [-2147483648, 2147483647]; references '__stop_data' -# CHECK-NOT: error: - -#--- a.s - movl __stop_data@GOTPCREL(%rip), %eax # out of range - movq __stop_data@GOTPCREL(%rip), %rax # out of range - movq __stop_data@GOTPCREL(%rip), %rax # in range - -.section data,"aw",@progbits -.space 13 - -#--- lds -SECTIONS { - .text 0x200000 : { *(.text) } - data 0x80200000 : { *(data) } -} diff --git a/lld/test/ELF/x86-64-gotpc-relax-too-far.s b/lld/test/ELF/x86-64-gotpc-relax-too-far.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/x86-64-gotpc-relax-too-far.s @@ -0,0 +1,29 @@ +# REQUIRES: x86 +# RUN: split-file %s %t +# RUN: llvm-mc -filetype=obj -triple=x86_64 %t/a.s -o %t/a.o +# RUN: ld.lld -T %t/lds %t/a.o -o %t/bin +# RUN: llvm-objdump --no-print-imm-hex -d %t/bin | FileCheck --check-prefix=DISASM %s + +# DISASM: <_start>: +# DISASM-NEXT: movl -2097158(%rip), %eax +# DISASM-NEXT: movq -2097165(%rip), %rax +# DISASM-NEXT: leaq 2147483641(%rip), %rax +# DISASM-NEXT: leal 2147483635(%rip), %eax + +#--- a.s +.globl _start +.type _start, @function +_start: + movl __stop_data@GOTPCREL(%rip), %eax # out of range + movq __stop_data@GOTPCREL(%rip), %rax # out of range + movq __stop_data@GOTPCREL(%rip), %rax # in range + movl __stop_data@GOTPCREL(%rip), %eax # in range + +.section data,"aw",@progbits +.space 13 + +#--- lds +SECTIONS { + .text 0x200000 : { *(.text) } + data 0x80200000 : { *(data) } +}