From patchwork Tue Jun 23 21:38:06 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter0x44 X-Patchwork-Id: 137669 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 4151A4BA2E19 for ; Tue, 23 Jun 2026 21:40:29 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 4151A4BA2E19 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1782250829; bh=sZS0v2ipOn1IaSfOcd0Wgz/rMz3JqhqaglnfTGQVaZ8=; h=To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=e0c/EFjdLMKFWv2VPi2mu95sGBj5hWcoGFlhK7/YnNAEgpYblWfCczKT9e67ld/Rr M/WmhNWBJUp0YEPVR+qdal4/loAEviEv90rX5Q1OSfPUQnzQ4C15BrhrhehavGA9XF lczqpMkydh/14FAOyz4nms72HEHiWAAn8wJeSOxw= X-Original-To: binutils@sourceware.org Delivered-To: binutils@sourceware.org Received: from layka.disroot.org (layka.disroot.org [178.21.23.139]) by sourceware.org (Postfix) with ESMTPS id 6338E4BA2E17 for ; Tue, 23 Jun 2026 21:38:27 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 6338E4BA2E17 ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 6338E4BA2E17 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1782250707; cv=none; b=sezC2vff1y9jwFKow43tPu9gb2jtZpZ0+h+YYJ6TylojgJbm5kDxKhmLr+lCozqxTImHQ0+C+lEKGqHl45s0QKXgf5X3Lr4xfe0in5x89sfWG9phlI9GZW1D4Sj2TSwTIyFLlo20gn8OtAGIyEEpHpOEkdFPAtUwOE0kq/xhqi4= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1782250707; c=relaxed/simple; bh=ATtdQFnlZDE5geAHuDpcMPBsVAe9KA73VBVTuNKIy6w=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=EoVKAQVvhzYgn6fr2/hNmAlaaj5LhaoRpV/5mTwNSp6Hga3G6mkKZcSg6bUQySWQ7ln6JpWoO5QK4LYCBJCsnA3i5/eOGOI/x++lJfXqTlzLTYUnYUzODThqGPEAG6BbTiJu8OmmnzWrImgKYYMkaIB1P+2ayRkK7UBfdpCjjiw= ARC-Authentication-Results: i=1; sourceware.org; dkim=pass (2048-bit key, secure) header.d=disroot.org header.i=@disroot.org header.a=rsa-sha256 header.s=mail header.b=fMcnCj6Z DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 6338E4BA2E17 Received: from mail01.disroot.lan (localhost [127.0.0.1]) by disroot.org (Postfix) with ESMTP id 8043426F06; Tue, 23 Jun 2026 23:38:26 +0200 (CEST) X-Virus-Scanned: SPAM Filter at disroot.org Received: from layka.disroot.org ([127.0.0.1]) by localhost (disroot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id 9QlnHiNSWypL; Tue, 23 Jun 2026 23:38:25 +0200 (CEST) To: binutils@sourceware.org Cc: amodra@gmail.com, jbeulich@suse.com, martin@martin.st, Peter Damianov Subject: [PATCH v3 3/4] bfd: Handle PE weak externals with real fallbacks in archives Date: Tue, 23 Jun 2026 17:38:06 -0400 Message-ID: <8292b88c6b7216d8eedba27e1d1bd6a8c7da34d5.1782245474.git.peter0x44@disroot.org> In-Reply-To: References: MIME-Version: 1.0 X-Spam-Status: No, score=-11.9 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, SPF_HELO_NONE, SPF_PASS, TXREP shortcircuit=no autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: binutils@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Binutils mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Peter Damianov via Binutils From: Peter0x44 Reply-To: Peter Damianov Errors-To: binutils-bounces~patchwork=sourceware.org@sourceware.org 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. --- 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;