From: Jan Beulich Date: Fri, 24 Oct 2025 13:11:11 +0000 (+0200) Subject: bfd: generalize _bfd_elf_merge_sections() X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=810b19952d5d89405c80b468ba5182a5319dbb3e;p=thirdparty%2Fbinutils-gdb.git 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 --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