public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
From: "H.J. Lu" <hjl.tools@gmail.com>
To: binutils@sourceware.org
Subject: [PATCH] elf: Always honor the first definition in shared object and archive
Date: Thu, 14 Mar 2024 16:19:16 -0700	[thread overview]
Message-ID: <20240314231916.1071837-1-hjl.tools@gmail.com> (raw)

GCC doesn't put builtin function symbol references, which are defined in
the shared C library, in the IR symbol table.  When linker rescans shared
objects and archives for newly added symbol references generated from the
IR inputs, it skips definitions of the builtin functions in shared
objects and archives.

Add first_hash to bfd_link_info to track unreferenced definitions defined
first in shared objects and archives.  Always use them to resolve any
references.

bfd/

	PR ld/31482
	PR ld/31489
	* elflink.c (elf_link_add_object_symbols): Initialize first_hash
	for an IR input.  Always use the first definition in shared
	object.  Add the first unreferenced dynamic definition to
	first_hash.
	(_bfd_elf_archive_symbol_lookup): Add the first unreferenced
	definition to first_hash..
	(elf_link_add_archive_symbols): Use the symbol definition in
	archive if symbol is defined first in this archive.

include/

	PR ld/31482
	PR ld/31489
	* bfdlink.h (bfd_link_info): Add first_hash.

ld/

	PR ld/31482
	PR ld/31489
	* ldlang.c (lang_process): Free first_hash when it is done.
	* testsuite/ld-plugin/lto.exp: Add PR ld/31482 and PR ld/31489
	tests.
	* testsuite/ld-plugin/pass1.out: New file.
	* testsuite/ld-plugin/pr31482a.c: Likewise.
	* testsuite/ld-plugin/pr31482b.c: Likewise.
	* testsuite/ld-plugin/pr31482c.c: Likewise.
---
 bfd/elflink.c                     | 166 +++++++++++++++++++++++-------
 include/bfdlink.h                 |   4 +
 ld/ldlang.c                       |   6 ++
 ld/testsuite/ld-plugin/lto.exp    |  32 ++++++
 ld/testsuite/ld-plugin/pass1.out  |   1 +
 ld/testsuite/ld-plugin/pr31482a.c |   8 ++
 ld/testsuite/ld-plugin/pr31482b.c |   9 ++
 ld/testsuite/ld-plugin/pr31482c.c |   9 ++
 8 files changed, 198 insertions(+), 37 deletions(-)
 create mode 100644 ld/testsuite/ld-plugin/pass1.out
 create mode 100644 ld/testsuite/ld-plugin/pr31482a.c
 create mode 100644 ld/testsuite/ld-plugin/pr31482b.c
 create mode 100644 ld/testsuite/ld-plugin/pr31482c.c

diff --git a/bfd/elflink.c b/bfd/elflink.c
index 5a6cb07b2ce..efcd25530f2 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -4265,7 +4265,21 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
     }
 
   if ((abfd->flags & DYNAMIC) == 0)
