/* Look up a symbol in a shared object loaded by `dlopen'.
- Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ Copyright (C) 1999,2000,2001,2002,2004,2006 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
#include <dlfcn.h>
#include <ldsodefs.h>
#include <dl-hash.h>
+#ifdef USE_TLS
+# include <dl-tls.h>
+#endif
-void *
-internal_function
-_dl_sym (void *handle, const char *name, void *who)
-{
- const ElfW(Sym) *ref = NULL;
- lookup_t result;
- ElfW(Addr) caller = (ElfW(Addr)) who;
- struct link_map *match;
- struct link_map *l;
-
- /* If the address is not recognized the call comes from the main
- program (we hope). */
- match = GL(dl_loaded);
-
- /* Find the highest-addressed object that CALLER is not below. */
- for (l = GL(dl_loaded); l != NULL; l = l->l_next)
- if (caller >= l->l_map_start && caller < l->l_map_end)
- {
- /* There must be exactly one DSO for the range of the virtual
- memory. Otherwise something is really broken. */
- match = l;
- break;
- }
-
- if (handle == RTLD_DEFAULT)
- /* Search the global scope as seen in the caller object. */
- result = _dl_lookup_symbol (name, match, &ref, match->l_scope, 0, 0);
- else
- {
- if (handle != RTLD_NEXT)
- {
- /* Search the scope of the given object. */
- struct link_map *map = handle;
-
- result = _dl_lookup_symbol (name, match, &ref, map->l_local_scope,
- 0, 1);
- }
- else
- {
- if (__builtin_expect (match == GL(dl_loaded), 0))
- {
- if (! GL(dl_loaded)
- || caller < GL(dl_loaded)->l_map_start
- || caller >= GL(dl_loaded)->l_map_end)
- _dl_signal_error (0, NULL, NULL, N_("\
-RTLD_NEXT used in code not dynamically loaded"));
- }
- l = match;
- while (l->l_loader != NULL)
- l = l->l_loader;
+#if defined USE_TLS && defined SHARED
+/* Systems which do not have tls_index also probably have to define
+ DONT_USE_TLS_INDEX. */
- result = _dl_lookup_symbol_skip (name, l, &ref, l->l_local_scope,
- match);
- }
- }
+# ifndef __TLS_GET_ADDR
+# define __TLS_GET_ADDR __tls_get_addr
+# endif
- if (ref != NULL)
+/* Return the symbol address given the map of the module it is in and
+ the symbol record. This is used in dl-sym.c. */
+static void *
+internal_function
+_dl_tls_symaddr (struct link_map *map, const ElfW(Sym) *ref)
+{
+# ifndef DONT_USE_TLS_INDEX
+ tls_index tmp =
{
-#if defined USE_TLS && defined SHARED
- if (ELFW(ST_TYPE) (ref->st_info) == STT_TLS)
- /* The found symbol is a thread-local storage variable.
- Return the address for to the current thread. */
- return _dl_tls_symaddr (result, ref);
+ .ti_module = map->l_tls_modid,
+ .ti_offset = ref->st_value
+ };
+
+ return __TLS_GET_ADDR (&tmp);
+# else
+ return __TLS_GET_ADDR (map->l_tls_modid, ref->st_value);
+# endif
+}
#endif
- return DL_SYMBOL_ADDRESS (result, ref);
- }
-
- return NULL;
-}
-void *
+static void *
internal_function
-_dl_vsym (void *handle, const char *name, const char *version, void *who)
+do_sym (void *handle, const char *name, void *who,
+ struct r_found_version *vers, int flags)
{
const ElfW(Sym) *ref = NULL;
- struct r_found_version vers;
lookup_t result;
ElfW(Addr) caller = (ElfW(Addr)) who;
- struct link_map *match;
- struct link_map *l;
-
- /* Compute hash value to the version string. */
- vers.name = version;
- vers.hidden = 1;
- vers.hash = _dl_elf_hash (version);
- /* We don't have a specific file where the symbol can be found. */
- vers.filename = NULL;
/* If the address is not recognized the call comes from the main
program (we hope). */
- match = GL(dl_loaded);
+ struct link_map *match = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
/* Find the highest-addressed object that CALLER is not below. */
- for (l = GL(dl_loaded); l != NULL; l = l->l_next)
- if (caller >= l->l_map_start && caller < l->l_map_end)
- {
- /* There must be exactly one DSO for the range of the virtual
- memory. Otherwise something is really broken. */
- match = l;
- break;
- }
+ for (Lmid_t ns = 0; ns < DL_NNS; ++ns)
+ for (struct link_map *l = GL(dl_ns)[ns]._ns_loaded; l != NULL;
+ l = l->l_next)
+ if (caller >= l->l_map_start && caller < l->l_map_end)
+ {
+ /* There must be exactly one DSO for the range of the virtual
+ memory. Otherwise something is really broken. */
+ match = l;
+ break;
+ }
if (handle == RTLD_DEFAULT)
/* Search the global scope. */
- result = _dl_lookup_versioned_symbol (name, match, &ref, match->l_scope,
- &vers, 0, 0);
+ result = GLRO(dl_lookup_symbol_x) (name, match, &ref, match->l_scope,
+ vers, 0, flags|DL_LOOKUP_ADD_DEPENDENCY,
+ NULL);
else if (handle == RTLD_NEXT)
{
- if (__builtin_expect (match == GL(dl_loaded), 0))
+ if (__builtin_expect (match == GL(dl_ns)[LM_ID_BASE]._ns_loaded, 0))
{
- if (! GL(dl_loaded)
- || caller < GL(dl_loaded)->l_map_start
- || caller >= GL(dl_loaded)->l_map_end)
- _dl_signal_error (0, NULL, NULL, N_("\
+ if (match == NULL
+ || caller < match->l_map_start
+ || caller >= match->l_map_end)
+ GLRO(dl_signal_error) (0, NULL, NULL, N_("\
RTLD_NEXT used in code not dynamically loaded"));
}
- l = match;
+ struct link_map *l = match;
while (l->l_loader != NULL)
l = l->l_loader;
- result = _dl_lookup_versioned_symbol_skip (name, l, &ref,
- l->l_local_scope,
- &vers, match);
+ result = GLRO(dl_lookup_symbol_x) (name, match, &ref, l->l_local_scope,
+ vers, 0, 0, match);
}
else
{
/* Search the scope of the given object. */
struct link_map *map = handle;
- result = _dl_lookup_versioned_symbol (name, map, &ref,
- map->l_local_scope, &vers, 0, 1);
+ result = GLRO(dl_lookup_symbol_x) (name, map, &ref, map->l_local_scope,
+ vers, 0, flags, NULL);
}
if (ref != NULL)
{
+ void *value;
+
#if defined USE_TLS && defined SHARED
if (ELFW(ST_TYPE) (ref->st_info) == STT_TLS)
/* The found symbol is a thread-local storage variable.
Return the address for to the current thread. */
- return _dl_tls_symaddr (result, ref);
+ value = _dl_tls_symaddr (result, ref);
+ else
+#endif
+ value = DL_SYMBOL_ADDRESS (result, ref);
+
+#ifdef SHARED
+ /* Auditing checkpoint: we have a new binding. Provide the
+ auditing libraries the possibility to change the value and
+ tell us whether further auditing is wanted. */
+ if (__builtin_expect (GLRO(dl_naudit) > 0, 0))
+ {
+ const char *strtab = (const char *) D_PTR (result,
+ l_info[DT_STRTAB]);
+ /* Compute index of the symbol entry in the symbol table of
+ the DSO with the definition. */
+ unsigned int ndx = (ref - (ElfW(Sym) *) D_PTR (result,
+ l_info[DT_SYMTAB]));
+
+ if ((match->l_audit_any_plt | result->l_audit_any_plt) != 0)
+ {
+ unsigned int altvalue = 0;
+ struct audit_ifaces *afct = GLRO(dl_audit);
+ /* Synthesize a symbol record where the st_value field is
+ the result. */
+ ElfW(Sym) sym = *ref;
+ sym.st_value = (ElfW(Addr)) value;
+
+ for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+ {
+ if (afct->symbind != NULL
+ && ((match->l_audit[cnt].bindflags & LA_FLG_BINDFROM)
+ != 0
+ || ((result->l_audit[cnt].bindflags & LA_FLG_BINDTO)
+ != 0)))
+ {
+ unsigned int flags = altvalue | LA_SYMB_DLSYM;
+ uintptr_t new_value
+ = afct->symbind (&sym, ndx,
+ &match->l_audit[cnt].cookie,
+ &result->l_audit[cnt].cookie,
+ &flags, strtab + ref->st_name);
+ if (new_value != (uintptr_t) sym.st_value)
+ {
+ altvalue = LA_SYMB_ALTVALUE;
+ sym.st_value = new_value;
+ }
+ }
+
+ afct = afct->next;
+ }
+
+ value = (void *) sym.st_value;
+ }
+ }
#endif
- return DL_SYMBOL_ADDRESS (result, ref);
+ return value;
}
return NULL;
}
+
+
+void *
+internal_function
+_dl_vsym (void *handle, const char *name, const char *version, void *who)
+{
+ struct r_found_version vers;
+
+ /* Compute hash value to the version string. */
+ vers.name = version;
+ vers.hidden = 1;
+ vers.hash = _dl_elf_hash (version);
+ /* We don't have a specific file where the symbol can be found. */
+ vers.filename = NULL;
+
+ return do_sym (handle, name, who, &vers, 0);
+}
+
+
+void *
+internal_function
+_dl_sym (void *handle, const char *name, void *who)
+{
+ return do_sym (handle, name, who, NULL, DL_LOOKUP_RETURN_NEWEST);
+}