]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
PR 33726, symbols in excluded sections
authorAlan Modra <amodra@gmail.com>
Thu, 25 Dec 2025 11:47:10 +0000 (22:17 +1030)
committerAlan Modra <amodra@gmail.com>
Fri, 26 Dec 2025 00:24:53 +0000 (10:54 +1030)
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.

bfd/elflink.c
bfd/linker.c
include/bfdlink.h
ld/ldemul.c
ld/ldlang.c
ld/ldlang.h
ld/ldmain.c

index 3f60dba1fef8f0e08ba27f347bb639ed7366abbc..d3084720382832b2f6fbf14d1df3e7a0fc0a9443 100644 (file)
@@ -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
index 247c259a95efd0a889d5d114a39d259f14c945b6..137184a6c12808619c42d9f724c335b6f3b4f2ab 100644 (file)
@@ -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);
 }
 
 /*
index 00fe0f8c7c831c840a9e37c149b0a835e816d957..0bd2132df7c4201757ce81013b4362862a8a2f9c 100644 (file)
@@ -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
index 35f91a287caa212aa14458e1abf0fff2794d1cc2..b8517a917e143a6277e30643ab26906f071c3a74 100644 (file)
@@ -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
index 9963d5eb98fbc0d8ba2540073414941c570e294f..17fb249ef1ad5539ac8fd7f48c76ce2a5f7a2231 100644 (file)
@@ -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;
index a9607bef765324e9b498d6d643ec0852f96b949e..ea7654fad2a90e40e26f8e885ed880ea76bb4ec6 100644 (file)
@@ -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
index afffdd2e43de5f59affa187734fcb3e1f8a383c4..bfd3923024b10886fed7fc0fdee9e7d5fc2849f4 100644 (file)
@@ -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,