From patchwork Mon Jun 22 03:39:24 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter0x44 X-Patchwork-Id: 137519 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 66F674BA9011 for ; Mon, 22 Jun 2026 03:40:32 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 66F674BA9011 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1782099632; bh=vVCEzMNNpMDFfovbFVg7i/MCsO8ExRXlZxqKy+U1qjM=; 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=B3yCrNpQTb7Oq7vX2lIOu1lo60Q+xGmhqmz9R14ip3kYnkSKcsZnmqNVlpD3AB8ZL oM2NUmCsfKjI9VMHhEyCsWAQKugJXrUJo/u7tQI8a35nbPRxxsto+gq6tKXrzSSpPP ZM6llKo9FgCWUibVXIC8RPExlmV2XXHNxqPE9I/8= 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 979BF4BA2E0D for ; Mon, 22 Jun 2026 03:39:57 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 979BF4BA2E0D ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 979BF4BA2E0D ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1782099597; cv=none; b=r7wXult4eiJZdCZLAEVHBAfL99nfcsJfsoeCG/bddZ4/rCxQ467GKzq3A60mdW2QgswCz8TZ7ZSWASQTWHc6SNIFjjQUGngS3XcOeiLOtkWU7ezUqQ62QwZK5P7sJ9LKi7plspDDvUA2FHf3/ahp6QzTqjQYdepQNw5Qr/vMaIQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1782099597; c=relaxed/simple; bh=tmR8E+Rm36DcA4r7o9on7SJqAJnSZwkcTCC1LBMHrFI=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=i8iy5dR1usxy4RF0YftZ0T+8+w4kIDCAVjWm59KlQgeNvic0F0efHdMD7TCEwJppErva4lq5+BQQqeFdsXybirILiiadl/Mid4q+cYbUCYsDoaD0wtlSrd2Q7cn9AcSteMkf4e+Jv3qbMb1A+M6+pz11TkHzov0Gvdx7umz+PUw= 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=XRGpJn+K DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 979BF4BA2E0D Received: from mail01.disroot.lan (localhost [127.0.0.1]) by disroot.org (Postfix) with ESMTP id 75E3727094; Mon, 22 Jun 2026 05:39:56 +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 v2mBdKcCE7lD; Mon, 22 Jun 2026 05:39:56 +0200 (CEST) To: binutils@sourceware.org Cc: amodra@gmail.com, jbeulich@suse.com, martin@martin.st, Peter Damianov Subject: [PATCH v2 1/3] PE-COFF: Fix weak external symbol resolution when strong undef is seen first Date: Sun, 21 Jun 2026 23:39:24 -0400 Message-ID: <20260622033926.220317-2-peter0x44@disroot.org> In-Reply-To: <20260622033926.220317-1-peter0x44@disroot.org> References: <20260530191522.57144-1-peter0x44@disroot.org> <20260622033926.220317-1-peter0x44@disroot.org> 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, 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 When linking PE-COFF objects, a weak external symbol (C_NT_WEAK with an aux record specifying a fallback alias) may fail to resolve if a strong undefined reference to the same symbol is encountered before the weak definition. This causes "undefined reference" errors for symbols like operator new or personality routines that GCC emits as weak externals with a fallback to a default implementation. There are two problems: 1. In coff_link_add_symbols, when the generic linker resolves a weak undefined against an existing strong undefined (NOACT in the action table), the COFF-specific symbol_class and aux record were not stored because the existing hash entry already had non-null class/type from the first (strong) object file. 2. In _bfd_coff_generic_relocate_section, the weak alias fallback only triggered for bfd_link_hash_undefweak symbols. When a strong undef is seen first, the hash type stays bfd_link_hash_undefined (the generic linker does not downgrade it), so the fallback was skipped. Fix by extending the condition in coff_link_add_symbols to also update symbol_class and aux when the incoming symbol is a PE weak external with aux and the existing hash is still undefined. Also extend the relocation handler to resolve the weak alias fallback for bfd_link_hash_undefined symbols that carry C_NT_WEAK class and have an aux record. bfd/ * cofflink.c (coff_link_add_symbols): Also store symbol_class and aux record when a PE weak external with aux meets an existing undefined hash entry. (_bfd_coff_generic_relocate_section): Also resolve weak alias fallback for undefined symbols with C_NT_WEAK class and aux. --- bfd/cofflink.c | 55 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/bfd/cofflink.c b/bfd/cofflink.c index e5c8a987d69..eba0fad4c3f 100644 --- a/bfd/cofflink.c +++ b/bfd/cofflink.c @@ -477,13 +477,20 @@ coff_link_add_symbols (bfd *abfd, /* If we don't have any symbol information currently in the hash table, or if we are looking at a symbol definition, then update the symbol class and type in - the hash table. */ + the hash table. Also update if the incoming symbol is + a weak external with an aux record (PE COFF weak alias) + and the existing symbol is still undefined, so the + fallback alias information is preserved for the linker's + relocation resolution. */ if (((*sym_hash)->symbol_class == C_NULL && (*sym_hash)->type == T_NULL) || sym.n_scnum != 0 || (sym.n_value != 0 && (*sym_hash)->root.type != bfd_link_hash_defined - && (*sym_hash)->root.type != bfd_link_hash_defweak)) + && (*sym_hash)->root.type != bfd_link_hash_defweak) + || (IS_WEAK_EXTERNAL (abfd, sym) + && sym.n_numaux > 0 + && (*sym_hash)->root.type == bfd_link_hash_undefined)) { (*sym_hash)->symbol_class = sym.n_sclass; if (sym.n_type != T_NULL) @@ -521,10 +528,9 @@ coff_link_add_symbols (bfd *abfd, union internal_auxent *iaux; (*sym_hash)->numaux = sym.n_numaux; - alloc = ((union internal_auxent *) - bfd_hash_allocate (&info->hash->table, - (sym.n_numaux - * sizeof (*alloc)))); + alloc = bfd_hash_allocate (&info->hash->table, + (sym.n_numaux + * sizeof (*alloc))); if (alloc == NULL) goto error_return; for (i = 0, eaux = esym + symesz, iaux = alloc; @@ -3065,14 +3071,24 @@ _bfd_coff_generic_relocate_section (bfd *output_bfd, + sec->output_offset); } - else if (h->root.type == bfd_link_hash_undefweak) + else if (h->root.type == bfd_link_hash_undefweak + || (h->root.type == bfd_link_hash_undefined + && h->symbol_class == C_NT_WEAK && h->numaux == 1)) { - if (h->symbol_class == C_NT_WEAK && h->numaux == 1) + /* Weak undefined symbol: either GNU weak (no aux record) or + PE COFF weak external (C_NT_WEAK with aux record). + Also handles strong undefined symbols that carry PE weak + external metadata (when strong undef is seen before weak def, + the hash type stays bfd_link_hash_undefined but we preserve + the weak external class and aux for later resolution). */ + + bool is_pe_weak = (h->symbol_class == C_NT_WEAK && h->numaux == 1); + + if (is_pe_weak) { - /* See _Microsoft Portable Executable and Common Object + /* PE COFF weak external: resolve via fallback alias. + See _Microsoft Portable Executable and Common Object File Format Specification_, section 5.5.3. - Note that weak symbols without aux records are a GNU - extension. FIXME: All weak externals are treated as having characteristic IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY (1). These behave as per SVR4 ABI: A library member @@ -3081,24 +3097,37 @@ _bfd_coff_generic_relocate_section (bfd *output_bfd, See also linker.c: generic_link_check_archive_element. */ struct coff_link_hash_entry *h2 = NULL; unsigned long symndx2 = h->aux->x_sym.x_tagndx.u32; + if (symndx2 < obj_raw_syment_count (h->auxbfd)) h2 = obj_coff_sym_hashes (h->auxbfd)[symndx2]; if (!h2 || h2->root.type == bfd_link_hash_undefined) { + /* Fallback alias not found or still undefined. + Resolve to NULL. */ sec = bfd_abs_section_ptr; val = 0; } else { + /* Use fallback alias target. */ sec = h2->root.u.def.section; val = h2->root.u.def.value + sec->output_section->vma + sec->output_offset; } } else - /* This is a GNU extension. */ - val = 0; + { + /* GNU extension: ELF-style weak symbol in COFF without + PE weak external aux record. COFF has no native support + for weak symbols (unlike ELF where they're part of the + format). PE COFF adds them via C_NT_WEAK storage class + with an aux record pointing to a fallback symbol. GNU ld + extends this by allowing __attribute__((weak)) in COFF + objects even without the PE aux structure, treating them + like ELF weak symbols: resolve to NULL if not defined. */ + val = 0; + } } else if (! bfd_link_relocatable (info))