[binutils-gdb] ld: sframe: do not generate .sframe for PLT if no .sframe is in input BFDs
Indu Bhagat
ibhagat@sourceware.org
Sun Jan 25 07:00:18 GMT 2026
https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=11e0dbc08927f9d84ebbd98346d7016140049f5b
commit 11e0dbc08927f9d84ebbd98346d7016140049f5b
Author: Indu Bhagat <indu.bhagat@oracle.com>
Date: Thu Jan 22 10:00:36 2026 +0200
ld: sframe: do not generate .sframe for PLT if no .sframe is in input BFDs
GNU ld creates SFrame stack trace info for the PLT. For x86 the linker-
created .sframe section is created in setup_gnu_properties. For s390 it
is created in create_dynamic_sections. For both, the section data is
itself emitted a bit later in late_size_sections. Note that for aarch64 the
linker does not create .sframe for PLT yet.
Recall that a previous patch 832ca9ef670 uncoupled
--no-ld-generated-unwind-info from the linker-generated .sframe
sections. This means that the linker now generates .sframe section (for
.plt*) for the first input BFD enthusiatically even when none of the
input BFDs have any .sframe section, unless --discard-sframe is also
added. The issue is that these (unexpected) linker-generated .sframe
sections (on x86_64, and s390) may now trip the linking process, e.g.,
when using --orphan-handling=error together with a linker script that
treats .sframe differently than the default linker script.
https://sourceware.org/pipermail/binutils/2026-January/147826.html
Further, with SFrame sections to be soon marked KEEP for fixing
GC/SFrame (PR ld/32769), the presence of these linker generated SFrame
sections will also cause emission of an empty .sframe (for x86_64 and
s390x), even when all input bfd's have no .sframe section.
This patch avoids creation of .sframe for .plt* if none of the input
BFDs had any .sframe section. This then avoids creation of empty
.sframe in linked objects on x86_64 and s390x, when none of the inputs
have SFrame sections. This also fixes PR ld/33830.
For the code changes:
Reviewed-by: Jens Remus <jremus@linux.ibm.com>
New testcases have (since the above Reviewed-by) been added. Since
--no-ld-generated-unwind-info is not supported on aarch64, add
target-specific ld tests. Additionally add a generic test (for all
targets that support SFrame) to ensure no output .sframe is generated if
users says no --gsframe or similar.
bfd/
PR ld/33830
* elf-bfd.h (_bfd_elf_sframe_present_input_bfds): New
declaration.
* elf-sframe.c (_bfd_elf_sframe_present_input_bfds): New
definition.
* elf64-s390.c (elf_s390_create_dynamic_sections): Do not
generate .sframe for .plt unconditionally.
* elfxx-x86.c (_bfd_x86_elf_link_setup_gnu_properties):
Likewise.
ld/testsuite/
PR ld/33830
* ld-s390/no-sframe.ld: Linker script with no specification for
SFrame sections.
* ld-s390/s390.exp: Add new test.
* ld-s390/sframe-command-line-2.d: New testcase that uses
--no-ld-generated-unwind-info and a linker script that has no
specific rules for .sframe.
* ld-x86-64/no-sframe.ld: Likewise for x86_64.
* ld-x86-64/sframe-command-line-2.d: Likewise for x86_64.
* ld-x86-64/x86-64.exp: Add new test.
* ld-sframe/no-ld-generated-sframe.d: Ensure no .sframe in
output if no .sframe in input.
* ld-sframe/no-sframe.ld: Linker script with no specification
for SFrame sections.
* ld-sframe/test.s: Add new test.
Diff:
---
bfd/elf-bfd.h | 2 ++
bfd/elf-sframe.c | 18 ++++++++++++++++++
bfd/elf64-s390.c | 11 +++++++++--
bfd/elfxx-x86.c | 13 +++++++++++--
ld/testsuite/ld-s390/no-sframe.ld | 6 ++++++
ld/testsuite/ld-s390/s390.exp | 3 +++
ld/testsuite/ld-s390/sframe-command-line-2.d | 10 ++++++++++
ld/testsuite/ld-sframe/no-ld-generated-sframe.d | 10 ++++++++++
ld/testsuite/ld-sframe/no-sframe.ld | 6 ++++++
ld/testsuite/ld-sframe/test.s | 11 +++++++++++
ld/testsuite/ld-x86-64/no-sframe.ld | 6 ++++++
ld/testsuite/ld-x86-64/sframe-command-line-2.d | 10 ++++++++++
ld/testsuite/ld-x86-64/x86-64.exp | 3 +++
13 files changed, 105 insertions(+), 4 deletions(-)
diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index f368986dd05..c7ef031979e 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -2628,6 +2628,8 @@ extern bool _bfd_elf_maybe_strip_eh_frame_hdr
extern bool _bfd_elf_sframe_present
(struct bfd_link_info *) ATTRIBUTE_HIDDEN;
+extern bool _bfd_elf_sframe_present_input_bfds
+ (struct bfd_link_info *) ATTRIBUTE_HIDDEN;
extern bool _bfd_elf_parse_sframe
(bfd *, struct bfd_link_info *, asection *, struct elf_reloc_cookie *)
ATTRIBUTE_HIDDEN;
diff --git a/bfd/elf-sframe.c b/bfd/elf-sframe.c
index 33e4df5b1c7..77d9d33d602 100644
--- a/bfd/elf-sframe.c
+++ b/bfd/elf-sframe.c
@@ -171,6 +171,24 @@ sframe_read_value (bfd *abfd, bfd_byte *contents, unsigned int offset,
return value;
}
+/* Return true if any of the input BFDs contains at least one .sframe
+ section. */
+
+bool
+_bfd_elf_sframe_present_input_bfds (struct bfd_link_info *info)
+{
+ /* Find if any input file has an .sframe section. */
+ for (bfd *pbfd = info->input_bfds; pbfd != NULL; pbfd = pbfd->link.next)
+ if (bfd_get_flavour (pbfd) == bfd_target_elf_flavour
+ && bfd_count_sections (pbfd) != 0)
+ {
+ asection *sec = bfd_get_section_by_name (pbfd, ".sframe");
+ if (sec != NULL)
+ return true;
+ }
+ return false;
+}
+
/* Return true if there is at least one non-empty .sframe section in
input files. Can only be called after ld has mapped input to
output sections, and before sections are stripped. */
diff --git a/bfd/elf64-s390.c b/bfd/elf64-s390.c
index 8ad10ce6bd1..b109ae3154e 100644
--- a/bfd/elf64-s390.c
+++ b/bfd/elf64-s390.c
@@ -4308,8 +4308,15 @@ elf_s390_create_dynamic_sections (bfd *dynobj,
}
}
- /* Create .sframe section for .plt section. */
- if (!info->discard_sframe)
+ /* Create .sframe section for .plt section.
+ Do not make SFrame sections for dynobj unconditionally. If there
+ are no SFrame sections for any input files, skip creating the linker
+ created SFrame sections too. Since SFrame sections are marked KEEP,
+ prohibiting these linker-created SFrame sections when unnecessary,
+ helps avoid creating of empty SFrame sections in the output. */
+ bool gen_plt_sframe_p = (_bfd_elf_sframe_present_input_bfds (info)
+ && !info->discard_sframe);
+ if (gen_plt_sframe_p)
{
flagword flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
| SEC_HAS_CONTENTS | SEC_IN_MEMORY
diff --git a/bfd/elfxx-x86.c b/bfd/elfxx-x86.c
index 901b858fb34..f476c04c888 100644
--- a/bfd/elfxx-x86.c
+++ b/bfd/elfxx-x86.c
@@ -4826,8 +4826,17 @@ _bfd_x86_elf_link_setup_gnu_properties
}
}
- /* .sframe sections are emitted for AMD64 ABI only. */
- if (ABI_64_P (info->output_bfd) && !info->discard_sframe)
+ /* Create .sframe section for .plt section. SFrame sections are
+ supported for AMD64 ABI only. Further, do not make SFrame sections
+ for dynobj unconditionally. If there are no SFrame sections for any
+ input files, skip creating the linker created SFrame sections too.
+ Since SFrame sections are marked KEEP, prohibiting these
+ linker-created SFrame sections, when unnecessary, helps avoid creation
+ of empty SFrame sections in the output. */
+ bool gen_plt_sframe_p = (_bfd_elf_sframe_present_input_bfds (info)
+ && !info->discard_sframe
+ && ABI_64_P (info->output_bfd));
+ if (gen_plt_sframe_p)
{
flagword flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
| SEC_HAS_CONTENTS | SEC_IN_MEMORY
diff --git a/ld/testsuite/ld-s390/no-sframe.ld b/ld/testsuite/ld-s390/no-sframe.ld
new file mode 100644
index 00000000000..13224d84573
--- /dev/null
+++ b/ld/testsuite/ld-s390/no-sframe.ld
@@ -0,0 +1,6 @@
+ENTRY(_start)
+SECTIONS
+{
+ . = SIZEOF_HEADERS;
+ .text : { *(.text) }
+}
diff --git a/ld/testsuite/ld-s390/s390.exp b/ld/testsuite/ld-s390/s390.exp
index 0fdfb39c5aa..80536e2e39a 100644
--- a/ld/testsuite/ld-s390/s390.exp
+++ b/ld/testsuite/ld-s390/s390.exp
@@ -192,6 +192,9 @@ if [istarget "s390x-*-*"] {
if { ![skip_sframe_tests] } {
run_dump_test "sframe-simple-1"
run_dump_test "sframe-plt-1"
+ # Test --discard-sframe
run_dump_test "sframe-command-line-1"
+ # Test --no-ld-generated-unwind-info
+ run_dump_test "sframe-command-line-2"
}
}
diff --git a/ld/testsuite/ld-s390/sframe-command-line-2.d b/ld/testsuite/ld-s390/sframe-command-line-2.d
new file mode 100644
index 00000000000..941e845139d
--- /dev/null
+++ b/ld/testsuite/ld-s390/sframe-command-line-2.d
@@ -0,0 +1,10 @@
+#as:
+#source: sframe-foo.s
+#ld: --no-ld-generated-unwind-info -T no-sframe.ld -e foo
+#objdump: -hw
+#name: No interaction between --no-ld-generated-unwind-info and SFrame
+
+#failif
+#...
+ [0-9] .sframe .*
+#...
diff --git a/ld/testsuite/ld-sframe/no-ld-generated-sframe.d b/ld/testsuite/ld-sframe/no-ld-generated-sframe.d
new file mode 100644
index 00000000000..bc9396e6ff4
--- /dev/null
+++ b/ld/testsuite/ld-sframe/no-ld-generated-sframe.d
@@ -0,0 +1,10 @@
+#as:
+#source: test.s
+#ld: -T no-sframe.ld
+#objdump: -hw
+#name: No SFrame section in output with no --gsframe
+
+#failif
+#...
+ [0-9] .sframe .*
+#...
diff --git a/ld/testsuite/ld-sframe/no-sframe.ld b/ld/testsuite/ld-sframe/no-sframe.ld
new file mode 100644
index 00000000000..13224d84573
--- /dev/null
+++ b/ld/testsuite/ld-sframe/no-sframe.ld
@@ -0,0 +1,6 @@
+ENTRY(_start)
+SECTIONS
+{
+ . = SIZEOF_HEADERS;
+ .text : { *(.text) }
+}
diff --git a/ld/testsuite/ld-sframe/test.s b/ld/testsuite/ld-sframe/test.s
new file mode 100644
index 00000000000..31e8ea8e7b6
--- /dev/null
+++ b/ld/testsuite/ld-sframe/test.s
@@ -0,0 +1,11 @@
+ .text
+ .globl foo
+ .type foo, @function
+foo:
+ .cfi_startproc
+ .cfi_def_cfa_offset 16
+ .cfi_endproc
+
+ .globl _start
+_start:
+ .long foo
diff --git a/ld/testsuite/ld-x86-64/no-sframe.ld b/ld/testsuite/ld-x86-64/no-sframe.ld
new file mode 100644
index 00000000000..13224d84573
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/no-sframe.ld
@@ -0,0 +1,6 @@
+ENTRY(_start)
+SECTIONS
+{
+ . = SIZEOF_HEADERS;
+ .text : { *(.text) }
+}
diff --git a/ld/testsuite/ld-x86-64/sframe-command-line-2.d b/ld/testsuite/ld-x86-64/sframe-command-line-2.d
new file mode 100644
index 00000000000..941e845139d
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/sframe-command-line-2.d
@@ -0,0 +1,10 @@
+#as:
+#source: sframe-foo.s
+#ld: --no-ld-generated-unwind-info -T no-sframe.ld -e foo
+#objdump: -hw
+#name: No interaction between --no-ld-generated-unwind-info and SFrame
+
+#failif
+#...
+ [0-9] .sframe .*
+#...
diff --git a/ld/testsuite/ld-x86-64/x86-64.exp b/ld/testsuite/ld-x86-64/x86-64.exp
index 8bed9b566bc..5608c42a0db 100644
--- a/ld/testsuite/ld-x86-64/x86-64.exp
+++ b/ld/testsuite/ld-x86-64/x86-64.exp
@@ -588,7 +588,10 @@ run_dump_test "tls-le-pic-3-x32"
if { ![skip_sframe_tests] } {
run_dump_test "sframe-simple-1"
run_dump_test "sframe-link-1"
+ # Test --discard-sframe
run_dump_test "sframe-command-line-1"
+ # Test --no-ld-generated-unwind-info
+ run_dump_test "sframe-command-line-2"
run_dump_test "sframe-reloc-1"
run_dump_test "sframe-plt-1"
run_dump_test "sframe-ibt-plt-1"
More information about the Binutils-cvs
mailing list