[binutils-gdb] PR 33726, symbols in excluded sections

Alan Modra amodra@sourceware.org
Fri Dec 26 01:04:55 GMT 2025


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

commit 4dc7130975cfcbc263f5bad2aa9e35fc3eb10d5c
Author: Alan Modra <amodra@gmail.com>
Date:   Thu Dec 25 22:17:10 2025 +1030

    PR 33726, symbols in excluded sections
    
    This improves "nearby" section choice when memory regions are active,
    preferring a section in the same region as the excluded section over
    other sections.
    
            PR 33726
    include/
            * bfdlink.h (struct bfd_link_callbacks): Add nearby_section.
            (_bfd_nearby_section): Delete.
            (bfd_fix_excluded_sec_syms): Rename and remove bfd param from
            _bfd_fix_excluded_sec_syms.
    bfd/
            * linker.c (_bfd_nearby_section): Delete.
            (fix_syms): Use linker callback.
            * elflink.c (elf_link_input_bfd): Likewise.
            (_bfd_elf_final_link): Update.
    ld/
            * ldemul.c (finish_default): Update.
            * ldlang.c (lang_output_section_get): Delete.
            (ldlang_nearby_section): New function.
            * ldlang.h (ldlang_nearby_section): Declare.
            (lang_output_section_get): New static inline.
            * ldmain.c (link_callbacks): Add ldlang_nearby_section.

Diff:
---
 bfd/elflink.c     |  6 ++--
 bfd/linker.c      | 80 ++++-------------------------------------------
 include/bfdlink.h | 11 ++++---
 ld/ldemul.c       |  2 +-
 ld/ldlang.c       | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
 ld/ldlang.h       | 12 +++++--
 ld/ldmain.c       |  1 +
 7 files changed, 112 insertions(+), 93 deletions(-)

diff --git a/bfd/elflink.c b/bfd/elflink.c
index 3f60dba1fef..d3084720382 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -12113,8 +12113,8 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
 			      if (r_symndx == STN_UNDEF)
 				{
 				  irela->r_addend += osec->vma;
-				  osec = _bfd_nearby_section (output_bfd, osec,
-							      osec->vma);
+				  osec = flinfo->info->callbacks->nearby_section
+				    (output_bfd, osec, osec->vma);
 				  irela->r_addend -= osec->vma;
 				  r_symndx = osec->target_index;
 				}
@@ -12755,7 +12755,7 @@ _bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
 	}
     }
   if (sections_removed)
-    _bfd_fix_excluded_sec_syms (abfd, info);
+    bfd_fix_excluded_sec_syms (info);
 
   /* Count up the number of relocations we will output for each output
      section, so that we know the sizes of the reloc sections.  We
diff --git a/bfd/linker.c b/bfd/linker.c
index 247c259a95e..137184a6c12 100644
--- a/bfd/linker.c
+++ b/bfd/linker.c
@@ -3093,82 +3093,13 @@ _bfd_generic_section_already_linked (bfd *abfd ATTRIBUTE_UNUSED,
   return false;
 }
 
-/* Choose a neighbouring section to S in OBFD that will be output, or
-   the absolute section if ADDR is out of bounds of the neighbours.  */
-
-asection *
-_bfd_nearby_section (bfd *obfd, asection *s, bfd_vma addr)
-{
-  asection *next, *prev, *best;
-
-  /* Find preceding kept section.  */
-  for (prev = s->prev; prev != NULL; prev = prev->prev)
-    if ((prev->flags & SEC_EXCLUDE) == 0
-	&& !bfd_section_removed_from_list (obfd, prev))
-      break;
-
-  /* Find following kept section.  Start at prev->next because
-     other sections may have been added after S was removed.  */
-  if (s->prev != NULL)
-    next = s->prev->next;
-  else
-    next = s->owner->sections;
-  for (; next != NULL; next = next->next)
-    if ((next->flags & SEC_EXCLUDE) == 0
-	&& !bfd_section_removed_from_list (obfd, next))
-      break;
-
-  /* Choose better of two sections, based on flags.  The idea
-     is to choose a section that will be in the same segment
-     as S would have been if it was kept.  */
-  best = next;
-  if (prev == NULL)
-    {
-      if (next == NULL)
-	best = bfd_abs_section_ptr;
-    }
-  else if (next == NULL)
-    best = prev;
-  else if (((prev->flags ^ next->flags)
-	    & (SEC_ALLOC | SEC_THREAD_LOCAL | SEC_LOAD)) != 0)
-    {
-      if (((next->flags ^ s->flags)
-	   & (SEC_ALLOC | SEC_THREAD_LOCAL)) != 0
-	  /* We prefer to choose a loaded section.  Section S
-	     doesn't have SEC_LOAD set (it being excluded, that
-	     part of the flag processing didn't happen) so we
-	     can't compare that flag to those of NEXT and PREV.  */
-	  || ((prev->flags & SEC_LOAD) != 0
-	      && (next->flags & SEC_LOAD) == 0))
-	best = prev;
-    }
-  else if (((prev->flags ^ next->flags) & SEC_READONLY) != 0)
-    {
-      if (((next->flags ^ s->flags) & SEC_READONLY) != 0)
-	best = prev;
-    }
-  else if (((prev->flags ^ next->flags) & SEC_CODE) != 0)
-    {
-      if (((next->flags ^ s->flags) & SEC_CODE) != 0)
-	best = prev;
-    }
-  else
-    {
-      /* Flags we care about are the same.  Prefer the following
-	 section if that will result in a positive valued sym.  */
-      if (addr < next->vma)
-	best = prev;
-    }
-
-  return best;
-}
-
 /* Convert symbols in excluded output sections to use a kept section.  */
 
 static bool
 fix_syms (struct bfd_link_hash_entry *h, void *data)
 {
-  bfd *obfd = (bfd *) data;
+  struct bfd_link_info *info = data;
+  bfd *obfd = info->output_bfd;
 
   if (h->type == bfd_link_hash_defined
       || h->type == bfd_link_hash_defweak)
@@ -3182,7 +3113,8 @@ fix_syms (struct bfd_link_hash_entry *h, void *data)
 	  asection *op;
 
 	  h->u.def.value += s->output_offset + s->output_section->vma;
-	  op = _bfd_nearby_section (obfd, s->output_section, h->u.def.value);
+	  op = info->callbacks->nearby_section (obfd, s->output_section,
+						h->u.def.value);
 	  h->u.def.value -= op->vma;
 	  h->u.def.section = op;
 	}
