[lld] [ELF] Retain .rela.dyn for PPC64 PI long-branch thunks (PR #212078)

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Sat Aug 1 10:46:17 PDT 2026


https://github.com/MaskRay updated https://github.com/llvm/llvm-project/pull/212078

>From 00416b60e14a661a716ca3ead1878df5335819b0 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sat, 25 Jul 2026 18:16:08 -0700
Subject: [PATCH 1/3] [ELF] Retain .rela.dyn for PPC64 PI long-branch thunks

PPC64PILongBranchThunk adds a relative relocation, which runs after
removeUnusedSyntheticSections. With no other dynamic relocation
.rela.dyn is incorrectly removed.

Extract the .rela.auth.dyn condition (#96496, #195649) to `mayGrowLate`
and add a .branch_lt check.
---
 lld/ELF/Writer.cpp                       | 24 ++++++++++++++----------
 lld/test/ELF/ppc64-long-branch-rel14.s   |  6 +++++-
 lld/test/ELF/ppc64-pcrel-call-to-pcrel.s | 20 ++++++++++----------
 3 files changed, 29 insertions(+), 21 deletions(-)

diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 22946090b4891..bc0caf6beec32 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -1747,6 +1747,17 @@ template <class ELFT> void Writer<ELFT>::optimizeBasicBlockJumps() {
       is->trim();
 }
 
+// Sections that finalizeAddressDependentContent may add to.
+static bool mayGrowLate(Ctx &ctx, SyntheticSection *sec) {
+  if (sec != ctx.in.relaDyn.get())
+    return false;
+  // Relocations may move here from .relr.auth.dyn.
+  if (ctx.in.relrAuthDyn && ctx.in.relrAuthDyn->isNeeded())
+    return true;
+  // PPC64PILongBranchThunk adds a relative relocation for its .branch_lt entry.
+  return ctx.in.ppc64LongBranchTarget && ctx.arg.isPic;
+}
+
 // In order to allow users to manipulate linker-synthesized sections,
 // we had to add synthetic sections to the input section list early,
 // even before we make decisions whether they are needed. This allows
@@ -1758,7 +1769,8 @@ template <class ELFT> void Writer<ELFT>::optimizeBasicBlockJumps() {
 //
 // To deal with the above problem, this function is called after
 // scanRelocations is called to remove synthetic sections that turn
-// out to be empty.
+// out to be empty. It runs before finalizeAddressDependentContent, which may
+// add to a section mayGrowLate reports.
 static void removeUnusedSyntheticSections(Ctx &ctx) {
   // All input synthetic sections that can be empty are placed after
   // all regular ones. Reverse iterate to find the first synthetic section
@@ -1773,15 +1785,7 @@ static void removeUnusedSyntheticSections(Ctx &ctx) {
   auto end =
       std::remove_if(start, ctx.inputSections.end(), [&](InputSectionBase *s) {
         auto *sec = cast<SyntheticSection>(s);
-        if (sec->getParent() && sec->isNeeded())
-          return false;
-        // .relr.auth.dyn relocations may be moved to .rela.dyn in
-        // finalizeAddressDependentContent, making .rela.dyn no longer empty.
-        // Conservatively keep .rela.dyn. .relr.auth.dyn can be made empty, but
-        // we would fail to remove it here.
-        if (ctx.arg.emachine == EM_AARCH64 && ctx.arg.relrPackDynRelocs &&
-            sec == ctx.in.relaDyn.get() && ctx.in.relrAuthDyn &&
-            ctx.in.relrAuthDyn->isNeeded())
+        if ((sec->getParent() && sec->isNeeded()) || mayGrowLate(ctx, sec))
           return false;
         unused.insert(sec);
         return true;
diff --git a/lld/test/ELF/ppc64-long-branch-rel14.s b/lld/test/ELF/ppc64-long-branch-rel14.s
index 722f3b6513bd9..5ac3f16a4eca1 100644
--- a/lld/test/ELF/ppc64-long-branch-rel14.s
+++ b/lld/test/ELF/ppc64-long-branch-rel14.s
@@ -11,11 +11,15 @@
 
 # RUN: llvm-mc -filetype=obj -triple=powerpc64le %s -o %t.o
 # RUN: ld.lld -pie -T %t.lds %t.o -o %t
-# RUN: llvm-readelf -r %t | FileCheck --check-prefix=SEC %s
+# RUN: llvm-readelf -r %t | FileCheck --check-prefix=SEC-PIE %s
 # RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck %s
 
 # SEC: There are no relocations in this file.
 
+# SEC-PIE:      Relocation section '.rela.dyn' {{.*}} contains 2 entries:
+# SEC-PIE:      000000000000a0b0 {{[0-9a-f]+}} R_PPC64_RELATIVE a004
+# SEC-PIE-NEXT: 000000000000a0b8 {{[0-9a-f]+}} R_PPC64_RELATIVE a010
+
 # CHECK-LABEL: <_start>:
 # CHECK-NEXT:  2000: bt 2, 0x2020
 # CHECK-NEXT:        bt+ 2, 0x2020
diff --git a/lld/test/ELF/ppc64-pcrel-call-to-pcrel.s b/lld/test/ELF/ppc64-pcrel-call-to-pcrel.s
index 6cbbbfb886e24..4662fe7fd1ff0 100644
--- a/lld/test/ELF/ppc64-pcrel-call-to-pcrel.s
+++ b/lld/test/ELF/ppc64-pcrel-call-to-pcrel.s
@@ -28,16 +28,16 @@
 # RUN: llvm-readelf -s %t | FileCheck %s --check-prefix=SYMBOL-GLOBAL
 # RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck %s
 
-# SYMBOL:      2: 0000000010010000 0 NOTYPE LOCAL DEFAULT 5 callee1_stother0_default
-# SYMBOL-NEXT: 3: 0000000010010004 0 NOTYPE LOCAL DEFAULT [<other: 0x20>] 5 caller1
-# SYMBOL-NEXT: 4: 0000000010020000 0 NOTYPE LOCAL DEFAULT 6 func_local
-# SYMBOL-NEXT: 5: 0000000010020004 0 NOTYPE LOCAL DEFAULT [<other: 0x20>] 6 callee2_stother1_default
-# SYMBOL-NEXT: 6: 000000001002000c 0 NOTYPE LOCAL DEFAULT [<other: 0x20>] 6 caller2
-# SYMBOL-NEXT: 7: 0000000010030000 0 NOTYPE LOCAL DEFAULT [<other: 0x20>] 7 caller3
-# SYMBOL-NEXT: 8: 0000000010040000 0 NOTYPE LOCAL DEFAULT [<other: 0x20>] 8 caller4
-# SYMBOL-NEXT: 9: 0000000010040008 0 NOTYPE LOCAL DEFAULT 9 func_extern
-# SYMBOL-NEXT: 10: 000000001004000c 0 NOTYPE LOCAL HIDDEN 9 callee3_stother0_hidden
-# SYMBOL-NEXT: 11: 0000000010040010 0 NOTYPE LOCAL HIDDEN [<other: 0x22>] 9 callee4_stother1_hidden
+# SYMBOL:      2: 0000000010010000 0 NOTYPE LOCAL DEFAULT 6 callee1_stother0_default
+# SYMBOL-NEXT: 3: 0000000010010004 0 NOTYPE LOCAL DEFAULT [<other: 0x20>] 6 caller1
+# SYMBOL-NEXT: 4: 0000000010020000 0 NOTYPE LOCAL DEFAULT 7 func_local
+# SYMBOL-NEXT: 5: 0000000010020004 0 NOTYPE LOCAL DEFAULT [<other: 0x20>] 7 callee2_stother1_default
+# SYMBOL-NEXT: 6: 000000001002000c 0 NOTYPE LOCAL DEFAULT [<other: 0x20>] 7 caller2
+# SYMBOL-NEXT: 7: 0000000010030000 0 NOTYPE LOCAL DEFAULT [<other: 0x20>] 8 caller3
+# SYMBOL-NEXT: 8: 0000000010040000 0 NOTYPE LOCAL DEFAULT [<other: 0x20>] 9 caller4
+# SYMBOL-NEXT: 9: 0000000010040008 0 NOTYPE LOCAL DEFAULT 10 func_extern
+# SYMBOL-NEXT: 10: 000000001004000c 0 NOTYPE LOCAL HIDDEN 10 callee3_stother0_hidden
+# SYMBOL-NEXT: 11: 0000000010040010 0 NOTYPE LOCAL HIDDEN [<other: 0x22>] 10 callee4_stother1_hidden
 
 # SYMBOL-GLOBAL:      2: 0000000010010004 0 NOTYPE LOCAL DEFAULT [<other: 0x20>] 1 caller1
 # SYMBOL-GLOBAL-NEXT: 3: 0000000010020000 0 NOTYPE LOCAL DEFAULT 2 func_local

>From 93710d8711d87fef6fcb6ff67d537fdeb01db300 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sat, 25 Jul 2026 18:51:41 -0700
Subject: [PATCH 2/3] add assert

---
 lld/ELF/Config.h   | 4 ++++
 lld/ELF/Writer.cpp | 8 ++++++++
 2 files changed, 12 insertions(+)

diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index 1426aab12758f..54e0aa58591ad 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -770,6 +770,10 @@ struct Ctx : CommonLinkerContext {
   unsigned scriptSymOrderCounter = 1;
   llvm::DenseMap<const Symbol *, unsigned> scriptSymOrder;
 
+  // Used to assert removeUnusedSyntheticSections-removed sections cannot become
+  // needed again.
+  SmallVector<SyntheticSection *, 0> removedSyntheticSections;
+
   // The set of TOC entries (.toc + addend) for which we should not apply
   // toc-indirect to toc-relative relaxation. const Symbol * refers to the
   // STT_SECTION symbol associated to the .toc input section.
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index bc0caf6beec32..4a9f650994550 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -1788,6 +1788,10 @@ static void removeUnusedSyntheticSections(Ctx &ctx) {
         if ((sec->getParent() && sec->isNeeded()) || mayGrowLate(ctx, sec))
           return false;
         unused.insert(sec);
+        // LinkerScript::discard clears the parent. Losing later additions to
+        // such a section is intended.
+        if (sec->getParent())
+          ctx.removedSyntheticSections.push_back(sec);
         return true;
       });
   ctx.inputSections.erase(end, ctx.inputSections.end());
@@ -2121,6 +2125,10 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
   //    values. They also might change after adding the thunks.
   finalizeAddressDependentContent();
 
+  // A section dropped as unneeded must have stayed unneeded.
+  assert(llvm::none_of(ctx.removedSyntheticSections,
+                       [](SyntheticSection *sec) { return sec->isNeeded(); }));
+
   // All information needed for OutputSection part of Map file is available.
   if (errCount(ctx))
     return;

>From 536aa1a0f030ad290dbedd4ce128553395f97728 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sat, 1 Aug 2026 10:45:52 -0700
Subject: [PATCH 3/3] use picThunk

---
 lld/ELF/Writer.cpp                     |  2 +-
 lld/test/ELF/ppc64-long-branch-rel14.s | 13 +++++++++----
 2 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 4a9f650994550..8cf88cd99cf8a 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -1755,7 +1755,7 @@ static bool mayGrowLate(Ctx &ctx, SyntheticSection *sec) {
   if (ctx.in.relrAuthDyn && ctx.in.relrAuthDyn->isNeeded())
     return true;
   // PPC64PILongBranchThunk adds a relative relocation for its .branch_lt entry.
-  return ctx.in.ppc64LongBranchTarget && ctx.arg.isPic;
+  return ctx.in.ppc64LongBranchTarget && ctx.arg.picThunk;
 }
 
 // In order to allow users to manipulate linker-synthesized sections,
diff --git a/lld/test/ELF/ppc64-long-branch-rel14.s b/lld/test/ELF/ppc64-long-branch-rel14.s
index 681f40787caa7..5a6e1b88b7301 100644
--- a/lld/test/ELF/ppc64-long-branch-rel14.s
+++ b/lld/test/ELF/ppc64-long-branch-rel14.s
@@ -11,14 +11,19 @@
 
 # RUN: llvm-mc -filetype=obj -triple=powerpc64le %s -o %t.o
 # RUN: ld.lld -pie -T %t.lds %t.o -o %t
-# RUN: llvm-readelf -r %t | FileCheck --check-prefix=SEC-PIE %s
+# RUN: llvm-readelf -r %t | FileCheck --check-prefix=SEC-PI -DOFF0=a0b0 -DOFF1=a0b8 %s
+# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck %s
+
+## --pic-veneer selects PI long-branch thunks in a non-PIC link.
+# RUN: ld.lld --pic-veneer -T %t.lds %t.o -o %t
+# RUN: llvm-readelf -r %t | FileCheck --check-prefix=SEC-PI -DOFF0=a010 -DOFF1=a018 %s
 # RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck %s
 
 # SEC: There are no relocations in this file.
 
-# SEC-PIE:      Relocation section '.rela.dyn' {{.*}} contains 2 entries:
-# SEC-PIE:      000000000000a0b0 {{[0-9a-f]+}} R_PPC64_RELATIVE a004
-# SEC-PIE-NEXT: 000000000000a0b8 {{[0-9a-f]+}} R_PPC64_RELATIVE a010
+# SEC-PI:      Relocation section '.rela.dyn' {{.*}} contains 2 entries:
+# SEC-PI:      {{0+}}[[OFF0]] {{[0-9a-f]+}} R_PPC64_RELATIVE a004
+# SEC-PI-NEXT: {{0+}}[[OFF1]] {{[0-9a-f]+}} R_PPC64_RELATIVE a010
 
 # CHECK-LABEL: <_start>:
 # CHECK-NEXT:  2000: bt 2, 0x2040



More information about the llvm-commits mailing list