-    dynamic = false;
+    {
+      dynamic = false;
+      if ((abfd->flags & BFD_PLUGIN) != 0
+	  && info->first_hash == NULL)
+	{
+	  /* Initialize first_hash for an IR input.  */
+	  info->first_hash = (struct bfd_link_hash_table *)
+	    xmalloc (sizeof (struct bfd_link_hash_table));
+	  if (!bfd_hash_table_init (&info->first_hash->table,
+				    _bfd_link_hash_newfunc,
+				    sizeof (struct bfd_link_hash_entry)))
+	    info->callbacks->einfo
+	      (_("%F%P: first_hash failed to initialize: %E\n"));
+	}
+    }
   else
     {
       dynamic = true;
@@ -5118,16 +5132,33 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
 	  if (skip)
 	    continue;
 
-	  /* Override a definition only if the new symbol matches the
-	     existing one.  */
-	  if (override && matched)
-	    definition = false;
-
 	  h = *sym_hash;
 	  while (h->root.type == bfd_link_hash_indirect
 		 || h->root.type == bfd_link_hash_warning)
 	    h = (struct elf_link_hash_entry *) h->root.u.i.link;
 
+	  /* Override a definition only if the new symbol matches the
+	     existing one.  */
+	  if (override && matched)
+	    {
+	      definition = false;
+	      if (info->first_hash != NULL
+		  && (elf_dyn_lib_class (abfd) & DYN_AS_NEEDED) != 0
+		  && h->root.non_ir_ref_regular)
+		{
+		  /* When reloading --as-needed shared objects for new
+		     symbols added from IR inputs, if this shared object
+		     has the first definition, use it.  */
+		  struct bfd_link_hash_entry *e
+		    = bfd_link_hash_lookup (info->first_hash, name,
+					    false, false, true);
+		  if (e != NULL
+		      && e->type == bfd_link_hash_defined
+		      && e->u.undef.abfd == abfd)
+		    definition = true;
+		}
+	    }
+
 	  if (h->versioned != unversioned
 	      && elf_tdata (abfd)->verdef != NULL
 	      && vernum > 1
@@ -5477,8 +5508,9 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
 	  if (!add_needed
 	      && matched
 	      && definition
-	      && h->root.type != bfd_link_hash_indirect
-	      && ((dynsym
+	      && h->root.type != bfd_link_hash_indirect)
+	    {
+	      if ((dynsym
 		   && h->ref_regular_nonweak)
 		  || (old_bfd != NULL
 		      && (old_bfd->flags & BFD_PLUGIN) != 0
@@ -5487,37 +5519,60 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
 		  || (h->ref_dynamic_nonweak
 		      && (elf_dyn_lib_class (abfd) & DYN_AS_NEEDED) != 0
 		      && !on_needed_list (elf_dt_name (abfd),
-					  htab->needed, NULL))))
-	    {
-	      const char *soname = elf_dt_name (abfd);
+					  htab->needed, NULL)))
+		{
+		  const char *soname = elf_dt_name (abfd);
+
+		  info->callbacks->minfo ("%!", soname, old_bfd,
+					  h->root.root.string);
+
+		  /* A symbol from a library loaded via DT_NEEDED of some
+		     other library is referenced by a regular object.
+		     Add a DT_NEEDED entry for it.  Issue an error if
+		     --no-add-needed is used and the reference was not
+		     a weak one.  */
+		  if (old_bfd != NULL
+		      && (elf_dyn_lib_class (abfd) & DYN_NO_NEEDED) != 0)
+		    {
+		      _bfd_error_handler
+			/* xgettext:c-format */
+			(_("%pB: undefined reference to symbol '%s'"),
+			 old_bfd, name);
+		      bfd_set_error (bfd_error_missing_dso);
+		      goto error_free_vers;
+		    }
 
-	      info->callbacks->minfo ("%!", soname, old_bfd,
-				      h->root.root.string);
+		  elf_dyn_lib_class (abfd) = (enum dynamic_lib_link_class)
+		    (elf_dyn_lib_class (abfd) & ~DYN_AS_NEEDED);
 
-	      /* A symbol from a library loaded via DT_NEEDED of some
-		 other library is referenced by a regular object.
-		 Add a DT_NEEDED entry for it.  Issue an error if
-		 --no-add-needed is used and the reference was not
-		 a weak one.  */
-	      if (old_bfd != NULL
-		  && (elf_dyn_lib_class (abfd) & DYN_NO_NEEDED) != 0)
-		{
-		  _bfd_error_handler
-		    /* xgettext:c-format */
-		    (_("%pB: undefined reference to symbol '%s'"),
-		     old_bfd, name);
-		  bfd_set_error (bfd_error_missing_dso);
-		  goto error_free_vers;
+		  /* Create dynamic sections for backends that require
+		     that be done before setup_gnu_properties.  */
+		  if (!_bfd_elf_link_create_dynamic_sections (abfd, info))
+		    return false;
+		  add_needed = true;
 		}
+	      else if (dynamic
+		       && info->first_hash != NULL
+		       && h->root.u.def.section->owner == abfd)
+		{
+		  /* Add this symbol to first hash if this shared
+		     object has the first definition.  */
+		  struct bfd_link_hash_entry *e
+		    = bfd_link_hash_lookup (info->first_hash, name, true,
+					    false, true);
+		  if (e == NULL)
+		    info->callbacks->einfo
+		      (_("%F%P: %pB: failed to add %s to first hash\n"),
+		       abfd, name);
 
-	      elf_dyn_lib_class (abfd) = (enum dynamic_lib_link_class)
-		(elf_dyn_lib_class (abfd) & ~DYN_AS_NEEDED);
-
-	      /* Create dynamic sections for backends that require
-		 that be done before setup_gnu_properties.  */
-	      if (!_bfd_elf_link_create_dynamic_sections (abfd, info))
-		return false;
-	      add_needed = true;
+		  if (e->type == bfd_link_hash_new)
+		    {
+		      /* Change the type to bfd_link_hash_defined and
+			 store ABFD in u.undef->abfd.  */
+		      e->type = bfd_link_hash_defined;
+		      e->u.undef.abfd = abfd;
+		    }
+		}
 	    }
 	}
     }
@@ -5963,7 +6018,29 @@ _bfd_elf_archive_symbol_lookup (bfd *abfd,
 
   p = strchr (name, ELF_VER_CHR);
   if (p == NULL || p[1] != ELF_VER_CHR)
-    return h;
+    {
+      if (info->first_hash != NULL)
+	{
+	  /* Add this symbol to first hash if this archive has the
+	     first definition.  */
+	  struct bfd_link_hash_entry *e
+	    = bfd_link_hash_lookup (info->first_hash, name, true,
+				    false, true);
+	  if (e == NULL)
+	    info->callbacks->einfo
+	      (_("%F%P: %pB: failed to add %s to first hash\n"),
+	       abfd, name);
+
+	  if (e->type == bfd_link_hash_new)
+	    {
+	      /* Change the type to bfd_link_hash_defined and store
+		 ABFD in u.undef->abfd.  */
+	      e->type = bfd_link_hash_defined;
+	      e->u.undef.abfd = abfd;
+	    }
+	}
+      return h;
+    }
 
   /* First check with only one `@'.  */
   len = strlen (name);
@@ -6102,7 +6179,22 @@ elf_link_add_archive_symbols (bfd *abfd, struct bfd_link_info *info)
 	      if (h->type != bfd_link_hash_undefweak)
 		/* Symbol must be defined.  Don't check it again.  */
 		included[i] = true;
-	      continue;
+
+	      /* Ignore the archive if the symbol isn't defined in a
+		 shared object.  */
+	      if (!((struct elf_link_hash_entry *) h)->def_dynamic)
+		continue;
+	      /* Ignore the dynamic definition if symbol is first
+		 defined in this archive.  */
+	      if (info->first_hash != NULL)
+		{
+		  struct bfd_link_hash_entry *e
+		    = bfd_link_hash_lookup (info->first_hash,
+					    symdef->name, false, false,
+					    true);
+		  if (e == NULL || e->u.undef.abfd != abfd)
+		    continue;
+		}
 	    }
 
 	  /* We need to include this archive member.  */
diff --git a/include/bfdlink.h b/include/bfdlink.h
index eac07d78364..0b71c2de79d 100644
--- a/include/bfdlink.h
+++ b/include/bfdlink.h
@@ -616,6 +616,10 @@ struct bfd_link_info
   /* Hash table handled by BFD.  */
   struct bfd_link_hash_table *hash;
 
+  /* Hash table of symbols which are first defined in archives or shared
+     objects when there are any IR inputs.  */
+  struct bfd_link_hash_table *first_hash;
+
   /* Hash table of symbols to keep.  This is NULL unless strip is
      strip_some.  */
   struct bfd_hash_table *keep_hash;
diff --git a/ld/ldlang.c b/ld/ldlang.c
index 54d1af62ebe..19fb26a695d 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -8466,6 +8466,12 @@ lang_process (void)
   /* Check any required symbols are known.  */
   ldlang_check_require_defined_symbols ();
 
+  if (link_info.first_hash != NULL)
+    {
+      bfd_hash_table_free (&link_info.first_hash->table);
+      free (link_info.first_hash);
+    }
+
   lang_end ();
 }
 
