ld.so: Support moving versioned symbols between sonames [BZ #24741]

Message ID 87woh7t7f7.fsf@oldenburg2.str.redhat.com
State Superseded
Headers

Commit Message

Florian Weimer June 27, 2019, 2:43 p.m. UTC
  This change should be fully backwards-compatible because the old
code aborted the load if a soname mismatch was encountered
(instead of searching further for a matching symbol).  This means
that no different symbols are found.

The soname check was explicitly disabled for the skip_map != NULL
case.  However, this only happens with dl(v)sym and RTLD_NEXT,
and those lookups do not come with a verneed entry that could be used
for the check.

The error check was already explicitly disabled for the skip_map !=
NULL case, that is, when dl(v)sym was called with RTLD_NEXT.  But
_dl_vsym always sets filename in the struct r_found_version argument
to NULL, so the check was not active anyway.  This means that
symbol lookup results for the skip_map != NULL case do not change,
either.

2019-06-27  Florian Weimer  <fweimer@redhat.com>

	[BZ #24741]
	* elf/dl-lookup.c (do_lookup_x): Do not fail if there is a soname
	mismatch in a versioned symbol reference.
	(_dl_lookup_symbol_x): Do not report soname mismatch failures.
	* elf/Makefile [$(build-shared)] (tests): Add tst-sonamemove,
	tst-sonamemove-dlopen.
	(module-names): Add tst-sonamemove-linkmod1,
	tst-sonamemove-runmod1, tst-sonamemove-runmod2.
	(LDFLAGS-tst-sonamemove-linkmod1.so): Set.
	(LDFLAGS-tst-sonamemove-runmod1.so): Likewise.
	(LDFLAGS-tst-sonamemove-runmod2.so): Likewise.
	(tst-sonamemove-runmod1.so): Link against
	tst-sonamemove-runmod2.so.
	(tst-sonamemove): Link against tst-sonamemove-linkmod1.so.
	(tst-sonamemove.out): Depend on tst-sonamemove-runmod1.so,
	tst-sonamemove-runmod2.so.
	(tst-sonamemove-dlopen.out): Likewise.
	* elf/tst-sonamemove.c: New file.
	* elf/tst-sonamemove-dlopen.c: Likewise.
	* elf/tst-sonamemove-linkmod1.c: Likewise.
	* elf/tst-sonamemove-linkmod1.map: Likewise.
	* elf/tst-sonamemove-runmod1.c: Likewise.
	* elf/tst-sonamemove-runmod1.map: Likewise.
	* elf/tst-sonamemove-runmod2.c: Likewise.
	* elf/tst-sonamemove-runmod2.map: Likewise.
	* support/xdlfcn.h (xdlvsym): Declare function.
	* support/xdlfcn.c (xdlvsym): Define funciton.
  

Comments

Zack Weinberg June 27, 2019, 2:48 p.m. UTC | #1
On Thu, Jun 27, 2019 at 10:44 AM Florian Weimer <fweimer@redhat.com> wrote:
>
> This change should be fully backwards-compatible because the old
> code aborted the load if a soname mismatch was encountered
> (instead of searching further for a matching symbol).  This means
> that no different symbols are found.

I am not familiar enough with the guts of the dynamic linker to review
your code changes, but I endorse the idea.  This should make it
possible to eliminate most of the double definitions of functions in
libc.so and libpthread.so without needing to leave forwarding stubs
behind in libpthread.

zw
  
Florian Weimer June 27, 2019, 3:08 p.m. UTC | #2
* Zack Weinberg:

> On Thu, Jun 27, 2019 at 10:44 AM Florian Weimer <fweimer@redhat.com> wrote:
>>
>> This change should be fully backwards-compatible because the old
>> code aborted the load if a soname mismatch was encountered
>> (instead of searching further for a matching symbol).  This means
>> that no different symbols are found.
>
> I am not familiar enough with the guts of the dynamic linker to review
> your code changes, but I endorse the idea.  This should make it
> possible to eliminate most of the double definitions of functions in
> libc.so and libpthread.so without needing to leave forwarding stubs
> behind in libpthread.

Right, I want to use this to fix bug 20188, among other things.

It's also a prerequisite for weak symbol version support.  (In my
interpretation of Ulrich's design, a weak symbol version reference
should be generated by the link editor if all symbols that reference
this version are weak.)  ld.so is expected to support those already, but
any use of them currently runs into that error:

  <https://sourceware.org/bugzilla/show_bug.cgi?id=24718#c16>

Thanks,
Florian
  
Yann Droneaud June 27, 2019, 4:07 p.m. UTC | #3
Hi,

Le jeudi 27 juin 2019 à 16:43 +0200, Florian Weimer a écrit :
> This change should be fully backwards-compatible because the old
> code aborted the load if a soname mismatch was encountered
> (instead of searching further for a matching symbol).  This means
> that no different symbols are found.
> 
> The soname check was explicitly disabled for the skip_map != NULL
> case.  However, this only happens with dl(v)sym and RTLD_NEXT,
> and those lookups do not come with a verneed entry that could be used
> for the check.
> 
> The error check was already explicitly disabled for the skip_map !=
> NULL case, that is, when dl(v)sym was called with RTLD_NEXT.  But
> _dl_vsym always sets filename in the struct r_found_version argument
> to NULL, so the check was not active anyway.  This means that
> symbol lookup results for the skip_map != NULL case do not change,
> either.
> 
> 2019-06-27  Florian Weimer  <fweimer@redhat.com>
> 
> 	[BZ #24741]
> 	* elf/dl-lookup.c (do_lookup_x): Do not fail if there is a soname
> 	mismatch in a versioned symbol reference.
> 	(_dl_lookup_symbol_x): Do not report soname mismatch failures.
> 	* elf/Makefile [$(build-shared)] (tests): Add tst-sonamemove,
> 	tst-sonamemove-dlopen.
> 	(module-names): Add tst-sonamemove-linkmod1,
> 	tst-sonamemove-runmod1, tst-sonamemove-runmod2.
> 	(LDFLAGS-tst-sonamemove-linkmod1.so): Set.
> 	(LDFLAGS-tst-sonamemove-runmod1.so): Likewise.
> 	(LDFLAGS-tst-sonamemove-runmod2.so): Likewise.
> 	(tst-sonamemove-runmod1.so): Link against
> 	tst-sonamemove-runmod2.so.
> 	(tst-sonamemove): Link against tst-sonamemove-linkmod1.so.
> 	(tst-sonamemove.out): Depend on tst-sonamemove-runmod1.so,
> 	tst-sonamemove-runmod2.so.
> 	(tst-sonamemove-dlopen.out): Likewise.
> 	* elf/tst-sonamemove.c: New file.
> 	* elf/tst-sonamemove-dlopen.c: Likewise.
> 	* elf/tst-sonamemove-linkmod1.c: Likewise.
> 	* elf/tst-sonamemove-linkmod1.map: Likewise.
> 	* elf/tst-sonamemove-runmod1.c: Likewise.
> 	* elf/tst-sonamemove-runmod1.map: Likewise.
> 	* elf/tst-sonamemove-runmod2.c: Likewise.
> 	* elf/tst-sonamemove-runmod2.map: Likewise.
> 	* support/xdlfcn.h (xdlvsym): Declare function.
> 	* support/xdlfcn.c (xdlvsym): Define funciton.
> 
> diff --git a/NEWS b/NEWS
> index 8a2fecef47..8cea9f5825 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -34,6 +34,12 @@ Major new features:
>    pointer subtraction within the allocated object, where results might
>    overflow the ptrdiff_t type.
>  
> +* The dynamic linker no longer refuses to load objects which reference
> +  versioned symbols whose implementation has moved to a different soname
> +  since the object has been linked.  The old error message, symbol
> +  FUNCTION-NAME, version SYMBOL-VERSION not defined in file DSO-NAME with
> +  link time reference, is gone.
> +
>  Deprecated and removed features, and other changes affecting compatibility:
>  
>  * The functions clock_gettime, clock_getres, clock_settime,
> diff --git a/elf/Makefile b/elf/Makefile
> index 27a2fa8c14..76b0565054 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -191,7 +191,8 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
>  	 tst-nodelete2 tst-audit11 tst-audit12 tst-dlsym-error tst-noload \
>  	 tst-latepthread tst-tls-manydynamic tst-nodelete-dlclose \
>  	 tst-debug1 tst-main1 tst-absolute-sym tst-absolute-zero tst-big-note \
> -	 tst-unwind-ctor tst-unwind-main tst-audit13
> +	 tst-unwind-ctor tst-unwind-main tst-audit13 \
> +	 tst-sonamemove tst-sonamemove-dlopen

tst-sonamemove could be name tst-sonamemove-link to make obvious the
difference with -dlopen.

>  #	 reldep9
>  tests-internal += loadtest unload unload2 circleload1 \
>  	 neededtest neededtest2 neededtest3 neededtest4 \
> @@ -281,7 +282,8 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
>  		tst-nodelete-dlclose-dso tst-nodelete-dlclose-plugin \
>  		tst-main1mod tst-libc_dlvsym-dso tst-absolute-sym-lib \
>  		tst-absolute-zero-lib tst-big-note-lib tst-unwind-ctor-lib \
> -		tst-audit13mod1
> +		tst-audit13mod1 tst-sonamemove-linkmod1 \
> +		tst-sonamemove-runmod1 tst-sonamemove-runmod2
>  # Most modules build with _ISOMAC defined, but those filtered out
>  # depend on internal headers.
>  modules-names-tests = $(filter-out ifuncmod% tst-libc_dlvsym-dso tst-tlsmod%,\
> @@ -1410,6 +1412,28 @@ $(objpfx)tst-audit13.out: $(objpfx)tst-audit13mod1.so
>  LDFLAGS-tst-audit13mod1.so = -Wl,-z,lazy
>  tst-audit13-ENV = LD_AUDIT=$(objpfx)tst-audit13mod1.so
>  
> +# tst-sonamemove links against an older implementation of the library.
> +LDFLAGS-tst-sonamemove-linkmod1.so = \
> +  -Wl,--version-script=tst-sonamemove-linkmod1.map \
> +  -Wl,-soname,tst-sonamemove-runmod1.so
> +LDFLAGS-tst-sonamemove-runmod1.so = -Wl,--no-as-needed \
> +  -Wl,--version-script=tst-sonamemove-runmod1.map \
> +  -Wl,-soname,tst-sonamemove-runmod1.so
> +LDFLAGS-tst-sonamemove-runmod2.so = \
> +  -Wl,--version-script=tst-sonamemove-runmod2.map \
> +  -Wl,-soname,tst-sonamemove-runmod2.so
> +$(objpfx)tst-sonamemove-runmod1.so: $(objpfx)tst-sonamemove-runmod2.so
> +# Link against the link module, but depend on the run-time modules
> +# for execution.
> +$(objpfx)tst-sonamemove: $(objpfx)tst-sonamemove-linkmod1.so
> +$(objpfx)tst-sonamemove.out: \
> +  $(objpfx)tst-sonamemove-runmod1.so \
> +  $(objpfx)tst-sonamemove-runmod2.so
> +$(objpfx)tst-sonamemove-dlopen: $(libdl)
> +$(objpfx)tst-sonamemove.out: \
> +  $(objpfx)tst-sonamemove-runmod1.so \
> +  $(objpfx)tst-sonamemove-runmod2.so
> +

The last three lines duplicate those above.


>  # Override -z defs, so that we can reference an undefined symbol.
>  # Force lazy binding for the same reason.
>  LDFLAGS-tst-latepthreadmod.so = \

[...]

> diff --git a/elf/tst-sonamemove-dlopen.c b/elf/tst-sonamemove-dlopen.c
> new file mode 100644
> index 0000000000..c496705044
> --- /dev/null
> +++ b/elf/tst-sonamemove-dlopen.c
> @@ -0,0 +1,35 @@
> +/* Check that a moved versioned symbol can be found using dlsym, dlvsym.
> +   Copyright (C) 2019 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <http://www.gnu.org/licenses/>;.  */
> +
> +#include <stddef.h>
> +#include <support/check.h>
> +#include <support/xdlfcn.h>
> +
> +static int
> +do_test (void)
> +{
> +  /* tst-sonamemove-runmod1.so does not define moved_function, but it
> +     depends on tst-sonamemove-runmod2.so, which does.  */
> +  void *handle = xdlopen ("tst-sonamemove-runmod1.so", RTLD_NOW);
> +  TEST_VERIFY (xdlsym (handle, "moved_function") != NULL);
> +  TEST_VERIFY (xdlvsym (handle, "moved_function", "SONAME_MOVE") != NULL);
> +
> +  return 0;
> +}
> +
> +#include <support/test-driver.c>
> diff --git a/elf/tst-sonamemove-linkmod1.c b/elf/tst-sonamemove-linkmod1.c
> new file mode 100644
> index 0000000000..b8a354e5e3
> --- /dev/null
> +++ b/elf/tst-sonamemove-linkmod1.c
> @@ -0,0 +1,25 @@
> +/* Link interface for (lack of) soname matching in versioned symbol refs.
> +   Copyright (C) 2019 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <http://www.gnu.org/licenses/>;.  */
> +
> +/* This function moved from tst-sonamemove-runmod1.so.  This module is
> +   intended for linking only, to simulate an old application which was
> +   linked against an older version of the library.  */
> +void
> +moved_function (void)
> +{
> +}
> diff --git a/elf/tst-sonamemove-linkmod1.map b/elf/tst-sonamemove-linkmod1.map
> new file mode 100644
> index 0000000000..8fe5904018
> --- /dev/null
> +++ b/elf/tst-sonamemove-linkmod1.map
> @@ -0,0 +1,3 @@
> +SONAME_MOVE {
> +  global: moved_function;
> +};
> diff --git a/elf/tst-sonamemove-runmod1.c b/elf/tst-sonamemove-runmod1.c
> new file mode 100644
> index 0000000000..5c409e2289
> --- /dev/null
> +++ b/elf/tst-sonamemove-runmod1.c
> @@ -0,0 +1,23 @@
> +/* Run-time module whose moved_function moved to a library dependency.
> +   Copyright (C) 2019 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <http://www.gnu.org/licenses/>;.  */
> +
> +/* Dummy function to add the required symbol version.  */
> +void
> +other_function (void)
> +{
> +}
> diff --git a/elf/tst-sonamemove-runmod1.map b/elf/tst-sonamemove-runmod1.map
> new file mode 100644
> index 0000000000..2ea81c6e6f
> --- /dev/null
> +++ b/elf/tst-sonamemove-runmod1.map
> @@ -0,0 +1,3 @@
> +SONAME_MOVE {
> +  global: other_function;
> +};
> diff --git a/elf/tst-sonamemove-runmod2.c b/elf/tst-sonamemove-runmod2.c
> new file mode 100644
> index 0000000000..b5e482eff5
> --- /dev/null
> +++ b/elf/tst-sonamemove-runmod2.c
> @@ -0,0 +1,24 @@
> +/* Run-time module with the actual implementation of moved_function.
> +   Copyright (C) 2019 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <http://www.gnu.org/licenses/>;.  */
> +
> +/* In the test scenario, this function was originally in
> +   tst-sonamemove-runmod1.so.  */
> +void
> +moved_function (void)
> +{
> +}
> diff --git a/elf/tst-sonamemove-runmod2.map b/elf/tst-sonamemove-runmod2.map
> new file mode 100644
> index 0000000000..8fe5904018
> --- /dev/null
> +++ b/elf/tst-sonamemove-runmod2.map
> @@ -0,0 +1,3 @@
> +SONAME_MOVE {
> +  global: moved_function;
> +};
> diff --git a/elf/tst-sonamemove.c b/elf/tst-sonamemove.c
> new file mode 100644
> index 0000000000..a80ebcb36f
> --- /dev/null
> +++ b/elf/tst-sonamemove.c
> @@ -0,0 +1,30 @@
> +/* Check that a versioned symbol can move from one library to another.
> +   Copyright (C) 2019 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <http://www.gnu.org/licenses/>;.  */
> +
> +/* moved_function is linked against tst-sonamemove-runmod1.so, but the
> +   actual implementation is in tst-sonamemove-runmod1.so. */

I found this comment unclear. tst-sonamemove is linked against tst-
sonamemove-linkmod1.so, which has tst-sonamemove-runmod1.so soname.
When tst-sonamemove is run, tst-sonamemove-runmod1.so is loaded, which
imply loading tst-sonamemove-runmod2.so as a dependency. tst-
sonamemove-runmod2.so is where move_function's implementation is.

> +void moved_function (void);
> +
> +static int
> +do_test (void)
> +{
> +  moved_function ();
> +  return 0;
> +}
> +
> +#include <support/test-driver.c>

Regards.
  

Patch

diff --git a/NEWS b/NEWS
index 8a2fecef47..8cea9f5825 100644
--- a/NEWS
+++ b/NEWS
@@ -34,6 +34,12 @@  Major new features:
   pointer subtraction within the allocated object, where results might
   overflow the ptrdiff_t type.
 
+* The dynamic linker no longer refuses to load objects which reference
+  versioned symbols whose implementation has moved to a different soname
+  since the object has been linked.  The old error message, symbol
+  FUNCTION-NAME, version SYMBOL-VERSION not defined in file DSO-NAME with
+  link time reference, is gone.
+
 Deprecated and removed features, and other changes affecting compatibility:
 
 * The functions clock_gettime, clock_getres, clock_settime,
diff --git a/elf/Makefile b/elf/Makefile
index 27a2fa8c14..76b0565054 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -191,7 +191,8 @@  tests += restest1 preloadtest loadfail multiload origtest resolvfail \
 	 tst-nodelete2 tst-audit11 tst-audit12 tst-dlsym-error tst-noload \
 	 tst-latepthread tst-tls-manydynamic tst-nodelete-dlclose \
 	 tst-debug1 tst-main1 tst-absolute-sym tst-absolute-zero tst-big-note \
-	 tst-unwind-ctor tst-unwind-main tst-audit13
+	 tst-unwind-ctor tst-unwind-main tst-audit13 \
+	 tst-sonamemove tst-sonamemove-dlopen
 #	 reldep9
 tests-internal += loadtest unload unload2 circleload1 \
 	 neededtest neededtest2 neededtest3 neededtest4 \
@@ -281,7 +282,8 @@  modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
 		tst-nodelete-dlclose-dso tst-nodelete-dlclose-plugin \
 		tst-main1mod tst-libc_dlvsym-dso tst-absolute-sym-lib \
 		tst-absolute-zero-lib tst-big-note-lib tst-unwind-ctor-lib \
-		tst-audit13mod1
+		tst-audit13mod1 tst-sonamemove-linkmod1 \
+		tst-sonamemove-runmod1 tst-sonamemove-runmod2
 # Most modules build with _ISOMAC defined, but those filtered out
 # depend on internal headers.
 modules-names-tests = $(filter-out ifuncmod% tst-libc_dlvsym-dso tst-tlsmod%,\
@@ -1410,6 +1412,28 @@  $(objpfx)tst-audit13.out: $(objpfx)tst-audit13mod1.so
 LDFLAGS-tst-audit13mod1.so = -Wl,-z,lazy
 tst-audit13-ENV = LD_AUDIT=$(objpfx)tst-audit13mod1.so
 
+# tst-sonamemove links against an older implementation of the library.
+LDFLAGS-tst-sonamemove-linkmod1.so = \
+  -Wl,--version-script=tst-sonamemove-linkmod1.map \
+  -Wl,-soname,tst-sonamemove-runmod1.so
+LDFLAGS-tst-sonamemove-runmod1.so = -Wl,--no-as-needed \
+  -Wl,--version-script=tst-sonamemove-runmod1.map \
+  -Wl,-soname,tst-sonamemove-runmod1.so
+LDFLAGS-tst-sonamemove-runmod2.so = \
+  -Wl,--version-script=tst-sonamemove-runmod2.map \
+  -Wl,-soname,tst-sonamemove-runmod2.so
+$(objpfx)tst-sonamemove-runmod1.so: $(objpfx)tst-sonamemove-runmod2.so
+# Link against the link module, but depend on the run-time modules
+# for execution.
+$(objpfx)tst-sonamemove: $(objpfx)tst-sonamemove-linkmod1.so
+$(objpfx)tst-sonamemove.out: \
+  $(objpfx)tst-sonamemove-runmod1.so \
+  $(objpfx)tst-sonamemove-runmod2.so
+$(objpfx)tst-sonamemove-dlopen: $(libdl)
+$(objpfx)tst-sonamemove.out: \
+  $(objpfx)tst-sonamemove-runmod1.so \
+  $(objpfx)tst-sonamemove-runmod2.so
+
 # Override -z defs, so that we can reference an undefined symbol.
 # Force lazy binding for the same reason.
 LDFLAGS-tst-latepthreadmod.so = \
diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
index e3f42a1efb..eb23cca4e3 100644
--- a/elf/dl-lookup.c
+++ b/elf/dl-lookup.c
@@ -536,11 +536,7 @@  do_lookup_x (const char *undef_name, uint_fast32_t new_hash,
 	}
 
 skip:
-      /* If this current map is the one mentioned in the verneed entry
-	 and we have not found a weak entry, it is a bug.  */
-      if (symidx == STN_UNDEF && version != NULL && version->filename != NULL
-	  && __glibc_unlikely (_dl_name_match_p (version->filename, map)))
-	return -1;
+      ;
     }
   while (++i < n);
 
@@ -810,34 +806,10 @@  _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map,
 
   /* Search the relevant loaded objects for a definition.  */
   for (size_t start = i; *scope != NULL; start = 0, ++scope)
-    {
-      int res = do_lookup_x (undef_name, new_hash, &old_hash, *ref,
-			     &current_value, *scope, start, version, flags,
-			     skip_map, type_class, undef_map);
-      if (res > 0)
-	break;
-
-      if (__glibc_unlikely (res < 0) && skip_map == NULL)
-	{
-	  /* Oh, oh.  The file named in the relocation entry does not
-	     contain the needed symbol.  This code is never reached
-	     for unversioned lookups.  */
-	  assert (version != NULL);
-	  const char *reference_name = undef_map ? undef_map->l_name : "";
-	  struct dl_exception exception;
-	  /* XXX We cannot translate the message.  */
-	  _dl_exception_create_format
-	    (&exception, DSO_FILENAME (reference_name),
-	     "symbol %s version %s not defined in file %s"
-	     " with link time reference%s",
-	     undef_name, version->name, version->filename,
-	     res == -2 ? " (no version symbols)" : "");
-	  _dl_signal_cexception (0, &exception, N_("relocation error"));
-	  _dl_exception_free (&exception);
-	  *ref = NULL;
-	  return 0;
-	}
-    }
+    if (do_lookup_x (undef_name, new_hash, &old_hash, *ref,
+		     &current_value, *scope, start, version, flags,
+		     skip_map, type_class, undef_map) != 0)
+      break;
 
   if (__glibc_unlikely (current_value.s == NULL))
     {
diff --git a/elf/tst-sonamemove-dlopen.c b/elf/tst-sonamemove-dlopen.c
new file mode 100644
index 0000000000..c496705044
--- /dev/null
+++ b/elf/tst-sonamemove-dlopen.c
@@ -0,0 +1,35 @@ 
+/* Check that a moved versioned symbol can be found using dlsym, dlvsym.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <stddef.h>
+#include <support/check.h>
+#include <support/xdlfcn.h>
+
+static int
+do_test (void)
+{
+  /* tst-sonamemove-runmod1.so does not define moved_function, but it
+     depends on tst-sonamemove-runmod2.so, which does.  */
+  void *handle = xdlopen ("tst-sonamemove-runmod1.so", RTLD_NOW);
+  TEST_VERIFY (xdlsym (handle, "moved_function") != NULL);
+  TEST_VERIFY (xdlvsym (handle, "moved_function", "SONAME_MOVE") != NULL);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-sonamemove-linkmod1.c b/elf/tst-sonamemove-linkmod1.c
new file mode 100644
index 0000000000..b8a354e5e3
--- /dev/null
+++ b/elf/tst-sonamemove-linkmod1.c
@@ -0,0 +1,25 @@ 
+/* Link interface for (lack of) soname matching in versioned symbol refs.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* This function moved from tst-sonamemove-runmod1.so.  This module is
+   intended for linking only, to simulate an old application which was
+   linked against an older version of the library.  */
+void
+moved_function (void)
+{
+}
diff --git a/elf/tst-sonamemove-linkmod1.map b/elf/tst-sonamemove-linkmod1.map
new file mode 100644
index 0000000000..8fe5904018
--- /dev/null
+++ b/elf/tst-sonamemove-linkmod1.map
@@ -0,0 +1,3 @@ 
+SONAME_MOVE {
+  global: moved_function;
+};
diff --git a/elf/tst-sonamemove-runmod1.c b/elf/tst-sonamemove-runmod1.c
new file mode 100644
index 0000000000..5c409e2289
--- /dev/null
+++ b/elf/tst-sonamemove-runmod1.c
@@ -0,0 +1,23 @@ 
+/* Run-time module whose moved_function moved to a library dependency.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* Dummy function to add the required symbol version.  */
+void
+other_function (void)
+{
+}
diff --git a/elf/tst-sonamemove-runmod1.map b/elf/tst-sonamemove-runmod1.map
new file mode 100644
index 0000000000..2ea81c6e6f
--- /dev/null
+++ b/elf/tst-sonamemove-runmod1.map
@@ -0,0 +1,3 @@ 
+SONAME_MOVE {
+  global: other_function;
+};
diff --git a/elf/tst-sonamemove-runmod2.c b/elf/tst-sonamemove-runmod2.c
new file mode 100644
index 0000000000..b5e482eff5
--- /dev/null
+++ b/elf/tst-sonamemove-runmod2.c
@@ -0,0 +1,24 @@ 
+/* Run-time module with the actual implementation of moved_function.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* In the test scenario, this function was originally in
+   tst-sonamemove-runmod1.so.  */
+void
+moved_function (void)
+{
+}
diff --git a/elf/tst-sonamemove-runmod2.map b/elf/tst-sonamemove-runmod2.map
new file mode 100644
index 0000000000..8fe5904018
--- /dev/null
+++ b/elf/tst-sonamemove-runmod2.map
@@ -0,0 +1,3 @@ 
+SONAME_MOVE {
+  global: moved_function;
+};
diff --git a/elf/tst-sonamemove.c b/elf/tst-sonamemove.c
new file mode 100644
index 0000000000..a80ebcb36f
--- /dev/null
+++ b/elf/tst-sonamemove.c
@@ -0,0 +1,30 @@ 
+/* Check that a versioned symbol can move from one library to another.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* moved_function is linked against tst-sonamemove-runmod1.so, but the
+   actual implementation is in tst-sonamemove-runmod1.so. */
+void moved_function (void);
+
+static int
+do_test (void)
+{
+  moved_function ();
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/support/xdlfcn.c b/support/xdlfcn.c
index b2e5c21134..11fe4896d1 100644
--- a/support/xdlfcn.c
+++ b/support/xdlfcn.c
@@ -51,6 +51,26 @@  xdlsym (void *handle, const char *symbol)
   return sym;
 }
 
+void *
+xdlvsym (void *handle, const char *symbol, const char *version)
+{
+  /* Clear any pending errors.  */
+  dlerror ();
+
+  void *sym = dlvsym (handle, symbol, version);
+
+  if (sym == NULL)
+    {
+      const char *error = dlerror ();
+      if (error != NULL)
+        FAIL_EXIT1 ("error: dlvsym: %s\n", error);
+      /* If there was no error, we found a NULL symbol.  Return the
+         NULL value in this case.  */
+    }
+
+  return sym;
+}
+
 void
 xdlclose (void *handle)
 {
diff --git a/support/xdlfcn.h b/support/xdlfcn.h
index c9cd810a8a..7f8d4930fc 100644
--- a/support/xdlfcn.h
+++ b/support/xdlfcn.h
@@ -27,6 +27,7 @@  __BEGIN_DECLS
 void *xdlopen (const char *filename, int flags);
 void *xdlmopen (Lmid_t lmid, const char *filename, int flags);
 void *xdlsym (void *handle, const char *symbol);
+void *xdlvsym (void *handle, const char *symbol, const char *version);
 void xdlclose (void *handle);
 
 __END_DECLS