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.c187
1 files changed, 93 insertions, 94 deletions
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, &current_value,
- (*scope)->l_searchlist, 0, (*scope)->l_nsearchlist,
- reference_name, version, NULL, flags))
- break;
+ {
+ int res = do_lookup (undef_name, hash, ref, &current_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, &current_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, &current_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;