diff --git a/ld/testsuite/ld-plugin/lto.exp b/ld/testsuite/ld-plugin/lto.exp
index cf1691fec6d..9e53e2d3cec 100644
--- a/ld/testsuite/ld-plugin/lto.exp
+++ b/ld/testsuite/ld-plugin/lto.exp
@@ -539,6 +539,22 @@ set lto_link_elf_tests [list \
    "" \
    "pr30281.so" \
   ] \
+  [list \
+   "Build pr31482b.a" \
+   "" \
+   "" \
+   {pr31482b.c} \
+   "" \
+   "pr31482b.a" \
+  ] \
+  [list \
+   "Build pr31482c.so" \
+   "-shared" \
+   "-fPIC" \
+   {pr31482c.c} \
+   "" \
+   "pr31482c.so" \
+  ] \
 ]
 
 # PR 14918 checks that libgcc is not spuriously included in a shared link of
@@ -722,6 +738,22 @@ set lto_run_elf_shared_tests [list \
    {-Wl,--as-needed,-R,tmpdir} {} \
    {lto-19c.c} {lto-19.exe} {pass.out} {-flto -O2} {c} {} \
    {tmpdir/liblto-19.so tmpdir/liblto-19.a}] \
+  [list {pr31482a} \
+   {-Wl,--no-as-needed,-R,tmpdir} {} \
+   {pr31482a.c} {pr31482a.exe} {pass.out} {-flto} {c} {} \
+   {tmpdir/pr31482b.a tmpdir/pr31482c.so}] \
+  [list {pr31482b} \
+   {-Wl,--no-as-needed,-R,tmpdir} {} \
+   {pr31482a.c} {pr31482b.exe} {pass1.out} {-flto} {c} {} \
+   {tmpdir/pr31482c.so tmpdir/pr31482b.a}] \
+  [list {pr31489a} \
+   {-Wl,--as-needed,-R,tmpdir} {} \
+   {pr31482a.c} {pr31489a.exe} {pass.out} {-flto} {c} {} \
+   {tmpdir/pr31482b.a tmpdir/pr31482c.so}] \
+  [list {pr31489b} \
+   {-Wl,--as-needed,-R,tmpdir} {} \
+   {pr31482a.c} {pr31489b.exe} {pass1.out} {-flto} {c} {} \
+   {tmpdir/pr31482c.so tmpdir/pr31482b.a}] \
 ]
 
 # LTO run-time tests for ELF
