[PATCH v2 1/5] ld: fix segfault caused by untagged stub sections

Christophe Lyon christophe.lyon@linaro.org
Wed Oct 15 11:28:34 GMT 2025


On Tue, 14 Oct 2025 at 12:47, Matthieu Longo <matthieu.longo@arm.com> wrote:
>
> In the case of non-contiguous memory regions, a far-call stub section
> must be assigned to the memory of the section it was originally emitted
> for. If the stub section does not fit, the section is marked as dropped,
> and removed later. To emit a useful message to the user, however, a stub
> section needs to be discernible from sections originating from input
> objects.
>
> Previously [1], this distinction was made using the SEC_LINKER_CREATED
> flag only in the AArch32 backend handler <arch>_add_stub_section. Other
> backends that didn't set this flag on their stub sections skipped required
> checks in ld/ldlang.c:size_input_section(). On AArch64, this caused the
> linker to proceed into code paths that assumed output sections were set,
> instead of reporting fatal errors, and ultimately led to a segmentation
> fault.
>
> However, the SEC_LINKER_CREATED flag does not solely indicate that a
> section was created by the linker. Its original meaning also meant that
> the section should not be handled by the generic relocation code. Reusing
> this flag to identify stub sections, while it appeared to fix the issue,
> introduced unintended side effects. On PowerPC, for instance, it skipped
> relocations present in the stubs and interpreted them as absolute
> addresses.
>
> This patch proposes a new attribute 'veneer', indicating that a section
> contains branch veneers. The attribute is set on AArch32, AArch64 and
> PowerPC immediately after the creation of the stub section. Others
> architectures are left unchanged, as they do not appear to support
> non-contiguous memory regions (no tests were found to verify the fix).
> Additionally, the diagnostic message was improved when a stub cannot be
> placed in the same memory region as its referencing code.
>

Hi Matthieu,

Thanks for this fix, and the time spent on analyzing the problem.

I clearly missed the other implications of SEC_LINKER_CREATED when I
originally implemented support for non-contiguous memory regions.

Regarding other targets, I did not write additional tests, but I did
run the testsuite on a long list of targets provided by Alan at that
time:
https://sourceware.org/pipermail/binutils/2020-February/110310.html

Maybe you did that too? At least that would give confidence that there
is no unexpected side-effect.

FWIW, the approach LGTM.
Thanks for making the error messages clearer ;-)

Christophe