@@ -3192,9 +3124,9 @@ fix_syms (struct bfd_link_hash_entry *h, void *data)
 }
 
 void
-_bfd_fix_excluded_sec_syms (bfd *obfd, struct bfd_link_info *info)
+bfd_fix_excluded_sec_syms (struct bfd_link_info *info)
 {
-  bfd_link_hash_traverse (info->hash, fix_syms, obfd);
+  bfd_link_hash_traverse (info->hash, fix_syms, info);
 }
 
 /*
diff --git a/include/bfdlink.h b/include/bfdlink.h
index 00fe0f8c7c8..0bd2132df7c 100644
--- a/include/bfdlink.h
+++ b/include/bfdlink.h
@@ -900,6 +900,11 @@ struct bfd_link_callbacks
     (struct bfd_link_info *, bfd * abfd,
      asection * current_section, asection * previous_section,
      bool new_segment);
+  /* Choose a neighbouring section to the given excluded section, or
+     the absolute section if no suitable neighbours are found that
+     will be output.  */
+  asection *(*nearby_section)
+    (bfd *, asection *, bfd_vma);
   /* This callback provides a chance for callers of the BFD to examine the
      ELF (dynamic) string table once it is complete.  */
   void (*examine_strtab)
@@ -1028,11 +1033,7 @@ extern bool _bfd_handle_already_linked
   (struct bfd_section *, struct bfd_section_already_linked *,
    struct bfd_link_info *);
 
