[lld] [lld][ELF] Fix crash when relaxation pass encounters synthetic sections (PR #184758)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Mar 5 00:41:25 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-lld-elf
Author: wanglei (wangleiat)
<details>
<summary>Changes</summary>
In LoongArch and RISC-V, the relaxation pass iterates over input sections
within executable output sections. When a linker script places a synthetic
section (e.g., .got) into such an output section, the linker would crash
because synthetic sections do not have the relaxAux field initialized.
The relaxAux data structure is only allocated for non-synthetic sections
in initSymbolAnchors. This patch adds the necessary null checks in the
relaxation loops (relaxOnce and finalizeRelax) to skip sections that
do not require relaxation.
A null check is also added to elf::initSymbolAnchors to ensure the
subsequent sorting of anchors is safe.
Fixes: #<!-- -->184757
---
Full diff: https://github.com/llvm/llvm-project/pull/184758.diff
4 Files Affected:
- (modified) lld/ELF/Arch/LoongArch.cpp (+6-1)
- (modified) lld/ELF/Arch/RISCV.cpp (+10-1)
- (added) lld/test/ELF/loongarch-relax-synthetic-in-text.s (+27)
- (added) lld/test/ELF/riscv-relax-synthetic-in-text.s (+28)
``````````diff
diff --git a/lld/ELF/Arch/LoongArch.cpp b/lld/ELF/Arch/LoongArch.cpp
index 90641de736c74..03eb0badac77c 100644
--- a/lld/ELF/Arch/LoongArch.cpp
+++ b/lld/ELF/Arch/LoongArch.cpp
@@ -1685,8 +1685,11 @@ bool LoongArch::relaxOnce(int pass) const {
for (OutputSection *osec : ctx.outputSections) {
if (!(osec->flags & SHF_EXECINSTR))
continue;
- for (InputSection *sec : getInputSections(*osec, storage))
+ for (InputSection *sec : getInputSections(*osec, storage)) {
+ if (!sec->relaxAux)
+ continue;
changed |= relax(ctx, *sec);
+ }
}
return changed;
}
@@ -1698,6 +1701,8 @@ void LoongArch::finalizeRelax(int passes) const {
if (!(osec->flags & SHF_EXECINSTR))
continue;
for (InputSection *sec : getInputSections(*osec, storage)) {
+ if (!sec->relaxAux)
+ continue;
RelaxAux &aux = *sec->relaxAux;
if (!aux.relocDeltas)
continue;
diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 85f49c9260565..43afa4c115459 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -743,6 +743,8 @@ void elf::initSymbolAnchors(Ctx &ctx) {
if (!(osec->flags & SHF_EXECINSTR))
continue;
for (InputSection *sec : getInputSections(*osec, storage)) {
+ if (isa<SyntheticSection>(sec))
+ continue;
sec->relaxAux = make<RelaxAux>();
if (sec->relocs().size()) {
sec->relaxAux->relocDeltas =
@@ -787,6 +789,8 @@ void elf::initSymbolAnchors(Ctx &ctx) {
if (!(osec->flags & SHF_EXECINSTR))
continue;
for (InputSection *sec : getInputSections(*osec, storage)) {
+ if (!sec->relaxAux)
+ continue;
llvm::sort(sec->relaxAux->anchors, [](auto &a, auto &b) {
return std::make_pair(a.offset, a.end) <
std::make_pair(b.offset, b.end);
@@ -1016,8 +1020,11 @@ bool RISCV::relaxOnce(int pass) const {
for (OutputSection *osec : ctx.outputSections) {
if (!(osec->flags & SHF_EXECINSTR))
continue;
- for (InputSection *sec : getInputSections(*osec, storage))
+ for (InputSection *sec : getInputSections(*osec, storage)) {
+ if (!sec->relaxAux)
+ continue;
changed |= relax(ctx, pass, *sec);
+ }
}
return changed;
}
@@ -1141,6 +1148,8 @@ void RISCV::finalizeRelax(int passes) const {
if (!(osec->flags & SHF_EXECINSTR))
continue;
for (InputSection *sec : getInputSections(*osec, storage)) {
+ if (!sec->relaxAux)
+ continue;
RelaxAux &aux = *sec->relaxAux;
if (!aux.relocDeltas)
continue;
diff --git a/lld/test/ELF/loongarch-relax-synthetic-in-text.s b/lld/test/ELF/loongarch-relax-synthetic-in-text.s
new file mode 100644
index 0000000000000..d96873d4e53da
--- /dev/null
+++ b/lld/test/ELF/loongarch-relax-synthetic-in-text.s
@@ -0,0 +1,27 @@
+# REQUIRES: loongarch
+# RUN: rm -rf %t && split-file %s %t
+# RUN: llvm-mc --filetype=obj -triple=loongarch64 %t/a.s -o %t/a.o
+# RUN: ld.lld -T %t/a.ld %t/a.o -o /dev/null
+
+## This test ensures that the relaxation pass does not crash when it encounters
+## a synthetic section (like .got) that has been placed inside an executable
+## output section via a linker script. Synthetic sections do not have relaxAux
+## data structures initialized.
+
+#--- a.s
+.global _start
+_start:
+ pcalau12i $a0, %got_pc_hi20(sym)
+ ld.d $a0, $a0, %got_pc_lo12(sym)
+
+.data
+sym:
+ .word 0
+
+#--- a.ld
+SECTIONS {
+ .text : {
+ *(.text)
+ *(.got)
+ }
+}
diff --git a/lld/test/ELF/riscv-relax-synthetic-in-text.s b/lld/test/ELF/riscv-relax-synthetic-in-text.s
new file mode 100644
index 0000000000000..3b01c147c41fe
--- /dev/null
+++ b/lld/test/ELF/riscv-relax-synthetic-in-text.s
@@ -0,0 +1,28 @@
+# REQUIRES: riscv
+# RUN: rm -rf %t && split-file %s %t
+# RUN: llvm-mc --filetype=obj -triple=riscv64 %t/a.s -o %t/a.o
+# RUN: ld.lld -T %t/a.ld %t/a.o -o /dev/null
+
+## This test ensures that the relaxation pass does not crash when it encounters
+## a synthetic section (like .got) that has been placed inside an executable
+## output section via a linker script. Synthetic sections do not have relaxAux
+## data structures initialized.
+
+#--- a.s
+.global _start
+_start:
+1:
+ auipc a0, %got_pcrel_hi(sym)
+ ld a0, %pcrel_lo(1b)(a0)
+
+.data
+sym:
+ .word 0
+
+#--- a.ld
+SECTIONS {
+ .text : {
+ *(.text)
+ *(.got)
+ }
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/184758
More information about the llvm-commits
mailing list