[v2] elf: Verify that there is only one default version

Message ID CAMe9rOqSQ4utzu+r-YQjCVnzTsQG22yuNRq5-Y=uVKfGQo_SHA@mail.gmail.com
State New
Headers
Series [v2] elf: Verify that there is only one default version |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_binutils_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Test passed

Commit Message

H.J. Lu Aug. 27, 2026, 12:12 a.m. UTC
  On Wed, Aug 26, 2026 at 11:35 AM H.J. Lu <hjl.tools@gmail.com> wrote:
>
> When assigning the symbol version, verify that there is only one default
> version.
>

Here is the v2 patch to provide info of where multiple default versions
are defined:

ld -shared -o pr34550.so pr34550a.o pr34550b.o --version-script pr34550.t
/usr/local/bin/ld: pr34550.so: multiple default versions of `fmod':
`GLIBC_2.0' in pr34550a.o and `GLIBC_2.43' in pr34550b.o.
/usr/local/bin/ld: failed to set dynamic section sizes: bad value
  

Comments

Alan Modra Aug. 28, 2026, 6:01 a.m. UTC | #1
On Thu, Aug 27, 2026 at 08:12:55AM +0800, H.J. Lu wrote:
> When assigning the symbol version, verify that there is only one default
> version.
> 
> bfd/
> 
> 	PR ld/34550
> 	* elflink.c (_bfd_elf_link_assign_sym_version): Verify that there
> 	is only one default version.
> 
> ld/
> 
> 	PR ld/34550
> 	* testsuite/ld-elf/pr34550.d: New test.
> 	* testsuite/ld-elf/pr34550.t: Likewise.
> 	* testsuite/ld-elf/pr34550a.s: Likewise.
> 	* testsuite/ld-elf/pr34550b.s: Likewise.

OK if this has been tested properly over multiple targets.
  

Patch

From 43096b92b19323dbfbfe5ee41986c54032b96b92 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Wed, 26 Aug 2026 11:29:39 +0800
Subject: [PATCH v2] elf: Verify that there is only one default version

When assigning the symbol version, verify that there is only one default
version.

bfd/

	PR ld/34550
	* elflink.c (_bfd_elf_link_assign_sym_version): Verify that there
	is only one default version.

ld/

	PR ld/34550
	* testsuite/ld-elf/pr34550.d: New test.
	* testsuite/ld-elf/pr34550.t: Likewise.
	* testsuite/ld-elf/pr34550a.s: Likewise.
	* testsuite/ld-elf/pr34550b.s: Likewise.

Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
---
 bfd/elflink.c                  | 52 +++++++++++++++++++++++++++++++++-
 ld/testsuite/ld-elf/pr34550.d  |  6 ++++
 ld/testsuite/ld-elf/pr34550.t  | 13 +++++++++
 ld/testsuite/ld-elf/pr34550a.s |  5 ++++
 ld/testsuite/ld-elf/pr34550b.s |  6 ++++
 5 files changed, 81 insertions(+), 1 deletion(-)
 create mode 100644 ld/testsuite/ld-elf/pr34550.d
 create mode 100644 ld/testsuite/ld-elf/pr34550.t
 create mode 100644 ld/testsuite/ld-elf/pr34550a.s
 create mode 100644 ld/testsuite/ld-elf/pr34550b.s