-extern struct bfd_section *_bfd_nearby_section
-  (bfd *, struct bfd_section *, bfd_vma);
-
-extern void _bfd_fix_excluded_sec_syms
-  (bfd *, struct bfd_link_info *);
+extern void bfd_fix_excluded_sec_syms (struct bfd_link_info *);
 
 /* These structures are used to describe version information for the
    ELF linker.  These structures could be manipulated entirely inside
diff --git a/ld/ldemul.c b/ld/ldemul.c
index 35f91a287ca..b8517a917e1 100644
--- a/ld/ldemul.c
+++ b/ld/ldemul.c
@@ -314,7 +314,7 @@ finish_default (void)
       os->data = NULL;
     }
   if (!bfd_link_relocatable (&link_info))
-    _bfd_fix_excluded_sec_syms (link_info.output_bfd, &link_info);
+    bfd_fix_excluded_sec_syms (&link_info);
 }
 
 void
diff --git a/ld/ldlang.c b/ld/ldlang.c
index 9963d5eb98f..17fb249ef1a 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -1578,14 +1578,6 @@ lang_memory_default (asection *section)
   return lang_memory_region_lookup (DEFAULT_MEMORY_REGION, false);
 }
 
-/* Get the output section statement directly from the userdata.  */
-
-lang_output_section_statement_type *
-lang_output_section_get (const asection *output_section)
-{
-  return bfd_section_userdata (output_section);
-}
-
 /* Find or create an output_section_statement with the given NAME.
    If CONSTRAINT is non-zero match one with that constraint, otherwise
    match any non-negative constraint.  If CREATE is 0 return NULL when
@@ -6983,6 +6975,91 @@ section_for_dot (void)
   return bfd_abs_section_ptr;
 }
 
+/* Choose a neighbouring section to S in OBFD that will be output, or
+   the absolute section if no suitable neighbours are found.  This is
+   used to give symbols in excluded sections another section.  */
+
+asection *
+ldlang_nearby_section (bfd *obfd, asection *s, bfd_vma addr)
+{
+  asection *next, *prev, *best;
+  lang_memory_region_type *region = lang_output_section_get (s)->region;
+  int match;
+
+  /* Try for a neighbour in the same region first.  If there are none,
+     then accept sections in other regions.  */
+  for (match = 1; match >= 0; --match)
+    {
+      /* Find preceding kept section.  */
+      for (prev = s->prev; prev != NULL; prev = prev->prev)
+	if ((prev->flags & SEC_EXCLUDE) == 0
+	    && !bfd_section_removed_from_list (obfd, prev)
+	    && (lang_output_section_get (prev)->region == region || !match))
+	  break;
+
+      /* Find following kept section.  Start at prev->next because
+	 other sections may have been added after S was removed.  */
+      if (s->prev != NULL)
+	next = s->prev->next;
+      else
+	next = s->owner->sections;
+      for (; next != NULL; next = next->next)
+	if ((next->flags & SEC_EXCLUDE) == 0
+	    && !bfd_section_removed_from_list (obfd, next)
+	    && (lang_output_section_get (next)->region == region || !match))
+	  break;
+
+      /* Choose better of two sections, based on flags.  The idea
+	 is to choose a section that will be in the same segment
+	 as S would have been if it was kept.  */
+      best = next;
+      if (prev == NULL)
+	;
+      else if (next == NULL)
+	best = prev;
+      else if (((prev->flags ^ next->flags)
+		& (SEC_ALLOC | SEC_THREAD_LOCAL | SEC_LOAD)) != 0)
+	{
+	  if (((next->flags ^ s->flags)
+	       & (SEC_ALLOC | SEC_THREAD_LOCAL)) != 0
+	      /* We prefer to choose a loaded section.  Section S
+		 doesn't have SEC_LOAD set (it being excluded, that
+		 part of the flag processing didn't happen) so we
+		 can't compare that flag to those of NEXT and PREV.  */
+	      || ((prev->flags & SEC_LOAD) != 0
+		  && (next->flags & SEC_LOAD) == 0))
+	    best = prev;
+	}
+      else if (((prev->flags ^ next->flags) & SEC_READONLY) != 0)
+	{
+	  if (((next->flags ^ s->flags) & SEC_READONLY) != 0)
+	    best = prev;
+	}
+      else if (((prev->flags ^ next->flags) & SEC_CODE) != 0)
+	{
+	  if (((next->flags ^ s->flags) & SEC_CODE) != 0)
+	    best = prev;
+	}
+      else
+	{
+	  /* Flags we care about are the same.  Prefer the following
+	     section if that will result in a positive valued sym.  */
+	  if (addr < next->vma)
+	    best = prev;
+	}
+      if (best != NULL)
+	return best;
+    }
+  /* For those targets that implement absolute symbols "properly" in
+     ld and ld.so, ie. their value is not relocated, it is very likely
+     wrong to transform a symbol in a removed section to an absolute
+     symbol.  In a PIE or shared library a symbol value in an
+     allocated section ought to be relocated by the base address.
+     However, we will only get here if there are no sections at all,
+     so this should not be a concern except in odd testcases.  */
+  return bfd_abs_section_ptr;
+}
+
 /* Array of __start/__stop/.startof./.sizeof/ symbols.  */
 
 static struct bfd_link_hash_entry **start_stop_syms;
diff --git a/ld/ldlang.h b/ld/ldlang.h
index a9607bef765..ea7654fad2a 100644
--- a/ld/ldlang.h
+++ b/ld/ldlang.h
@@ -607,6 +607,8 @@ extern void lang_do_assignments
   (lang_phase_type);
 extern asection *section_for_dot
   (void);
+extern asection *ldlang_nearby_section
+  (bfd *, asection *, bfd_vma);
 
 #define LANG_FOR_EACH_INPUT_STATEMENT(statement)			\
   lang_input_statement_type *statement;					\
@@ -631,8 +633,6 @@ extern lang_input_statement_type *lang_add_input_file
   (const char *, lang_input_file_enum_type, const char *);
 extern void lang_add_keepsyms_file
   (const char *);
-extern lang_output_section_statement_type *lang_output_section_get
-  (const asection *);
 extern lang_output_section_statement_type *lang_output_section_statement_lookup
   (const char *, int, int);
 extern lang_output_section_statement_type *next_matching_output_section_statement
@@ -794,4 +794,12 @@ extern void cmdline_emit_object_only_section (void);
 extern void cmdline_check_object_only_section (bfd *, bool);
 extern void cmdline_remove_object_only_files (void);
 
+/* Get the output section statement from section userdata.  */
+
+static inline lang_output_section_statement_type *
+lang_output_section_get (const asection *output_section)
+{
+  return bfd_section_userdata (output_section);
+}
+
 #endif
diff --git a/ld/ldmain.c b/ld/ldmain.c
index afffdd2e43d..bfd3923024b 100644
--- a/ld/ldmain.c
+++ b/ld/ldmain.c
@@ -155,6 +155,7 @@ static struct bfd_link_callbacks link_callbacks =
   info_msg,
   minfo,
   ldlang_override_segment_assignment,
+  ldlang_nearby_section,
   ldlang_ctf_acquire_strings,
   NULL,
   ldlang_ctf_new_dynsym,


More information about the Binutils-cvs mailing list