From 871b91589bf4f6dfe19d5987b0a05bd7cf936ecc Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Mon, 10 Jul 2006 21:59:43 +0000 Subject: * elf/dl-lookup.c (dl_new_hash): New functions. (_dl_lookup_symbol_x): Rename hash to old_hash and don't compute value here. Compute new-style hash value. Pass new hash value and reference to variable with the old value to do_lookup_x. (_dl_setup_hash): If DT_GNU_HASH is defined, use it and not old-style hash table. (_dl_debug_bindings): Pass new hash value and reference to variable with the old value to do_lookup_x. * elf/do-lookup.h (do_lookup_x): Accept additional parameter with new-style hash value and change old-style hash value parameter to be a reference. Reoganize functions to determine whether new-style hash table is available. Only fall back on old-style table. If old-style hash value is needed, compute it here. * elf/dynamic-link.h (elf_get_dynamic_info): Relocate DT_GNU_HASH entry. * elf/elf.h: Define SHT_GNU_HASH, DT_GNU_HASH, DT_TLSDEC_PLT, DT_TLSDEC_GOT. Adjust DT_ADDRNUM. * include/link.h (struct link_map): Add l_gnu_bitmask_idxbits, l_gnu_shift, l_gnu_bitmask, l_gnu_buckets and l_gnu_chain_zero. * Makeconfig: If linker supports --hash-style option add it to all linker command lines to build DSOs. * config.make.in: Define have-hash-style. * configure.in: Test whether linker supports --hash-style option. * elf/dl-misc.c (_dl_name_match_p): Make MAP parameter const. * sysdeps/generic/ldsodefs.h: Adjust prototype. --- elf/do-lookup.h | 258 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 150 insertions(+), 108 deletions(-) (limited to 'elf/do-lookup.h') diff --git a/elf/do-lookup.h b/elf/do-lookup.h index 7b62b0f..f40ab9d 100644 --- a/elf/do-lookup.h +++ b/elf/do-lookup.h @@ -17,32 +17,29 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ + /* 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 int __attribute_noinline__ -do_lookup_x (const char *undef_name, unsigned long int hash, - const ElfW(Sym) *ref, struct sym_val *result, - struct r_scope_elem *scope, size_t i, +do_lookup_x (const char *undef_name, uint_fast32_t new_hash, + unsigned long int *old_hash, const ElfW(Sym) *ref, + struct sym_val *result, struct r_scope_elem *scope, size_t i, const struct r_found_version *const version, int flags, struct link_map *skip, int type_class) { struct link_map **list = scope->r_list; size_t n = scope->r_nlist; - struct link_map *map; do { - const ElfW(Sym) *symtab; - const char *strtab; - const ElfW(Half) *verstab; + /* These variables are used in the nested function. */ Elf_Symndx symidx; - const ElfW(Sym) *sym; int num_versions = 0; const ElfW(Sym) *versioned_sym = NULL; - map = list[i]->l_real; + const struct link_map *map = list[i]->l_real; /* Here come the extra test needed for `_dl_lookup_symbol_skip'. */ if (map == skip) @@ -63,109 +60,158 @@ do_lookup_x (const char *undef_name, unsigned long int hash, map->l_name[0] ? map->l_name : rtld_progname, map->l_ns); - symtab = (const void *) D_PTR (map, l_info[DT_SYMTAB]); - strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]); - verstab = map->l_versyms; + /* If the hash table is empty there is nothing to do here. */ + if (map->l_nbuckets == 0) + continue; - /* 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]) - { - sym = &symtab[symidx]; - - assert (ELF_RTYPE_CLASS_PLT == 1); - if ((sym->st_value == 0 /* No value. */ -#ifdef USE_TLS - && ELFW(ST_TYPE) (sym->st_info) != STT_TLS -#endif - ) - || (type_class & (sym->st_shndx == SHN_UNDEF))) - continue; - - if (ELFW(ST_TYPE) (sym->st_info) > STT_FUNC -#ifdef USE_TLS - && ELFW(ST_TYPE) (sym->st_info) != STT_TLS -#endif - ) - /* Ignore all but STT_NOTYPE, STT_OBJECT and STT_FUNC - entries (and STT_TLS if TLS is supported) since these - are no code/data definitions. */ - continue; - - if (sym != ref && strcmp (strtab + sym->st_name, undef_name)) - /* Not the symbol we are looking for. */ - continue; - - if (version != NULL) - { - if (__builtin_expect (verstab == NULL, 0)) - { - /* We need a versioned symbol 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. - - It would also be a bug in the object since it means that - the list of required versions is incomplete and so the - tests in dl-version.c haven't found a problem.*/ - assert (version->filename == NULL - || ! _dl_name_match_p (version->filename, map)); - - /* Otherwise we accept the symbol. */ - } - else - { - /* We can match the version information or use the - default one if it is not hidden. */ - ElfW(Half) ndx = verstab[symidx] & 0x7fff; - if ((map->l_versions[ndx].hash != version->hash - || strcmp (map->l_versions[ndx].name, version->name)) - && (version->hidden || map->l_versions[ndx].hash - || (verstab[symidx] & 0x8000))) - /* It's not the version we want. */ - continue; - } - } - else - { - /* No specific version is selected. There are two ways we - can got here: + /* The tables for this map. */ + const ElfW(Sym) *symtab = (const void *) D_PTR (map, l_info[DT_SYMTAB]); + const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]); + + + /* Nested routine to check whether the symbol matches. */ + const ElfW(Sym) * + __attribute_noinline__ + check_match (const ElfW(Sym) *sym) + { + assert (ELF_RTYPE_CLASS_PLT == 1); + if (__builtin_expect ((sym->st_value == 0 /* No value. */ + && ELFW(ST_TYPE) (sym->st_info) != STT_TLS) + || (type_class & (sym->st_shndx == SHN_UNDEF)), + 0)) + return NULL; + + if (__builtin_expect (ELFW(ST_TYPE) (sym->st_info) > STT_FUNC + && ELFW(ST_TYPE) (sym->st_info) != STT_TLS, 0)) + /* Ignore all but STT_NOTYPE, STT_OBJECT and STT_FUNC + entries (and STT_TLS if TLS is supported) since these + are no code/data definitions. */ + return NULL; + + if (sym != ref && strcmp (strtab + sym->st_name, undef_name)) + /* Not the symbol we are looking for. */ + return NULL; + + const ElfW(Half) *verstab = map->l_versyms; + if (version != NULL) + { + if (__builtin_expect (verstab == NULL, 0)) + { + /* We need a versioned symbol 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. + + It would also be a bug in the object since it means that + the list of required versions is incomplete and so the + tests in dl-version.c haven't found a problem.*/ + assert (version->filename == NULL + || ! _dl_name_match_p (version->filename, map)); - - a binary which does not include versioning information - is loaded + /* Otherwise we accept the symbol. */ + } + else + { + /* We can match the version information or use the + default one if it is not hidden. */ + ElfW(Half) ndx = verstab[symidx] & 0x7fff; + if ((map->l_versions[ndx].hash != version->hash + || strcmp (map->l_versions[ndx].name, version->name)) + && (version->hidden || map->l_versions[ndx].hash + || (verstab[symidx] & 0x8000))) + /* It's not the version we want. */ + return NULL; + } + } + else + { + /* No specific version is selected. There are two ways we + can got here: - - dlsym() instead of dlvsym() is used to get a symbol which - might exist in more than one form + - a binary which does not include versioning information + is loaded - If the library does not provide symbol version - information there is no problem at at: we simply use the - symbol if it is defined. + - dlsym() instead of dlvsym() is used to get a symbol which + might exist in more than one form - These two lookups need to be handled differently if the - library defines versions. In the case of the old - unversioned application the oldest (default) version - should be used. In case of a dlsym() call the latest and - public interface should be returned. */ - if (verstab != NULL) + If the library does not provide symbol version information + there is no problem at at: we simply use the symbol if it + is defined. + + These two lookups need to be handled differently if the + library defines versions. In the case of the old + unversioned application the oldest (default) version + should be used. In case of a dlsym() call the latest and + public interface should be returned. */ + if (verstab != NULL) + { + if ((verstab[symidx] & 0x7fff) + >= ((flags & DL_LOOKUP_RETURN_NEWEST) ? 2 : 3)) + { + /* Don't accept hidden symbols. */ + if ((verstab[symidx] & 0x8000) == 0 + && num_versions++ == 0) + /* No version so far. */ + versioned_sym = sym; + + return NULL; + } + } + } + + /* There cannot be another entry for this symbol so stop here. */ + return sym; + } + + const ElfW(Sym) *sym; + const ElfW(Addr) *bitmask = map->l_gnu_bitmask; + if (__builtin_expect (bitmask != NULL, 1)) + { + ElfW(Addr) bitmask_word + = bitmask[(new_hash / __ELF_NATIVE_CLASS) + & map->l_gnu_bitmask_idxbits]; + + unsigned int hashbit1 = new_hash & (__ELF_NATIVE_CLASS - 1); + unsigned int hashbit2 = ((new_hash >> map->l_gnu_shift) + & (__ELF_NATIVE_CLASS - 1)); + + if (__builtin_expect ((bitmask_word >> hashbit1) + & (bitmask_word >> hashbit2) & 1, 0)) + { + Elf32_Word bucket = map->l_gnu_buckets[new_hash + % map->l_nbuckets]; + if (bucket != 0) { - if ((verstab[symidx] & 0x7fff) - >= ((flags & DL_LOOKUP_RETURN_NEWEST) ? 2 : 3)) - { - /* Don't accept hidden symbols. */ - if ((verstab[symidx] & 0x8000) == 0 - && num_versions++ == 0) - /* No version so far. */ - versioned_sym = sym; + const Elf32_Word *hasharr = &map->l_gnu_chain_zero[bucket]; - continue; - } + do + if ((*hasharr & ~1u) == (new_hash & ~1u)) + { + symidx = hasharr - map->l_gnu_chain_zero; + sym = check_match (&symtab[symidx]); + if (sym != NULL) + goto found_it; + } + while ((*hasharr++ & 1u) == 0); } } + } + else + { + if (*old_hash == 0xffffffff) + *old_hash = _dl_elf_hash (undef_name); - /* There cannot be another entry for this symbol so stop here. */ - goto found_it; + /* Use the old SysV-style hash table. Search the appropriate + hash bucket in this object's symbol table for a definition + for the same symbol name. */ + for (symidx = map->l_buckets[*old_hash % map->l_nbuckets]; + symidx != STN_UNDEF; + symidx = map->l_chain[symidx]) + { + sym = check_match (&symtab[symidx]); + if (sym != NULL) + goto found_it; + } } /* If we have seen exactly one versioned symbol while we are @@ -186,7 +232,7 @@ do_lookup_x (const char *undef_name, unsigned long int hash, if (! result->s) { result->s = sym; - result->m = map; + result->m = (struct link_map *) map; } break; } @@ -194,7 +240,7 @@ do_lookup_x (const char *undef_name, unsigned long int hash, case STB_GLOBAL: /* Global definition. Just what we need. */ result->s = sym; - result->m = map; + result->m = (struct link_map *) map; return 1; default: /* Local symbols are ignored. */ @@ -213,7 +259,3 @@ do_lookup_x (const char *undef_name, unsigned long int hash, /* We have not found anything until now. */ return 0; } - -#undef FCT -#undef ARG -#undef VERSIONED -- cgit v1.1