[binutils-gdb] bfd: generalize _bfd_elf_merge_sections()

Jan Beulich jbeulich@sourceware.org
Fri Oct 24 13:14:52 GMT 2025


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

commit 810b19952d5d89405c80b468ba5182a5319dbb3e
Author: Jan Beulich <jbeulich@suse.com>
Date:   Fri Oct 24 15:11:11 2025 +0200

    bfd: generalize _bfd_elf_merge_sections()
    
    Except for the ELF class check, which isn't needed anymore when the
    generic linker knows how to deal with SEC_MERGE sections, there isn't
    anything substantially ELF-specific left in the function.
    
    This also eliminates the need for the "remove_hook" callback.
    
    As a result, section merging itself now works for mixed-class ELF input
    objects (issues with dropping of symbols and relocations that were there
    before for such cases remain present, though), i.e. the PR ld/19013
    testcases need adjusting accordingly: Both now expect identical .rodata
    contents. While making the change, add another line of expected output,
    to properly match after "#...". Else a mismatch on the important line
    isn't properly visible in ld.log.
    
    In set_symbol_from_hash() additionally set BSF_GLOBAL when dealing with a
    defined symbol. Without that the if() body ahead of the one being added to
    default_indirect_link_order() would not be entered once previously
    undefined symbols become defined (suggesting that there is a pre-existing
    issue there).

Diff:
---
 bfd/bfd-in2.h                        |  3 +++
 bfd/elf-bfd.h                        |  2 --
 bfd/elflink.c                        | 40 ------------------------------------
 bfd/elfxx-target.h                   |  2 +-
 bfd/libbfd-in.h                      |  8 +-------
 bfd/libbfd.h                         |  8 +-------
 bfd/linker.c                         | 36 ++++++++++++++++++++++++++++----
 bfd/merge.c                          | 39 +++++++++++++++++++++++++++--------
 bfd/reloc.c                          | 30 +++++++++++++++++++++------
 bfd/syms.c                           |  3 +++
 ld/testsuite/ld-x86-64/pr19013-x32.d |  5 +----
 ld/testsuite/ld-x86-64/pr19013.d     |  3 ++-
 12 files changed, 99 insertions(+), 80 deletions(-)

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index ad6077f093a..8f3b0f7c255 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -1190,6 +1190,9 @@ typedef struct bfd_symbol
   /* This section symbol should be included in the symbol table.  */
 #define BSF_SECTION_SYM_USED    (1 << 24)
 
+  /* This symbol underwent section merge resolution.  */
+#define BSF_MERGE_RESOLVED      (1 << 25)
+
   flagword flags;
 
   /* A pointer to the section to which this symbol is
diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index 4ed71095ea6..850a86fda25 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -2363,8 +2363,6 @@ extern bool _bfd_elf_link_hash_table_init
    unsigned int);
 extern bool _bfd_elf_slurp_version_tables
   (bfd *, bool);
-extern bool _bfd_elf_merge_sections
-  (bfd *, struct bfd_link_info *);
 extern bool _bfd_elf_match_sections_by_type
   (bfd *, const asection *, bfd *, const asection *);
 extern bool bfd_elf_is_group_section
diff --git a/bfd/elflink.c b/bfd/elflink.c
index 2dd121de4a7..fd0e549eb10 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -8158,46 +8158,6 @@ bfd_elf_size_dynsym_hash_dynstr (bfd *output_bfd, struct bfd_link_info *info)
   return true;
 }
 

-/* Make sure sec_info_type is cleared if sec_info is cleared too.  */
-
-static void
-merge_sections_remove_hook (bfd *abfd ATTRIBUTE_UNUSED,
-			    asection *sec)
-{
-  BFD_ASSERT (sec->sec_info_type == SEC_INFO_TYPE_MERGE);
-  sec->sec_info_type = SEC_INFO_TYPE_NONE;
-}
-
-/* Finish SHF_MERGE section merging.  */
-
-bool
-_bfd_elf_merge_sections (bfd *obfd, struct bfd_link_info *info)
-{
-  bfd *ibfd;
-  asection *sec;
-
-  if (ENABLE_CHECKING && !is_elf_hash_table (info->hash))
-    abort ();
-
-  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
-    if ((ibfd->flags & DYNAMIC) == 0
-	&& bfd_get_flavour (ibfd) == bfd_target_elf_flavour
-	&& (elf_elfheader (ibfd)->e_ident[EI_CLASS]
-	    == get_elf_backend_data (obfd)->s->elfclass))
-      for (sec = ibfd->sections; sec != NULL; sec = sec->next)
-	if ((sec->flags & SEC_MERGE) != 0
-	    && !bfd_is_abs_section (sec->output_section)
-	    && !_bfd_add_merge_section (obfd,
-					&info->hash->merge_info,
-					sec))
-	      return false;
-
-  if (info->hash->merge_info != NULL)
-    return _bfd_merge_sections (obfd, info, info->hash->merge_info,
-				merge_sections_remove_hook);
-  return true;
-}
-
 /* Create an entry in an ELF linker hash table.  */
 
 struct bfd_hash_entry *
