[lld] 9d6ec28 - [lld/ELF] Don't relax R_X86_64_(REX_)GOTPCRELX when offset is too far

Arthur Eubanks via llvm-commits llvm-commits at lists.llvm.org
Wed Oct 4 13:04:10 PDT 2023


Author: Arthur Eubanks
Date: 2023-10-04T13:03:56-07:00
New Revision: 9d6ec280fc537b55ef8641ea85d3522426bc5766

URL: https://github.com/llvm/llvm-project/commit/9d6ec280fc537b55ef8641ea85d3522426bc5766
DIFF: https://github.com/llvm/llvm-project/commit/9d6ec280fc537b55ef8641ea85d3522426bc5766.diff

LOG: [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

Added: 
    lld/test/ELF/x86-64-gotpc-relax-too-far.s

Modified: 
    lld/ELF/Arch/X86_64.cpp
    lld/ELF/Relocations.cpp
    lld/ELF/Relocations.h
    lld/ELF/Writer.cpp
    lld/test/ELF/x86-64-gotpc-relax.s

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


################################################################################
diff  --git a/lld/ELF/Arch/X86_64.cpp b/lld/ELF/Arch/X86_64.cpp
index 9e49f54f6389a6c..2135ac234864513 100644
--- a/lld/ELF/Arch/X86_64.cpp
+++ b/lld/ELF/Arch/X86_64.cpp
@@ -7,12 +7,14 @@
 //===----------------------------------------------------------------------===//
 
 #include "OutputSections.h"
+#include "Relocations.h"
 #include "Symbols.h"
 #include "SyntheticSections.h"
 #include "Target.h"
 #include "lld/Common/ErrorHandler.h"
 #include "llvm/BinaryFormat/ELF.h"
 #include "llvm/Support/Endian.h"
+#include "llvm/Support/MathExtras.h"
 
 using namespace llvm;
 using namespace llvm::object;
@@ -47,6 +49,7 @@ class X86_64 : public TargetInfo {
                                         uint8_t stOther) const override;
   bool deleteFallThruJmpInsn(InputSection &is, InputFile *file,
                              InputSection *nextIS) const override;
+  bool relaxOnce(int pass) const override;
 };
 } // namespace
 
@@ -305,6 +308,43 @@ bool X86_64::deleteFallThruJmpInsn(InputSection &is, InputFile *file,
   return true;
 }
 
+bool X86_64::relaxOnce(int pass) const {
+  uint64_t minVA = UINT64_MAX, maxVA = 0;
+  for (OutputSection *osec : outputSections) {
+    minVA = std::min(minVA, osec->addr);
+    maxVA = std::max(maxVA, osec->addr + osec->size);
+  }
+  // If the max VA 
diff erence is under 2^31, GOT-generating relocations with a 32-bit range cannot overflow.
+  if (isUInt<31>(maxVA - minVA))
+    return false;
+
+  SmallVector<InputSection *, 0> 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.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 (isInt<32>(v))
+          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) {
@@ -912,7 +952,8 @@ static void relaxGotNoPic(uint8_t *loc, uint64_t val, uint8_t op,
 }
 
 static void relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) {
-  checkInt(loc, val, 32, rel);
+  assert(isInt<32>(val) &&
+         "GOTPCRELX should not have been relaxed if it overflows");
   const uint8_t op = loc[-2];
   const uint8_t modRm = loc[-1];
 

diff  --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 9305c959d0f0f68..ee27cc15e040a49 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -904,7 +904,7 @@ static void addPltEntry(PltSection &plt, GotPltSection &gotPlt,
                 sym, 0, R_ABS});
 }
 
-static void addGotEntry(Symbol &sym) {
+void elf::addGotEntry(Symbol &sym) {
   in.got->addEntry(sym);
   uint64_t off = sym.getGotOffset();
 
@@ -1055,6 +1055,10 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset,
     } else if (!isAbsoluteValue(sym)) {
       expr =
           target->adjustGotPcExpr(type, addend, sec->content().data() + offset);
+      // If the target adjusted the expression to R_RELAX_GOT_PC, we may end up
+      // needing the GOT if we can't relax everything.
+      if (expr == R_RELAX_GOT_PC)
+        in.got->hasGotOffRel.store(true, std::memory_order_relaxed);
     }
   }
 