diff --git a/bfd/elflink.c b/bfd/elflink.c
index 09eaacc8497..e7348901b8c 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -2697,10 +2697,14 @@  _bfd_elf_link_assign_sym_version (struct elf_link_hash_entry *h, void *data)
   if (p != NULL && h->verinfo.vertree == NULL)
     {
       struct bfd_elf_version_tree *t;
+      bool default_version = false;
 
       ++p;
       if (*p == ELF_VER_CHR)
-	++p;
+	{
+	  default_version = true;
+	  ++p;
+	}
 
       /* If there is no version string, we can just return out.  */
       if (*p == '\0')
@@ -2714,6 +2718,52 @@  _bfd_elf_link_assign_sym_version (struct elf_link_hash_entry *h, void *data)
 
       if (hide)
 	obed->elf_backend_hide_symbol (info, h, true);
+      else if (default_version)
+	{
+	  /* Get the unversioned symbol for the default version.  */
+	  struct elf_link_hash_entry *h_u;
+	  size_t size = p - h->root.root.string - 1;
+	  char *unversioned_name = bfd_malloc (size);
+	  if (unversioned_name == NULL)
+	    {
+	      sinfo->failed = true;
+	      return false;
+	    }
+	  memcpy (unversioned_name, h->root.root.string, size - 1);
+	  unversioned_name[size - 1] = 0;
+	  h_u = elf_link_hash_lookup (elf_hash_table (info),
+				      unversioned_name, false,
+				      false, false);
+
+	  /* There must be an unversioned symbol. */
+	  if (h_u == NULL)
+	    abort ();
+
+	  while (h_u->root.type == bfd_link_hash_indirect
+		 || h_u->root.type == bfd_link_hash_warning)
+	    h_u = (struct elf_link_hash_entry *) h_u->root.u.i.link;
+
+	  /* Verify that there is only one default version.  */
+	  if (h_u->versioned != versioned_hidden
+	      && h_u->verinfo.vertree != h->verinfo.vertree)
+	    {
+	      /* xgettext:c-format */
+	      info->callbacks->einfo
+		(_("%X%P: %pB: multiple default versions of `%s': "
+		   "`%s' in %pB and `%s' in %pB.\n"),
+		 info->output_bfd, unversioned_name,
+		 h_u->verinfo.vertree->name,
+		 h_u->root.u.def.section->owner,
+		 h->verinfo.vertree->name,
+		 h->root.u.def.section->owner);
+	      bfd_set_error (bfd_error_bad_value);
+	      sinfo->failed = true;
+	      free (unversioned_name);
+	      return false;
+	    }
+
+	  free (unversioned_name);
+	}
 
       /* If we are building an application, we need to create a
 	 version node for this version.  */
diff --git a/ld/testsuite/ld-elf/pr34550.d b/ld/testsuite/ld-elf/pr34550.d
new file mode 100644
index 00000000000..260c6cab1ee
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr34550.d
@@ -0,0 +1,6 @@ 
+#source: pr34550a.s
+#source: pr34550b.s
+#target: [check_shared_lib_support]
+#as:
+#ld: -shared --version-script=pr34550.t
+#error: multiple default versions of `fmod': `GLIBC_2.0' in tmpdir/pr34550a.o and `GLIBC_2.43' in tmpdir/pr34550b.o.
diff --git a/ld/testsuite/ld-elf/pr34550.t b/ld/testsuite/ld-elf/pr34550.t
new file mode 100644
index 00000000000..37dff255cdc
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr34550.t
@@ -0,0 +1,13 @@ 
+GLIBC_2.0 {
+  global:
+    fmod;
+  local:
+    *;
+};
+
+GLIBC_2.43 {
+  global:
+    fmod;
+  local:
+    *;
+} GLIBC_2.0;
diff --git a/ld/testsuite/ld-elf/pr34550a.s b/ld/testsuite/ld-elf/pr34550a.s
new file mode 100644
index 00000000000..fea245035b9
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr34550a.s
@@ -0,0 +1,5 @@ 
+	.text
+	.type	fmod,%function
+	.globl fmod
+fmod:
+	.dc.a 0
diff --git a/ld/testsuite/ld-elf/pr34550b.s b/ld/testsuite/ld-elf/pr34550b.s
new file mode 100644
index 00000000000..babe762f882
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr34550b.s
@@ -0,0 +1,6 @@ 
+	.text
+	.type	fmod_new,%function
+	.globl fmod_new
+fmod_new:
+	.dc.a 0
+	.symver fmod_new, fmod@@GLIBC_2.43
-- 
2.55.0