diff --git a/bfd/elfxx-target.h b/bfd/elfxx-target.h
index 4cf02bbedd7..839555647b0 100644
--- a/bfd/elfxx-target.h
+++ b/bfd/elfxx-target.h
@@ -281,7 +281,7 @@
 #define bfd_elfNN_bfd_final_link	bfd_elf_final_link
 #endif
 #ifndef bfd_elfNN_bfd_merge_sections
-#define bfd_elfNN_bfd_merge_sections	_bfd_elf_merge_sections
+#define bfd_elfNN_bfd_merge_sections	_bfd_merge_sections
 #endif
 #else /* ! defined (elf_backend_relocate_section) */
 /* If no backend relocate_section routine, use the generic linker.
diff --git a/bfd/libbfd-in.h b/bfd/libbfd-in.h
index 5a0a6696b51..8caec1c76a6 100644
--- a/bfd/libbfd-in.h
+++ b/bfd/libbfd-in.h
@@ -688,16 +688,10 @@ extern bfd_reloc_status_type _bfd_relocate_contents
 extern bfd_reloc_status_type _bfd_clear_contents
   (reloc_howto_type *, bfd *, asection *, bfd_byte *, bfd_vma) ATTRIBUTE_HIDDEN;
 
-/* Register a SEC_MERGE section as a candidate for merging.  */
-
-extern bool _bfd_add_merge_section
-  (bfd *, void **, asection *) ATTRIBUTE_HIDDEN;
-
 /* Attempt to merge SEC_MERGE sections.  */
 
 extern bool _bfd_merge_sections
-  (bfd *, struct bfd_link_info *, void *, void (*) (bfd *, asection *))
-  ATTRIBUTE_HIDDEN;
+  (bfd *, struct bfd_link_info *) ATTRIBUTE_HIDDEN;
 
 /* Write out a merged section.  */
 
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index 9c3c98e04cb..617df41924e 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -694,16 +694,10 @@ extern bfd_reloc_status_type _bfd_relocate_contents
 extern bfd_reloc_status_type _bfd_clear_contents
   (reloc_howto_type *, bfd *, asection *, bfd_byte *, bfd_vma) ATTRIBUTE_HIDDEN;
 
-/* Register a SEC_MERGE section as a candidate for merging.  */
-
-extern bool _bfd_add_merge_section
-  (bfd *, void **, asection *) ATTRIBUTE_HIDDEN;
-
 /* Attempt to merge SEC_MERGE sections.  */
 
 extern bool _bfd_merge_sections
-  (bfd *, struct bfd_link_info *, void *, void (*) (bfd *, asection *))
-  ATTRIBUTE_HIDDEN;
+  (bfd *, struct bfd_link_info *) ATTRIBUTE_HIDDEN;
 
 /* Write out a merged section.  */
 
