[PATCH 6/8] bfd: generalize _bfd_elf_merge_sections()
Jan Beulich
jbeulich@suse.com
Mon Oct 13 09:10:19 GMT 2025
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).
---
It looks pretty wasteful for default_indirect_link_order() to loop over
set_symbol_from_hash() again and again, for every section that it gets to
see for an input object (which likely is either none or all of them). Even
if bfd_generic_link_read_symbols() managed to still add something to the
hash table the first time through (which seems unlikely, as the specific
linker would already have populated it), it wouldn't change anymore for
subsequent invocations for the same input object. Nor would the results
change for the same symbols referenced by another input object. (In fact,
the prior lack of setting BSF_GLOBAL means the results so far could
wrongly have changed.) Generalizing BSF_MERGE_RESOLVED would allow to
reduce that overhead some.
For the handling of relocations referencing a section symbol, it wasn't
quite clear to me whether that should be done at the call site(s) of
bfd_perform_relocation(), or in the function itself. There's only one
other caller (in elfxx-mips.c), and I wasn't able to derive whether
somehow that already deals with merged sections. Nor do I know under what
condition(s) _bfd_elf_mips_get_relocated_section_contents() would be used,
instead of _bfd_mips_elf_relocate_section() (the sole caller of
mips_elf_adjust_addend(), which in turn calls _bfd_elf_rela_local_sym()).
The SEC_DEBUGGING check there also feels bogus, yet apparently references
into (e.g.) .debug_str are dealt with elsewhere / differently.
--- 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
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -2363,8 +2363,6 @@ extern bool _bfd_elf_link_hash_table_ini
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
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -8172,46 +8172,6 @@ bfd_elf_size_dynsym_hash_dynstr (bfd *ou
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 *
--- 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.
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -694,16 +694,10 @@ extern bfd_reloc_status_type _bfd_reloca
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. */
--- a/bfd/libbfd-in.h
+++ b/bfd/libbfd-in.h
@@ -688,16 +688,10 @@ extern bfd_reloc_status_type _bfd_reloca
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. */
--- a/bfd/linker.c
+++ b/bfd/linker.c
@@ -2310,7 +2310,9 @@ _bfd_generic_link_output_symbols (bfd *o
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, stru
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 (s
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
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_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
--- a/bfd/merge.c
+++ b/bfd/merge.c
@@ -608,7 +608,7 @@ sec_merge_emit (bfd *abfd, struct sec_me
/* 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 *si
/* 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
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -8554,12 +8554,30 @@ bfd_generic_get_relocated_section_conten
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)
{
--- 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
--- 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
--- 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
mailing list