diff options
Diffstat (limited to 'elf')
-rw-r--r-- | elf/dl-hash.h | 69 | ||||
-rw-r--r-- | elf/dl-load.c | 2 | ||||
-rw-r--r-- | elf/dl-lookup.c | 187 | ||||
-rw-r--r-- | elf/dl-version.c | 13 | ||||
-rw-r--r-- | elf/dlvsym.c | 18 | ||||
-rw-r--r-- | elf/link.h | 23 |
6 files changed, 198 insertions, 114 deletions
diff --git a/elf/dl-hash.h b/elf/dl-hash.h new file mode 100644 index 0000000000..007f192a23 --- /dev/null +++ b/elf/dl-hash.h @@ -0,0 +1,69 @@ +/* Compute hash alue for given string according to ELF standard. + 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 + 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. */ + +#ifndef _DL_HASH_H +#define _DL_HASH_H 1 + + +/* 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; + if (*name != '\0') + { + 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++; + 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; +} + +#endif /* dl-hash.h */ diff --git a/elf/dl-load.c b/elf/dl-load.c index e3de0764ec..a103d38548 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -540,7 +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 (_dl_does_name_match_p (name, l) || + if (_dl_name_match_p (name, l) || /* If the requested name matches the soname of a loaded object, use that object. */ (l->l_info[DT_SONAME] && diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c index 06d1d09b90..8702a85bbb 100644 --- a/elf/dl-lookup.c +++ b/elf/dl-lookup.c @@ -22,6 +22,7 @@ #include <assert.h> #include <string.h> +#include <dl-hash.h> #include "../stdio-common/_itoa.h" #define VERSTAG(tag) (DT_NUM + DT_PROCNUM + DT_VERSIONTAGIDX (tag)) @@ -33,58 +34,32 @@ struct sym_val }; -/* 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; - if (*name != '\0') - { - 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++; - 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; -} - - -/* Inner part of the lookup functions. */ -static inline ElfW(Addr) +#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; \ + }) + + +/* Inner part of the lookup functions. We return a value > 0 if we + found the symbol, the value 0 if nothing is found and < 0 if + something bad happened. */ +static inline int 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, const hash_name_pair *version, + const char *reference_name, const struct r_found_version *version, struct link_map *skip, int flags) { struct link_map *map; @@ -108,8 +83,7 @@ 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) + if (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 @@ -143,13 +117,41 @@ do_lookup (const char *undef_name, unsigned long int hash, /* Not the symbol we are looking for. */ continue; - if (verstab != NULL) + if (version == 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; + /* No specific version is selected. When the object + file also does not define a version we have a match. + Otherwise we only accept the default version, i.e., + the version which name is "". */ + if (verstab != NULL) + { + ElfW(Half) ndx = verstab[symidx] & 0x7fff; + if (map->l_versions[ndx].hash != 0) + continue; + } + } + else + { + if (verstab == NULL) + { + /* We need a versioned system but haven't found any. + If this is the object which is referenced in the + verneed entry it is a bug in the library since a + symbol must not simply disappear. */ + if (version->filename != NULL + && _dl_name_match_p (version->filename, map)) + return -1; + /* Otherwise we accept the symbol. */ + } + else + { + /* 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)) + /* It's not the version we want. */ + continue; + } } switch (ELFW(ST_BIND) (sym->st_info)) @@ -173,6 +175,12 @@ do_lookup (const char *undef_name, unsigned long int hash, break; } } + + /* If this current is the one mentioned in the verneed entry it + is a bug. */ + if (version != NULL && version->filename != NULL + && _dl_name_match_p (version->filename, map)) + return -1; } /* We have not found anything until now. */ @@ -204,15 +212,9 @@ _dl_lookup_symbol (const char *undef_name, const ElfW(Sym) **ref, 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 msg[] = "undefined symbol: "; - const size_t len = strlen (undef_name); - char buf[sizeof msg + len]; - memcpy (buf, msg, sizeof msg - 1); - memcpy (&buf[sizeof msg - 1], undef_name, len + 1); - _dl_signal_error (0, reference_name, buf); - } + /* We could find no value for a strong reference. */ + _dl_signal_error (0, reference_name, + make_string ("undefined symbol: ", undef_name)); *ref = current_value.s; return current_value.a; @@ -264,7 +266,7 @@ 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 struct r_found_version *version, int flags) { const unsigned long int hash = _dl_elf_hash (undef_name); struct sym_val current_value = { 0, NULL }; @@ -272,28 +274,30 @@ _dl_lookup_versioned_symbol (const char *undef_name, const ElfW(Sym) **ref, /* 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; + { + int res = do_lookup (undef_name, hash, ref, ¤t_value, + (*scope)->l_searchlist, 0, (*scope)->l_nsearchlist, + reference_name, version, NULL, flags); + if (res > 0) + break; + + if (res < 0) + /* Oh, oh. The file named in the relocation entry does not + contain the needed symbol. */ + _dl_signal_error (0, *reference_name ? reference_name : NULL, + make_string ("symbol ", undef_name, ", version ", + version->name, + " not defined in file ", + version->filename, + " with link time reference")); + } 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); - } + /* We could find no value for a strong reference. */ + _dl_signal_error (0, reference_name, + make_string ("undefined symbol: ", undef_name, + ", version ", version->name ?: NULL)); *ref = current_value.s; return current_value.a; @@ -307,20 +311,15 @@ _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, + const struct r_found_version *version, 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) @@ -328,11 +327,11 @@ _dl_lookup_versioned_symbol_skip (const char *undef_name, if (! do_lookup (undef_name, hash, ref, ¤t_value, (*scope)->l_dupsearchlist, i, (*scope)->l_ndupsearchlist, - reference_name, &version, skip_map, flags)) + 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, &version, skip_map, flags)) + reference_name, version, skip_map, flags)) break; *ref = current_value.s; diff --git a/elf/dl-version.c b/elf/dl-version.c index 4382a4c0ae..ae1c2f34f7 100644 --- a/elf/dl-version.c +++ b/elf/dl-version.c @@ -58,7 +58,7 @@ 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])) + if (_dl_name_match_p (name, map->l_searchlist[n])) return map->l_searchlist[n]; /* Should never happen. */ @@ -246,13 +246,14 @@ _dl_check_map_versions (struct link_map *map, int verbose) /* Now we are ready to build the array with the version names which can be indexed by the version index in the VERSYM section. */ - map->l_versions = (hash_name_pair*) malloc ((ndx_high + 1) - * sizeof (hash_name_pair)); - memset (map->l_versions, '\0', (ndx_high + 1) * sizeof (hash_name_pair)); + map->l_versions = (struct r_found_version *) + malloc ((ndx_high + 1) * sizeof (*map->l_versions)); + memset (map->l_versions, '\0', + (ndx_high + 1) * sizeof (*map->l_versions)); if (map->l_versions == NULL) { _dl_signal_error (ENOMEM, (*map->l_name ? map->l_name : _dl_argv[0]), - "cannot allocate version name table"); + "cannot allocate version reference table"); result = 1; } else @@ -273,6 +274,7 @@ _dl_check_map_versions (struct link_map *map, int verbose) ElfW(Half) ndx = aux->vna_other & 0x7fff; map->l_versions[ndx].hash = aux->vna_hash; map->l_versions[ndx].name = &strtab[aux->vna_name]; + map->l_versions[ndx].filename = &strtab[ent->vn_file]; if (aux->vna_next == 0) /* No more symbols. */ @@ -308,6 +310,7 @@ _dl_check_map_versions (struct link_map *map, int verbose) ElfW(Half) ndx = ent->vd_ndx & 0x7fff; map->l_versions[ndx].hash = ent->vd_hash; map->l_versions[ndx].name = &strtab[aux->vda_name]; + map->l_versions[ndx].filename = NULL; } if (ent->vd_next == 0) diff --git a/elf/dlvsym.c b/elf/dlvsym.c index 5063b3ba0d..922a6eed1f 100644 --- a/elf/dlvsym.c +++ b/elf/dlvsym.c @@ -1,4 +1,4 @@ -/* Look up a symbol in a shared object loaded by `dlopen'. +/* Look up a versioned symbol in a shared object loaded by `dlopen'. Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc. This file is part of the GNU C Library. @@ -22,12 +22,14 @@ #include <dlfcn.h> #include <setjmp.h> +#include <dl-hash.h> void * -__dlvsym (void *handle, const char *name, const char *version) +__dlvsym (void *handle, const char *name, const char *version_str) { ElfW(Addr) caller = (ElfW(Addr)) __builtin_return_address (0); ElfW(Addr) loadbase; + struct r_found_version version; const ElfW(Sym) *ref = NULL; void doit (void) { @@ -35,7 +37,7 @@ __dlvsym (void *handle, const char *name, const char *version) /* Search the global scope. */ loadbase = _dl_lookup_versioned_symbol (name, &ref, &(_dl_global_scope ?: _dl_default_scope)[2], NULL, - version, 0); + &version, 0); else if (handle == RTLD_NEXT) { struct link_map *l, *match; @@ -55,7 +57,7 @@ RTLD_NEXT used in code not dynamically loaded")); l = l->l_loader; loadbase = _dl_lookup_versioned_symbol_skip - (name, &ref, &_dl_loaded, NULL, version, l, 0); + (name, &ref, &_dl_loaded, NULL, &version, l, 0); } else { @@ -63,10 +65,16 @@ RTLD_NEXT used in code not dynamically loaded")); struct link_map *map = handle; struct link_map *mapscope[2] = { map, NULL }; loadbase = _dl_lookup_versioned_symbol - (name, &ref, mapscope, map->l_name, version, 0); + (name, &ref, mapscope, map->l_name, &version, 0); } } + /* Compute hash value to the version string. */ + version.name = version_str; + version.hash = _dl_elf_hash (version_str); + /* We don't have a specific file where the symbol can be found. */ + version.filename = NULL; + return _dlerror_run (doit) ? NULL : (void *) (loadbase + ref->st_value); } weak_alias (__dlvsym, dlvsym) diff --git a/elf/link.h b/elf/link.h index 58041332e9..b0b920074b 100644 --- a/elf/link.h +++ b/elf/link.h @@ -25,6 +25,8 @@ #include <elf.h> +__BEGIN_DECLS + /* We use this macro to refer to ELF types independent of the native wordsize. `ElfW(TYPE)' is used in place of `Elf32_TYPE' or `Elf64_TYPE'. */ #define ElfW(type) _ElfW (Elf, __ELF_NATIVE_CLASS, type) @@ -76,11 +78,13 @@ extern ElfW(Dyn) _DYNAMIC[]; /* For the version handling we need an array with only names and their hash values. */ -typedef struct -{ - const char *name; - ElfW(Word) hash; -} hash_name_pair; +struct r_found_version + { + const char *name; + ElfW(Word) hash; + + const char *filename; + }; /* Structure describing a loaded shared object. The `l_next' and `l_prev' members form a chain of all the shared objects loaded at startup. @@ -153,14 +157,14 @@ struct link_map /* Array with version names. */ unsigned int l_nversions; - hash_name_pair *l_versions; + struct r_found_version *l_versions; }; /* Test whether given NAME matches any of the names of the given object. */ static inline int __attribute__ ((unused)) -_dl_does_name_match_p (const char *__name, struct link_map *__map) +_dl_name_match_p (const char *__name, struct link_map *__map) { int __found = strcmp (__name, __map->l_name) == 0; struct libname_list *__runp = __map->l_libname; @@ -301,7 +305,7 @@ extern ElfW(Addr) _dl_lookup_versioned_symbol (const char *undef, const ElfW(Sym) **sym, struct link_map *symbol_scope[], const char *reference_name, - const hash_name_pair *version, + const struct r_found_version *version, int flags); /* For handling RTLD_NEXT we must be able to skip shared objects. */ @@ -318,7 +322,7 @@ extern ElfW(Addr) _dl_lookup_versioned_symbol_skip (const char *undef, const ElfW(Sym) **sym, struct link_map *symbol_scope[], const char *reference_name, - const char *version_name, + const struct r_found_version *version, struct link_map *skip_this, int flags); @@ -398,5 +402,6 @@ extern void _dl_debug_state (void); in the `r_ldbase' member. Returns the address of the structure. */ extern struct r_debug *_dl_debug_initialize (ElfW(Addr) ldbase); +__END_DECLS #endif /* link.h */ |