diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h --- a/lld/ELF/LinkerScript.h +++ b/lld/ELF/LinkerScript.h @@ -318,6 +318,8 @@ void processSymbolAssignments(); void declareSymbols(); + bool isDiscarded(const OutputSection *sec) const; + // Used to handle INSERT AFTER statements. void processInsertCommands(); diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -1034,7 +1034,7 @@ } } -static bool isDiscardable(OutputSection &sec) { +static bool isDiscardable(const OutputSection &sec) { if (sec.name == "/DISCARD/") return true; @@ -1063,6 +1063,11 @@ return true; } +bool LinkerScript::isDiscarded(const OutputSection *sec) const { + return hasSectionsCommand && (getFirstInputSection(sec) == nullptr) && + isDiscardable(*sec); +} + static void maybePropagatePhdrs(OutputSection &sec, std::vector &phdrs) { if (sec.phdrs.empty()) { diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -2276,7 +2276,7 @@ Default = Out::elfHeader; auto define = [=](StringRef start, StringRef end, OutputSection *os) { - if (os) { + if (os && !script->isDiscarded(os)) { addOptionalRegular(start, os, 0); addOptionalRegular(end, os, -1); } else { diff --git a/lld/test/ELF/linkerscript/preinit-array-empty.test b/lld/test/ELF/linkerscript/preinit-array-empty.test new file mode 100644 --- /dev/null +++ b/lld/test/ELF/linkerscript/preinit-array-empty.test @@ -0,0 +1,39 @@ +# REQUIRES: x86 +# RUN: split-file %s %t +# RUN: llvm-mc -filetype=obj -triple=x86_64 %t/t.s -o %t.o + +## PR52534: https://bugs.llvm.org/show_bug.cgi?id=52534 +## Check case where .preinit_array is discarded. +## Link should succeed without causing an out of range relocation error. +# RUN: ld.lld -T %t/discarded.script %t.o -o %t1 --image-base=0x80000000 +# RUN: llvm-readelf -s %t1 | FileCheck --check-prefixes=CHECK,DISCARDED %s + +## Check case where .preinit_array is emitted but empty. +# RUN: ld.lld -T %t/empty.script %t.o -o %t2 +# RUN: llvm-readelf -s %t2 | FileCheck --check-prefixes=CHECK,EMPTY %s + +# CHECK: [[#%x,ADDR:]] 0 NOTYPE LOCAL HIDDEN [[#]] __preinit_array_start +# CHECK-NEXT: [[#ADDR]] 0 NOTYPE LOCAL HIDDEN [[#]] __preinit_array_end + +# DISCARDED-NEXT: [[#ADDR]] 0 NOTYPE GLOBAL DEFAULT [[#]] _start + +# EMPTY-NOT: [[#ADDR]] 0 NOTYPE GLOBAL DEFAULT [[#]] _start +# EMPTY: [[#ADDR]] 0 NOTYPE GLOBAL DEFAULT [[#]] ADDR + +#--- t.s +.global _start +_start: + movq __preinit_array_start@GOTPCREL(%rip),%rax + movq __preinit_array_end@GOTPCREL(%rip),%rax + +#--- discarded.script +SECTIONS { + .text : { *(.text); } + .preinit_array : { *(.preinit_array); } +} + +#--- empty.script +SECTIONS { + .text : { *(.text); } + .preinit_array : { ADDR = .; *(.preinit_array); } +}