[PATCH v3 3/4] bfd: Handle PE weak externals with real fallbacks in archives

Peter Damianov via Binutils <[email protected]>
Newsgroups gmane.comp.gnu.binutils
Message-ID <8292b88c6b7216d8eedba27e1d1bd6a8c7da34d5.1782245474.git.peter0x44@disroot.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;
-- 
2.54.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.