> [1]: abf874a, Add support for non-contiguous memory regions.
> ---
>  bfd/bfd-in2.h                                      |  6 ++++++
>  bfd/libbfd.h                                       |  3 +++
>  bfd/section.c                                      |  9 +++++++++
>  ld/emultempl/aarch64elf.em                         |  2 ++
>  ld/emultempl/armelf.em                             |  5 +++--
>  ld/emultempl/ppc64elf.em                           |  2 ++
>  ld/ldlang.c                                        | 13 +++++++------
>  ld/testsuite/ld-arm/non-contiguous-arm4.d          |  2 +-
>  ld/testsuite/ld-powerpc/non-contiguous-powerpc64.d |  2 +-
>  9 files changed, 34 insertions(+), 10 deletions(-)
>
> diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
> index 5e7c6ddf1ee..6d0c574c601 100644
> --- a/bfd/bfd-in2.h
> +++ b/bfd/bfd-in2.h
> @@ -702,6 +702,12 @@ typedef struct bfd_section
>    /* Nonzero if section contents should not be freed.  */
>    unsigned int alloced:1;
>
> +  /* Indicate that the section contains branch veneers.  This is used when
> +     support for non-contiguous memory regions is enabled.  The veneers have
> +     to be allocated to the same memory region as the code they are refered
> +     by, i.e. they cannot be moved to a subsequent memory region.  */
> +  unsigned int veneer : 1;
> +
>    /* Bits used by various backends.  The generic code doesn't touch
>       these fields.  */
>
> diff --git a/bfd/libbfd.h b/bfd/libbfd.h
> index f2485d99078..5e2bfdb7c61 100644
> --- a/bfd/libbfd.h
> +++ b/bfd/libbfd.h
> @@ -3670,6 +3670,9 @@ bool _bfd_unrecognized_reloc
>    /* segment_mark, sec_info_type, use_rela_p, mmapped_p, alloced,   */ \
>       0,            0,             0,          0,         0,            \
>                                                                        \
> +  /* veneer,                                                        */ \
> +     0,                                                                \
> +                                                                      \
>    /* sec_flg0, sec_flg1, sec_flg2, sec_flg3, sec_flg4, sec_flg5,    */ \
>       0,        0,        0,        0,        0,        0,              \
>                                                                        \
> diff --git a/bfd/section.c b/bfd/section.c
> index 5f0cf6e71cb..9356d1a30f7 100644
> --- a/bfd/section.c
> +++ b/bfd/section.c
> @@ -428,6 +428,12 @@ CODE_FRAGMENT
>  .  {* Nonzero if section contents should not be freed.  *}
>  .  unsigned int alloced:1;
>  .
> +.  {* Indicate that the section contains branch veneers.  This is used when
> +.     support for non-contiguous memory regions is enabled.  The veneers have
> +.     to be allocated to the same memory region as the code they are refered
> +.     by, i.e. they cannot be moved to a subsequent memory region.  *}
> +.  unsigned int veneer : 1;
> +.
>  .  {* Bits used by various backends.  The generic code doesn't touch
>  .     these fields.  *}
>  .
> @@ -723,6 +729,9 @@ INTERNAL
>  .  {* segment_mark, sec_info_type, use_rela_p, mmapped_p, alloced,   *}        \
>  .     0,            0,             0,          0,         0,           \
>  .                                                                      \
> +.  {* veneer,                                                        *}        \
> +.     0,                                                               \
> +.                                                                      \
>  .  {* sec_flg0, sec_flg1, sec_flg2, sec_flg3, sec_flg4, sec_flg5,    *}        \
>  .     0,        0,        0,        0,        0,        0,             \
>  .                                                                      \
> diff --git a/ld/emultempl/aarch64elf.em b/ld/emultempl/aarch64elf.em
> index 91d58d8fe5a..353d6b53425 100644
> --- a/ld/emultempl/aarch64elf.em
> +++ b/ld/emultempl/aarch64elf.em
> @@ -200,6 +200,8 @@ elf${ELFSIZE}_aarch64_add_stub_section (const char *stub_sec_name,
>    if (stub_sec == NULL)
>      goto err_ret;
>
> +  stub_sec->veneer = 1;
> +
>    /* Long branch stubs contain a 64-bit address, so the section requires
>       8 byte alignment.  */
>    bfd_set_section_alignment (stub_sec, 3);
> diff --git a/ld/emultempl/armelf.em b/ld/emultempl/armelf.em
> index 6f652c59a3c..04c8cbd323c 100644
> --- a/ld/emultempl/armelf.em
> +++ b/ld/emultempl/armelf.em
> @@ -237,13 +237,14 @@ elf32_arm_add_stub_section (const char * stub_sec_name,
>    struct hook_stub_info info;
>
>    flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE
> -          | SEC_HAS_CONTENTS | SEC_RELOC | SEC_IN_MEMORY | SEC_KEEP
> -          | SEC_LINKER_CREATED);
> +          | SEC_HAS_CONTENTS | SEC_RELOC | SEC_IN_MEMORY | SEC_KEEP);
>    stub_sec = bfd_make_section_anyway_with_flags (stub_file->the_bfd,
>                                                  stub_sec_name, flags);
>    if (stub_sec == NULL)
>      goto err_ret;
>
> +  stub_sec->veneer = 1;
> +
>    bfd_set_section_alignment (stub_sec, alignment_power);
>
>    os = lang_output_section_get (output_section);
> diff --git a/ld/emultempl/ppc64elf.em b/ld/emultempl/ppc64elf.em
> index 857cf54ad06..5291a8c704a 100644
> --- a/ld/emultempl/ppc64elf.em
> +++ b/ld/emultempl/ppc64elf.em
> @@ -440,6 +440,8 @@ ppc_add_stub_section (const char *stub_sec_name, asection *input_section)
>                                                 : 5)))
>      goto err_ret;
>
> +  stub_sec->veneer = 1;
> +
>    output_section = input_section->output_section;
>    os = lang_output_section_get (output_section);
>
> diff --git a/ld/ldlang.c b/ld/ldlang.c
> index 3c57bf7541c..bdda599a7a4 100644
> --- a/ld/ldlang.c
> +++ b/ld/ldlang.c
> @@ -5637,10 +5637,12 @@ size_input_section
>
>               if (dot + TO_ADDR (i->size) > end)
>                 {
> -                 if (i->flags & SEC_LINKER_CREATED)
> -                   fatal (_("%P: Output section `%pA' not large enough for "
> -                            "the linker-created stubs section `%pA'.\n"),
> -                          i->output_section, i);
> +                 if (i->veneer)
> +                   fatal (_("%P: Memory region `%s' not large enough for the "
> +                            "linker-created stubs section `%pA' associated to "
> +                            "output section `%pA'\n"),
> +                          output_section_statement->region->name_list.name, i,
> +                          i->output_section);
>
>                   if (i->rawsize && i->rawsize != i->size)
>                     fatal (_("%P: Relaxation not supported with "
> @@ -8239,8 +8241,7 @@ warn_non_contiguous_discards (void)
>         continue;
>
>        for (asection *s = file->the_bfd->sections; s != NULL; s = s->next)
> -       if (s->output_section == NULL
> -           && (s->flags & SEC_LINKER_CREATED) == 0)
> +       if (s->output_section == NULL && !s->veneer)
>           einfo (_("%P: warning: --enable-non-contiguous-regions "
>                    "discards section `%pA' from `%pB'\n"),
>                  s, file->the_bfd);
> diff --git a/ld/testsuite/ld-arm/non-contiguous-arm4.d b/ld/testsuite/ld-arm/non-contiguous-arm4.d
> index a8e9d66caca..d4c4b284dce 100644
> --- a/ld/testsuite/ld-arm/non-contiguous-arm4.d
> +++ b/ld/testsuite/ld-arm/non-contiguous-arm4.d
> @@ -1,4 +1,4 @@
>  #name: non-contiguous-arm4
>  #source: non-contiguous-arm.s
>  #ld: --enable-non-contiguous-regions -T non-contiguous-arm4.ld
> -# error: .*Output section .?\.ramu.? not large enough for the linker-created stubs section .?\.code\.3\.__stub.\.?
> +# error: Memory region `RAMU' not large enough for the linker-created stubs section `\.code\.3\.__stub' associated to output section `\.ramu'
> diff --git a/ld/testsuite/ld-powerpc/non-contiguous-powerpc64.d b/ld/testsuite/ld-powerpc/non-contiguous-powerpc64.d
> index 9f903bbea35..1a7e4c5a23b 100644
> --- a/ld/testsuite/ld-powerpc/non-contiguous-powerpc64.d
> +++ b/ld/testsuite/ld-powerpc/non-contiguous-powerpc64.d
> @@ -2,4 +2,4 @@
>  #source: non-contiguous-powerpc.s
>  #as: -a64
>  #ld: -melf64ppc --enable-non-contiguous-regions -T non-contiguous-powerpc.ld
> -#error: .*Could not assign .?\.text\.one\.stub.? to an output section\. Retry without --enable-non-contiguous-regions\.
> +#error: Memory region `one' not large enough for the linker-created stubs section `\.text\.one\.stub' associated to output section `one'
> --
> 2.51.0
>


More information about the Binutils mailing list