diff options
Diffstat (limited to 'elf')
-rw-r--r-- | elf/Makefile | 10 | ||||
-rw-r--r-- | elf/dl-deps.c | 31 | ||||
-rw-r--r-- | elf/dl-lookup.c | 211 | ||||
-rw-r--r-- | elf/dlfcn.h | 6 | ||||
-rw-r--r-- | elf/dlsym.c | 38 | ||||
-rw-r--r-- | elf/ldd.bash.in | 45 | ||||
-rw-r--r-- | elf/ldd.sh.in | 38 | ||||
-rw-r--r-- | elf/link.h | 13 |
8 files changed, 288 insertions, 104 deletions
diff --git a/elf/Makefile b/elf/Makefile index 58b3a90f20..4cb8d7e53f 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -99,11 +99,21 @@ $(objpfx)libdl.so: $(objpfx)libdl_pic.a $(common-objpfx)libc.so $(objpfx)ld.so $(slibdir)/$(rtld-installed-name): $(objpfx)ld.so; $(do-install-program) +ifneq ($(have-bash2),yes) $(objpfx)ldd: ldd.sh.in Makefile sed -e 's%@RTLD@%$(slibdir)/$(rtld-installed-name)%g' \ -e 's%@VERSION@%$(version)%g' < $< > $@.new chmod 555 $@.new mv -f $@.new $@ +else +$(objpfx)ldd: ldd.bash.in Makefile + sed -e 's%@BASH@%$(BASH)%g' \ + -e 's%@RTLD@%$(slibdir)/$(rtld-installed-name)%g' \ + -e 's%@VERSION@%$(version)%g' \ + -e 's%@TEXTDOMAINDIR@%$(localedir)%g' < $< > $@.new + chmod 555 $@.new + mv -f $@.new $@ +endif # muwahaha diff --git a/elf/dl-deps.c b/elf/dl-deps.c index 977b3237aa..115982f375 100644 --- a/elf/dl-deps.c +++ b/elf/dl-deps.c @@ -33,16 +33,24 @@ _dl_map_object_deps (struct link_map *map, struct list *next; }; struct list head[1 + npreloads], *tailp, *scanp; + struct list duphead, *duptailp; unsigned int nlist; + unsigned int nduplist; /* Start the search list with one element: MAP itself. */ head[0].map = map; + /* We use `l_reserved' as a mark bit to detect objects we have already + put in the search list and avoid adding duplicate elements later in + the list. */ + map->l_reserved = 1; + /* Add the preloaded items after MAP but before any of its dependencies. */ for (nlist = 0; nlist < npreloads; ++nlist) { head[nlist].next = &head[nlist + 1]; head[nlist + 1].map = preloads[nlist]; + preloads[nlist]->l_reserved = 1; } /* Terminate the list. */ @@ -51,10 +59,10 @@ _dl_map_object_deps (struct link_map *map, /* Start here for adding dependencies to the list. */ tailp = &head[nlist++]; - /* We use `l_reserved' as a mark bit to detect objects we have already - put in the search list and avoid adding duplicate elements later in - the list. */ - map->l_reserved = 1; + /* Until now we have the same number of libraries in the normal and + the list with duplicates. */ + nduplist = nlist; + duptailp = &duphead; /* Process each element of the search list, loading each of its immediate dependencies and appending them to the list as we step through it. @@ -94,6 +102,13 @@ _dl_map_object_deps (struct link_map *map, /* Set the mark bit that says it's already in the list. */ dep->l_reserved = 1; } + + /* In any case Append DEP to the duplicates search list. */ + duptailp->next = alloca (sizeof *duptailp); + duptailp = duptailp->next; + duptailp->map = dep; + duptailp->next = NULL; + ++nduplist; } } } @@ -112,4 +127,12 @@ _dl_map_object_deps (struct link_map *map, to avoid duplicates, so the next call starts fresh. */ scanp->map->l_reserved = 0; } + + map->l_dupsearchlist = malloc (nduplist * sizeof (struct link_map *)); + map->l_ndupsearchlist = nduplist; + + for (nlist = 0; nlist < npreloads + 1; ++nlist) + map->l_dupsearchlist[nlist] = head[nlist].map; + for (scanp = duphead.next; scanp; scanp = scanp->next) + map->l_dupsearchlist[nlist++] = scanp->map; } diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c index f35bbbe967..717ac83f7a 100644 --- a/elf/dl-lookup.c +++ b/elf/dl-lookup.c @@ -23,6 +23,13 @@ #include <string.h> +struct sym_val + { + ElfW(Addr) a; + const ElfW(Sym) *s; + }; + + /* This is the hashing function specified by the ELF ABI. */ static inline unsigned _dl_elf_hash (const char *name) @@ -44,6 +51,90 @@ _dl_elf_hash (const char *name) return hash; } + +/* Inner part of the lookup functions. */ +static inline ElfW(Addr) +do_lookup (const char *undef_name, unsigned long int hash, + const ElfW(Sym) **ref, struct sym_val *result, + struct link_map *list[], size_t i, size_t n, + const char *reference_name, struct link_map *skip, int flags) +{ + struct link_map *map; + + for (; i < n; ++i) + { + const ElfW(Sym) *symtab; + const char *strtab; + ElfW(Symndx) symidx; + + map = list[i]; + + /* Here come the extra test needed for `_dl_lookup_symbol_skip'. */ + if (skip != NULL && map == skip) + continue; + + /* Don't search the executable when resolving a copy reloc. */ + if (flags & DL_LOOKUP_NOEXEC && map->l_type == lt_executable) + continue; + + symtab = ((void *) map->l_addr + map->l_info[DT_SYMTAB]->d_un.d_ptr); + strtab = ((void *) map->l_addr + map->l_info[DT_STRTAB]->d_un.d_ptr); + + /* Search the appropriate hash bucket in this object's symbol table + for a definition for the same symbol name. */ + for (symidx = map->l_buckets[hash % map->l_nbuckets]; + symidx != STN_UNDEF; + symidx = map->l_chain[symidx]) + { + const ElfW(Sym) *sym = &symtab[symidx]; + + if (sym->st_value == 0 || /* No value. */ + ((flags & DL_LOOKUP_NOPLT) != 0 /* Reject PLT entry. */ + && sym->st_shndx == SHN_UNDEF)) + continue; + + switch (ELFW(ST_TYPE) (sym->st_info)) + { + case STT_NOTYPE: + case STT_FUNC: + case STT_OBJECT: + break; + default: + /* Not a code/data definition. */ + continue; + } + + if (sym != *ref && strcmp (strtab + sym->st_name, undef_name)) + /* Not the symbol we are looking for. */ + continue; + + switch (ELFW(ST_BIND) (sym->st_info)) + { + case STB_GLOBAL: + /* Global definition. Just what we need. */ + result->s = sym; + result->a = map->l_addr; + return 1; + case STB_WEAK: + /* Weak definition. Use this value if we don't find + another. */ + if (! result->s) + { + result->s = sym; + result->a = map->l_addr; + } + break; + default: + /* Local symbols are ignored. */ + break; + } + } + } + + /* We have not found anything until now. */ + return 0; +} + /* Search loaded objects' symbol tables for a definition of the symbol UNDEF_NAME. FLAGS is a set of flags. If DL_LOOKUP_NOEXEC is set, then don't search the executable for a definition; this used for @@ -57,82 +148,17 @@ _dl_lookup_symbol (const char *undef_name, const ElfW(Sym) **ref, int flags) { const unsigned long int hash = _dl_elf_hash (undef_name); - struct - { - ElfW(Addr) a; - const ElfW(Sym) *s; - } weak_value = { 0, NULL }; - size_t i; - struct link_map **scope, *map; + struct sym_val current_value = { 0, NULL }; + struct link_map **scope; /* Search the relevant loaded objects for a definition. */ for (scope = symbol_scope; *scope; ++scope) - for (i = 0; i < (*scope)->l_nsearchlist; ++i) - { - const ElfW(Sym) *symtab; - const char *strtab; - ElfW(Symndx) symidx; - - map = (*scope)->l_searchlist[i]; - - /* Don't search the executable when resolving a copy reloc. */ - if (flags & DL_LOOKUP_NOEXEC && map->l_type == lt_executable) - continue; - - symtab = ((void *) map->l_addr + map->l_info[DT_SYMTAB]->d_un.d_ptr); - strtab = ((void *) map->l_addr + map->l_info[DT_STRTAB]->d_un.d_ptr); - - /* Search the appropriate hash bucket in this object's symbol table - for a definition for the same symbol name. */ - for (symidx = map->l_buckets[hash % map->l_nbuckets]; - symidx != STN_UNDEF; - symidx = map->l_chain[symidx]) - { - const ElfW(Sym) *sym = &symtab[symidx]; - - if (sym->st_value == 0 || /* No value. */ - ((flags & DL_LOOKUP_NOPLT) != 0 /* Reject PLT entry. */ - && sym->st_shndx == SHN_UNDEF)) - continue; - - switch (ELFW(ST_TYPE) (sym->st_info)) - { - case STT_NOTYPE: - case STT_FUNC: - case STT_OBJECT: - break; - default: - /* Not a code/data definition. */ - continue; - } - - if (sym != *ref && strcmp (strtab + sym->st_name, undef_name)) - /* Not the symbol we are looking for. */ - continue; + if (do_lookup (undef_name, hash, ref, ¤t_value, + (*scope)->l_searchlist, 0, (*scope)->l_nsearchlist, + reference_name, NULL, flags)) + break; - switch (ELFW(ST_BIND) (sym->st_info)) - { - case STB_GLOBAL: - /* Global definition. Just what we need. */ - *ref = sym; - return map->l_addr; - case STB_WEAK: - /* Weak definition. Use this value if we don't find - another. */ - if (! weak_value.s) - { - weak_value.s = sym; - weak_value.a = map->l_addr; - } - break; - default: - /* Local symbols are ignored. */ - break; - } - } - } - - if (weak_value.s == NULL && + if (current_value.s == NULL && (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK)) { /* We could find no value for a strong reference. */ @@ -144,8 +170,45 @@ _dl_lookup_symbol (const char *undef_name, const ElfW(Sym) **ref, _dl_signal_error (0, reference_name, buf); } - *ref = weak_value.s; - return weak_value.a; + *ref = current_value.s; + return current_value.a; +} + + +/* This function is nearly the same as `_dl_lookup_symbol' but it + skips in the first list all objects until SKIP_MAP is found. I.e., + it only considers objects which were loaded after the described + object. If there are more search lists the object described by + SKIP_MAP is only skipped. */ +ElfW(Addr) +_dl_lookup_symbol_skip (const char *undef_name, const ElfW(Sym) **ref, + struct link_map *symbol_scope[], + const char *reference_name, + struct link_map *skip_map, + int flags) +{ + int found_entry = 0; + const unsigned long int hash = _dl_elf_hash (undef_name); + struct sym_val current_value = { 0, NULL }; + struct link_map **scope; + size_t i; + + /* Search the relevant loaded objects for a definition. */ + scope = symbol_scope; + for (i = 0; (*scope)->l_dupsearchlist[i] != skip_map; ++i) + assert (i < (*scope)->l_ndupsearchlist); + + if (! do_lookup (undef_name, hash, ref, ¤t_value, + (*scope)->l_dupsearchlist, i, (*scope)->l_ndupsearchlist, + reference_name, skip_map, flags)) + while (*++scope) + if (do_lookup (undef_name, hash, ref, ¤t_value, + (*scope)->l_dupsearchlist, 0, (*scope)->l_ndupsearchlist, + reference_name, skip_map, flags)) + break; + + *ref = current_value.s; + return current_value.a; } diff --git a/elf/dlfcn.h b/elf/dlfcn.h index b59bebd5b1..c2b66a7797 100644 --- a/elf/dlfcn.h +++ b/elf/dlfcn.h @@ -32,6 +32,12 @@ visible as if the object were linked directly into the program. */ #define RTLD_GLOBAL 0x100 +/* If the first argument of `dlsym' is set to RTLD_NEXT the run-time + address of the symbol called NAME in the next shared object is + returned. The "next" relation is defined by the order the shared + objects were loaded. */ +#define RTLD_NEXT ((void *) -1l) + /* Open the shared object FILE and map it in; return a handle that can be passed to `dlsym' to get symbol values from it. */ extern void *dlopen __P ((__const char *__file, int __mode)); diff --git a/elf/dlsym.c b/elf/dlsym.c index edfe1c6cbb..12a29e4b4c 100644 --- a/elf/dlsym.c +++ b/elf/dlsym.c @@ -26,25 +26,43 @@ void * dlsym (void *handle, const char *name) { + ElfW(Addr) caller = (ElfW(Addr)) __builtin_return_address (0); ElfW(Addr) loadbase; const ElfW(Sym) *ref = NULL; void doit (void) { - struct link_map *map = handle, **scope, *mapscope[2] = { map, NULL }; - const char *owner; - - if (map) + if (handle == NULL) + /* Search the global scope. */ + loadbase = _dl_lookup_symbol + (name, &ref, &(_dl_global_scope ?: _dl_default_scope)[2], NULL, 0); + else if (handle == RTLD_NEXT) { - /* Search the scope of the given object. */ - scope = mapscope; - owner = map->l_name; + struct link_map *l, *match; + + /* Find the highest-addressed object that CALLER is not below. */ + match = NULL; + for (l = _dl_loaded; l; l = l->l_next) + if (caller >= l->l_addr && (!match || match->l_addr < l->l_addr)) + match = l; + + if (! match) + _dl_signal_error (0, NULL, _("\ +RTLD_NEXT used in code not dynamically loaded")); + + l = match; + while (l->l_loader) + l = l->l_loader; + + loadbase = _dl_lookup_symbol_skip + (name, &ref, &_dl_loaded, NULL, l, 0); } else { - scope = &(_dl_global_scope ?: _dl_default_scope)[2]; - owner = NULL; + /* Search the scope of the given object. */ + struct link_map *map = handle; + struct link_map *mapscope[2] = { map, NULL }; + loadbase = _dl_lookup_symbol (name, &ref, mapscope, map->l_name, 0); } - loadbase = _dl_lookup_symbol (name, &ref, scope, owner, 0); } return _dlerror_run (doit) ? NULL : (void *) (loadbase + ref->st_value); diff --git a/elf/ldd.bash.in b/elf/ldd.bash.in index e7ad21fa9d..5269708f43 100644 --- a/elf/ldd.bash.in +++ b/elf/ldd.bash.in @@ -1,24 +1,50 @@ #! @BASH@ +# Copyright (C) 1996 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 Library General Public License as +# published by the Free Software Foundation; either version 2 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 +# Library General Public License for more details. + +# You should have received a copy of the GNU Library General Public +# License along with the GNU C Library; see the file COPYING.LIB. If not, +# write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + + # This is the `ldd' command, which lists what shared libraries are # used by given dynamically-linked executables. It works by invoking the # run-time dynamic linker as a command and setting the environment # variable LD_TRACE_LOADED_OBJECTS to a non-empty value. +# We should be able to find the translation right at the beginning. +TEXTDOMAIN=libc +TEXTDOMAINDIR=@TEXTDOMAINDIR@ + RTLD=@RTLD@ -usage=$"\ -ldd [OPTION]... FILE... - --help print this help and exit - --version print version information and exit -Report bugs to <bug-glibc@prep.ai.mit.edu>." while test $# -gt 0; do case "$1" in --v*) - echo 'ldd (GNU libc) @VERSION@'; exit 0 ;; + echo $"ldd (GNU libc) @VERSION@ +Copyright (C) 1996 Free Software Foundation, Inc. +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + exit 0 ;; --h*) - echo "$usage"; exit 0 ;; - --) # Stop option prcessing + echo $"ldd [OPTION]... FILE... + --help print this help and exit + --version print version information and exit +Report bugs to <bug-glibc@prep.ai.mit.edu>." + exit 0 ;; + --) # Stop option prcessing. shift; break ;; *) break ;; @@ -64,3 +90,6 @@ Try \`ldd --help' for more information." esac exit 0 +# Local Variables: +# mode:ksh +# End: diff --git a/elf/ldd.sh.in b/elf/ldd.sh.in index 16d3fd8b79..f8df62d223 100644 --- a/elf/ldd.sh.in +++ b/elf/ldd.sh.in @@ -1,24 +1,46 @@ #! /bin/sh +# Copyright (C) 1996 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 Library General Public License as +# published by the Free Software Foundation; either version 2 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 +# Library General Public License for more details. + +# You should have received a copy of the GNU Library General Public +# License along with the GNU C Library; see the file COPYING.LIB. If not, +# write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + + # This is the `ldd' command, which lists what shared libraries are # used by given dynamically-linked executables. It works by invoking the # run-time dynamic linker as a command and setting the environment # variable LD_TRACE_LOADED_OBJECTS to a non-empty value. RTLD=@RTLD@ -usage="\ -ldd [OPTION]... FILE... - --help print this help and exit - --version print version information and exit -Report bugs to <bug-glibc@prep.ai.mit.edu>." while test $# -gt 0; do case "$1" in --v*) - echo 'ldd (GNU libc) @VERSION@'; exit 0 ;; + echo 'ldd (GNU libc) @VERSION@ +Copyright (C) 1996 Free Software Foundation, Inc. +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.' + exit 0 ;; --h*) - echo "$usage"; exit 0 ;; - --) # Stop option prcessing + echo 'ldd [OPTION]... FILE... + --help print this help and exit + --version print version information and exit +Report bugs to <bug-glibc@prep.ai.mit.edu>.' + exit 0 ;; + --) # Stop option prcessing. shift; break ;; *) break ;; diff --git a/elf/link.h b/elf/link.h index 17fea305ed..b277bf75c1 100644 --- a/elf/link.h +++ b/elf/link.h @@ -109,6 +109,11 @@ struct link_map struct link_map **l_searchlist; unsigned int l_nsearchlist; + /* We keep another list in which we keep duplicates. This is + needed in _dl_lookup_symbol_skip to implemented RTLD_NEXT. */ + struct link_map **l_dupsearchlist; + unsigned int l_ndupsearchlist; + /* Dependent object that first caused this object to be loaded. */ struct link_map *l_loader; @@ -239,6 +244,14 @@ extern ElfW(Addr) _dl_lookup_symbol (const char *undef, const char *reference_name, int flags); +/* For handling RTLD_NEXT we must be able to skip shared objects. */ +extern ElfW(Addr) _dl_lookup_symbol_skip (const char *undef, + const ElfW(Sym) **sym, + struct link_map *symbol_scope[], + const char *reference_name, + struct link_map *skip_this, + int flags); + /* Look up symbol NAME in MAP's scope and return its run-time address. */ extern ElfW(Addr) _dl_symbol_value (struct link_map *map, const char *name); |