summaryrefslogtreecommitdiff
path: root/elf/dl-lookup.c
diff options
context:
space:
mode:
Diffstat (limited to 'elf/dl-lookup.c')
-rw-r--r--elf/dl-lookup.c159
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, &current_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, &current_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, &current_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, &current_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, &current_value,
+ (*scope)->l_dupsearchlist, i, (*scope)->l_ndupsearchlist,
+ reference_name, &version, skip_map, flags))
while (*++scope)
if (do_lookup (undef_name, hash, ref, &current_value,
(*scope)->l_dupsearchlist, 0, (*scope)->l_ndupsearchlist,
- reference_name, skip_map, flags))
+ reference_name, &version, skip_map, flags))
break;
*ref = current_value.s;