From 45e4762c2c7d04cd51213735c2b440c94cdcf28a Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Sat, 15 Mar 2003 23:16:22 +0000 Subject: [PATCH] * dlfcn/tst-dlinfo.c: New file. * dlfcn/Makefile (tests): Add tst-dlinfo. ($(objpfx)tst-dlinfo): New target. * dlfcn/dlinfo.c: New file. * dlfcn/Makefile (libdl-routines): Add it. * dlfcn/Versions (libdl: GLIBC_2.3.3): Add dlinfo. * dlfcn/dlfcn.h [__USE_GNU]: Declare dlinfo. [__USE_GNU] (RTLD_DI_*): New enum constants. [__USE_GNU] (Dl_serpath, Dl_serinfo): New types. * elf/dl-load.c (cache_rpath): New inline function. (_dl_map_object): Use it. (_dl_rtld_di_serinfo): New function. * sysdeps/generic/ldsodefs.h: Declare it. * elf/Versions (ld: GLIBC_PRIVATE): Add it. --- ChangeLog | 16 ++++ NEWS | 7 +- dlfcn/Makefile | 8 +- dlfcn/Versions | 2 +- dlfcn/dlfcn.h | 54 +++++++++++- dlfcn/dlinfo.c | 87 ++++++++++++++++++ dlfcn/tst-dlinfo.c | 96 ++++++++++++++++++++ elf/Versions | 2 +- elf/dl-load.c | 175 +++++++++++++++++++++++++------------ sysdeps/generic/ldsodefs.h | 10 +++ 10 files changed, 396 insertions(+), 61 deletions(-) create mode 100644 dlfcn/dlinfo.c create mode 100644 dlfcn/tst-dlinfo.c diff --git a/ChangeLog b/ChangeLog index 9a793132ee..a387e8f8eb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,21 @@ 2003-03-15 Roland McGrath + * dlfcn/tst-dlinfo.c: New file. + * dlfcn/Makefile (tests): Add tst-dlinfo. + ($(objpfx)tst-dlinfo): New target. + + * dlfcn/dlinfo.c: New file. + * dlfcn/Makefile (libdl-routines): Add it. + * dlfcn/Versions (libdl: GLIBC_2.3.3): Add dlinfo. + * dlfcn/dlfcn.h [__USE_GNU]: Declare dlinfo. + [__USE_GNU] (RTLD_DI_*): New enum constants. + [__USE_GNU] (Dl_serpath, Dl_serinfo): New types. + * elf/dl-load.c (cache_rpath): New inline function. + (_dl_map_object): Use it. + (_dl_rtld_di_serinfo): New function. + * sysdeps/generic/ldsodefs.h: Declare it. + * elf/Versions (ld: GLIBC_PRIVATE): Add it. + * sysdeps/powerpc/elf/libc-start.c (AUX_VECTOR_INIT): Define it. (LIBC_START_MAIN, LIBC_START_MAIN_AUXVEC_ARG, MAIN_AUXVEC_ARG) (INIT_MAIN_ARGS): Define, and #include . diff --git a/NEWS b/NEWS index 418949200c..9ac019c660 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,5 @@ -GNU C Library NEWS -- history of user-visible changes. 2003-3-2 -Copyright (C) 1992-2002, 2003 Free Software Foundation, Inc. +GNU C Library NEWS -- history of user-visible changes. 2003-3-15 +Copyright (C) 1992-2002,2003 Free Software Foundation, Inc. See the end for copying conditions. Please send GNU C library bug reports using the `glibcbug' script to @@ -7,6 +7,9 @@ Please send GNU C library bug reports using the `glibcbug' script to Version 2.3.3 +* New functions `dladdr1' and `dlinfo' in provide more ways to + interrogate the dynamic linker, compatible with the Solaris interface. + * ELF thread-local storage support (TLS) now works on PowerPC and PowerPC64; implemented by Paul Mackerras, Steven Munroe, and Roland McGrath. diff --git a/dlfcn/Makefile b/dlfcn/Makefile index d11336140e..9115d30c2a 100644 --- a/dlfcn/Makefile +++ b/dlfcn/Makefile @@ -19,7 +19,8 @@ subdir := dlfcn headers := bits/dlfcn.h dlfcn.h extra-libs := libdl -libdl-routines := dlopen dlclose dlsym dlvsym dlerror dladdr dladdr1 eval +libdl-routines := dlopen dlclose dlsym dlvsym dlerror dladdr dladdr1 dlinfo \ + eval distribute := dlopenold.c glreflib1.c glreflib2.c failtestmod.c eval.c \ defaultmod1.c defaultmod2.c errmsg1mod.c modatexit.c \ modcxaatexit.c modstatic.c \ @@ -37,7 +38,7 @@ libdl-shared-only-routines += eval ifeq (yes,$(build-shared)) tests = glrefmain failtest tst-dladdr default errmsg1 tstcxaatexit \ - bug-dlopen1 bug-dlsym1 + bug-dlopen1 bug-dlsym1 tst-dlinfo ifeq (yes,$(have-protected)) tests += tstatexit endif @@ -74,6 +75,9 @@ $(objpfx)failtest.out: $(objpfx)failtestmod.so $(objpfx)tst-dladdr: $(libdl) $(objpfx)tst-dladdr.out: $(objpfx)glreflib1.so +$(objpfx)tst-dlinfo: $(libdl) +$(objpfx)tst-dlinfo.out: $(objpfx)glreflib1.so + LDFLAGS-default = $(LDFLAGS-rdynamic) $(objpfx)default: $(libdl) $(objpfx)defaultmod1.so $(objpfx)defaultmod2.so $(objpfx)defaultmod1.so: $(libdl) $(common-objpfx)libc_nonshared.a diff --git a/dlfcn/Versions b/dlfcn/Versions index ca44a4678f..95ede25e47 100644 --- a/dlfcn/Versions +++ b/dlfcn/Versions @@ -6,6 +6,6 @@ libdl { dlopen; dlvsym; } GLIBC_2.3.3 { - dladdr1; + dladdr1; dlinfo; } } diff --git a/dlfcn/dlfcn.h b/dlfcn/dlfcn.h index 6c8e4abcc4..9d8ee0d6d1 100644 --- a/dlfcn/dlfcn.h +++ b/dlfcn/dlfcn.h @@ -21,6 +21,8 @@ #define _DLFCN_H 1 #include +#define __need_size_t +#include /* Collect various system dependent definitions and declarations. */ #include @@ -100,7 +102,57 @@ enum RTLD_DL_LINKMAP = 2 }; -#endif + +/* Get information about the shared object HANDLE refers to. + REQUEST is from among the values below, and determines the use of ARG. + + On success, returns zero. On failure, returns -1 and records an error + message to be fetched with `dlerror'. */ +extern int dlinfo (void *__restrict __handle, + int __request, void *__restrict __arg); + +/* These are the possible values for the REQUEST argument to `dlinfo'. */ +enum + { + /* Treat ARG as `struct link_map **'; + store the `struct link_map *' for HANDLE there. */ + RTLD_DI_LINKMAP = 2, + + /* Treat ARG as `Dl_serinfo *' (see below), and fill in to describe the + directories that will be searched for dependencies of this object. + RTLD_DI_SERINFOSIZE fills in just the `dls_cnt' and `dls_size' + entries to indicate the size of the buffer that must be passed to + RTLD_DI_SERINFO to fill in the full information. */ + RTLD_DI_SERINFO = 4, + RTLD_DI_SERINFOSIZE = 5, + + /* Treat ARG as `char *', and store there the directory name used to + expand $ORIGIN in this shared object's dependency file names. */ + RTLD_DI_ORIGIN = 6, + + RTLD_DI_LMID = 1, /* Unsupported, defined by Solaris. */ + RTLD_DI_CONFIGADDR = 3 /* Unsupported, defined by Solaris. */ + }; + + +/* This is the type of elements in `Dl_serinfo', below. + The `dls_name' member points to space in the buffer passed to `dlinfo'. */ +typedef struct +{ + char *dls_name; /* Name of library search path directory. */ + unsigned int dls_flags; /* Indicates where this directory came from. */ +} Dl_serpath; + +/* This is the structure that must be passed (by reference) to `dlinfo' for + the RTLD_DI_SERINFO and RTLD_DI_SERINFOSIZE requests. */ +typedef struct +{ + size_t dls_size; /* Size in bytes of the whole buffer. */ + unsigned int dls_cnt; /* Number of elements in `dls_serpath'. */ + Dl_serpath dls_serpath[1]; /* Actually longer, dls_cnt elements. */ +} Dl_serinfo; +#endif /* __USE_GNU */ + __END_DECLS diff --git a/dlfcn/dlinfo.c b/dlfcn/dlinfo.c new file mode 100644 index 0000000000..37177f1f1b --- /dev/null +++ b/dlfcn/dlinfo.c @@ -0,0 +1,87 @@ +/* dlinfo -- Get information from the dynamic linker. + Copyright (C) 2003 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include + +struct dlinfo_args +{ + ElfW(Addr) caller; + void *handle; + int request; + void *arg; +}; + +static void +dlinfo_doit (void *argsblock) +{ + struct dlinfo_args *const args = argsblock; + struct link_map *l = args->handle; + +#if 0 + if (args->handle == RTLD_SELF) + { + + /* Find the highest-addressed object that CALLER is not below. */ + for (l = GL(dl_loaded); l != NULL; l = l->l_next) + if (caller >= l->l_map_start && caller < l->l_map_end) + /* There must be exactly one DSO for the range of the virtual + memory. Otherwise something is really broken. */ + break; + + if (l == NULL) + _dl_signal_error (0, NULL, NULL, N_("\ +RTLD_SELF used in code not dynamically loaded")); + } +#endif + + switch (args->request) + { + case RTLD_DI_LMID: + case RTLD_DI_CONFIGADDR: + default: + _dl_signal_error (0, NULL, NULL, N_("unsupported dlinfo request")); + break; + + case RTLD_DI_LINKMAP: + *(struct link_map **) args->arg = l; + break; + + case RTLD_DI_SERINFO: + _dl_rtld_di_serinfo (l, args->arg, false); + break; + case RTLD_DI_SERINFOSIZE: + _dl_rtld_di_serinfo (l, args->arg, true); + break; + + case RTLD_DI_ORIGIN: + strcpy (args->arg, l->l_origin); + break; + } +} + +int +dlinfo (void *handle, int request, void *arg) +{ + struct dlinfo_args args = { (ElfW(Addr)) RETURN_ADDRESS (0), + handle, request, arg }; + return _dlerror_run (&dlinfo_doit, &args) ? -1 : 0; +} diff --git a/dlfcn/tst-dlinfo.c b/dlfcn/tst-dlinfo.c new file mode 100644 index 0000000000..70906ebdf1 --- /dev/null +++ b/dlfcn/tst-dlinfo.c @@ -0,0 +1,96 @@ +/* Test for dlinfo. + Copyright (C) 2003 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include + +#define TEST_FUNCTION do_test () + +static int +do_test (void) +{ + int status = 0; + + void *handle = dlopen ("glreflib1.so", RTLD_NOW); + if (handle == NULL) + error (EXIT_FAILURE, 0, "cannot load: glreflib1.so: %s", dlerror ()); + +#define TRY(req, arg) \ + if (dlinfo (handle, req, arg) != 0) \ + { \ + printf ("dlinfo failed for %s: %s\n", #req, dlerror ()); \ + status = 1; \ + } \ + else + + struct link_map *l; + TRY (RTLD_DI_LINKMAP, &l) + { + if (l != handle) + { + printf ("bogus link_map? %p != %p\n", l, handle); + status = 1; + } + } + + char origin[8192]; /* >= PATH_MAX, in theory */ + TRY (RTLD_DI_ORIGIN, origin) + { + printf ("origin: %s\n", origin); + } + + Dl_serinfo counts; + TRY (RTLD_DI_SERINFOSIZE, &counts) + { + Dl_serinfo *buf = alloca (counts.dls_size); + buf->dls_cnt = counts.dls_cnt; + buf->dls_size = counts.dls_size; + printf ("%u library directories\n", buf->dls_cnt); + TRY (RTLD_DI_SERINFO, buf) + { + if (counts.dls_cnt != buf->dls_cnt) + { + printf ("??? became %u library directories\n", buf->dls_cnt); + status = 1; + } + for (unsigned int i = 0; i < buf->dls_cnt; ++i) + printf ("\t%#02x\t%s\n", + buf->dls_serpath[i].dls_flags, + buf->dls_serpath[i].dls_name); + } + } + + unsigned long int lmid = 0xdeadbeefUL; + if (dlinfo (handle, RTLD_DI_LMID, &lmid) != 0) + printf ("dlinfo refuses RTLD_DI_LMID: %s\n", dlerror ()); + else + { + printf ("dlinfo RTLD_DI_LMID worked? %#lx\n", lmid); + status = lmid == 0xdeadbeefUL; + } + +#undef TRY + dlclose (handle); + + return status; +} + +#include "../test-skeleton.c" diff --git a/elf/Versions b/elf/Versions index b9e99f27fd..e6709bb71b 100644 --- a/elf/Versions +++ b/elf/Versions @@ -51,6 +51,6 @@ ld { _dl_unload_cache; _rtld_global; _dl_tls_symaddr; _dl_allocate_tls; _dl_deallocate_tls; _dl_get_tls_static_info; _dl_allocate_tls_init; - _dl_get_origin; _dl_tls_setup; + _dl_get_origin; _dl_tls_setup; _dl_rtld_di_serinfo; } } diff --git a/elf/dl-load.c b/elf/dl-load.c index bc0f942c37..290236b5e1 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -576,6 +576,34 @@ decompose_rpath (struct r_search_path_struct *sps, sps->malloced = 1; } +/* Make sure cached path information is stored in *SP + and return true if there are any paths to search there. */ +static inline bool +cache_rpath (struct link_map *l, + struct r_search_path_struct *sp, + int tag, + const char *what) +{ + if (sp->dirs == (void *) -1) + return false; + + if (sp->dirs != NULL) + return true; + + if (l->l_info[tag] == NULL) + { + /* There is no path. */ + sp->dirs = (void *) -1; + return false; + } + + /* Make sure the cache information is available. */ + decompose_rpath (sp, (const char *) (D_PTR (l, l_info[DT_STRTAB]) + + l->l_info[tag]->d_un.d_val), + l, what); + return true; +} + void internal_function @@ -1747,29 +1775,9 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded, /* First try the DT_RPATH of the dependent object that caused NAME to be loaded. Then that object's dependent, and on up. */ for (l = loader; fd == -1 && l; l = l->l_loader) - { - if (l->l_rpath_dirs.dirs == NULL) - { - if (l->l_info[DT_RPATH] == NULL) - { - /* There is no path. */ - l->l_rpath_dirs.dirs = (void *) -1; - continue; - } - else - { - /* Make sure the cache information is available. */ - size_t ptrval = (D_PTR (l, l_info[DT_STRTAB]) - + l->l_info[DT_RPATH]->d_un.d_val); - decompose_rpath (&l->l_rpath_dirs, - (const char *) ptrval, l, "RPATH"); - } - } - - if (l->l_rpath_dirs.dirs != (void *) -1) - fd = open_path (name, namelen, preloaded, &l->l_rpath_dirs, - &realname, &fb); - } + if (cache_rpath (l, &l->l_rpath_dirs, DT_RPATH, "RPATH")) + fd = open_path (name, namelen, preloaded, &l->l_rpath_dirs, + &realname, &fb); /* If dynamically linked, try the DT_RPATH of the executable itself. */ @@ -1785,37 +1793,12 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded, fd = open_path (name, namelen, preloaded, &env_path_list, &realname, &fb); - /* Look at the RUNPATH information for this binary. - - Note that this is no real loop. 'while' is used only to enable - us to use 'break' instead of a 'goto' to jump to the end. The - loop is always left after the first round. */ - while (fd == -1 && loader != NULL - && loader->l_runpath_dirs.dirs != (void *) -1) - { - if (loader->l_runpath_dirs.dirs == NULL) - { - if (loader->l_info[DT_RUNPATH] == NULL) - { - /* No RUNPATH. */ - loader->l_runpath_dirs.dirs = (void *) -1; - break; - } - else - { - /* Make sure the cache information is available. */ - size_t ptrval = (D_PTR (loader, l_info[DT_STRTAB]) - + loader->l_info[DT_RUNPATH]->d_un.d_val); - decompose_rpath (&loader->l_runpath_dirs, - (const char *) ptrval, loader, "RUNPATH"); - } - } - - if (loader->l_runpath_dirs.dirs != (void *) -1) - fd = open_path (name, namelen, preloaded, - &loader->l_runpath_dirs, &realname, &fb); - break; - } + /* Look at the RUNPATH information for this binary. */ + if (fd == -1 && loader != NULL + && cache_rpath (loader, &loader->l_runpath_dirs, + DT_RUNPATH, "RUNPATH")) + fd = open_path (name, namelen, preloaded, + &loader->l_runpath_dirs, &realname, &fb); if (fd == -1 && (__builtin_expect (! preloaded, 1) @@ -1939,3 +1922,87 @@ cannot create shared object descriptor")); return _dl_map_object_from_fd (name, fd, &fb, realname, loader, type, mode); } INTDEF (_dl_map_object) + +void +internal_function +_dl_rtld_di_serinfo (struct link_map *loader, Dl_serinfo *si, bool counting) +{ + if (counting) + { + si->dls_cnt = 0; + si->dls_size = 0; + } + + unsigned int idx = 0; + char *allocptr = (char *) &si->dls_serpath[si->dls_cnt]; + inline void add_path (const struct r_search_path_struct *sps, + unsigned int flags) +# define add_path(sps, flags) add_path(sps, 0) /* XXX */ + { + if (sps->dirs != (void *) -1) + { + struct r_search_path_elem **dirs = sps->dirs; + do + { + const struct r_search_path_elem *const r = *dirs++; + if (counting) + { + si->dls_cnt++; + si->dls_size += r->dirnamelen; + } + else + { + Dl_serpath *const sp = &si->dls_serpath[idx++]; + sp->dls_name = allocptr; + allocptr = __mempcpy (allocptr, + r->dirname, r->dirnamelen - 1); + *allocptr++ = '\0'; + sp->dls_flags = flags; + } + } + while (*dirs != NULL); + } + } + + /* When the object has the RUNPATH information we don't use any RPATHs. */ + if (loader->l_info[DT_RUNPATH] == NULL) + { + /* First try the DT_RPATH of the dependent object that caused NAME + to be loaded. Then that object's dependent, and on up. */ + + struct link_map *l = loader; + do + { + if (cache_rpath (l, &l->l_rpath_dirs, DT_RPATH, "RPATH")) + add_path (&l->l_rpath_dirs, XXX_RPATH); + l = l->l_loader; + } + while (l != NULL); + + /* If dynamically linked, try the DT_RPATH of the executable itself. */ + l = GL(dl_loaded); + if (l != NULL && l->l_type != lt_loaded && l != loader) + if (cache_rpath (l, &l->l_rpath_dirs, DT_RPATH, "RPATH")) + add_path (&l->l_rpath_dirs, XXX_RPATH); + } + + /* Try the LD_LIBRARY_PATH environment variable. */ + add_path (&env_path_list, XXX_ENV); + + /* Look at the RUNPATH information for this binary. */ + if (cache_rpath (loader, &loader->l_runpath_dirs, DT_RUNPATH, "RUNPATH")) + add_path (&loader->l_runpath_dirs, XXX_RUNPATH); + + /* XXX + Here is where ld.so.cache gets checked, but we don't have + a way to indicate that in the results for Dl_serinfo. */ + + /* Finally, try the default path. */ + if (!(loader->l_flags_1 & DF_1_NODEFLIB)) + add_path (&rtld_search_dirs, XXX_default); + + if (counting) + /* Count the struct size before the string area, which we didn't + know before we completed dls_cnt. */ + si->dls_size += (char *) &si->dls_serpath[si->dls_cnt] - (char *) si; +} diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h index 2d8041fa68..8bd2efb8f0 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -554,6 +554,16 @@ extern void _dl_setup_hash (struct link_map *map) internal_function attribute_hidden; +/* Collect the directories in the search path for LOADER's dependencies. + The data structure is defined in . If COUNTING is true, + SI->dls_cnt and SI->dls_size are set; if false, those must be as set + by a previous call with COUNTING set, and SI must point to SI->dls_size + bytes to be used in filling in the result. */ +extern void _dl_rtld_di_serinfo (struct link_map *loader, + Dl_serinfo *si, bool counting) + internal_function; + + /* Search loaded objects' symbol tables for a definition of the symbol referred to by UNDEF. *SYM is the symbol table entry containing the reference; it is replaced with the defining symbol, and the base load