]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
ld: sframe: do not generate .sframe for PLT if no .sframe is in input BFDs
authorIndu Bhagat <indu.bhagat@oracle.com>
Thu, 22 Jan 2026 08:00:36 +0000 (10:00 +0200)
committerIndu Bhagat <indu.bhagat@oracle.com>
Sun, 25 Jan 2026 06:48:18 +0000 (22:48 -0800)
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.

13 files changed:
bfd/elf-bfd.h
bfd/elf-sframe.c
bfd/elf64-s390.c
bfd/elfxx-x86.c
ld/testsuite/ld-s390/no-sframe.ld [new file with mode: 0644]
ld/testsuite/ld-s390/s390.exp
ld/testsuite/ld-s390/sframe-command-line-2.d [new file with mode: 0644]
ld/testsuite/ld-sframe/no-ld-generated-sframe.d [new file with mode: 0644]
ld/testsuite/ld-sframe/no-sframe.ld [new file with mode: 0644]
ld/testsuite/ld-sframe/test.s [new file with mode: 0644]
ld/testsuite/ld-x86-64/no-sframe.ld [new file with mode: 0644]
ld/testsuite/ld-x86-64/sframe-command-line-2.d [new file with mode: 0644]
ld/testsuite/ld-x86-64/x86-64.exp

index f368986dd055486e1639ca322018459f53fcfe4d..c7ef031979e309ed25c780fc61c191f55aecfe0e 100644 (file)
@@ -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;
index 33e4df5b1c751367439ced3ab88cba36a5f1c6dd..77d9d33d6022129d51ce3e904c74f4dfec25cb6e 100644 (file)
@@ -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.  */
index 8ad10ce6bd1d289992d55206e7180433b484620f..b109ae3154ec1f9636389e56b78466260d6034fc 100644 (file)
@@ -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
index 901b858fb3446c1e9afb0b489016cc494d664e45..f476c04c8885ba7890ada881a499b8e3c2ecc7dc 100644 (file)
@@ -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 (file)
index 0000000..13224d8
--- /dev/null
@@ -0,0 +1,6 @@
+ENTRY(_start)
+SECTIONS
+{
+  . = SIZEOF_HEADERS;
+  .text : { *(.text) }
+}
index 0fdfb39c5aa2026c792e534831a4879a2b8ffdd5..80536e2e39a0382794ae0ddcd5f3745e61d033b6 100644 (file)
@@ -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 (file)
index 0000000..941e845
--- /dev/null
@@ -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 (file)
index 0000000..bc9396e
--- /dev/null
@@ -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 (file)
index 0000000..13224d8
--- /dev/null
@@ -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 (file)
index 0000000..31e8ea8
--- /dev/null
@@ -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 (file)
index 0000000..13224d8
--- /dev/null
@@ -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 (file)
index 0000000..941e845
--- /dev/null
@@ -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 .*
+#...
index 8bed9b566bc3bcb0e6700de3695a0715ac8ce0e2..5608c42a0db26bc2963ee0c042776eba258cbb6d 100644 (file)
@@ -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"