[lld] [lld][ELF] Refresh .eh_frame_hdr cache after cfi jump table relaxation (PR #228218)
Martin Levesque via llvm-commits
llvm-commits at lists.llvm.org
Thu Oct 1 12:46:09 PDT 2026
https://github.com/wehzzz created https://github.com/llvm/llvm-project/pull/228218
relaxCFIJumpTables() can change section placement without changing the output size. Preserve that change in the fixed-point loop so at least one more address assignment is done, thus rebuilding the cached FDE entries in .eh_frame_hdr.
Fixes #226166
>From 70136109b3eb8fabd9d7ee3332e3c43b4aedf152 Mon Sep 17 00:00:00 2001
From: Martin Levesque <martin.levesque at datadoghq.com>
Date: Thu, 1 Oct 2026 19:33:31 +0000
Subject: [PATCH] [lld][ELF] Refresh .eh_frame_hdr cache after cfi jump table
relaxation
relaxCFIJumpTables() can change section placement without changing the
output size. Preserve that change in the fixed-point loop so at least one
more address assignment is done, thus rebuilding the cached FDE
entries in .eh_frame_hdr.
Fixes #226166
---
lld/ELF/Arch/X86_64.cpp | 7 +-
lld/ELF/Target.h | 5 +-
lld/ELF/Writer.cpp | 11 +--
lld/test/ELF/eh-frame-hdr-cfi-jump-tables.s | 78 +++++++++++++++++++++
4 files changed, 92 insertions(+), 9 deletions(-)
create mode 100644 lld/test/ELF/eh-frame-hdr-cfi-jump-tables.s
diff --git a/lld/ELF/Arch/X86_64.cpp b/lld/ELF/Arch/X86_64.cpp
index c1d1db1d0133c..596b3c47115a6 100644
--- a/lld/ELF/Arch/X86_64.cpp
+++ b/lld/ELF/Arch/X86_64.cpp
@@ -51,7 +51,7 @@ class X86_64 : public TargetInfo {
bool deleteFallThruJmpInsn(InputSection &is,
InputSection *nextIS) const override;
bool relaxOnce(int pass) const override;
- void relaxCFIJumpTables() const override;
+ bool relaxCFIJumpTables() const override;
void applyBranchToBranchOpt() const override;
template <class ELFT, class RelTy>
void scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
@@ -314,7 +314,7 @@ bool X86_64::deleteFallThruJmpInsn(InputSection &is,
return true;
}
-void X86_64::relaxCFIJumpTables() const {
+bool X86_64::relaxCFIJumpTables() const {
// Relax CFI jump tables.
// - Split jump table into pieces and place target functions inside the jump
// table if small enough.
@@ -480,7 +480,7 @@ void X86_64::relaxCFIJumpTables() const {
}
if (sectionReplacements.empty())
- return;
+ return false;
// Now that we have the complete mapping of replacements, go through the input
// section lists and apply the replacements.
@@ -502,6 +502,7 @@ void X86_64::relaxCFIJumpTables() const {
isd->sections = std::move(newSections);
}
}
+ return true;
}
bool X86_64::relaxOnce(int pass) const {
diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index 174e389db155d..ef9c2eb1f484a 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -115,8 +115,9 @@ class TargetInfo {
// Do a linker relaxation pass and return true if we changed something.
virtual bool relaxOnce(int pass) const { return false; }
- // Relax CFI jump tables if implemented by target.
- virtual void relaxCFIJumpTables() const {}
+ // Relax CFI jump tables if implemented by target and return true if we
+ // changed something.
+ virtual bool relaxCFIJumpTables() const { return false; }
virtual bool synthesizeAlign(uint64_t &dot, InputSection *sec) {
return false;
}
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 4fac47b548936..eaddc96804e0d 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -1520,16 +1520,19 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
if (ctx.arg.randomizeSectionPadding)
randomizeSectionPadding(ctx);
+ bool cfiJumpTablesChanged = false;
if (ctx.arg.branchToBranch)
- ctx.target->relaxCFIJumpTables();
+ cfiJumpTablesChanged = ctx.target->relaxCFIJumpTables();
// Iterate until a fixed point is reached, skipping relocatable links since
// the final addresses are unavailable.
uint32_t pass = 0, assignPasses = 0;
while (!ctx.arg.relocatable) {
- bool changed = ctx.target->needsThunks
- ? tc.createThunks(pass, ctx.outputSections)
- : ctx.target->relaxOnce(pass);
+ bool changed = cfiJumpTablesChanged;
+ cfiJumpTablesChanged = false;
+ changed |= ctx.target->needsThunks
+ ? tc.createThunks(pass, ctx.outputSections)
+ : ctx.target->relaxOnce(pass);
bool spilled = ctx.script->spillSections();
changed |= spilled;
++pass;
diff --git a/lld/test/ELF/eh-frame-hdr-cfi-jump-tables.s b/lld/test/ELF/eh-frame-hdr-cfi-jump-tables.s
new file mode 100644
index 0000000000000..9bbeddd4690aa
--- /dev/null
+++ b/lld/test/ELF/eh-frame-hdr-cfi-jump-tables.s
@@ -0,0 +1,78 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
+# RUN: ld.lld --eh-frame-hdr -O2 --no-branch-to-branch %t.o -shared -o %t2
+# RUN: llvm-readelf --unwind %t2 | FileCheck %s
+# RUN: ld.lld --eh-frame-hdr -O2 %t.o -shared -o %t
+# RUN: llvm-readelf --unwind %t | FileCheck %s
+
+# CHECK: fde_count: 1
+# CHECK: entry 0 {
+# CHECK-NEXT: initial_location: [[PC:0x[0-9a-f]+]]
+# CHECK-NEXT: address: [[FDE:0x[0-9a-f]+]]
+# CHECK: [{{ *}}[[FDE]]{{ *}}] FDE
+# CHECK-NEXT: initial_location: [[PC]]
+
+.section .text.jt,"ax", at llvm_cfi_jump_table,8
+.type f1, at function
+f1:
+ jmp f1.cfi
+ .balign 8, 0xcc
+.type f2, at function
+f2:
+ jmp f2.cfi
+ .balign 8, 0xcc
+.type f3, at function
+f3:
+ jmp f3.cfi
+ .balign 8, 0xcc
+.type f4, at function
+f4:
+ jmp f4.cfi
+ .balign 8, 0xcc
+
+.section .text.f1,"ax", at progbits
+f1.cfi:
+ ret
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+
+.section .text.f2,"ax", at progbits
+f2.cfi:
+ ret
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+
+.section .text.f3,"ax", at progbits
+f3.cfi:
+ ret
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+
+.section .text.f4,"ax", at progbits
+.type f4.cfi, at function
+f4.cfi:
+ .cfi_startproc
+ ret
+ .cfi_endproc
+.size f4.cfi, .-f4.cfi
More information about the llvm-commits
mailing list