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.
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;
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. */
}
}
- /* 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
}
}
- /* .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
--- /dev/null
+ENTRY(_start)
+SECTIONS
+{
+ . = SIZEOF_HEADERS;
+ .text : { *(.text) }
+}
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"
}
}
--- /dev/null
+#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 .*
+#...
--- /dev/null
+#as:
+#source: test.s
+#ld: -T no-sframe.ld
+#objdump: -hw
+#name: No SFrame section in output with no --gsframe
+
+#failif
+#...
+ [0-9] .sframe .*
+#...
--- /dev/null
+ENTRY(_start)
+SECTIONS
+{
+ . = SIZEOF_HEADERS;
+ .text : { *(.text) }
+}
--- /dev/null
+ .text
+ .globl foo
+ .type foo, @function
+foo:
+ .cfi_startproc
+ .cfi_def_cfa_offset 16
+ .cfi_endproc
+
+ .globl _start
+_start:
+ .long foo
--- /dev/null
+ENTRY(_start)
+SECTIONS
+{
+ . = SIZEOF_HEADERS;
+ .text : { *(.text) }
+}
--- /dev/null
+#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 .*
+#...
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"