This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] ld.so: Enable preloading of new symbol versions [BZ #24974]


This commit adds the --no-version-coverage-check option to the
dynamic loader, and the LD_NO_VERSION_COVERAGE_CHECK environment
variable.

The new _dl_no_version_coverage_check field in struct rtld_global_ro
lands in previously-unused padding (on x86-64).

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

	[BZ #24974]
	* elf/rtld.c (dl_main): Add --no-version-coverage-check option.
	(process_envvars): Handle LD_NO_VERSION_COVERAGE_CHECK.
	* sysdeps/generic/ldsodefs.h (struct rtld_global_ro): Add
	_dl_no_version_coverage_check field.
	* elf/dl-version.c (match_symbol): Check it before reporting an
	error.
	* elf/Makefile [$(build-shared)] (tests): Add
	tst-no-version-coverage-lazy, tst-no-version-coverage-now.
	(modules-names): Add tst-no-version-coverage-linkmod,
	tst-no-version-coverage-runmod, tst-no-version-coverage-preloadmod.
	(LDFLAGS-tst-no-version-coverage-linkmod.so): Set version map and
	soname.
	(LDFLAGS-tst-no-version-coverage-runmod.so): Likewise.
	(LDFLAGS-tst-no-version-coverage-preloadmod.so): Likewise.
	(LDFLAGS-tst-no-version-coverage-lazy): Enable lazy binding.
	(tst-no-version-coverage-lazy): Link with
	tst-no-version-coverage-linkmod.so.
	(tst-no-version-coverage-lazy-ENV): Disable symbol version
	coverage check.
	(tst-no-version-coverage-lazy.out): Depend on
	tst-no-version-coverage-runmod.so.
	(LDFLAGS-tst-no-version-coverage-now): Disable lazy binding.
	(tst-no-version-coverage-now): Link with
	tst-no-version-coverage-linkmod.so.
	(tst-no-version-coverage-now-ENV): Preload
	tst-no-version-coverage-preloadmod.so.  Disable symbol version
	coverage check.
	(tst-no-version-coverage-now.out): Depend on
	tst-no-version-coverage-preloadmod.so,
	tst-no-version-coverage-runmod.so.
	* elf/tst-no-version-coverage-lazy.c: New file.
	* elf/tst-no-version-coverage-linkmod.c:: Likewise.
	* elf/tst-no-version-coverage-linkmod.map: Likewise.
	* elf/tst-no-version-coverage-now.c: Likewise.
	* elf/tst-no-version-coverage-preloadmod.c: Likewise.
	* elf/tst-no-version-coverage-preloadmod.map: Likewise.
	* elf/tst-no-version-coverage-runmod.c: Likewise.
	* elf/tst-no-version-coverage-runmod.map: Likewise.

diff --git a/NEWS b/NEWS
index a64b89986a..4038bb3d5c 100644
--- a/NEWS
+++ b/NEWS
@@ -21,6 +21,14 @@ Major new features:
   18661-1:2014 and TS 18661-3:2015 as amended by the resolution of
   Clarification Request 13 to TS 18661-3.
 
+* A new command line option, --no-version-coverage-check, and a new
+  environment variable, LD_NO_VERSION_COVERAGE_CHECK=1, have been added to
+  the dynamic linker.  If enabled, it is possible to load objects which do
+  not provide the full set of version definitions required by other objects.
+  Instead, it is expected that the missing symbols and their versions are
+  provided by another shared object, via LD_PRELOAD.  In effect, this
+  permits adding new symbol versions to existing shared objects at run time.
+
 Deprecated and removed features, and other changes affecting compatibility:
 
 * The totalorder and totalordermag functions, and the corresponding
diff --git a/elf/Makefile b/elf/Makefile
index d470e41402..8ed23f6986 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -192,7 +192,8 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
 	 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-sonamemove-link tst-sonamemove-dlopen tst-dlopen-aout
+	 tst-sonamemove-link tst-sonamemove-dlopen tst-dlopen-aout \
+	 tst-no-version-coverage-lazy tst-no-version-coverage-now
 #	 reldep9
 tests-internal += loadtest unload unload2 circleload1 \
 	 neededtest neededtest2 neededtest3 neededtest4 \
@@ -279,7 +280,10 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
 		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-sonamemove-linkmod1 \
-		tst-sonamemove-runmod1 tst-sonamemove-runmod2
+		tst-sonamemove-runmod1 tst-sonamemove-runmod2 \
+		tst-no-version-coverage-linkmod \
+		tst-no-version-coverage-runmod \
+		tst-no-version-coverage-preloadmod
 # 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%,\
@@ -1432,6 +1436,32 @@ $(objpfx)tst-sonamemove-dlopen.out: \
   $(objpfx)tst-sonamemove-runmod1.so \
   $(objpfx)tst-sonamemove-runmod2.so
 
+LDFLAGS-tst-no-version-coverage-linkmod.so = \
+  -Wl,--version-script=tst-no-version-coverage-linkmod.map \
+  -Wl,-soname,tst-no-version-coverage-runmod.so
+LDFLAGS-tst-no-version-coverage-runmod.so = \
+  -Wl,--version-script=tst-no-version-coverage-runmod.map \
+  -Wl,-soname,tst-no-version-coverage-runmod.so
+LDFLAGS-tst-no-version-coverage-preloadmod.so = \
+  -Wl,--version-script=tst-no-version-coverage-preloadmod.map \
+  -Wl,-soname,tst-no-version-coverage-preloadmod.so
+LDFLAGS-tst-no-version-coverage-lazy = -Wl,-z,lazy
+$(objpfx)tst-no-version-coverage-lazy: \
+  $(objpfx)tst-no-version-coverage-linkmod.so
+tst-no-version-coverage-lazy-ENV = \
+  LD_NO_VERSION_COVERAGE_CHECK=1
+$(objpfx)tst-no-version-coverage-lazy.out: \
+  $(objpfx)tst-no-version-coverage-runmod.so
+LDFLAGS-tst-no-version-coverage-now = -Wl,-z,now
+$(objpfx)tst-no-version-coverage-now: \
+  $(objpfx)tst-no-version-coverage-linkmod.so
+tst-no-version-coverage-now-ENV = \
+  LD_NO_VERSION_COVERAGE_CHECK=1 \
+  LD_PRELOAD=tst-no-version-coverage-preloadmod.so
+$(objpfx)tst-no-version-coverage-now.out: \
+  $(objpfx)tst-no-version-coverage-preloadmod.so \
+  $(objpfx)tst-no-version-coverage-runmod.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-version.c b/elf/dl-version.c
index 53c0af3d15..e54b2cd7ff 100644
--- a/elf/dl-version.c
+++ b/elf/dl-version.c
@@ -125,6 +125,11 @@ checking for version `%s' in file %s [%lu] required by file %s [%lu]\n",
       def = (ElfW(Verdef) *) ((char *) def + def->vd_next);
     }
 
+  /* If the dynamic linker was told not check for version coverage, do
+     not report this error.  */
+  if (GLRO (dl_no_version_coverage_check))
+    return 0;
+
   /* Symbol not found.  If it was a weak reference it is not fatal.  */
   if (__glibc_likely (weak))
     {
diff --git a/elf/rtld.c b/elf/rtld.c
index c9490ff694..aed1b0d98c 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -1197,6 +1197,12 @@ dl_main (const ElfW(Phdr) *phdr,
 	    _dl_argc -= 2;
 	    _dl_argv += 2;
 	  }
+	else if (strcmp (_dl_argv[1], "--no-version-coverage-check") == 0)
+	  {
+	    GLRO (dl_no_version_coverage_check) = true;
+	    --_dl_argc;
+	    ++_dl_argv;
+	  }
 	else
 	  break;
 
@@ -1226,7 +1232,11 @@ of this helper program; chances are you did not intend to run this program.\n\
   --inhibit-rpath LIST  ignore RUNPATH and RPATH information in object names\n\
 			in LIST\n\
   --audit LIST          use objects named in LIST as auditors\n\
-  --preload LIST        preload objects named in LIST\n");
+  --preload LIST        preload objects named in LIST\n\
+  --no-version-coverage-check\n\
+                        allow loading of objects which reference symbol\n\
+                        versions which are missing from other objects\n\
+");
 
       ++_dl_skip_args;
       --_dl_argc;
@@ -2677,6 +2687,12 @@ process_envvars (enum mode *modep)
 	    mode = trace;
 	  break;
 
+	case 25:
+	  if (!__libc_enable_secure
+	      && memcmp (envline, "NO_VERSION_COVERAGE_CHECK", 25) == 0)
+	    GLRO (dl_no_version_coverage_check) = envline[26] == '1';
+	  break;
+
 	  /* We might have some extra environment variable to handle.  This
 	     is tricky due to the pre-processing of the length of the name
 	     in the switch statement here.  The code here assumes that added
diff --git a/elf/tst-no-version-coverage-lazy.c b/elf/tst-no-version-coverage-lazy.c
new file mode 100644
index 0000000000..71de1ac935
--- /dev/null
+++ b/elf/tst-no-version-coverage-lazy.c
@@ -0,0 +1,44 @@
+/* Test for the no-version-coverage mode in ld.so, with lazy binding.
+   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/>.  */
+
+/* Defined in tst-no-version-coverage-linkmod.so and in
+   tst-no-version-coverage-runmod.so.  */
+void version_1_function (void);
+
+/* Only defined in tst-no-version-coverage-linkmod.so.  */
+void version_2_function (void);
+
+__attribute__ ((noinline, noclone, weak))
+void
+compiler_barrier (int call_2)
+{
+  version_1_function ();
+  if (call_2)
+    /* This function is not defined at run time, but the test should
+       still pass due to lazy binding.  */
+    version_2_function ();
+}
+
+static int
+do_test (void)
+{
+  compiler_barrier (0);
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-no-version-coverage-linkmod.c b/elf/tst-no-version-coverage-linkmod.c
new file mode 100644
index 0000000000..fb6c1a3a98
--- /dev/null
+++ b/elf/tst-no-version-coverage-linkmod.c
@@ -0,0 +1,29 @@
+/* Link interface for exercising the no-version-coverage mode in ld.so.
+   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 is still present at run time.  */
+void
+version_1_function (void)
+{
+}
+
+/* This function will be removed at run time.  */
+void
+version_2_function (void)
+{
+}
diff --git a/elf/tst-no-version-coverage-linkmod.map b/elf/tst-no-version-coverage-linkmod.map
new file mode 100644
index 0000000000..c1126d8940
--- /dev/null
+++ b/elf/tst-no-version-coverage-linkmod.map
@@ -0,0 +1,7 @@
+VERSION_1 {
+  version_1_function;
+};
+
+VERSION_2 {
+  version_2_function;
+} VERSION_1;
diff --git a/elf/tst-no-version-coverage-now.c b/elf/tst-no-version-coverage-now.c
new file mode 100644
index 0000000000..3133c46d3b
--- /dev/null
+++ b/elf/tst-no-version-coverage-now.c
@@ -0,0 +1,35 @@
+/* Test for the no-version-coverage mode in ld.so, with BIND_NOW.
+   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/>.  */
+
+/* Defined in tst-no-version-coverage-linkmod.so and in
+   tst-no-version-coverage-runmod.so.  */
+void version_1_function (void);
+
+/* Defined in tst-no-version-coverage-linkmod.so and in
+   tst-no-version-coverage-preloadmod.so.  */
+void version_2_function (void);
+
+static int
+do_test (void)
+{
+  version_1_function ();
+  version_2_function ();
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-no-version-coverage-preloadmod.c b/elf/tst-no-version-coverage-preloadmod.c
new file mode 100644
index 0000000000..4bff8c496c
--- /dev/null
+++ b/elf/tst-no-version-coverage-preloadmod.c
@@ -0,0 +1,23 @@
+/* Preloaded DSO for exercising the no-version-coverage mode in ld.so.
+   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 supplies the required function.  */
+void
+version_2_function (void)
+{
+}
diff --git a/elf/tst-no-version-coverage-preloadmod.map b/elf/tst-no-version-coverage-preloadmod.map
new file mode 100644
index 0000000000..7613b6d7de
--- /dev/null
+++ b/elf/tst-no-version-coverage-preloadmod.map
@@ -0,0 +1,3 @@
+VERSION_2 {
+  version_2_function;
+};
diff --git a/elf/tst-no-version-coverage-runmod.c b/elf/tst-no-version-coverage-runmod.c
new file mode 100644
index 0000000000..39d9d063e3
--- /dev/null
+++ b/elf/tst-no-version-coverage-runmod.c
@@ -0,0 +1,23 @@
+/* Run-time DSO for exercising the no-version-coverage mode in ld.so.
+   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 is still present at run time.  */
+void
+version_1_function (void)
+{
+}
diff --git a/elf/tst-no-version-coverage-runmod.map b/elf/tst-no-version-coverage-runmod.map
new file mode 100644
index 0000000000..c5d9e29554
--- /dev/null
+++ b/elf/tst-no-version-coverage-runmod.map
@@ -0,0 +1,3 @@
+VERSION_1 {
+  version_1_function;
+};
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 1e193b05b0..4c75e207e9 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -629,6 +629,13 @@ struct rtld_global_ro
   /* List of auditing interfaces.  */
   struct audit_ifaces *_dl_audit;
   unsigned int _dl_naudit;
+#endif /* SHARED */
+
+  /* If true, allow references to versioned symbols which are not
+     defined.  */
+  bool _dl_no_version_coverage_check;
+
+#ifdef SHARED
 };
 # define __rtld_global_attribute__
 # if IS_IN (rtld)
@@ -645,7 +652,7 @@ extern const struct rtld_global_ro _rtld_global_ro
     attribute_relro __rtld_global_attribute__;
 # endif
 # undef __rtld_global_attribute__
-#endif
+#endif /* SHARED */
 #undef EXTERN
 
 #ifndef SHARED


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]