[PATCH v3 1/3] ld: sframe: do not generate .sframe for PLT if no .sframe is in input BFDs
Indu Bhagat
indu.bhagat@oracle.com
Sun Jan 25 00:15:51 GMT 2026
On 1/22/26 12:36 AM, Jens Remus wrote:
> On 1/22/2026 9:00 AM, claudiu.zissulescu-ianculescu@oracle.com wrote:
>> From: Indu Bhagat <indu.bhagat@oracle.com>
>>
>> GNU ld creates SFrame stack trace info for the .plt* sections. These
>> linker created sections are created in
>> _bfd_x86_elf_link_setup_gnu_properties (), but data is emitted into
>> them a bit later in _bfd_x86_elf_late_size_sections ().
>
> This is true for x86, but not s390. Maybe reword as follows:
>
> 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 it is emitted into the
> section a bit later in late_size_sections. Note that for aarch64 the
> linker does not create .sframe for PLT yet.
>
OK. Thanks.
>> With SFrame sections now marked KEEP in scripttempl/elf.sc, and the
>> presence of these linker created SFrame sections cause emission of an
>> empty .sframe for all ELF targets (IIUC), even when all input bfd's
>> have no .sframe section.
>
> Due to the reordering of the patches this is no longer true. Maybe
> reword as follows:
>
> A subsequent commit will mark .sframe sections KEEP in the default
> linker script (scripttempl/elf.sc), and the presence of these linker-
> created .sframe sections cause emission of an empty .sframe section for
> all ELF targets (IIUC), even when none of the input BFDs have any
> .sframe section.
>
> Can you please explain how the linker-created .sframe sections cause
> an empty .sframe section in general? How does the creation of an
> .sframe section on x86/s390 affect any other ELF target (e.g. aarch64)?
>
For x86/s390, we create .sframe sections early in the linking process as
you point out. Now if the sections are also marked KEEP, the linker
will setup an output .sframe section, seeing that some input BFDs have
.sframe section in them (the generic linker parts do not check for size
of the respective input sections etc. when they do this). This is done
in lang_add_section --> init_os () if I am not wrong.
So even if we later do not emit any data into the .sframe sections
(corresponding to the .plt*), empty .sframe will make it to the output file.
This should not (and does not) happen for aarch64, because there are no
linker-generated .sframe sections. IOW, we expect:
- no .sframe section in output file if the target does not support SFrame
- no .sframe section in output if the target supports SFrame, but
user did not enable SFrame (via --gsframe or similar).
>> This patch is an attempt to avoid creation of empty .sframe in linked
>> objects on x86_64 and s390x, when none of the inputs had SFrame sections.
>
> In general I understand that it is a good idea not to produce linker-
> created .sframe for PLT if none of the input BFDs have any .sframe.
> If a user compiles without --gsframe there should not be .sframe for
> the PLT after linking.
>
> But how this relates to empty .sframe sections to be generated is
> unclear.
>
Hopefully the above helps?
>>
>> bfd/
>> * 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.
>> ---
>> bfd/elf-bfd.h | 2 ++
>> bfd/elf-sframe.c | 18 ++++++++++++++++++
>> bfd/elf64-s390.c | 10 +++++++++-
>> bfd/elfxx-x86.c | 11 ++++++++++-
>> 4 files changed, 39 insertions(+), 2 deletions(-)
>
> For the code changes:
>
> Reviewed-by: Jens Remus <jremus@linux.ibm.com>
>
Thanks for reviewing. I am considering to commit this fix after testing
(and not wait for PR/32769) due to
https://sourceware.org/pipermail/binutils/2026-January/147828.html.
Filed PR ld/33830 to track that.
>>
>> diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
>> index 4fa4461cba3..c833b7d101b 100644
>> --- a/bfd/elf-bfd.h
>> +++ b/bfd/elf-bfd.h
>> @@ -2581,6 +2581,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..b6a903db42a 100644
>> --- a/bfd/elf64-s390.c
>> +++ b/bfd/elf64-s390.c
>> @@ -4309,7 +4309,15 @@ elf_s390_create_dynamic_sections (bfd *dynobj,
>> }
>>
>> /* Create .sframe section for .plt section. */
>> - if (!info->discard_sframe)
>> + bool gen_plt_sframe_p = !info->discard_sframe
>> + && _bfd_elf_sframe_present_input_bfds (info);
>> +
>> + /* 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. */
>> + 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..b450aed4038 100644
>> --- a/bfd/elfxx-x86.c
>> +++ b/bfd/elfxx-x86.c
>> @@ -4827,7 +4827,16 @@ _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)
>> + bool gen_plt_sframe_p = ABI_64_P (info->output_bfd)
>> + && !info->discard_sframe
>> + && _bfd_elf_sframe_present_input_bfds (info);
>> +
>> + /* 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. */
>> + if (gen_plt_sframe_p)
>> {
>> flagword flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
>> | SEC_HAS_CONTENTS | SEC_IN_MEMORY
>
> Regards,
> Jens
More information about the Binutils
mailing list