diff options
Diffstat (limited to 'trunk/src/ld-lookup.c')
-rw-r--r-- | trunk/src/ld-lookup.c | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/trunk/src/ld-lookup.c b/trunk/src/ld-lookup.c new file mode 100644 index 0000000..5128dbb --- /dev/null +++ b/trunk/src/ld-lookup.c @@ -0,0 +1,298 @@ +/* Copyright (C) 2003 MontaVista Software, Inc. + Written by Daniel Jacobowitz <drow@mvista.com>, 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <assert.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "prelinktab.h" +#include "reloc.h" + +#include "ld-libs.h" + +#ifndef ElfW +/* Default to 32-bit. */ +#define ElfW(x) Elf32_##x +#define ELFW(x) ELF32_##x +#endif + +static int _dl_soname_match_p (const char *name, struct ldlibs_link_map *map); + +struct sym_val +{ + const ElfW(Sym) *s; + struct ldlibs_link_map *m; +}; + +static unsigned long +rtld_elf_hash (const char *name) +{ + const unsigned char *str = (const unsigned char *) name; + unsigned long int hash = 0; + unsigned long int hi; + + while (*str != '\0') + { + hash = (hash << 4) + *str++; + hi = hash & 0xf0000000; + hash ^= hi; + hash ^= hi >> 24; + } + return hash & 0xffffffff; +} + +static unsigned long +rtld_elf_gnu_hash (const char *name) +{ + const unsigned char *str = (const unsigned char *) name; + unsigned long int hash = 5381; + + while (*str != '\0') + hash = (hash << 5) + hash + *str++; + return hash & 0xffffffff; +} + +static inline unsigned long +rtld_elf_any_hash (const char *name, int gnu_hash) +{ + return gnu_hash ? rtld_elf_gnu_hash (name) : rtld_elf_hash (name); +} + +static inline Elf_Symndx +do_lookup_get_first (struct ldlibs_link_map *map, + unsigned long int hash, int gnu_hash) +{ + Elf_Symndx symidx = STN_UNDEF; + + if (gnu_hash) + { + Elf_Symndx bucket; + int match; + + if (map->l_maskword64) + { + const Elf64_Xword *const masks = map->l_maskwords; + const Elf64_Xword mask = masks[(hash / 64) % map->l_nmaskwords]; + const Elf64_Xword set = ((1ULL << (hash % 64)) + | (1ULL << ((hash >> map->l_shift) % 64))); + + match = (mask & set) == set; + } + else + { + const Elf32_Word *const masks = map->l_maskwords; + const Elf32_Word mask = masks[(hash / 32) % map->l_nmaskwords]; + const Elf32_Word set = ((1UL << (hash % 32)) + | (1UL << ((hash >> map->l_shift) % 32))); + + match = (mask & set) == set; + } + + if (match + && (bucket = map->l_buckets[hash % map->l_nbuckets]) != STN_UNDEF) + do + if (((map->l_chain[bucket] ^ hash) & ~1UL) == 0) + { + symidx = bucket; + break; + } + while ((map->l_chain[bucket++] & 1) == 0); + } + else + symidx = map->l_buckets[hash % map->l_nbuckets]; + + return symidx; +} + +static inline Elf_Symndx +do_lookup_get_next (Elf_Symndx osymidx, struct ldlibs_link_map *map, + unsigned long int hash, int gnu_hash) +{ + Elf_Symndx symidx = STN_UNDEF; + + if (gnu_hash) + { + Elf_Symndx bucket = osymidx; + + while ((map->l_chain[bucket++] & 1) == 0) + if (((map->l_chain[bucket] ^ hash) & ~1UL) == 0) + { + symidx = bucket; + break; + } + } + else + symidx = map->l_chain[osymidx]; + + return symidx; +} + +#include "ld-do-lookup.h" +#define VERSIONED 1 +#include "ld-do-lookup.h" +#undef VERSIONED + +static int +_dl_soname_match_p (const char *name, struct ldlibs_link_map *map) +{ + if (strcmp (name, map->l_name) == 0) + return 1; + if (strcmp (name, map->l_soname) == 0) + return 1; + return 0; +} + +#if 0 +void +rtld_lookup_symbol (const char *name, const ElfW(Sym) *sym, + struct r_scope_elem *scope, int rtypeclass, + struct ldlibs_link_map *undef_map, int machine) +{ + int ret; + struct sym_val result; + + result.s = NULL; + ret = do_lookup (name, sym, &result, scope, 0, 0, NULL, rtypeclass); + if (ret > 0) + printf ("name %s /%d\n", name, rtypeclass); +#if 0 + printf ("name %s ret %d", name, ret); + if (result.s) + printf (" result sym 0x%08x (in %s)", result.s->st_value, result.m->l_name); + printf ("\n"); +#endif +} +#endif + +void +rtld_lookup_symbol (const char *name, const ElfW(Sym) *sym, + struct r_scope_elem *scope, + int rtypeclass, + struct ldlibs_link_map *undef_map, int machine) +{ + rtld_lookup_symbol_versioned (name, sym, scope, NULL, rtypeclass, undef_map, machine); +} + +void +rtld_lookup_symbol_versioned (const char *name, const ElfW(Sym) *sym, + struct r_scope_elem *scope, + struct r_found_version *version, int rtypeclass, + struct ldlibs_link_map *undef_map, int machine) +{ + int ret; + int conflict = 0; + int sym_offset; + struct sym_val result, result2; + unsigned int value1, value2; + + result.s = NULL; + result.m = NULL; + result2.s = NULL; + result2.m = NULL; + if (version) + ret = do_lookup_versioned (name, sym, &result, scope, 0, version, NULL, + rtypeclass, machine); + else + ret = do_lookup (name, sym, &result, scope, 0, 0, NULL, + rtypeclass, machine); + + if (result.s == NULL && ELFW(ST_BIND) (sym->st_info) != STB_WEAK) + printf ("undefined symbol: %s\t(%s)\n", name, undef_map->filename); + + if (ret <= 0) + return; + + /* Don't do conflict checking for references in the executable. */ + if (undef_map->l_local_scope != scope) + { + result2.s = NULL; + result2.m = NULL; + if (version) + ret = do_lookup_versioned (name, sym, &result2, + undef_map->l_local_scope, 0, version, NULL, + rtypeclass, machine); + else + ret = do_lookup (name, sym, &result2, + undef_map->l_local_scope, 0, 0, NULL, + rtypeclass, machine); + + if (result2.s != result.s + || result2.m != result.m) + conflict = 1; + } + + if (result.s && ELFW(ST_TYPE) (result.s->st_info) == STT_TLS) + rtypeclass = 4; + + /* Print out information for the requested object, all conflicts, and all TLS. */ + if (!conflict + && rtypeclass != 4 + && requested_map + && requested_map != undef_map) + return; + + /* FIXME: Careful with this if we change the size of symbols when reading in! */ + sym_offset = ((char *)sym) - ((char *)undef_map->l_info[DT_SYMTAB]); + sym_offset += undef_map->sym_base; + + value1 = 0; + if (machine == EM_ARM && result.s + && ELFW(ST_TYPE) (result.s->st_info) == STT_ARM_TFUNC) + value1 = 1; + + value2 = 0; + if (machine == EM_ARM && conflict && result2.s + && ELFW(ST_TYPE) (result2.s->st_info) == STT_ARM_TFUNC) + value2 = 1; + +#if defined(rtld_lookup_symbol) /* 64-bit */ + printf ("%s 0x%016" HOST_LONG_LONG_FORMAT "x " + "0x%016" HOST_LONG_LONG_FORMAT "x " + "-> 0x%016" HOST_LONG_LONG_FORMAT "x " + "0x%016" HOST_LONG_LONG_FORMAT "x ", + conflict ? "conflict" : "lookup", + (unsigned long long) undef_map->l_map_start, + (unsigned long long) sym_offset, + (unsigned long long) (result.s ? result.m->l_map_start : 0), + (unsigned long long) (result.s ? result.s->st_value | value1 : 0)); + if (conflict) + printf ("x 0x%016" HOST_LONG_LONG_FORMAT "x " + "0x%016" HOST_LONG_LONG_FORMAT "x ", + (unsigned long long) (result2.s ? result2.m->l_map_start : 0), + (unsigned long long) (result2.s ? result2.s->st_value | value2 : 0)); +#else + printf ("%s 0x%08x 0x%08x -> 0x%08x 0x%08x ", + conflict ? "conflict" : "lookup", + (uint32_t) undef_map->l_map_start, + (uint32_t) sym_offset, + (uint32_t) (result.s ? result.m->l_map_start : 0), + (uint32_t) (result.s ? result.s->st_value | value1 : 0)); + if (conflict) + printf ("x 0x%08x 0x%08x ", + (uint32_t) (result2.s ? result2.m->l_map_start : 0), + (uint32_t) (result2.s ? result2.s->st_value | value2 : 0)); +#endif + printf ("/%x %s\n", rtypeclass, name); +} + |