[binutils-gdb] ld: fix segfault caused by untagged stub sections

Matthieu Longo mlongo@sourceware.org
Fri Mar 6 10:36:15 GMT 2026


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=9bfff0276949797912a15e3d6aeb790ecd9c58b2

commit 9bfff0276949797912a15e3d6aeb790ecd9c58b2
Author: Matthieu Longo <matthieu.longo@arm.com>
Date:   Tue Sep 9 10:12:55 2025 +0100

    ld: fix segfault caused by untagged stub sections
    
    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.
    
    Approved-By: Jan Beulich <jbeulich@suse.com>
    Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31412
    
    [1]: abf874a, Add support for non-contiguous memory regions.

Diff:
---
 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 7e8d0e4053e..478a8af2bed 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -718,6 +718,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 bdd0bd22b95..74c0fe026f0 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -3600,6 +3600,9 @@ void _bfd_link_reloc_status_error
   /* 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 3224ddb6863..a958f71055a 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.  *}
 .
@@ -727,6 +733,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 5ddbed99724..010dbbfaed2 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 557d8aea3dc..a17d6136a2b 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 3b1c321e73d..c9e8db6e85d 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 df19dca6c17..05ea97517ff 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -5757,10 +5757,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 "
@@ -8448,8 +8450,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'


More information about the Binutils-cvs mailing list