[lld] [ELF] Retain relocation sections for GOT entries added by relaxOnce (PR #225594)

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 22 22:08:11 PDT 2026


https://github.com/MaskRay created https://github.com/llvm/llvm-project/pull/225594

x86-64 and SystemZ relax GOTPCRELX/GOTENT and, in relaxOnce, revert
relaxations that turn out to be out of range or misaligned. The reverted
relocation needs a GOT entry, which in -pie/-shared needs a relative
relocation. If .rela.dyn (or .relr.dyn) was empty, it has already been
removed by removeUnusedSyntheticSections, triggering an assertion
failure.

Retain the section that will receive the relative relocations when
relaxOnce may add GOT entries, and recompute .dynamic after relaxOnce.

LLM-aided. Fixes #224011

>From dbd0250562be68922deb8e7d26b82e3d530127f4 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Tue, 22 Sep 2026 10:52:27 -0700
Subject: [PATCH] [ELF] Retain relocation sections for GOT entries added by
 relaxOnce

x86-64 and SystemZ relax GOTPCRELX/GOTENT and, in relaxOnce, revert
relaxations that turn out to be out of range or misaligned. The reverted
relocation needs a GOT entry, which in -pie/-shared needs a relative
relocation. If .rela.dyn (or .relr.dyn) was empty, it has already been
removed by removeUnusedSyntheticSections, triggering an assertion
failure.

Retain the section that will receive the relative relocations when
relaxOnce may add GOT entries, and recompute .dynamic after relaxOnce.

LLM-aided. Fixes #224011
---
 lld/ELF/Relocations.cpp                       |  2 +-
 lld/ELF/SyntheticSections.cpp                 |  3 +-
 lld/ELF/SyntheticSections.h                   |  2 ++
 lld/ELF/Writer.cpp                            | 10 ++++++-
 lld/test/ELF/systemz-gotent-relax-align.s     | 16 ++++++++++
 .../ELF/x86-64-gotpc-relax-too-far-relr.s     | 29 +++++++++++--------
 6 files changed, 47 insertions(+), 15 deletions(-)

diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index b6540a943bd182..db5ba7d023b7f4 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -945,7 +945,7 @@ void RelocScan::process(RelExpr expr, RelType type, uint64_t offset,
       // If the target adjusted the expression to an optimizable form, we may
       // end up needing the GOT if we can't optimize everything.
       if (expr == R_RELAX_GOT_PC || expr == R_RELAX_GOT_PC_NOPIC)
-        ctx.in.got->hasGotOffRel.store(true, std::memory_order_relaxed);
+        ctx.in.got->hasDeferredEntries.store(true, std::memory_order_relaxed);
     }
   }
 
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index 2e7e898a55c63f..f59e676c2e6f55 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -579,7 +579,8 @@ void GotSection::finalizeContents() {
 bool GotSection::isNeeded() const {
   // Needed if the GOT symbol is used or the number of entries is more than just
   // the header. A GOT with just the header may not be needed.
-  return hasGotOffRel || numEntries > ctx.target->gotHeaderEntriesNum;
+  return hasGotOffRel || hasDeferredEntries ||
+         numEntries > ctx.target->gotHeaderEntriesNum;
 }
 
 void GotSection::writeTo(uint8_t *buf) {
diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h
index 355e1c35c20e30..85596e1339545e 100644
--- a/lld/ELF/SyntheticSections.h
+++ b/lld/ELF/SyntheticSections.h
@@ -136,6 +136,8 @@ class GotSection final : public SyntheticSection {
   // Flag to force GOT to be in output if we have relocations
   // that relies on its address.
   std::atomic<bool> hasGotOffRel = false;
+  // Set if relaxOnce may add entries after removeUnusedSyntheticSections.
+  std::atomic<bool> hasDeferredEntries = false;
 
 protected:
   size_t numEntries = 0;
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 1acab70af15fe7..0f72e2b43f47d6 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -1583,7 +1583,9 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
       changed |= ctx.in.relrDyn->updateAllocSize(ctx);
     if (ctx.in.relrAuthDyn)
       changed |= ctx.in.relrAuthDyn->updateAllocSize(ctx);
-    if (ctx.in.relrAuthDyn && ctx.in.dynamic && ctx.in.dynamic->getParent()) {
+    if ((ctx.in.relrAuthDyn ||
+         (ctx.in.got && ctx.in.got->hasDeferredEntries)) &&
+        ctx.in.dynamic && ctx.in.dynamic->getParent()) {
       size_t oldSize = ctx.in.dynamic->getSize();
       finalizeSynthetic(ctx, ctx.in.dynamic.get());
       changed |= (oldSize != ctx.in.dynamic->getSize());
@@ -1753,6 +1755,12 @@ template <class ELFT> void Writer<ELFT>::optimizeBasicBlockJumps() {
 
 // Sections that finalizeAddressDependentContent may add to.
 static bool mayGrowLate(Ctx &ctx, SyntheticSection *sec) {
+  // relaxOnce may add GOT entries that need relative relocations.
+  if (ctx.arg.isPic && ctx.in.got && ctx.in.got->hasDeferredEntries &&
+      sec == (ctx.in.relrDyn
+                  ? static_cast<SyntheticSection *>(ctx.in.relrDyn.get())
+                  : ctx.in.relaDyn.get()))
+    return true;
   if (sec != ctx.in.relaDyn.get())
     return false;
   // Relocations may move here from .relr.auth.dyn.
diff --git a/lld/test/ELF/systemz-gotent-relax-align.s b/lld/test/ELF/systemz-gotent-relax-align.s
index 125d74ba4a21e2..cfcf572c6781cd 100644
--- a/lld/test/ELF/systemz-gotent-relax-align.s
+++ b/lld/test/ELF/systemz-gotent-relax-align.s
@@ -6,6 +6,13 @@
 # RUN: llvm-readelf -S -r -x .got -x .got.plt %t1 | FileCheck --check-prefixes=CHECK %s
 # RUN: llvm-objdump --no-print-imm-hex -d %t1 | FileCheck --check-prefix=DISASM %s
 
+## In -pie, the retained .got entry needs a relative relocation, even if
+## .rela.dyn or .relr.dyn is empty before relaxOnce.
+# RUN: ld.lld -pie %t.o -o %t2
+# RUN: llvm-readelf -d -r %t2 | FileCheck --check-prefix=RELA %s
+# RUN: ld.lld -pie -z pack-relative-relocs %t.o -o %t3
+# RUN: llvm-readelf -d -r %t3 | FileCheck --check-prefix=RELR %s
+
 ## We retain one .got entry for the unaligned symbol.
 # CHECK:      Name              Type            Address          Off    Size   ES Flg Lk Inf Al
 # CHECK:      .got              PROGBITS        00000000010021e0 0001e0 000020 00  WA  0   0  8
@@ -16,6 +23,15 @@
 # CHECK-NEXT:    0x010021e0 00000000 00000000 00000000 00000000
 # CHECK-NEXT:    0x010021f0 00000000 00000000 00000000 01003205
 
+# RELA:      (RELASZ) 24 (bytes)
+# RELA:      (NULL) 0x0
+# RELA:      Relocation section '.rela.dyn' at offset {{.*}} contains 1 entries:
+# RELA-NEXT: Offset Info Type Symbol's Value Symbol's Name + Addend
+# RELA-NEXT: {{.*}} R_390_RELATIVE
+
+# RELR:      (RELRSZ) 8 (bytes)
+# RELR:      Relocation section '.relr.dyn' at offset {{.*}} contains 1 entries:
+
 # DISASM:      Disassembly of section .text:
 # DISASM:      <_start>:
 # DISASM-NEXT:   larl    %r1, 0x1003200
diff --git a/lld/test/ELF/x86-64-gotpc-relax-too-far-relr.s b/lld/test/ELF/x86-64-gotpc-relax-too-far-relr.s
index 6c5e5f4368a410..c8489e9d9c2a68 100644
--- a/lld/test/ELF/x86-64-gotpc-relax-too-far-relr.s
+++ b/lld/test/ELF/x86-64-gotpc-relax-too-far-relr.s
@@ -1,18 +1,24 @@
 # REQUIRES: x86
-## Test that RELR relocations added when unrelaxing GOTPCREL relocations
-## during layout optimization (relaxOnce) are included in .relr.dyn.
-## .quad foo ensures .relr.dyn is not removed before unrelaxing.
+## Test that relative relocations added when unrelaxing GOTPCREL relocations
+## during layout optimization (relaxOnce) are emitted, even if .rela.dyn or
+## .relr.dyn is empty before relaxOnce.
 
 # RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
 # RUN: ld.lld -pie --pack-dyn-relocs=relr --section-start=.text=0x10000 --section-start=.got=0x20000 --section-start=.data=0x100000000 %t.o -o %t
-# RUN: llvm-readobj -r %t | FileCheck %s
+# RUN: llvm-readelf -d -r %t | FileCheck %s --check-prefix=RELR
+# RUN: ld.lld -pie --section-start=.text=0x10000 --section-start=.got=0x20000 --section-start=.data=0x100000000 %t.o -o %t.rela
+# RUN: llvm-readelf -d -r %t.rela | FileCheck %s --check-prefix=RELA
 
-# CHECK:      Relocations [
-# CHECK-NEXT:   Section ({{.*}}) .relr.dyn {
-# CHECK-NEXT:     0x20000 R_X86_64_RELATIVE -
-# CHECK-NEXT:     0x100000000 R_X86_64_RELATIVE -
-# CHECK-NEXT:   }
-# CHECK-NEXT: ]
+# RELR:      (RELRSZ) 8 (bytes)
+# RELR:      Relocation section '.relr.dyn' at offset {{.*}} contains 1 entries:
+# RELR-NEXT: Index: Entry Address Symbolic Address
+# RELR-NEXT: 0000: 0000000000020000 0000000000020000
+
+# RELA:      (RELASZ) 24 (bytes)
+# RELA:      (NULL) 0x0
+# RELA:      Relocation section '.rela.dyn' at offset {{.*}} contains 1 entries:
+# RELA-NEXT: Offset Info Type Symbol's Value Symbol's Name + Addend
+# RELA-NEXT: 0000000000020000 0000000000000008 R_X86_64_RELATIVE 100000000
 
 .text
 .globl _start
@@ -20,7 +26,6 @@ _start:
   movq foo at GOTPCREL(%rip), %rax
 
 .section .data,"aw", at progbits
-.align 8
 .globl foo
 foo:
-  .quad foo
+  .quad 0



More information about the llvm-commits mailing list