diff options
Diffstat (limited to 'elf/dl-lookup.c')
-rw-r--r-- | elf/dl-lookup.c | 159 |
1 files changed, 144 insertions, 15 deletions
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; |