This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
[PATCH] ld.so: Enable preloading of new symbol versions [BZ #24974]
- From: Florian Weimer <fweimer at redhat dot com>
- To: libc-alpha at sourceware dot org
- Date: Fri, 06 Sep 2019 17:59:52 +0200
- Subject: [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