From bd9aa3ca5789c94b3a0d0c42b0aa8a94be4c0695 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Sat, 1 Aug 2026 10:55:34 -0700 Subject: [PATCH] [ELF] Retain .rela.dyn for PPC64 PI long-branch thunks (#212078) 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/Config.h | 4 +++ lld/ELF/Writer.cpp | 32 ++++++++++++++++-------- lld/test/ELF/ppc64-long-branch-rel14.s | 11 +++++++- lld/test/ELF/ppc64-pcrel-call-to-pcrel.s | 20 +++++++-------- 4 files changed, 46 insertions(+), 21 deletions(-) diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index 1426aab12758..54e0aa58591a 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -770,6 +770,10 @@ struct Ctx : CommonLinkerContext { unsigned scriptSymOrderCounter = 1; llvm::DenseMap scriptSymOrder; + // Used to assert removeUnusedSyntheticSections-removed sections cannot become + // needed again. + SmallVector 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 22946090b489..8cf88cd99cf8 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -1747,6 +1747,17 @@ template void Writer::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.picThunk; +} + // 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 void Writer::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,17 +1785,13 @@ static void removeUnusedSyntheticSections(Ctx &ctx) { auto end = std::remove_if(start, ctx.inputSections.end(), [&](InputSectionBase *s) { auto *sec = cast(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); + // 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()); @@ -2117,6 +2125,10 @@ template void Writer::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; diff --git a/lld/test/ELF/ppc64-long-branch-rel14.s b/lld/test/ELF/ppc64-long-branch-rel14.s index 436382cdfb26..5a6e1b88b730 100644 --- a/lld/test/ELF/ppc64-long-branch-rel14.s +++ b/lld/test/ELF/ppc64-long-branch-rel14.s @@ -11,11 +11,20 @@ # 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-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-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 # CHECK-NEXT: bt+ 2, 0x2040 diff --git a/lld/test/ELF/ppc64-pcrel-call-to-pcrel.s b/lld/test/ELF/ppc64-pcrel-call-to-pcrel.s index 6cbbbfb886e2..4662fe7fd1ff 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 [] 5 caller1 -# SYMBOL-NEXT: 4: 0000000010020000 0 NOTYPE LOCAL DEFAULT 6 func_local -# SYMBOL-NEXT: 5: 0000000010020004 0 NOTYPE LOCAL DEFAULT [] 6 callee2_stother1_default -# SYMBOL-NEXT: 6: 000000001002000c 0 NOTYPE LOCAL DEFAULT [] 6 caller2 -# SYMBOL-NEXT: 7: 0000000010030000 0 NOTYPE LOCAL DEFAULT [] 7 caller3 -# SYMBOL-NEXT: 8: 0000000010040000 0 NOTYPE LOCAL DEFAULT [] 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 [] 9 callee4_stother1_hidden +# SYMBOL: 2: 0000000010010000 0 NOTYPE LOCAL DEFAULT 6 callee1_stother0_default +# SYMBOL-NEXT: 3: 0000000010010004 0 NOTYPE LOCAL DEFAULT [] 6 caller1 +# SYMBOL-NEXT: 4: 0000000010020000 0 NOTYPE LOCAL DEFAULT 7 func_local +# SYMBOL-NEXT: 5: 0000000010020004 0 NOTYPE LOCAL DEFAULT [] 7 callee2_stother1_default +# SYMBOL-NEXT: 6: 000000001002000c 0 NOTYPE LOCAL DEFAULT [] 7 caller2 +# SYMBOL-NEXT: 7: 0000000010030000 0 NOTYPE LOCAL DEFAULT [] 8 caller3 +# SYMBOL-NEXT: 8: 0000000010040000 0 NOTYPE LOCAL DEFAULT [] 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 [] 10 callee4_stother1_hidden # SYMBOL-GLOBAL: 2: 0000000010010004 0 NOTYPE LOCAL DEFAULT [] 1 caller1 # SYMBOL-GLOBAL-NEXT: 3: 0000000010020000 0 NOTYPE LOCAL DEFAULT 2 func_local