[v3,3/4] bfd: Handle PE weak externals with real fallbacks in archives
Commit Message
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(-)
@@ -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;
@@ -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));
+}
@@ -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);
@@ -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;