[PATCH v2] dlfcn: dlinfo (RTLD_DI_PHDR) should work for proxy link maps (bug 32060)
Florian Weimer
fweimer@redhat.com
Tue Dec 3 12:29:12 GMT 2024
Previously, 0 was returned as the program header address.
To get a clean test run for the new dlfcn/tst-dlinfo-dlmopen test,
it is necessary to handle RTLD_DI_ORIGIN failures in dlinfo, too.
---
v2: Handle proxy link maps correctly for RTLD_DI_LINKMAP, RTLD_DI_LMID.
Add new test dlfcn/tst-dlinfo-dlmopen. Fix RTLD_DI_ORIGIN for
link maps with unknown origin. Use RTLD_DI_LINKMAP in
dlfcn/tst-dlinfo-phdr per Adhemerval's suggestion.
dlfcn/Makefile | 1 +
dlfcn/dlinfo.c | 26 +++++++-----
dlfcn/tst-dlinfo-dlmopen.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++
dlfcn/tst-dlinfo-phdr.c | 73 +++++++++++++++++++++-------------
4 files changed, 163 insertions(+), 36 deletions(-)
diff --git a/dlfcn/Makefile b/dlfcn/Makefile
index d473773dd4..dcc0fd2b70 100644
--- a/dlfcn/Makefile
+++ b/dlfcn/Makefile
@@ -124,6 +124,7 @@ tststatic4-ENV = $(tststatic-ENV)
tststatic5-ENV = $(tststatic-ENV)
tests-internal += \
+ tst-dlinfo-dlmopen \
tst-dlinfo-phdr \
# tests-internal
diff --git a/dlfcn/dlinfo.c b/dlfcn/dlinfo.c
index b0feb9362d..7d13b29822 100644
--- a/dlfcn/dlinfo.c
+++ b/dlfcn/dlinfo.c
@@ -49,41 +49,49 @@ dlinfo_doit (void *argsblock)
break;
case RTLD_DI_LMID:
+ /* Return the proxy namespace (if any). */
*(Lmid_t *) args->arg = l->l_ns;
break;
case RTLD_DI_LINKMAP:
+ /* Return the proxy link map (if any). */
*(struct link_map **) args->arg = l;
break;
case RTLD_DI_SERINFO:
- _dl_rtld_di_serinfo (l, args->arg, false);
+ _dl_rtld_di_serinfo (l->l_real, args->arg, false);
break;
case RTLD_DI_SERINFOSIZE:
- _dl_rtld_di_serinfo (l, args->arg, true);
+ _dl_rtld_di_serinfo (l->l_real, args->arg, true);
break;
case RTLD_DI_ORIGIN:
- strcpy (args->arg, l->l_origin);
+ /* If the object was loaded by the kernel, origin information
+ could be unavailable. */
+ if (l->l_real->l_origin == NULL || l->l_real->l_origin == (void *) -1)
+ {
+ args->result = -1;
+ _dl_signal_error (0, NULL, NULL, N_("RTLD_DI_ORIGIN unavailable"));
+ }
+ strcpy (args->arg, l->l_real->l_origin);
break;
case RTLD_DI_TLS_MODID:
- *(size_t *) args->arg = 0;
- *(size_t *) args->arg = l->l_tls_modid;
+ *(size_t *) args->arg = l->l_real->l_tls_modid;
break;
case RTLD_DI_TLS_DATA:
{
void *data = NULL;
- if (l->l_tls_modid != 0)
- data = GLRO(dl_tls_get_addr_soft) (l);
+ if (l->l_real->l_tls_modid != 0)
+ data = GLRO(dl_tls_get_addr_soft) (l->l_real);
*(void **) args->arg = data;
break;
}
case RTLD_DI_PHDR:
- *(const ElfW(Phdr) **) args->arg = l->l_phdr;
- args->result = l->l_phnum;
+ *(const ElfW(Phdr) **) args->arg = l->l_real->l_phdr;
+ args->result = l->l_real->l_phnum;
break;
}
}
diff --git a/dlfcn/tst-dlinfo-dlmopen.c b/dlfcn/tst-dlinfo-dlmopen.c
new file mode 100644
index 0000000000..ef44005803
--- /dev/null
+++ b/dlfcn/tst-dlinfo-dlmopen.c
@@ -0,0 +1,99 @@
+/* Test for dlinfo with dlmopen.
+ Copyright (C) 2022-2024 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
+ <https://www.gnu.org/licenses/>. */
+
+/* This internal test assumes that dlopen handles are inplemented as
+ struct link_map pointers. */
+
+#include <dlfcn.h>
+#include <gnu/lib-names.h>
+#include <link.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/auxv.h>
+#include <unistd.h>
+
+#include <support/check.h>
+#include <support/xdlfcn.h>
+
+/* Per-link-map checks. */
+static void
+checks (struct link_map *l, Lmid_t expected_nsid)
+{
+ printf ("info: checking link map %p for \"%s\" [%ld]\n",
+ l, l->l_name, l->l_ns);
+
+ /* Cause dlerror () to return an error message. */
+ dlsym (RTLD_DEFAULT, "does-not-exist");
+
+ /* Use the extension that link maps are valid dlopen handles. */
+ struct link_map *l2 = NULL;
+ TEST_COMPARE (dlinfo (l, RTLD_DI_LINKMAP, &l2), 0);
+ /* This equality should hold even if l is a proxy link map. */
+ TEST_VERIFY (l2 == l);
+ /* Verify that the error message has been cleared. */
+ TEST_COMPARE_STRING (dlerror (), NULL);
+
+ /* Cause dlerror () to return an error message. */
+ dlsym (RTLD_DEFAULT, "does-not-exist");
+
+ Lmid_t lmid = -1;
+ TEST_COMPARE (dlinfo (l, RTLD_DI_LMID, &lmid), 0);
+ /* Assume that namespaces are allocated sequentially. */
+ TEST_COMPARE (lmid, expected_nsid);
+ /* Verify that the error message has been cleared. */
+ TEST_COMPARE_STRING (dlerror (), NULL);
+
+ char origin[4096]; /* Cannot size properly (bug 24298). */
+ origin[0] = '\0';
+ int result = dlinfo (l, RTLD_DI_ORIGIN, origin);
+ if (result != 0)
+ {
+ /* The RTLD_DI_ORIGIN request can fail, but should not crash. */
+ TEST_COMPARE (result, -1);
+ TEST_VERIFY (dlerror () != NULL);
+ }
+ else
+ /* This should be an existing path. */
+ TEST_COMPARE (access (origin, F_OK), 0);
+}
+
+static int
+do_test (void)
+{
+ /* Avoid a copy relocation. */
+ struct r_debug *debug = xdlsym (RTLD_DEFAULT, "_r_debug");
+ struct link_map *l = (struct link_map *) debug->r_map;
+ TEST_VERIFY_EXIT (l != NULL);
+
+ for (; l != NULL; l = l->l_next)
+ checks (l, LM_ID_BASE);
+
+ /* The secondary namespace does not contain the main executable, and
+ dl_iterate_phdr does not cover it. */
+ struct link_map *secondary_libc = xdlmopen (LM_ID_NEWLM, LIBC_SO, RTLD_NOW);
+ for (l = secondary_libc; l != NULL; l = l->l_next)
+ /* Assume that namespaces are allocated sequentially. */
+ checks (l, LM_ID_BASE + 1);
+ TEST_VERIFY (secondary_libc->l_real == secondary_libc);
+ xdlclose (secondary_libc);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/dlfcn/tst-dlinfo-phdr.c b/dlfcn/tst-dlinfo-phdr.c
index fdffb17724..9e61134210 100644
--- a/dlfcn/tst-dlinfo-phdr.c
+++ b/dlfcn/tst-dlinfo-phdr.c
@@ -17,6 +17,7 @@
<https://www.gnu.org/licenses/>. */
#include <dlfcn.h>
+#include <gnu/lib-names.h>
#include <link.h>
#include <stdbool.h>
#include <stdio.h>
@@ -54,6 +55,42 @@ dlip_callback (struct dl_phdr_info *dlpi, size_t size, void *closure)
return 0;
}
+/* Set by basic_checks. */
+static const ElfW(Phdr) *phdr;
+static int phnum;
+
+/* Basic checks that can be used regardless of namespace. */
+static void
+basic_checks (struct link_map *l)
+{
+ printf ("info: checking link map %p (%p) for \"%s\"\n",
+ l, l->l_phdr, l->l_name);
+
+ /* Cause dlerror () to return an error message. */
+ dlsym (RTLD_DEFAULT, "does-not-exist");
+
+ /* Use the extension that link maps are valid dlopen handles. */
+ phnum = dlinfo (l, RTLD_DI_PHDR, &phdr);
+ TEST_VERIFY (phnum >= 0);
+ /* Verify that the error message has been cleared. */
+ TEST_COMPARE_STRING (dlerror (), NULL);
+
+ TEST_VERIFY (phdr == l->l_real->l_phdr);
+ TEST_COMPARE (phnum, l->l_real->l_phnum);
+
+ /* Check that we can find PT_DYNAMIC among the array. */
+ {
+ bool dynamic_found = false;
+ for (int i = 0; i < phnum; ++i)
+ if (phdr[i].p_type == PT_DYNAMIC)
+ {
+ dynamic_found = true;
+ TEST_COMPARE ((ElfW(Addr)) l->l_ld, l->l_addr + phdr[i].p_vaddr);
+ }
+ TEST_VERIFY (dynamic_found);
+ }
+}
+
static int
do_test (void)
{
@@ -64,33 +101,7 @@ do_test (void)
do
{
- printf ("info: checking link map %p (%p) for \"%s\"\n",
- l, l->l_phdr, l->l_name);
-
- /* Cause dlerror () to return an error message. */
- dlsym (RTLD_DEFAULT, "does-not-exist");
-
- /* Use the extension that link maps are valid dlopen handles. */
- const ElfW(Phdr) *phdr;
- int phnum = dlinfo (l, RTLD_DI_PHDR, &phdr);
- TEST_VERIFY (phnum >= 0);
- /* Verify that the error message has been cleared. */
- TEST_COMPARE_STRING (dlerror (), NULL);
-
- TEST_VERIFY (phdr == l->l_phdr);
- TEST_COMPARE (phnum, l->l_phnum);
-
- /* Check that we can find PT_DYNAMIC among the array. */
- {
- bool dynamic_found = false;
- for (int i = 0; i < phnum; ++i)
- if (phdr[i].p_type == PT_DYNAMIC)
- {
- dynamic_found = true;
- TEST_COMPARE ((ElfW(Addr)) l->l_ld, l->l_addr + phdr[i].p_vaddr);
- }
- TEST_VERIFY (dynamic_found);
- }
+ basic_checks (l);
/* Check that dl_iterate_phdr finds the link map with the same
program headers. */
@@ -119,6 +130,14 @@ do_test (void)
}
while (l != NULL);
+ /* The secondary namespace does not contain the main executable, and
+ dl_iterate_phdr does not cover it. */
+ struct link_map *secondary_libc = xdlmopen (LM_ID_NEWLM, LIBC_SO, RTLD_NOW);
+ TEST_COMPARE (dlinfo (secondary_libc, RTLD_DI_LINKMAP, &l), 0);
+ for (; l != NULL; l = l->l_next)
+ basic_checks (l);
+ xdlclose (secondary_libc);
+
return 0;
}
base-commit: f43eb2cf30fdff39bda1c2018246d4badabbc576
More information about the Libc-alpha
mailing list