diff --git a/ld/testsuite/ld-plugin/pass1.out b/ld/testsuite/ld-plugin/pass1.out
new file mode 100644
index 00000000000..8e5c818f1a6
--- /dev/null
+++ b/ld/testsuite/ld-plugin/pass1.out
@@ -0,0 +1 @@
+PASS1
diff --git a/ld/testsuite/ld-plugin/pr31482a.c b/ld/testsuite/ld-plugin/pr31482a.c
new file mode 100644
index 00000000000..0693e471123
--- /dev/null
+++ b/ld/testsuite/ld-plugin/pr31482a.c
@@ -0,0 +1,8 @@
+#include <stdlib.h>
+
+int
+main()
+{
+  abort ();
+  return 0;
+}
diff --git a/ld/testsuite/ld-plugin/pr31482b.c b/ld/testsuite/ld-plugin/pr31482b.c
new file mode 100644
index 00000000000..3c241735c16
--- /dev/null
+++ b/ld/testsuite/ld-plugin/pr31482b.c
@@ -0,0 +1,9 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+void
+abort (void)
+{
+  printf ("PASS\n");
+  exit (0);
+}
diff --git a/ld/testsuite/ld-plugin/pr31482c.c b/ld/testsuite/ld-plugin/pr31482c.c
new file mode 100644
index 00000000000..5c3b5099b52
--- /dev/null
+++ b/ld/testsuite/ld-plugin/pr31482c.c
@@ -0,0 +1,9 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+void
+abort (void)
+{
+  printf ("PASS1\n");
+  exit (0);
+}
-- 
2.44.0


                 reply	other threads:[~2024-03-14 23:19 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240314231916.1071837-1-hjl.tools@gmail.com \
    --to=hjl.tools@gmail.com \
    --cc=binutils@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).