diff options
Diffstat (limited to 'elf')
| -rw-r--r-- | elf/Makefile | 4 | ||||
| -rw-r--r-- | elf/dl-error.c | 4 | ||||
| -rw-r--r-- | elf/dl-load.c | 34 | ||||
| -rw-r--r-- | elf/dl-lookup.c | 159 | ||||
| -rw-r--r-- | elf/dl-object.c | 9 | ||||
| -rw-r--r-- | elf/dl-open.c | 4 | ||||
| -rw-r--r-- | elf/dl-reloc.c | 11 | ||||
| -rw-r--r-- | elf/dl-runtime.c | 35 | ||||
| -rw-r--r-- | elf/dl-version.c | 337 | ||||
| -rw-r--r-- | elf/dlerror.c | 4 | ||||
| -rw-r--r-- | elf/dlfcn.h | 13 | ||||
| -rw-r--r-- | elf/dlopen.c | 4 | ||||
| -rw-r--r-- | elf/dlsym.c | 4 | ||||
| -rw-r--r-- | elf/dlvsym.c | 72 | ||||
| -rw-r--r-- | elf/do-rel.h | 36 | ||||
| -rw-r--r-- | elf/elf.h | 48 | ||||
| -rw-r--r-- | elf/ldd.bash.in | 14 | ||||
| -rw-r--r-- | elf/ldd.sh.in | 14 | ||||
| -rw-r--r-- | elf/link.h | 66 | ||||
| -rw-r--r-- | elf/rtld.c | 77 |
20 files changed, 851 insertions, 98 deletions
diff --git a/elf/Makefile b/elf/Makefile index 4666919478..b69b8a0754 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -22,7 +22,7 @@ subdir := elf headers = elf.h elfclass.h link.h dlfcn.h routines = $(dl-routines) dl-open dl-close dl-symbol dl-support \ - enbl-secure + dl-version enbl-secure # The core dynamic linking functions are in libc for the static and # profiled libraries. @@ -39,7 +39,7 @@ distribute = $(rtld-routines:=.c) dynamic-link.h do-rel.h dl-machine.h \ extra-libs = libdl extra-libs-others = $(extra-libs) -libdl-routines := dlopen dlclose dlsym dlerror dladdr +libdl-routines := dlopen dlclose dlsym dlvsym dlerror dladdr before-compile = $(objpfx)trusted-dirs.h diff --git a/elf/dl-error.c b/elf/dl-error.c index a19ccff626..263bd65eb0 100644 --- a/elf/dl-error.c +++ b/elf/dl-error.c @@ -66,9 +66,9 @@ _dl_signal_error (int errcode, else if (receiver) { /* We are inside _dl_receive_error. Call the user supplied - handler and resume the work. The receiver will still + handler and resume the work. The receiver will still be installed. */ - (*receiver) (errstring, objname); + (*receiver) (errcode, objname, errstring); } else { diff --git a/elf/dl-load.c b/elf/dl-load.c index 7368858f64..e3de0764ec 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -154,11 +154,34 @@ _dl_map_object_from_fd (char *name, int fd, char *realname, for (l = _dl_loaded; l; l = l->l_next) if (! strcmp (realname, l->l_name)) { + struct libname_list *lnp, *lastp; /* The object is already loaded. Just bump its reference count and return it. */ __close (fd); - free (name); + + /* If the name is not in the list of names for this object add + it. */ free (realname); + lastp = NULL; + for (lnp = l->l_libname; lnp != NULL; lastp = lnp, lnp = lnp->next) + if (strcmp (name, lnp->name) == 0) + { + free (name); + break; + } + if (lnp == NULL) + { + struct libname_list *newname = malloc (sizeof *newname); + if (newname == NULL) + /* No more memory. */ + lose (ENOMEM, "cannot allocate name record"); + /* The object should have a libname set. */ + assert (lastp != NULL); + + newname->name = name; + newname->next = NULL; + lastp->next = newname; + } ++l->l_opencount; return l; } @@ -517,8 +540,7 @@ _dl_map_object (struct link_map *loader, const char *name, int type, /* Look for this name among those already loaded. */ for (l = _dl_loaded; l; l = l->l_next) - if (! strcmp (name, l->l_libname) || /* NAME was requested before. */ - ! strcmp (name, l->l_name) || /* NAME was found before. */ + if (_dl_does_name_match_p (name, l) || /* If the requested name matches the soname of a loaded object, use that object. */ (l->l_info[DT_SONAME] && @@ -559,15 +581,17 @@ _dl_map_object (struct link_map *loader, const char *name, int type, l->l_info[DT_STRTAB]->d_un.d_ptr + l->l_info[DT_RPATH]->d_un.d_val), NULL); /* Try an environment variable (unless setuid). */ - if (fd == -1 && ! __libc_enable_secure) + if (fd == -1) { static const char *trusted_dirs[] = { #include "trusted-dirs.h" NULL }; + const char *ld_library_path = getenv ("LD_LIBRARY_PATH"); - trypath (getenv ("LD_LIBRARY_PATH"), trusted_dirs); + if (ld_library_path != NULL && *ld_library_path != '\0') + trypath (ld_library_path, trusted_dirs); } if (fd == -1) { diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c index 1000a52e0d..06d1d09b90 100644 --- a/elf/dl-lookup.c +++ b/elf/dl-lookup.c @@ -1,5 +1,5 @@ /* Look up a symbol in the loaded objects. - Copyright (C) 1995, 1996 Free Software Foundation, Inc. + Copyright (C) 1995, 1996, 1997 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 @@ -22,6 +22,9 @@ #include <assert.h> #include <string.h> +#include "../stdio-common/_itoa.h" + +#define VERSTAG(tag) (DT_NUM + DT_PROCNUM + DT_VERSIONTAGIDX (tag)) struct sym_val { @@ -30,22 +33,46 @@ struct sym_val }; -/* This is the hashing function specified by the ELF ABI. */ +/* This is the hashing function specified by the ELF ABI. In the + first five operations now overflow is possible so we optimized it a + bit. */ static inline unsigned _dl_elf_hash (const char *name) { unsigned long int hash = 0; - while (*name != '\0') + if (*name != '\0') { - unsigned long int hi; hash = (hash << 4) + *name++; - hi = hash & 0xf0000000; - if (hi != 0) + if (*name != '\0') { - hash ^= hi >> 24; - /* The ELF ABI says `hash &= ~hi', but this is equivalent - in this case and on some machines one insn instead of two. */ - hash ^= hi; + hash = (hash << 4) + *name++; + if (*name != '\0') + { + hash = (hash << 4) + *name++; + if (*name != '\0') + { + hash = (hash << 4) + *name++; + if (*name != '\0') + { + hash = (hash << 4) + *name++; + while (*name != '\0') + { + unsigned long int hi; + hash = (hash << 4) + *name++; + hi = hash & 0xf0000000; + if (hi != 0) + { + hash ^= hi >> 24; + /* The ELF ABI says `hash &= ~hi', but + this is equivalent in this case and + on some machines one insn instead of + two. */ + hash ^= hi; + } + } + } + } + } } } return hash; @@ -57,7 +84,8 @@ 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) + const char *reference_name, const hash_name_pair *version, + struct link_map *skip, int flags) { struct link_map *map; @@ -65,6 +93,7 @@ do_lookup (const char *undef_name, unsigned long int hash, { const ElfW(Sym) *symtab; const char *strtab; + const ElfW(Half) *verstab; ElfW(Symndx) symidx; map = list[i]; @@ -79,6 +108,12 @@ do_lookup (const char *undef_name, unsigned long int hash, 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); + if (version != NULL + && map->l_nversions > 0 && map->l_info[VERSTAG (DT_VERSYM)] != NULL) + verstab = ((void *) map->l_addr + + map->l_info[VERSTAG (DT_VERSYM)]->d_un.d_ptr); + else + verstab = NULL; /* Search the appropriate hash bucket in this object's symbol table for a definition for the same symbol name. */ @@ -108,6 +143,15 @@ do_lookup (const char *undef_name, unsigned long int hash, /* Not the symbol we are looking for. */ continue; + if (verstab != NULL) + { + /* We can match the version information. */ + ElfW(Half) ndx = verstab[symidx] & 0x7fff; + if (map->l_versions[ndx].hash != version->hash + || strcmp (map->l_versions[ndx].name, version->name)) + continue; + } + switch (ELFW(ST_BIND) (sym->st_info)) { case STB_GLOBAL: @@ -155,14 +199,14 @@ _dl_lookup_symbol (const char *undef_name, const ElfW(Sym) **ref, for (scope = symbol_scope; *scope; ++scope) if (do_lookup (undef_name, hash, ref, ¤t_value, (*scope)->l_searchlist, 0, (*scope)->l_nsearchlist, - reference_name, NULL, flags)) + reference_name, NULL, NULL, flags)) break; if (current_value.s == NULL && (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK)) { /* We could find no value for a strong reference. */ - const char msg[] = "undefined symbol: "; + static const char msg[] = "undefined symbol: "; const size_t len = strlen (undef_name); char buf[sizeof msg + len]; memcpy (buf, msg, sizeof msg - 1); @@ -199,11 +243,96 @@ _dl_lookup_symbol_skip (const char *undef_name, const ElfW(Sym) **ref, if (! do_lookup (undef_name, hash, ref, ¤t_value, (*scope)->l_dupsearchlist, i, (*scope)->l_ndupsearchlist, - reference_name, skip_map, flags)) + reference_name, NULL, skip_map, flags)) + while (*++scope) + if (do_lookup (undef_name, hash, ref, ¤t_value, + (*scope)->l_dupsearchlist, 0, (*scope)->l_ndupsearchlist, + reference_name, NULL, skip_map, flags)) + break; + + *ref = current_value.s; + return current_value.a; +} + + +/* This function works like _dl_lookup_symbol but it takes an + additional arguement with the version number of the requested + symbol. + + XXX We'll see whether we need this separate function. */ +ElfW(Addr) +_dl_lookup_versioned_symbol (const char *undef_name, const ElfW(Sym) **ref, + struct link_map *symbol_scope[], + const char *reference_name, + const hash_name_pair *version, int flags) +{ + const unsigned long int hash = _dl_elf_hash (undef_name); + 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) + if (do_lookup (undef_name, hash, ref, ¤t_value, + (*scope)->l_searchlist, 0, (*scope)->l_nsearchlist, + reference_name, version, NULL, flags)) + break; + + if (current_value.s == NULL && + (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK)) + { + /* We could find no value for a strong reference. */ + static const char msg1[] = "undefined symbol: "; + const size_t len = strlen (undef_name); + static const char msg2[] = ", version "; + const size_t verslen = strlen (version->name ?: "") + 1; + char buf[sizeof msg1 - 1 + len + sizeof msg2 - 1 + verslen]; + + memcpy (buf, msg1, sizeof msg1 - 1); + memcpy (&buf[sizeof msg1 - 1], undef_name, len + 1); + memcpy (&buf[sizeof msg1 - 1 + len], msg2, sizeof msg2 - 1); + memcpy (&buf[sizeof msg1 - 1 + len + sizeof msg2 - 1], version->name, + verslen); + _dl_signal_error (0, reference_name, buf); + } + + *ref = current_value.s; + return current_value.a; +} + + +/* Similar to _dl_lookup_symbol_skip but takes an additional argument + with the version we are looking for. */ +ElfW(Addr) +_dl_lookup_versioned_symbol_skip (const char *undef_name, + const ElfW(Sym) **ref, + struct link_map *symbol_scope[], + const char *reference_name, + const char *version_name, + struct link_map *skip_map, + int flags) +{ + const unsigned long int hash = _dl_elf_hash (undef_name); + struct sym_val current_value = { 0, NULL }; + struct link_map **scope; + hash_name_pair version; + size_t i; + + /* First convert the VERSION_NAME into a `hash_name_pair' value. */ + version.hash = _dl_elf_hash (version_name); + version.name = version_name; + + /* 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, &version, 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)) + reference_name, &version, skip_map, flags)) break; *ref = current_value.s; diff --git a/elf/dl-object.c b/elf/dl-object.c index 6986dc34d3..941bfa34cc 100644 --- a/elf/dl-object.c +++ b/elf/dl-object.c @@ -1,5 +1,5 @@ /* Storage management for the chain of loaded shared objects. - Copyright (C) 1995, 1996 Free Software Foundation, Inc. + Copyright (C) 1995, 1996, 1997 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 @@ -34,12 +34,15 @@ struct link_map * _dl_new_object (char *realname, const char *libname, int type) { struct link_map *new = malloc (sizeof *new); - if (! new) + struct libname_list *newname = malloc (sizeof *newname); + if (! new || ! newname) return NULL; memset (new, 0, sizeof *new); new->l_name = realname; - new->l_libname = libname; + newname->name = libname; + newname->next = NULL; + new->l_libname = newname; new->l_type = type; if (_dl_loaded == NULL) diff --git a/elf/dl-open.c b/elf/dl-open.c index fe9f8ff017..5915b7f2a2 100644 --- a/elf/dl-open.c +++ b/elf/dl-open.c @@ -1,5 +1,5 @@ /* Load a shared object at runtime, relocate it, and run its initializer. - Copyright (C) 1996 Free Software Foundation, Inc. + Copyright (C) 1996, 1997 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 @@ -54,6 +54,8 @@ _dl_open (const char *file, int mode) /* Load that object's dependencies. */ _dl_map_object_deps (new, NULL, 0, 0); + /* So far, so good. Now check the versions. */ + (void) _dl_check_map_versions (new, 0); /* Relocate the objects loaded. We do this in reverse order so that copy relocs of earlier objects overwrite the data written by later objects. */ diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c index e299e523ee..f1c43ea174 100644 --- a/elf/dl-reloc.c +++ b/elf/dl-reloc.c @@ -1,5 +1,5 @@ /* Relocate a shared object and resolve its references to other loaded objects. - Copyright (C) 1995, 1996 Free Software Foundation, Inc. + Copyright (C) 1995, 1996, 1997 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 @@ -57,9 +57,12 @@ _dl_relocate_object (struct link_map *l, struct link_map *scope[], int lazy) = ((void *) l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr); /* This macro is used as a callback from the ELF_DYNAMIC_RELOCATE code. */ -#define RESOLVE(ref, flags) \ - (_dl_lookup_symbol (strtab + (*ref)->st_name, ref, scope, \ - l->l_name, flags)) +#define RESOLVE(ref, version, flags) \ + ((version) != NULL && (version)->hash != 0 \ + ? _dl_lookup_versioned_symbol (strtab + (*ref)->st_name, (ref), scope, \ + l->l_name, (version), (flags)) \ + : _dl_lookup_symbol (strtab + (*ref)->st_name, (ref), scope, \ + l->l_name, (flags))) #include "dynamic-link.h" ELF_DYNAMIC_RELOCATE (l, lazy); diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c index 088ff64c6e..08c605cd0f 100644 --- a/elf/dl-runtime.c +++ b/elf/dl-runtime.c @@ -1,5 +1,5 @@ /* On-demand PLT fixup for shared objects. - Copyright (C) 1995, 1996 Free Software Foundation, Inc. + Copyright (C) 1995, 1996, 1997 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 @@ -74,15 +74,19 @@ _dl_object_relocation_scope (struct link_map *l) #define elf_machine_rel 1 #define elf_machine_rela 2 #if elf_machine_relplt == elf_machine_rel -#define PLTREL ElfW(Rel) +# define PLTREL ElfW(Rel) #elif elf_machine_relplt == elf_machine_rela -#define PLTREL ElfW(Rela) +# define PLTREL ElfW(Rela) #else -#error "dl-machine.h bug: elf_machine_relplt not rel or rela" +# error "dl-machine.h bug: elf_machine_relplt not rel or rela" #endif #undef elf_machine_rel #undef elf_machine_rela +#ifndef VERSYMIDX +# define VERSYMIDX(sym) (DT_NUM + DT_PROCNUM + DT_VERSIONTAGIDX (sym)) +#endif + /* We need to define the function as a local symbol so that the reference in the trampoline code will be a local PC-relative call. Tell the compiler not to worry that the function appears not to be called. */ @@ -122,13 +126,28 @@ fixup ( { /* This macro is used as a callback from the elf_machine_relplt code. */ -#define RESOLVE(ref, flags) \ - (_dl_lookup_symbol (strtab + (*ref)->st_name, ref, scope, \ - l->l_name, flags)) +#define RESOLVE(ref, version, flags) \ + ((version) != NULL && (version)->hash != 0 \ + ? _dl_lookup_versioned_symbol (strtab + (*ref)->st_name, (ref), scope, \ + l->l_name, (version), (flags)) \ + : _dl_lookup_symbol (strtab + (*ref)->st_name, (ref), scope, \ + l->l_name, (flags))) #include "dynamic-link.h" /* Perform the specified relocation. */ - elf_machine_relplt (l, reloc, &symtab[ELFW(R_SYM) (reloc->r_info)]); + if (l->l_info[VERSYMIDX (DT_VERNEEDNUM)]) + { + const ElfW(Half) * version = + (const ElfW(Half) *) (l->l_addr + + l->l_info[VERSYMIDX (DT_VERSYM)]->d_un.d_ptr); + ElfW(Half) ndx = version[ELFW(R_SYM) (reloc->r_info)]; + + elf_machine_relplt (l, reloc, &symtab[ELFW(R_SYM) (reloc->r_info)], + &l->l_versions[ndx]); + } + else + elf_machine_relplt (l, reloc, &symtab[ELFW(R_SYM) (reloc->r_info)], + NULL); } *_dl_global_scope_end = NULL; diff --git a/elf/dl-version.c b/elf/dl-version.c new file mode 100644 index 0000000000..4382a4c0ae --- /dev/null +++ b/elf/dl-version.c @@ -0,0 +1,337 @@ +/* Handle symbol and library versioning. + Copyright (C) 1997 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. + + 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. */ + +#include <elf.h> +#include <errno.h> +#include <link.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include "../stdio-common/_itoa.h" + + +/* Set in rtld.c at startup. */ +extern char **_dl_argv; + +#define VERSTAG(tag) (DT_NUM + DT_PROCNUM + DT_VERSIONTAGIDX (tag)) + + +#define make_string(string, rest...) \ + ({ \ + const char *all[] = { string, ## rest }; \ + size_t len, cnt; \ + char *result, *cp; \ + \ + len = 1; \ + for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt) \ + len += strlen (all[cnt]); \ + \ + cp = result = alloca (len); \ + for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt) \ + cp = stpcpy (cp, all[cnt]); \ + \ + result; \ + }) + + +static inline struct link_map * +find_needed (struct link_map *map, const char *name) +{ + unsigned int n; + + for (n = 0; n < map->l_nsearchlist; ++n) + if (_dl_does_name_match_p (name, map->l_searchlist[n])) + return map->l_searchlist[n]; + + /* Should never happen. */ + return NULL; +} + + +static int +match_symbol (const char *name, ElfW(Word) hash, const char *string, + struct link_map *map, int verbose, int weak) +{ + const char *strtab = (const char *) (map->l_addr + + map->l_info[DT_STRTAB]->d_un.d_ptr); + ElfW(Addr) def_offset = map->l_info[VERSTAG (DT_VERDEF)]->d_un.d_ptr; |