diff  --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h
index 0559245a8f2c006..15a2b5fc177c546 100644
--- a/lld/ELF/Relocations.h
+++ b/lld/ELF/Relocations.h
@@ -137,6 +137,7 @@ struct JumpInstrMod {
 template <class ELFT> void scanRelocations();
 void reportUndefinedSymbols();
 void postScanRelocations();
+void addGotEntry(Symbol &sym);
 
 void hexagonTLSSymbolUpdate(ArrayRef<OutputSection *> outputSections);
 bool hexagonNeedsTLSSymbol(ArrayRef<OutputSection *> outputSections);

diff  --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index d6adc4ff3d644db..ce72189015348a4 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -1672,6 +1672,7 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
       changed |= a32p.createFixes();
     }
 
+    finalizeSynthetic(in.got.get());
     if (in.mipsGot)
       in.mipsGot->updateAllocSize();
 

diff  --git a/lld/test/ELF/x86-64-gotpc-err.s b/lld/test/ELF/x86-64-gotpc-err.s
deleted file mode 100644
index 6fc7cbe0125c0aa..000000000000000
--- 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 at GOTPCREL(%rip), %eax  # out of range
-  movq __stop_data at GOTPCREL(%rip), %rax  # out of range
-  movq __stop_data at GOTPCREL(%rip), %rax  # in range
-
-.section data,"aw", at 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
index 000000000000000..74aa6d8f65a0d8d
--- /dev/null
+++ b/lld/test/ELF/x86-64-gotpc-relax-too-far.s
@@ -0,0 +1,55 @@
+# 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/lds1 %t/a.o -o %t/bin
+# RUN: llvm-objdump --no-print-imm-hex -d %t/bin | FileCheck --check-prefix=DISASM %s
+# RUN: llvm-readelf -S %t/bin | FileCheck --check-prefixes=GOT %s
+# RUN: ld.lld -T %t/lds2 %t/a.o -o %t/bin2
+# RUN: llvm-readelf -S %t/bin2 | FileCheck --check-prefixes=UNNECESSARY-GOT %s
+
+# DISASM:      <_foo>:
+# DISASM-NEXT: movl    2097146(%rip), %eax
+# DISASM:      <_start>:
+# DISASM-NEXT: movl    1048578(%rip), %eax
+# DISASM-NEXT: movq    1048571(%rip), %rax
+# DISASM-NEXT: leaq    2147483641(%rip), %rax
+# DISASM-NEXT: leal    2147483635(%rip), %eax
+
+# In our implementation, .got is retained even if all GOT-generating relocations are optimized.
+# Make sure .got still exists with the right size.
+# UNNECESSARY-GOT: .got PROGBITS 0000000000300000 101020 000000 00 WA 0 0 8
+#             GOT: .got PROGBITS 0000000000300000 102000 000010 00 WA 0 0 8
+
+#--- a.s
+.section .text.foo,"ax"
+.globl _foo
+.type _foo, @function
+_foo:
+  movl __start_data at GOTPCREL(%rip), %eax  # out of range
+
+.section .text,"ax"
+.globl _start
+.type _start, @function
+_start:
+  movl __stop_data at GOTPCREL(%rip), %eax  # out of range
+  movq __stop_data at GOTPCREL(%rip), %rax  # out of range
+  movq __stop_data at GOTPCREL(%rip), %rax  # in range
+  movl __stop_data at GOTPCREL(%rip), %eax  # in range
+
+.section data,"aw", at progbits
+.space 13
+
+#--- lds1
+SECTIONS {
+  .text.foo 0x100000 : { *(.text.foo) }
+  .text 0x200000 : { *(.text) }
+  .got 0x300000 : { *(.got) }
+  data 0x80200000 : { *(data) }
+}
+#--- lds2
+SECTIONS {
+  .text.foo 0x100000 : { *(.text.foo) }
+  .text 0x200000 : { *(.text) }
+  .got 0x300000 : { *(.got) }
+  data 0x400000 : { *(data) }
+}

diff  --git a/lld/test/ELF/x86-64-gotpc-relax.s b/lld/test/ELF/x86-64-gotpc-relax.s
index 0bcc43c72e42498..de858bffc07e398 100644
--- a/lld/test/ELF/x86-64-gotpc-relax.s
+++ b/lld/test/ELF/x86-64-gotpc-relax.s
@@ -13,18 +13,18 @@
 # RUN: ld.lld --no-relax %t.o -o %t2
 # RUN: llvm-objdump --no-print-imm-hex -d %t2 | FileCheck --check-prefix=NORELAX %s
 
-## .got is removed as all GOT-generating relocations are optimized.
+## In our implementation, .got is retained even if all GOT-generating relocations are optimized.
 # CHECK:      Name              Type            Address          Off    Size   ES Flg Lk Inf Al
