From: Indu Bhagat Date: Thu, 22 Jan 2026 08:00:36 +0000 (+0200) Subject: ld: sframe: do not generate .sframe for PLT if no .sframe is in input BFDs X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=11e0dbc08927f9d84ebbd98346d7016140049f5b;p=thirdparty%2Fbinutils-gdb.git 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 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 --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"