[binutils-gdb] bfd: Handle PE weak externals with real fallbacks in archives
Alan Modra via Binutils-cvs <[email protected]> Tue, 30 Jun 2026 23:48:53 +0000 (GMT)
| Newsgroups | gmane.comp.gnu.binutils.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=65043894aaacd56b37bd1d634a97393f654d0589 commit 65043894aaacd56b37bd1d634a97393f654d0589 Author: Peter Damianov <[email protected]> Date: Tue Jun 23 17:38:06 2026 -0400 bfd: Handle PE weak externals with real fallbacks in archives PE weak externals are canonicalized as undefined BFD symbols, even when their auxiliary entry names a real fallback definition in the same object. Archive map generation therefore skipped the public weak name, so a strong undefined reference to that name could not extract the archive member. Include such PE weak externals in the archive map, but only when the fallback is a real definition rather than the absolute-zero null symbol used for weak declarations with no fallback. Also avoid extracting an archive member again during repeated archive searches once the weak external resolves through a defined fallback. Fixes: https://gcc.gnu.org/PR124263 bfd/ * archive.c: Include coff-bfd.h. (_bfd_compute_and_push_armap): Include PE weak externals in the archive map when their fallback is a real definition. * coff-bfd.c (bfd_coff_pe_weak_external_has_real_fallback): New function. * coff-bfd.h (bfd_coff_pe_weak_external_has_real_fallback): Declare. * cofflink.c (coff_link_hash_pe_weak_external_has_real_fallback): New function. (coff_link_check_archive_element): Avoid extracting an archive member again for a PE weak external whose real fallback is already defined. Diff: --- bfd/archive.c | 22 ++++++++++++++++------ bfd/coff-bfd.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ bfd/coff-bfd.h | 3 +++ bfd/cofflink.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 112 insertions(+), 6 deletions(-) diff --git a/bfd/archive.c b/bfd/archive.c index 44030eb3d63..1bff31b6d09 100644 --- a/bfd/archive.c +++ b/bfd/archive.c @@ -142,6 +142,7 @@ SUBSECTION #include "filenames.h" #include "bfdlink.h" #include "plugin.h" +#include "coff-bfd.h" #ifndef errno extern int errno; @@ -2535,12 +2536,21 @@ _bfd_compute_and_push_armap flagword flags = (syms[src_count])->flags; asection *sec = syms[src_count]->section; - if (((flags & (BSF_GLOBAL - | BSF_WEAK - | BSF_INDIRECT - | BSF_GNU_UNIQUE)) != 0 - || bfd_is_com_section (sec)) - && ! bfd_is_und_section (sec)) + /* Include symbols that normally define archive-map entries, plus + PE weak externals whose fallback is a real definition. Those + weak externals are canonicalized as undefined symbols, but their + public names can still be needed to pull the archive member. */ + bool include_in_armap + = ((((flags & (BSF_GLOBAL + | BSF_WEAK + | BSF_INDIRECT + | BSF_GNU_UNIQUE)) != 0 + || bfd_is_com_section (sec)) + && ! bfd_is_und_section (sec)) + || bfd_coff_pe_weak_external_has_real_fallback + (bfd_asymbol_bfd (syms[src_count]), syms[src_count])); + + if (include_in_armap) { bfd_size_type namelen; struct orl *new_map; diff --git a/bfd/coff-bfd.c b/bfd/coff-bfd.c index d836355aa64..e57135ddd18 100644 --- a/bfd/coff-bfd.c +++ b/bfd/coff-bfd.c @@ -110,3 +110,50 @@ bfd_coff_get_auxent (bfd *abfd, return true; } + +/* Return TRUE if SYMBOL is a PE weak external whose fallback symbol is + a real definition. A weak declaration with no fallback uses the COFF + null symbol as its fallback; do not treat that as an archive provider. */ + +bool +bfd_coff_pe_weak_external_has_real_fallback (bfd *abfd, + asymbol *symbol) +{ + coff_symbol_type *csym; + combined_entry_type *aux; + combined_entry_type *fallback; + + if (bfd_get_flavour (abfd) != bfd_target_coff_flavour + || coff_data (abfd) == NULL + || ! obj_pe (abfd)) + return false; + + csym = coff_symbol_from (symbol); + if (csym == NULL + || csym->native == NULL + || ! csym->native->is_sym + || csym->native->u.syment.n_sclass != C_NT_WEAK + || csym->native->u.syment.n_numaux != 1) + return false; + + aux = csym->native + 1; + if (aux->is_sym) + return false; + + if (aux->fix_tag) + fallback = (combined_entry_type *) aux->u.auxent.x_sym.x_tagndx.p; + else + { + uint32_t tagndx = aux->u.auxent.x_sym.x_tagndx.u32; + + if (tagndx >= obj_raw_syment_count (abfd)) + return false; + fallback = obj_raw_syments (abfd) + tagndx; + } + + return (fallback != NULL + && fallback->is_sym + && fallback->u.syment.n_scnum != N_UNDEF + && !(fallback->u.syment.n_scnum == N_ABS + && fallback->u.syment.n_value == 0)); +} diff --git a/bfd/coff-bfd.h b/bfd/coff-bfd.h index 70cc0c36902..6626c491ebb 100644 --- a/bfd/coff-bfd.h +++ b/bfd/coff-bfd.h @@ -81,5 +81,8 @@ extern bool bfd_coff_get_syment extern bool bfd_coff_get_auxent (bfd *, struct bfd_symbol *, int, union internal_auxent *); +extern bool bfd_coff_pe_weak_external_has_real_fallback + (bfd *, struct bfd_symbol *); + extern bool bfd_coff_set_symbol_class (bfd *, struct bfd_symbol *, unsigned int); diff --git a/bfd/cofflink.c b/bfd/cofflink.c index 75b4c47eacf..d029bec1678 100644 --- a/bfd/cofflink.c +++ b/bfd/cofflink.c @@ -36,6 +36,45 @@ static bool coff_link_check_archive_element bool *); static bool coff_link_add_symbols (bfd *, struct bfd_link_info *); +static bool +coff_link_hash_pe_weak_external_has_real_fallback + (struct coff_link_hash_entry *h) +{ + struct coff_link_hash_entry *h2; + unsigned long symndx; + + if (h->symbol_class != C_NT_WEAK + || h->numaux != 1 + || h->aux == NULL + || h->auxbfd == NULL + || ! obj_pe (h->auxbfd) + || obj_coff_sym_hashes (h->auxbfd) == NULL) + return false; + + /* The PE weak-external aux entry names the fallback symbol by raw + symbol index. Look up the corresponding link hash entry so we can + test the fallback's resolved state, not just its object-file entry. */ + symndx = h->aux->x_sym.x_tagndx.u32; + if (symndx >= obj_raw_syment_count (h->auxbfd)) + return false; + + h2 = obj_coff_sym_hashes (h->auxbfd)[symndx]; + if (h2 == NULL) + return false; + + while (h2->root.type == bfd_link_hash_indirect + || h2->root.type == bfd_link_hash_warning) + h2 = (struct coff_link_hash_entry *) h2->root.u.i.link; + + /* A weak declaration with no fallback uses the absolute-zero null + symbol. Only a defined real fallback means this archive member has + already satisfied the weak external. */ + return ((h2->root.type == bfd_link_hash_defined + || h2->root.type == bfd_link_hash_defweak) + && !(bfd_is_abs_section (h2->root.u.def.section) + && h2->root.u.def.value == 0)); +} + /* Return TRUE if SYM is a weak, external symbol. */ #define IS_WEAK_EXTERNAL(abfd, sym) \ ((sym).n_sclass == C_WEAKEXT \ @@ -255,6 +294,13 @@ coff_link_check_archive_element (bfd *abfd, if (((struct coff_link_hash_entry *) h)->indx == -3) return true; + /* A PE weak external can stay undefined even after its fallback has + been defined by this archive member. Avoid extracting the member + again if the same archive is searched more than once. */ + if (coff_link_hash_pe_weak_external_has_real_fallback + ((struct coff_link_hash_entry *) h)) + return true; + /* Include this element? */ if (!(*info->callbacks->add_archive_element) (info, abfd, name, &abfd)) return true;