-# CHECK:      .iplt             PROGBITS        0000000000201210 000210 000010 00  AX  0   0 16
-# CHECK-NEXT: .got.plt          PROGBITS        0000000000202220 000220 000008 00  WA  0   0  8
+# CHECK:      .iplt             PROGBITS        0000000000201280 000280 000010 00  AX  0   0 16
+# CHECK-NEXT: .got              PROGBITS        0000000000202290 000290 000000 00  WA  0   0  8
 
 ## There is one R_X86_64_IRELATIVE relocations.
 # RELOC-LABEL: Relocation section '.rela.dyn' at offset {{.*}} contains 1 entry:
 # CHECK:           Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
-# CHECK:       0000000000202220  0000000000000025 R_X86_64_IRELATIVE                        201172
+# CHECK:       0000000000203290  0000000000000025 R_X86_64_IRELATIVE                        2011e2
 # CHECK-LABEL: Hex dump of section '.got.plt':
-# NOAPPLY-NEXT:  0x00202220 00000000 00000000
-# APPLY-NEXT:    0x00202220 72112000 00000000
+# NOAPPLY-NEXT:  0x00203290 00000000 00000000
+# APPLY-NEXT:    0x00203290 e2112000 00000000
 
 # 0x201173 + 7 - 10 = 0x201170
 # 0x20117a + 7 - 17 = 0x201170
@@ -33,40 +33,40 @@
 # DISASM:      Disassembly of section .text:
 # DISASM-EMPTY:
 # DISASM-NEXT: <foo>:
-# DISASM-NEXT:   201170: 90 nop
+# DISASM-NEXT:   2011e0: 90 nop
 # DISASM:      <hid>:
-# DISASM-NEXT:   201171: 90 nop
+# DISASM-NEXT:   2011e1: 90 nop
 # DISASM:      <ifunc>:
-# DISASM-NEXT:   201172: c3 retq
+# DISASM-NEXT:   2011e2: c3 retq
 # DISASM:      <_start>:
 # DISASM-NEXT: leaq -10(%rip), %rax
 # DISASM-NEXT: leaq -17(%rip), %rax
 # DISASM-NEXT: leaq -23(%rip), %rax
 # DISASM-NEXT: leaq -30(%rip), %rax
-# DISASM-NEXT: movq 4234(%rip), %rax
-# DISASM-NEXT: movq 4227(%rip), %rax
+# DISASM-NEXT: movq 8330(%rip), %rax
+# DISASM-NEXT: movq 8323(%rip), %rax
 # DISASM-NEXT: leaq -52(%rip), %rax
 # DISASM-NEXT: leaq -59(%rip), %rax
 # DISASM-NEXT: leaq -65(%rip), %rax
 # DISASM-NEXT: leaq -72(%rip), %rax
-# DISASM-NEXT: movq 4192(%rip), %rax
-# DISASM-NEXT: movq 4185(%rip), %rax
-# DISASM-NEXT: callq 0x201170 <foo>
-# DISASM-NEXT: callq 0x201170 <foo>
-# DISASM-NEXT: callq 0x201171 <hid>
-# DISASM-NEXT: callq 0x201171 <hid>
-# DISASM-NEXT: callq *4155(%rip)
-# DISASM-NEXT: callq *4149(%rip)
-# DISASM-NEXT: jmp   0x201170 <foo>
+# DISASM-NEXT: movq 8288(%rip), %rax
+# DISASM-NEXT: movq 8281(%rip), %rax
+# DISASM-NEXT: callq 0x2011e0 <foo>
+# DISASM-NEXT: callq 0x2011e0 <foo>
+# DISASM-NEXT: callq 0x2011e1 <hid>
+# DISASM-NEXT: callq 0x2011e1 <hid>
+# DISASM-NEXT: callq *8251(%rip)
+# DISASM-NEXT: callq *8245(%rip)
+# DISASM-NEXT: jmp   0x2011e0 <foo>
 # DISASM-NEXT: nop
-# DISASM-NEXT: jmp   0x201170 <foo>
+# DISASM-NEXT: jmp   0x2011e0 <foo>
 # DISASM-NEXT: nop
-# DISASM-NEXT: jmp   0x201171 <hid>
+# DISASM-NEXT: jmp   0x2011e1 <hid>
 # DISASM-NEXT: nop
-# DISASM-NEXT: jmp   0x201171 <hid>
+# DISASM-NEXT: jmp   0x2011e1 <hid>
 # DISASM-NEXT: nop
-# DISASM-NEXT: jmpq  *4119(%rip)
-# DISASM-NEXT: jmpq  *4113(%rip)
+# DISASM-NEXT: jmpq  *8215(%rip)
+# DISASM-NEXT: jmpq  *8209(%rip)
 
 # NORELAX-LABEL: <_start>:
 # NORELAX-COUNT-12: movq


        


More information about the llvm-commits mailing list