Skip to content

Conversation

quic-k
Copy link
Contributor

@quic-k quic-k commented Oct 9, 2025

This patch makes LLD explicitly define the following symbols during linking:

  • __eh_frame_start
  • __eh_frame_end
  • __eh_frame_hdr_start
  • __eh_frame_hdr_end

These are expected by libunwind for exception handling in baremetal builds
Right now, these symbols need to be explicitly defined by the linker script.
I think its better that the linker defines them implicitly, so we don't have to create a linker script only because we needed to provide these symbols

I am interested in feedback on this change

Libunwind needs the following symbols for exception handling

__eh_frame_start __eh_frame_end __eh_frame_hdr_start __eh_frame_hdr_end

Instead of using a linker script just to define these symbols, let the
linker define them as standard symbols.
Now we can create static executable with libunwind without a linker
script.

Signed-off-by: Kushal Pal <[email protected]>
@llvmbot
Copy link
Member

llvmbot commented Oct 9, 2025

@llvm/pr-subscribers-lld

@llvm/pr-subscribers-lld-elf

Author: None (quic-k)

Changes

This patch makes LLD explicitly define the following symbols during linking:

  • __eh_frame_start
  • __eh_frame_end
  • __eh_frame_hdr_start
  • __eh_frame_hdr_end

These are expected by libunwind for exception handling in baremetal builds
Right now, these symbols need to be explicitly defined by the linker script.
I think its better that the linker defines them implicitly, so we don't have to create a linker script only because we needed to provide these symbols

I am interested in feedback on this change


Full diff: https://github.com/llvm/llvm-project/pull/162638.diff

2 Files Affected:

  • (modified) lld/ELF/Writer.cpp (+6)
  • (added) lld/test/ELF/eh-frame-syms.s (+25)
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 4fa80397cbfa7..145bff5007466 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -2269,6 +2269,12 @@ template <class ELFT> void Writer<ELFT>::addStartEndSymbols() {
   define("__preinit_array_start", "__preinit_array_end", ctx.out.preinitArray);
   define("__init_array_start", "__init_array_end", ctx.out.initArray);
   define("__fini_array_start", "__fini_array_end", ctx.out.finiArray);
+  // Define __eh_frame_* symbols, libunwind needs these symbols.
+  // Define them only if the corresponding output section exists.
+  if (OutputSection *sec = findSection(ctx, ".eh_frame"))
+    define("__eh_frame_start", "__eh_frame_end", sec);
+  if (OutputSection *sec = findSection(ctx, ".eh_frame_hdr"))
+    define("__eh_frame_hdr_start", "__eh_frame_hdr_end", sec);
 
   // As a special case, don't unnecessarily retain .ARM.exidx, which would
   // create an empty PT_ARM_EXIDX.
diff --git a/lld/test/ELF/eh-frame-syms.s b/lld/test/ELF/eh-frame-syms.s
new file mode 100644
index 0000000000000..074221ade4fb6
--- /dev/null
+++ b/lld/test/ELF/eh-frame-syms.s
@@ -0,0 +1,25 @@
+## Verify that lld defines __eh_frame_start/end and __eh_frame_hdr_start/end symbols
+
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: ld.lld %t.o -eh-frame-hdr -o %t
+# RUN: llvm-readelf -s %t | FileCheck %s --check-prefix=SYM
+
+# SYM: __eh_frame_start
+# SYM: __eh_frame_end
+# SYM: __eh_frame_hdr_start
+# SYM: __eh_frame_hdr_end
+
+.text
+.globl _start
+.type _start, @function
+_start:
+  nop
+
+# Emit references so the link will fail if these are not defined
+check_symbol:
+  .quad __eh_frame_start
+  .quad __eh_frame_end
+  .quad __eh_frame_hdr_start
+  .quad __eh_frame_hdr_end

define("__preinit_array_start", "__preinit_array_end", ctx.out.preinitArray);
define("__init_array_start", "__init_array_end", ctx.out.initArray);
define("__fini_array_start", "__fini_array_end", ctx.out.finiArray);
// Define __eh_frame_* symbols, libunwind needs these symbols.
Copy link
Collaborator

@smithp35 smithp35 Oct 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The __eh_frame_* symbols are equivalent to __exidx_start and __exidx_end so this seems reasonable to me.

llvm libunwind reference with suggested linker script fragment (https://github.com/llvm/llvm-project/blob/main/libunwind/src/AddressSpace.hpp#L75)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, with this patch we won't have to use that fragment just to provide these symbols

IIUC __exidx_start/end are ARM's version of __eh_frame_start/end, right?
does lld define them?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, just below on line 2281. I think I added that before we had good linker script support, could also be that some unwinders even on Linux might use those symbols.

@MaskRay
Copy link
Member

MaskRay commented Oct 12, 2025

These symbols not even in GNU ld's internal linker script. I think you should explicitly define them in linker scripts:

.eh_frame : {
  PROVIDE(__eh_frame_start = .);
  *(.eh_frame)
  PROVIDE(__eh_frame_end = .);
}

Copy link
Member

@MaskRay MaskRay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants