RFC: Don't output symbol version requirement for non-DT_NEEDED libs

Alan Modra amodra@gmail.com
Thu Nov 27 08:16:00 GMT 2014

This is a fix for
Inconsistency detected by ld.so: dl-version.c: 224: _dl_check_map_versions: Assertion `needed != ((void *)0)' failed!
a situation that can arise with weak references to versioned symbols
in dependent libraries.

Consider this testcase, a main that calls a weak "f", linked against a
shared library liba.so that has a dependency on libb.so where "f" is
defined as a versioned symbol.  A later revision of liba.so defines
"f" itself, without the version, and doesn't link against libb.so.
A further revision of liba.so doesn't link against libb.so and also
doesn't define "f".

cat > lib.c <<EOF
int f (void) { return 1; }
cat > lib.map <<EOF
FOO { global: f; };
cat > main.c <<EOF
int f (void) __attribute__ ((weak));
int main (void) { return f ? f() : 0; }
cat > empty.c <<EOF
gcc -o libb.so -shared -fPIC -Wl,-soname,libb.so,--version-script,lib.map lib.c
gcc -o liba.so.1 -shared -fPIC -Wl,--no-as-needed,-soname,liba.so -L. -lb
gcc -o liba.so.2 -shared -fPIC -Wl,-soname,liba.so lib.c
gcc -o liba.so.3 -shared -fPIC -Wl,-soname,liba.so empty.c
ln -sfn liba.so.1 liba.so
gcc -o pr16452 -fPIC main.c -Wl,--no-as-needed,--rpath,. -L. -la
echo $?
ln -sfn liba.so.2 liba.so
echo $?
ln -sfn liba.so.3 liba.so
echo $?

Currently the script output is
Inconsistency detected by ld.so: dl-version.c: 224: _dl_check_map_versions: Assertion `needed != ((void *)0)' failed!
Inconsistency detected by ld.so: dl-version.c: 224: _dl_check_map_versions: Assertion `needed != ((void *)0)' failed!

The problem here is that since binutils-2.22 we've defaulted to
--no-copy-dt-needed-entries (which was a good change despite the
number of bugreports it has generated).  So libb.so is not listed as
DT_NEEDED in the executable.  However, a version FOO from libb.so *is*
added to the executable, specifying that it came from libb.so.  When
we use rev 2 or 3 of liba.so, no libb.so is loaded and the version
check fails to find the required library.

You could argue that it's wrong for ld.so to complain about a weak
symbol version, but the ld.so check isn't being done against symbols.
ld.so is looking at .gnu.version_r, the version requirements
themselves, each possibly used by multiple symbols..

You might also argue that it's a user problem that f lost its version
in rev 2 of the testcase.  However this might be because liba.so is a
user library without any versioning and in rev 2, liba.so was linked
against a static libb.  (Typical libb in real life is libpthread.)

So, absent someone implementing a glibc fix, how about we just drop
the symbol versioning for weak symbols, when their defining library
won't be in DT_NEEDED?  Note that if "f" above was a strong symbol,
ld will still complain with "./libb.so: error adding symbols: DSO
missing from command line".

	PR 16452
	* elflink.c (_bfd_elf_link_find_version_dependencies): Exclude
	symbols from libraries that won't be listed in DT_NEEDED.
	(elf_link_output_extsym): Don't output verdefs for such symbols.

diff --git a/bfd/elflink.c b/bfd/elflink.c
index c8068c0..c964a98 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -1832,7 +1832,9 @@ _bfd_elf_link_find_version_dependencies (struct elf_link_hash_entry *h,
   if (!h->def_dynamic
       || h->def_regular
       || h->dynindx == -1
-      || h->verinfo.verdef == NULL)
+      || h->verinfo.verdef == NULL
+      || (elf_dyn_lib_class (h->verinfo.verdef->vd_bfd)
     return TRUE;
   /* See if we already know about this version.  */
@@ -9050,7 +9052,9 @@ elf_link_output_extsym (struct bfd_hash_entry *bh, void *data)
 	  if (!h->def_regular)
-	      if (h->verinfo.verdef == NULL)
+	      if (h->verinfo.verdef == NULL
+		  || (elf_dyn_lib_class (h->verinfo.verdef->vd_bfd)
 		iversym.vs_vers = 0;
 		iversym.vs_vers = h->verinfo.verdef->vd_exp_refno + 1;

Alan Modra
Australia Development Lab, IBM

More information about the Binutils mailing list