On 11/11/2021 09:39, Florian Weimer wrote:
> * Adhemerval Zanella:
>
>> For the la_objopen() the path listed in link_map->l_name for the main
>> application binary is empty, even though dladdr() is able to recover the
>> correct path.
>>
>> By setting the expected application name on l_name, it allows to
>> simplify the code to handle the special case and make the API that
>> return such information (la_objopen(), dladdr() and dlinfo() to have
>> similar semantic.
>
> _dl_argv[0] is not a path name.
>
> After “bash -c 'exec -a not-a-real-path cat'”, we have this:
>
> (gdb) print _dl_argv[0]
> $1 = 0x7ffc1feaf3de "not-a-real-path"
This is what program_invocation_name/__progname (initialized at __init_misc())
assumes. I think it is a fair assumption, since by convention the first string
in argv on execve() is the filename associated with the file being executed.
Also, if users really want something different to use as the program name
they can use argv[0] which will be aligned in the various APIs.
>
> I think dladdr is wrong to use this as a path.
I don't think so, glibc itself uses it in multiples places to get the
program name and also exports program_invocation_name/program_invocation_short_name
as GNU extensions. Another option would to read /proc/self/cmdline, but I don'
think this would be really an improvement (and it is not easily override by
the users).
>
> I think in practice l_name == "" means that the link map is for the main
> executable. This seems particularly relevant given that the public part
> of the link map does not expose lt_executable.
This is one reason I consider to not expose this in the link_map, however I
still think to align la_objopen(), dladdr() and dlinfo() seems to be a better
QoI than asking users to check program_invocation_name.
@@ -51,7 +51,7 @@ endif
ifeq (yes,$(build-shared))
tests = glrefmain failtest tst-dladdr default errmsg1 tstcxaatexit \
bug-dlopen1 bug-dlsym1 tst-dlinfo bug-atexit1 bug-atexit2 \
- bug-atexit3 tstatexit bug-dl-leaf tst-rec-dlopen
+ bug-atexit3 tstatexit bug-dl-leaf tst-rec-dlopen tst-dladdr-self
endif
modules-names = glreflib1 glreflib2 glreflib3 failtestmod defaultmod1 \
defaultmod2 errmsg1mod modatexit modcxaatexit \
@@ -143,3 +143,5 @@ $(objpfx)bug-dl-leaf.out: $(objpfx)bug-dl-leaf-lib-cb.so
$(objpfx)bug-dl-leaf-lib-cb.so: $(objpfx)bug-dl-leaf-lib.so
$(objpfx)tst-rec-dlopen.out: $(objpfx)moddummy1.so $(objpfx)moddummy2.so
+
+LDFLAGS-tst-dladdr-self = -rdynamic
new file mode 100644
@@ -0,0 +1,55 @@
+/* Check dladdr with the reference to own exectuable.
+ Copyright (C) 2021 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/>. */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <support/check.h>
+#include <support/xdlfcn.h>
+
+void
+test_symbol (void)
+{
+}
+
+static int
+do_test (void)
+{
+ void *handle = xdlopen (NULL, RTLD_NOW);
+ int (*sym)(void) = xdlsym (handle, "test_symbol");
+
+ Dl_info info = { 0 };
+ {
+ int r = dladdr (sym, &info);
+ TEST_VERIFY_EXIT (r != 0);
+ }
+
+ {
+ const char *p = strrchr (info.dli_fname, '/');
+ const char *dli_name = p == NULL ? info.dli_fname : p + 1;
+
+ TEST_COMPARE_STRING (dli_name, "tst-dladdr-self");
+ }
+
+ TEST_COMPARE_STRING (info.dli_sname, "test_symbol");
+ TEST_COMPARE ((uintptr_t) info.dli_saddr, (uintptr_t) test_symbol);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
@@ -30,11 +30,6 @@ determine_info (const ElfW(Addr) addr, struct link_map *match, Dl_info *info,
info->dli_fname = match->l_name;
info->dli_fbase = (void *) match->l_map_start;
- /* If this is the main program the information is incomplete. */
- if (__builtin_expect (match->l_name[0], 'a') == '\0'
- && match->l_type == lt_executable)
- info->dli_fname = _dl_argv[0];
-
const ElfW(Sym) *symtab
= (const ElfW(Sym) *) D_PTR (match, l_info[DT_SYMTAB]);
const char *strtab = (const char *) D_PTR (match, l_info[DT_STRTAB]);
@@ -44,7 +44,7 @@
auditing, in ld.so. */ \
if ((l)->l_origin == NULL) \
{ \
- assert ((l)->l_name[0] == '\0' || IS_RTLD (l)); \
+ assert ((l)->l_type == lt_executable || IS_RTLD (l)); \
(l)->l_origin = _dl_get_origin (); \
dst_len = ((l)->l_origin && (l)->l_origin != (char *) -1 \
? strlen ((l)->l_origin) : 0); \
@@ -39,8 +39,7 @@ call_init (struct link_map *l, int argc, char **argv, char **env)
l->l_init_called = 1;
/* Check for object which constructors we do not run here. */
- if (__builtin_expect (l->l_name[0], 'a') == '\0'
- && l->l_type == lt_executable)
+ if (l->l_type == lt_executable)
return;
/* Print a debug message if wanted. */
@@ -338,6 +338,7 @@ rtld_hidden_def (_dl_fatal_printf)
int
_dl_name_match_p (const char *name, const struct link_map *map)
{
+ /* Filter out the main executable. */
if (strcmp (name, map->l_name) == 0)
return 1;
@@ -1361,11 +1361,6 @@ dl_main (const ElfW(Phdr) *phdr,
phdr = main_map->l_phdr;
phnum = main_map->l_phnum;
- /* We overwrite here a pointer to a malloc()ed string. But since
- the malloc() implementation used at this point is the dummy
- implementations which has no real free() function it does not
- makes sense to free the old string first. */
- main_map->l_name = (char *) "";
*user_entry = main_map->l_entry;
/* Set bit indicating this is the main program map. */
@@ -1431,6 +1426,7 @@ dl_main (const ElfW(Phdr) *phdr,
information for the program. */
}
+ main_map->l_name = _dl_argv[0];
main_map->l_map_end = 0;
main_map->l_text_end = 0;
/* Perhaps the executable has no PT_LOAD header entries at all. */
@@ -94,6 +94,7 @@ do_test (int argc, char *argv[])
unsigned long vdso_process = 0;
unsigned long vdso_audit = 0;
+ bool mainapp_found = false;
FILE *out = fmemopen (result.err.buffer, result.err.length, "r");
TEST_VERIFY (out != NULL);
@@ -105,9 +106,12 @@ do_test (int argc, char *argv[])
vdso_process = parse_address (buffer + strlen ("vdso: "));
else if (startswith (buffer, "vdso found: "))
vdso_audit = parse_address (buffer + strlen ("vdso found: "));
+ else if (startswith (buffer, "mainapp found"))
+ mainapp_found = true;
}
TEST_COMPARE (vdso_process, vdso_audit);
+ TEST_COMPARE (mainapp_found, true);
free (buffer);
xfclose (out);
@@ -19,6 +19,7 @@
#include <link.h>
#include <inttypes.h>
#include <stdio.h>
+#include <string.h>
#include <sys/auxv.h>
unsigned int
@@ -30,6 +31,13 @@ la_version (unsigned int version)
unsigned int
la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
{
+ {
+ const char *p = strrchr (map->l_name, '/');
+ const char *l_name = p == NULL ? map->l_name : p + 1;
+ if (strcmp (l_name, "tst-audit22") == 0)
+ fprintf (stderr, "mainapp found\n");
+ }
+
if (map->l_addr == getauxval (AT_SYSINFO_EHDR))
fprintf (stderr, "vdso found: %" PRIxPTR "\n", (uintptr_t) map->l_addr);
@@ -48,16 +48,16 @@
#ifdef PIC
# include <link.h>
+# include <ldsodefs.h>
static int
callback (struct dl_phdr_info *info, size_t size, void *data)
{
- if (info->dlpi_name[0] == '\0')
+ if (info->dlpi_name == _dl_argv[0])
{
- /* The link map for the executable is created by calling
- _dl_new_object with "" as filename. dl_iterate_phdr
- calls the callback function with filename from the
- link map as dlpi_name. */
+ /* The link map l_name for the executable is set to the _dl_argv[0]
+ after argument processing. dl_iterate_phdr() calls the callback
+ function with filename from the link map as dlpi_name. */
u_long *load_address = data;
*load_address = (u_long) info->dlpi_addr;
return 1;