diff --git a/bfd/linker.c b/bfd/linker.c
index ae44ec3f2d2..6e2ef059b52 100644
--- a/bfd/linker.c
+++ b/bfd/linker.c
@@ -2310,7 +2310,9 @@ _bfd_generic_link_output_symbols (bfd *output_bfd,
    hash table entry.  */
 
 static void
-set_symbol_from_hash (asymbol *sym, struct bfd_link_hash_entry *h)
+set_symbol_from_hash (bfd *output_bfd,
+		      asymbol *sym,
+		      struct bfd_link_hash_entry *h)
 {
   switch (h->type)
     {
@@ -2341,13 +2343,26 @@ set_symbol_from_hash (asymbol *sym, struct bfd_link_hash_entry *h)
       sym->flags |= BSF_WEAK;
       break;
     case bfd_link_hash_defined:
+      sym->flags |= BSF_GLOBAL;
       sym->section = h->u.def.section;
       sym->value = h->u.def.value;
+      if (sym->section->sec_info_type == SEC_INFO_TYPE_MERGE)
+	{
+	  sym->value =
+	    _bfd_merged_section_offset (output_bfd, &sym->section, sym->value);
+	  sym->flags |= BSF_MERGE_RESOLVED;
+	}
       break;
     case bfd_link_hash_defweak:
       sym->flags |= BSF_WEAK;
       sym->section = h->u.def.section;
       sym->value = h->u.def.value;
+      if (sym->section->sec_info_type == SEC_INFO_TYPE_MERGE)
+	{
+	  sym->value =
+	    _bfd_merged_section_offset (output_bfd, &sym->section, sym->value);
+	  sym->flags |= BSF_MERGE_RESOLVED;
+	}
       break;
     case bfd_link_hash_common:
       sym->value = h->u.c.size;
@@ -2402,7 +2417,7 @@ _bfd_generic_link_write_global_symbol (struct generic_link_hash_entry *h,
       sym->flags = 0;
     }
 
-  set_symbol_from_hash (sym, &h->root);
+  set_symbol_from_hash (wginfo->output_bfd, sym, &h->root);
 
   sym->flags |= BSF_GLOBAL;
 
@@ -2690,7 +2705,7 @@ default_indirect_link_order (bfd *output_bfd,
       for (; sympp < symppend; sympp++)
 	{
 	  asymbol *sym;
-	  struct bfd_link_hash_entry *h;
+	  struct bfd_link_hash_entry *h = NULL;
 
 	  sym = *sympp;
 
@@ -2716,9 +2731,22 @@ default_indirect_link_order (bfd *output_bfd,
 					  bfd_asymbol_name (sym),
 					  false, false, true);
 	      if (h != NULL)
-		set_symbol_from_hash (sym, h);
+		set_symbol_from_hash (output_bfd, sym, h);
+	    }
+
+	  if (h == NULL
+	      && sym->section->sec_info_type == SEC_INFO_TYPE_MERGE
+	      && !(sym->flags & (BSF_SECTION_SYM | BSF_MERGE_RESOLVED)))
+	    {
+	      sym->value = _bfd_merged_section_offset (output_bfd,
+						       &sym->section,
+						       sym->value);
+	      sym->flags |= BSF_MERGE_RESOLVED;
 	    }
 	}
+
+      if (input_section->sec_info_type == SEC_INFO_TYPE_MERGE)
+	return _bfd_write_merged_section (output_bfd, input_section);
     }
 
   if ((output_section->flags & (SEC_GROUP | SEC_LINKER_CREATED)) == SEC_GROUP
diff --git a/bfd/merge.c b/bfd/merge.c
index 6f068b6117b..d89f4800f8a 100644
--- a/bfd/merge.c
+++ b/bfd/merge.c
@@ -608,7 +608,7 @@ sec_merge_emit (bfd *abfd, struct sec_merge_sec_info *secinfo,
 /* Register a SEC_MERGE section as a candidate for merging.
    This function is called for all non-dynamic SEC_MERGE input sections.  */
 
-bool
+static bool
 _bfd_add_merge_section (bfd *abfd, void **psinfo, asection *sec)
 {
   struct sec_merge_info *sinfo;
@@ -973,11 +973,10 @@ merge_strings (struct sec_merge_info *sinfo)
 /* This function is called once after all SEC_MERGE sections are registered
    with _bfd_merge_section.  */
 
-bool
-_bfd_merge_sections (bfd *abfd,
-		     struct bfd_link_info *info ATTRIBUTE_UNUSED,
-		     void *xsinfo,
-		     void (*remove_hook) (bfd *, asection *))
+static bool
+merge_sections (bfd *abfd,
+		struct bfd_link_info *info ATTRIBUTE_UNUSED,
+		void *xsinfo)
 {
   struct sec_merge_info *sinfo;
 
@@ -995,9 +994,9 @@ _bfd_merge_sections (bfd *abfd,
 	if (secinfo->sec->flags & SEC_EXCLUDE
 	    || !record_section (sinfo, secinfo))
 	  {
+	    BFD_ASSERT (secinfo->sec->sec_info_type == SEC_INFO_TYPE_MERGE);
 	    secinfo->sec->sec_info = NULL;
-	    if (remove_hook)
-	      (*remove_hook) (abfd, secinfo->sec);
+	    secinfo->sec->sec_info_type = SEC_INFO_TYPE_NONE;
 	  }
 	else if (align)
 	  {
@@ -1056,6 +1055,30 @@ _bfd_merge_sections (bfd *abfd,
   return true;
 }
 
+/* Finish SEC_MERGE section merging.  */
+
+bool
+_bfd_merge_sections (bfd *obfd, struct bfd_link_info *info)
+{
+  const bfd *ibfd;
+  asection *sec;
+
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+    if ((ibfd->flags & DYNAMIC) == 0)
+      for (sec = ibfd->sections; sec != NULL; sec = sec->next)
+	if ((sec->flags & SEC_MERGE) != 0
+	    && !bfd_is_abs_section (sec->output_section)
+	    && !_bfd_add_merge_section (obfd,
+					&info->hash->merge_info,
+					sec))
+	      return false;
+
+  if (info->hash->merge_info == NULL)
+    return true;
+
+  return merge_sections (obfd, info, info->hash->merge_info);
+}
+
 /* Write out the merged section.  */
 
 bool
diff --git a/bfd/reloc.c b/bfd/reloc.c
index c9d53bb9e11..aa69a3c2dc9 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -8554,12 +8554,30 @@ bfd_generic_get_relocated_section_contents (bfd *abfd,
 	      r = bfd_reloc_ok;
 	    }
 	  else
-	    r = bfd_perform_relocation (input_bfd,
-					*parent,
-					data,
-					input_section,
-					relocatable ? abfd : NULL,
-					&error_message);
+	    {
+	      if ((symbol->flags & BSF_SECTION_SYM)
+		  && symbol->section->sec_info_type == SEC_INFO_TYPE_MERGE
+		  /* This, while apparently necessary, feels bogus.  */
+		  && !(symbol->section->flags & SEC_DEBUGGING))
+		{
+		  asection *sec = symbol->section;
+
+		  (*parent)->addend =
+		    _bfd_merged_section_offset (abfd, &sec, (*parent)->addend);
+		  /* We may not change symbol->section, so the output_offset
+		     adjustment done in bfd_perform_relocation() needs taking
+		     care of (and compensating) here.  */
+		  (*parent)->addend +=
+		    sec->output_offset - symbol->section->output_offset;
+		}
+
+	      r = bfd_perform_relocation (input_bfd,
+					  *parent,
+					  data,
+					  input_section,
+					  relocatable ? abfd : NULL,
+					  &error_message);
+	    }
 
 	  if (relocatable)
 	    {
diff --git a/bfd/syms.c b/bfd/syms.c
index df2229b3fa3..b8f21c5fc0c 100644
--- a/bfd/syms.c
+++ b/bfd/syms.c
@@ -303,6 +303,9 @@ CODE_FRAGMENT
 .  {* This section symbol should be included in the symbol table.  *}
 .#define BSF_SECTION_SYM_USED    (1 << 24)
 .
+.  {* This symbol underwent section merge resolution.  *}
+.#define BSF_MERGE_RESOLVED      (1 << 25)
+.
 .  flagword flags;
 .
 .  {* A pointer to the section to which this symbol is
diff --git a/ld/testsuite/ld-x86-64/pr19013-x32.d b/ld/testsuite/ld-x86-64/pr19013-x32.d
index 5634b87f7c8..60f0be0b59e 100644
--- a/ld/testsuite/ld-x86-64/pr19013-x32.d
+++ b/ld/testsuite/ld-x86-64/pr19013-x32.d
@@ -2,7 +2,4 @@
 #as: --x32
 #ld: --oformat elf32-i386 -m elf32_x86_64
 #objdump: -s -j .rodata
-
-#...
- [0-9a-f]+ 02030041 42434400 +...ABCD. +
-#pass
+#dump: pr19013.d
diff --git a/ld/testsuite/ld-x86-64/pr19013.d b/ld/testsuite/ld-x86-64/pr19013.d
index 97137a793e1..2a1fe9b3e6f 100644
--- a/ld/testsuite/ld-x86-64/pr19013.d
+++ b/ld/testsuite/ld-x86-64/pr19013.d
@@ -3,5 +3,6 @@
 #objdump: -s -j .rodata
 
 #...
- [0-9a-f]+ 00000203 00414243 4400 +.....ABCD. +
+Contents of section \.rodata:
+ [0-9a-f]+ 02030041 42434400 +...ABCD. +
 #pass


More information about the Binutils-cvs mailing list