]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - elf/dl-lookup.c
Replace {u}int_fast{16|32} with {u}int32_t
[thirdparty/glibc.git] / elf / dl-lookup.c
index 895b60df7dc1c70e4b62266ac1e68ac5d0d480e7..989b073e4f74d2dad53387b64aeaea9022ff5dad 100644 (file)
@@ -1,5 +1,5 @@
 /* Look up a symbol in the loaded objects.
-   Copyright (C) 1995,96,97,98,99,2000,2001,2002 Free Software Foundation, Inc.
+   Copyright (C) 1995-2022 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
@@ -13,9 +13,8 @@
    Lesser General Public License for more details.
 
    You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
 
 #include <alloca.h>
 #include <libintl.h>
 #include <string.h>
 #include <unistd.h>
 #include <ldsodefs.h>
-#include "dl-hash.h"
+#include <dl-hash.h>
 #include <dl-machine.h>
-#include <bits/libc-lock.h>
+#include <dl-protected.h>
+#include <sysdep-cancel.h>
+#include <libc-lock.h>
 #include <tls.h>
+#include <atomic.h>
+#include <elf_machine_sym_no_match.h>
 
 #include <assert.h>
 
 #define VERSTAG(tag)   (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (tag))
 
-/* We need this string more than once.  */
-static const char undefined_msg[] = "undefined symbol: ";
-
-
 struct sym_val
   {
     const ElfW(Sym) *s;
@@ -43,23 +42,6 @@ struct sym_val
   };
 
 
-#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;                                                                  \
-  })
-
 /* Statistics function.  */
 #ifdef SHARED
 # define bump_num_relocations() ++GL(dl_num_relocations)
@@ -67,629 +49,878 @@ struct sym_val
 # define bump_num_relocations() ((void) 0)
 #endif
 
-
-
-/* We have two different situations when looking up a simple: with or
-   without versioning.  gcc is not able to optimize a single function
-   definition serving for both purposes so we define two functions.  */
-#define VERSIONED      0
-#include "do-lookup.h"
-
-#define VERSIONED      1
-#include "do-lookup.h"
-
-
-/* Add extra dependency on MAP to UNDEF_MAP.  */
-static int
-internal_function
-add_dependency (struct link_map *undef_map, struct link_map *map)
+/* Utility function for do_lookup_x. The caller is called with undef_name,
+   ref, version, flags and type_class, and those are passed as the first
+   five arguments. The caller then computes sym, symidx, strtab, and map
+   and passes them as the next four arguments. Lastly the caller passes in
+   versioned_sym and num_versions which are modified by check_match during
+   the checking process.  */
+static const ElfW(Sym) *
+check_match (const char *const undef_name,
+            const ElfW(Sym) *const ref,
+            const struct r_found_version *const version,
+            const int flags,
+            const int type_class,
+            const ElfW(Sym) *const sym,
+            const Elf_Symndx symidx,
+            const char *const strtab,
+            const struct link_map *const map,
+            const ElfW(Sym) **const versioned_sym,
+            int *const num_versions)
 {
-  struct link_map **list;
-  struct link_map *runp;
-  unsigned int act;
-  unsigned int i;
-  int result = 0;
-
-  /* Avoid self-references.  */
-  if (undef_map == map)
-    return 0;
-
-  /* Make sure nobody can unload the object while we are at it.  */
-  __libc_lock_lock_recursive (GL(dl_load_lock));
-
-  /* Determine whether UNDEF_MAP already has a reference to MAP.  First
-     look in the normal dependencies.  */
-  if (undef_map->l_searchlist.r_list != NULL)
+  unsigned int stt = ELFW(ST_TYPE) (sym->st_info);
+  assert (ELF_RTYPE_CLASS_PLT == 1);
+  if (__glibc_unlikely ((sym->st_value == 0 /* No value.  */
+                        && sym->st_shndx != SHN_ABS
+                        && stt != STT_TLS)
+                       || elf_machine_sym_no_match (sym)
+                       || (type_class & (sym->st_shndx == SHN_UNDEF))))
+    return NULL;
+
+  /* Ignore all but STT_NOTYPE, STT_OBJECT, STT_FUNC,
+     STT_COMMON, STT_TLS, and STT_GNU_IFUNC since these are no
+     code/data definitions.  */
+#define ALLOWED_STT \
+  ((1 << STT_NOTYPE) | (1 << STT_OBJECT) | (1 << STT_FUNC) \
+   | (1 << STT_COMMON) | (1 << STT_TLS) | (1 << STT_GNU_IFUNC))
+  if (__glibc_unlikely (((1 << stt) & ALLOWED_STT) == 0))
+    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)
     {
-      list = undef_map->l_initfini;
-
-      for (i = 0; list[i] != NULL; ++i)
-       if (list[i] == map)
-         goto out;
+      if (__glibc_unlikely (verstab == NULL))
+       {
+         /* 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.  */
+           return NULL;
+       }
     }
+  else
+    {
+      /* No specific version is selected.  There are two ways we
+        can got here:
 
-  /* No normal dependency.  See whether we already had to add it
-     to the special list of dynamic dependencies.  */
-  list = undef_map->l_reldeps;
-  act = undef_map->l_reldepsact;
+        - a binary which does not include versioning information
+        is loaded
 
-  for (i = 0; i < act; ++i)
-    if (list[i] == map)
-      goto out;
+        - dlsym() instead of dlvsym() is used to get a symbol which
+        might exist in more than one form
 
-  /* The object is not yet in the dependency list.  Before we add
-     it make sure just one more time the object we are about to
-     reference is still available.  There is a brief period in
-     which the object could have been removed since we found the
-     definition.  */
-  runp = GL(dl_loaded);
-  while (runp != NULL && runp != map)
-    runp = runp->l_next;
+        If the library does not provide symbol version information
+        there is no problem at all: we simply use the symbol if it
+        is defined.
 
-  if (runp != NULL)
-    {
-      /* The object is still available.  Add the reference now.  */
-      if (__builtin_expect (act >= undef_map->l_reldepsmax, 0))
+        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)
        {
-         /* Allocate more memory for the dependency list.  Since this
-            can never happen during the startup phase we can use
-            `realloc'.  */
-         void *newp;
-
-         undef_map->l_reldepsmax += 5;
-         newp = realloc (undef_map->l_reldeps,
-                         undef_map->l_reldepsmax
-                         * sizeof (struct link_map *));
-
-         if (__builtin_expect (newp != NULL, 1))
-           undef_map->l_reldeps = (struct link_map **) newp;
-         else
-           /* Correct the addition.  */
-           undef_map->l_reldepsmax -= 5;
+         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;
+           }
        }
+    }
 
-      /* If we didn't manage to allocate memory for the list this is
-        no fatal mistake.  We simply increment the use counter of the
-        referenced object and don't record the dependencies.  This
-        means this increment can never be reverted and the object
-        will never be unloaded.  This is semantically the correct
-        behaviour.  */
-      if (__builtin_expect (act < undef_map->l_reldepsmax, 1))
-       undef_map->l_reldeps[undef_map->l_reldepsact++] = map;
-
-      if (map->l_searchlist.r_list != NULL)
-       /* And increment the counter in the referenced object.  */
-       ++map->l_opencount;
-      else
-       /* We have to bump the counts for all dependencies since so far
-          this object was only a normal or transitive dependency.
-          Now it might be closed with _dl_close() directly.  */
-       for (list = map->l_initfini; *list != NULL; ++list)
-         ++(*list)->l_opencount;
+  /* There cannot be another entry for this symbol so stop here.  */
+  return sym;
+}
 
-      /* Display information if we are debugging.  */
-      if (__builtin_expect (GL(dl_debug_mask) & DL_DEBUG_FILES, 0))
-       INTUSE(_dl_debug_printf) ("\
-\nfile=%s;  needed by %s (relocation dependency)\n\n",
-                                 map->l_name[0] ? map->l_name : rtld_progname,
-                                 undef_map->l_name[0]
-                                 ? undef_map->l_name : rtld_progname);
+/* Utility function for do_lookup_unique.  Add a symbol to TABLE.  */
+static void
+enter_unique_sym (struct unique_sym *table, size_t size,
+                  unsigned int hash, const char *name,
+                  const ElfW(Sym) *sym, const struct link_map *map)
+{
+  size_t idx = hash % size;
+  size_t hash2 = 1 + hash % (size - 2);
+  while (table[idx].name != NULL)
+    {
+      idx += hash2;
+      if (idx >= size)
+        idx -= size;
     }
-  else
-    /* Whoa, that was bad luck.  We have to search again.  */
-    result = -1;
 
- out:
-  /* Release the lock.  */
-  __libc_lock_unlock_recursive (GL(dl_load_lock));
+  table[idx].hashval = hash;
+  table[idx].name = name;
+  table[idx].sym = sym;
+  table[idx].map = map;
+}
 
-  return result;
+/* Mark MAP as NODELETE according to the lookup mode in FLAGS.  During
+   initial relocation, NODELETE state is pending only.  */
+static void
+mark_nodelete (struct link_map *map, int flags)
+{
+  if (flags & DL_LOOKUP_FOR_RELOCATE)
+    map->l_nodelete_pending = true;
+  else
+    map->l_nodelete_active = true;
 }
 
-static int
-internal_function
-_dl_do_lookup (const char *undef_name, unsigned long int hash,
-              const ElfW(Sym) *ref, struct sym_val *result,
-              struct r_scope_elem *scope, size_t i, int flags,
-              struct link_map *skip, int type_class);
-static int
-internal_function
-_dl_do_lookup_versioned (const char *undef_name, unsigned long int hash,
-                        const ElfW(Sym) *ref, struct sym_val *result,
-                        struct r_scope_elem *scope, size_t i,
-                        const struct r_found_version *const version,
-                        struct link_map *skip, int type_class);
+/* Return true if MAP is marked as NODELETE according to the lookup
+   mode in FLAGS> */
+static bool
+is_nodelete (struct link_map *map, int flags)
+{
+  /* Non-pending NODELETE always counts.  Pending NODELETE only counts
+     during initial relocation processing.  */
+  return map->l_nodelete_active
+    || ((flags & DL_LOOKUP_FOR_RELOCATE) && map->l_nodelete_pending);
+}
 
+/* Utility function for do_lookup_x. Lookup an STB_GNU_UNIQUE symbol
+   in the unique symbol table, creating a new entry if necessary.
+   Return the matching symbol in RESULT.  */
 static void
-internal_function
-_dl_debug_bindings (const char *undef_name, struct link_map *undef_map,
-                   const ElfW(Sym) **ref, struct r_scope_elem *symbol_scope[],
-                   struct sym_val *value,
-                   const struct r_found_version *version, int type_class,
-                   int protected);
+do_lookup_unique (const char *undef_name, unsigned int new_hash,
+                 struct link_map *map, struct sym_val *result,
+                 int type_class, const ElfW(Sym) *sym, const char *strtab,
+                 const ElfW(Sym) *ref, const struct link_map *undef_map,
+                 int flags)
+{
+  /* We have to determine whether we already found a symbol with this
+     name before.  If not then we have to add it to the search table.
+     If we already found a definition we have to use it.  */
 
-/* Search loaded objects' symbol tables for a definition of the symbol
-   UNDEF_NAME.  */
+  struct unique_sym_table *tab
+    = &GL(dl_ns)[map->l_ns]._ns_unique_sym_table;
 
-lookup_t
-internal_function
-_dl_lookup_symbol (const char *undef_name, struct link_map *undef_map,
-                  const ElfW(Sym) **ref, struct r_scope_elem *symbol_scope[],
-                  int type_class, int flags)
-{
-  const unsigned long int hash = _dl_elf_hash (undef_name);
-  struct sym_val current_value = { NULL, NULL };
-  struct r_scope_elem **scope;
-  int protected;
+  __rtld_lock_lock_recursive (tab->lock);
 
-  bump_num_relocations ();
+  struct unique_sym *entries = tab->entries;
+  size_t size = tab->size;
+  if (entries != NULL)
+    {
+      size_t idx = new_hash % size;
+      size_t hash2 = 1 + new_hash % (size - 2);
+      while (1)
+       {
+         if (entries[idx].hashval == new_hash
+             && strcmp (entries[idx].name, undef_name) == 0)
+           {
+             if ((type_class & ELF_RTYPE_CLASS_COPY) != 0)
+               {
+                 /* We possibly have to initialize the central
+                    copy from the copy addressed through the
+                    relocation.  */
+                 result->s = sym;
+                 result->m = map;
+               }
+             else
+               {
+                 result->s = entries[idx].sym;
+                 result->m = (struct link_map *) entries[idx].map;
+               }
+             __rtld_lock_unlock_recursive (tab->lock);
+             return;
+           }
+
+         if (entries[idx].name == NULL)
+           break;
 
-  /* Search the relevant loaded objects for a definition.  */
-  for (scope = symbol_scope; *scope; ++scope)
-    if (do_lookup (undef_name, hash, *ref, &current_value, *scope, 0, flags,
-                  NULL, type_class))
-      break;
+         idx += hash2;
+         if (idx >= size)
+           idx -= size;
+       }
 
-  if (__builtin_expect (current_value.s == NULL, 0))
+      if (size * 3 <= tab->n_elements * 4)
+       {
+         /* Expand the table.  */
+#ifdef RTLD_CHECK_FOREIGN_CALL
+         /* This must not happen during runtime relocations.  */
+         assert (!RTLD_CHECK_FOREIGN_CALL);
+#endif
+         size_t newsize = _dl_higher_prime_number (size + 1);
+         struct unique_sym *newentries
+           = calloc (sizeof (struct unique_sym), newsize);
+         if (newentries == NULL)
+           {
+           nomem:
+             __rtld_lock_unlock_recursive (tab->lock);
+             _dl_fatal_printf ("out of memory\n");
+           }
+
+         for (idx = 0; idx < size; ++idx)
+           if (entries[idx].name != NULL)
+             enter_unique_sym (newentries, newsize, entries[idx].hashval,
+                                entries[idx].name, entries[idx].sym,
+                                entries[idx].map);
+
+         tab->free (entries);
+         tab->size = newsize;
+         size = newsize;
+         entries = tab->entries = newentries;
+         tab->free = __rtld_free;
+       }
+    }
+  else
     {
-      const char *reference_name = undef_map ? undef_map->l_name : NULL;
-
-      if (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK)
-       /* We could find no value for a strong reference.  */
-       /* XXX We cannot translate the messages.  */
-       _dl_signal_cerror (0, (reference_name[0]
-                              ? reference_name
-                              : (rtld_progname ?: "<main program>")),
-                          N_("relocation error"),
-                          make_string (undefined_msg, undef_name));
-      *ref = NULL;
-      return 0;
+#ifdef RTLD_CHECK_FOREIGN_CALL
+      /* This must not happen during runtime relocations.  */
+      assert (!RTLD_CHECK_FOREIGN_CALL);
+#endif
+
+#define INITIAL_NUNIQUE_SYM_TABLE 31
+      size = INITIAL_NUNIQUE_SYM_TABLE;
+      entries = calloc (sizeof (struct unique_sym), size);
+      if (entries == NULL)
+       goto nomem;
+
+      tab->entries = entries;
+      tab->size = size;
+      tab->free = __rtld_free;
     }
 
-  protected = *ref && ELFW(ST_VISIBILITY) ((*ref)->st_other) == STV_PROTECTED;
-  if (__builtin_expect (protected != 0, 0))
+  if ((type_class & ELF_RTYPE_CLASS_COPY) != 0)
+    enter_unique_sym (entries, size, new_hash, strtab + sym->st_name, ref,
+          undef_map);
+  else
     {
-      /* It is very tricky.  We need to figure out what value to
-         return for the protected symbol.  */
-      struct sym_val protected_value = { NULL, NULL };
-
-      for (scope = symbol_scope; *scope; ++scope)
-       if (_dl_do_lookup (undef_name, hash, *ref, &protected_value, *scope,
-                          0, flags, NULL, ELF_RTYPE_CLASS_PLT))
-         break;
+      enter_unique_sym (entries, size,
+                        new_hash, strtab + sym->st_name, sym, map);
 
-      if (protected_value.s != NULL && protected_value.m != undef_map)
+      if (map->l_type == lt_loaded && !is_nodelete (map, flags))
        {
-         current_value.s = *ref;
-         current_value.m = undef_map;
+         /* Make sure we don't unload this object by
+            setting the appropriate flag.  */
+         if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_BINDINGS))
+           _dl_debug_printf ("\
+marking %s [%lu] as NODELETE due to unique symbol\n",
+                             map->l_name, map->l_ns);
+         mark_nodelete (map, flags);
        }
     }
+  ++tab->n_elements;
 
-  /* We have to check whether this would bind UNDEF_MAP to an object
-     in the global scope which was dynamically loaded.  In this case
-     we have to prevent the latter from being unloaded unless the
-     UNDEF_MAP object is also unloaded.  */
-  if (__builtin_expect (current_value.m->l_type == lt_loaded, 0)
-      /* Don't do this for explicit lookups as opposed to implicit
-        runtime lookups.  */
-      && (flags & DL_LOOKUP_ADD_DEPENDENCY) != 0
-      /* Add UNDEF_MAP to the dependencies.  */
-      && add_dependency (undef_map, current_value.m) < 0)
-      /* Something went wrong.  Perhaps the object we tried to reference
-        was just removed.  Try finding another definition.  */
-      return INTUSE(_dl_lookup_symbol) (undef_name, undef_map, ref,
-                                       symbol_scope, type_class, flags);
-
-  if (__builtin_expect (GL(dl_debug_mask)
-                       & (DL_DEBUG_BINDINGS|DL_DEBUG_PRELINK), 0))
-    _dl_debug_bindings (undef_name, undef_map, ref, symbol_scope,
-                       &current_value, NULL, type_class, protected);
+  __rtld_lock_unlock_recursive (tab->lock);
 
-  *ref = current_value.s;
-  return LOOKUP_VALUE (current_value.m);
+  result->s = sym;
+  result->m = (struct link_map *) map;
 }
-INTDEF (_dl_lookup_symbol)
 
-
-/* This function is nearly the same as `_dl_lookup_symbol' but it
-   skips in the first list all objects until SKIP_MAP is found.  I.e.,
-   it only considers objects which were loaded after the described
-   object.  If there are more search lists the object described by
-   SKIP_MAP is only skipped.  */
-lookup_t
-internal_function
-_dl_lookup_symbol_skip (const char *undef_name,
-                       struct link_map *undef_map, const ElfW(Sym) **ref,
-                       struct r_scope_elem *symbol_scope[],
-                       struct link_map *skip_map)
+/* 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 int 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 *undef_map)
 {
-  const unsigned long int hash = _dl_elf_hash (undef_name);
-  struct sym_val current_value = { NULL, NULL };
-  struct r_scope_elem **scope;
-  size_t i;
-  int protected;
+  size_t n = scope->r_nlist;
+  /* Make sure we read the value before proceeding.  Otherwise we
+     might use r_list pointing to the initial scope and r_nlist being
+     the value after a resize.  That is the only path in dl-open.c not
+     protected by GSCOPE.  A read barrier here might be to expensive.  */
+  __asm volatile ("" : "+r" (n), "+m" (scope->r_list));
+  struct link_map **list = scope->r_list;
+
+  do
+    {
+      const struct link_map *map = list[i]->l_real;
 
-  bump_num_relocations ();
+      /* Here come the extra test needed for `_dl_lookup_symbol_skip'.  */
+      if (map == skip)
+       continue;
 
-  /* Search the relevant loaded objects for a definition.  */
-  scope = symbol_scope;
-  for (i = 0; (*scope)->r_list[i] != skip_map; ++i)
-    assert (i < (*scope)->r_nlist);
-
-  if (! _dl_do_lookup (undef_name, hash, *ref, &current_value, *scope, i,
-                      DL_LOOKUP_RETURN_NEWEST, skip_map, 0))
-    while (*++scope)
-      if (_dl_do_lookup (undef_name, hash, *ref, &current_value, *scope, 0,
-                        DL_LOOKUP_RETURN_NEWEST, skip_map, 0))
-       break;
-
-  if (__builtin_expect (current_value.s == NULL, 0))
-    {
-      *ref = NULL;
-      return 0;
-    }
+      /* Don't search the executable when resolving a copy reloc.  */
+      if ((type_class & ELF_RTYPE_CLASS_COPY) && map->l_type == lt_executable)
+       continue;
 
-  protected = *ref && ELFW(ST_VISIBILITY) ((*ref)->st_other) == STV_PROTECTED;
+      /* Do not look into objects which are going to be removed.  */
+      if (map->l_removed)
+       continue;
 
-  if (__builtin_expect (protected != 0, 0))
-    {
-      /* It is very tricky.  We need to figure out what value to
-         return for the protected symbol.  */
-      struct sym_val protected_value = { NULL, NULL };
-
-      if (i >= (*scope)->r_nlist
-         || !_dl_do_lookup (undef_name, hash, *ref, &protected_value, *scope,
-                            i, DL_LOOKUP_RETURN_NEWEST, skip_map,
-                            ELF_RTYPE_CLASS_PLT))
-       while (*++scope)
-         if (_dl_do_lookup (undef_name, hash, *ref, &protected_value, *scope,
-                            0, DL_LOOKUP_RETURN_NEWEST, skip_map,
-                            ELF_RTYPE_CLASS_PLT))
-           break;
+      /* Print some debugging info if wanted.  */
+      if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SYMBOLS))
+       _dl_debug_printf ("symbol=%s;  lookup in file=%s [%lu]\n",
+                         undef_name, DSO_FILENAME (map->l_name),
+                         map->l_ns);
+
+      /* If the hash table is empty there is nothing to do here.  */
+      if (map->l_nbuckets == 0)
+       continue;
+
+      Elf_Symndx symidx;
+      int num_versions = 0;
+      const ElfW(Sym) *versioned_sym = NULL;
+
+      /* 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]);
 
-      if (protected_value.s != NULL && protected_value.m != undef_map)
+      const ElfW(Sym) *sym;
+      const ElfW(Addr) *bitmask = map->l_gnu_bitmask;
+      if (__glibc_likely (bitmask != NULL))
        {
-         current_value.s = *ref;
-         current_value.m = undef_map;
+         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 (__glibc_unlikely ((bitmask_word >> hashbit1)
+                               & (bitmask_word >> hashbit2) & 1))
+           {
+             Elf32_Word bucket = map->l_gnu_buckets[new_hash
+                                                    % map->l_nbuckets];
+             if (bucket != 0)
+               {
+                 const Elf32_Word *hasharr = &map->l_gnu_chain_zero[bucket];
+
+                 do
+                   if (((*hasharr ^ new_hash) >> 1) == 0)
+                     {
+                       symidx = ELF_MACHINE_HASH_SYMIDX (map, hasharr);
+                       sym = check_match (undef_name, ref, version, flags,
+                                          type_class, &symtab[symidx], symidx,
+                                          strtab, map, &versioned_sym,
+                                          &num_versions);
+                       if (sym != NULL)
+                         goto found_it;
+                     }
+                 while ((*hasharr++ & 1u) == 0);
+               }
+           }
+         /* No symbol found.  */
+         symidx = SHN_UNDEF;
+       }
+      else
+       {
+         if (*old_hash == 0xffffffff)
+           *old_hash = _dl_elf_hash (undef_name);
+
+         /* 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 (undef_name, ref, version, flags,
+                                type_class, &symtab[symidx], symidx,
+                                strtab, map, &versioned_sym,
+                                &num_versions);
+             if (sym != NULL)
+               goto found_it;
+           }
        }
-    }
 
-  if (__builtin_expect (GL(dl_debug_mask)
-                       & (DL_DEBUG_BINDINGS|DL_DEBUG_PRELINK), 0))
-    _dl_debug_bindings (undef_name, undef_map, ref, symbol_scope,
-                       &current_value, NULL, 0, protected);
+      /* If we have seen exactly one versioned symbol while we are
+        looking for an unversioned symbol and the version is not the
+        default version we still accept this symbol since there are
+        no possible ambiguities.  */
+      sym = num_versions == 1 ? versioned_sym : NULL;
 
-  *ref = current_value.s;
-  return LOOKUP_VALUE (current_value.m);
+      if (sym != NULL)
+       {
+       found_it:
+         /* When UNDEF_MAP is NULL, which indicates we are called from
+            do_lookup_x on relocation against protected data, we skip
+            the data definion in the executable from copy reloc.  */
+         if (ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA
+             && undef_map == NULL
+             && map->l_type == lt_executable
+             && type_class == ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA)
+           {
+             const ElfW(Sym) *s;
+             unsigned int i;
+
+#if ! ELF_MACHINE_NO_RELA
+             if (map->l_info[DT_RELA] != NULL
+                 && map->l_info[DT_RELASZ] != NULL
+                 && map->l_info[DT_RELASZ]->d_un.d_val != 0)
+               {
+                 const ElfW(Rela) *rela
+                   = (const ElfW(Rela) *) D_PTR (map, l_info[DT_RELA]);
+                 unsigned int rela_count
+                   = map->l_info[DT_RELASZ]->d_un.d_val / sizeof (*rela);
+
+                 for (i = 0; i < rela_count; i++, rela++)
+                   if (elf_machine_type_class (ELFW(R_TYPE) (rela->r_info))
+                       == ELF_RTYPE_CLASS_COPY)
+                     {
+                       s = &symtab[ELFW(R_SYM) (rela->r_info)];
+                       if (!strcmp (strtab + s->st_name, undef_name))
+                         goto skip;
+                     }
+               }
+#endif
+#if ! ELF_MACHINE_NO_REL
+             if (map->l_info[DT_REL] != NULL
+                 && map->l_info[DT_RELSZ] != NULL
+                 && map->l_info[DT_RELSZ]->d_un.d_val != 0)
+               {
+                 const ElfW(Rel) *rel
+                   = (const ElfW(Rel) *) D_PTR (map, l_info[DT_REL]);
+                 unsigned int rel_count
+                   = map->l_info[DT_RELSZ]->d_un.d_val / sizeof (*rel);
+
+                 for (i = 0; i < rel_count; i++, rel++)
+                   if (elf_machine_type_class (ELFW(R_TYPE) (rel->r_info))
+                       == ELF_RTYPE_CLASS_COPY)
+                     {
+                       s = &symtab[ELFW(R_SYM) (rel->r_info)];
+                       if (!strcmp (strtab + s->st_name, undef_name))
+                         goto skip;
+                     }
+               }
+#endif
+           }
+
+         /* Hidden and internal symbols are local, ignore them.  */
+         if (__glibc_unlikely (dl_symbol_visibility_binds_local_p (sym)))
+           goto skip;
+
+         if (ELFW(ST_VISIBILITY) (sym->st_other) == STV_PROTECTED)
+           _dl_check_protected_symbol (undef_name, undef_map, ref, map,
+                                       type_class);
+
+         switch (ELFW(ST_BIND) (sym->st_info))
+           {
+           case STB_WEAK:
+             /* Weak definition.  Use this value if we don't find another.  */
+             if (__glibc_unlikely (GLRO(dl_dynamic_weak)))
+               {
+                 if (! result->s)
+                   {
+                     result->s = sym;
+                     result->m = (struct link_map *) map;
+                   }
+                 break;
+               }
+             /* FALLTHROUGH */
+           case STB_GLOBAL:
+             /* Global definition.  Just what we need.  */
+             result->s = sym;
+             result->m = (struct link_map *) map;
+             return 1;
+
+           case STB_GNU_UNIQUE:;
+             do_lookup_unique (undef_name, new_hash, (struct link_map *) map,
+                               result, type_class, sym, strtab, ref,
+                               undef_map, flags);
+             return 1;
+
+           default:
+             /* Local symbols are ignored.  */
+             break;
+           }
+       }
+
+skip:
+      ;
+    }
+  while (++i < n);
+
+  /* We have not found anything until now.  */
+  return 0;
 }
 
 
-/* This function works like _dl_lookup_symbol but it takes an
-   additional arguement with the version number of the requested
-   symbol.
+static uint32_t
+dl_new_hash (const char *s)
+{
+  uint32_t h = 5381;
+  for (unsigned char c = *s; c != '\0'; c = *++s)
+    h = h * 33 + c;
+  return h;
+}
 
-   XXX We'll see whether we need this separate function.  */
-lookup_t
-internal_function
-_dl_lookup_versioned_symbol (const char *undef_name,
-                            struct link_map *undef_map, const ElfW(Sym) **ref,
-                            struct r_scope_elem *symbol_scope[],
-                            const struct r_found_version *version,
-                            int type_class, int flags)
+
+/* Add extra dependency on MAP to UNDEF_MAP.  */
+static int
+add_dependency (struct link_map *undef_map, struct link_map *map, int flags)
 {
-  const unsigned long int hash = _dl_elf_hash (undef_name);
-  struct sym_val current_value = { NULL, NULL };
-  struct r_scope_elem **scope;
-  int protected;
+  struct link_map *runp;
+  unsigned int i;
+  int result = 0;
 
-  bump_num_relocations ();
+  /* Avoid self-references and references to objects which cannot be
+     unloaded anyway.  */
+  if (undef_map == map)
+    return 0;
 
-  /* No other flag than DL_LOOKUP_ADD_DEPENDENCY is allowed.  */
-  assert (flags == 0 || flags == DL_LOOKUP_ADD_DEPENDENCY);
+  /* Avoid references to objects which cannot be unloaded anyway.  We
+     do not need to record dependencies if this object goes away
+     during dlopen failure, either.  IFUNC resolvers with relocation
+     dependencies may pick an dependency which can be dlclose'd, but
+     such IFUNC resolvers are undefined anyway.  */
+  assert (map->l_type == lt_loaded);
+  if (is_nodelete (map, flags))
+    return 0;
 
-  /* Search the relevant loaded objects for a definition.  */
-  for (scope = symbol_scope; *scope; ++scope)
-    {
-      int res = do_lookup_versioned (undef_name, hash, *ref, &current_value,
-                                    *scope, 0, version, NULL, type_class);
-      if (res > 0)
-       break;
+  struct link_map_reldeps *l_reldeps
+    = atomic_forced_read (undef_map->l_reldeps);
 
-      if (__builtin_expect (res, 0) < 0)
-       {
-         /* Oh, oh.  The file named in the relocation entry does not
-            contain the needed symbol.  */
-         const char *reference_name = undef_map ? undef_map->l_name : NULL;
+  /* Make sure l_reldeps is read before l_initfini.  */
+  atomic_read_barrier ();
 
-         /* XXX We cannot translate the message.  */
-         _dl_signal_cerror (0, (reference_name[0]
-                                ? reference_name
-                                : (rtld_progname ?: "<main program>")),
-                            N_("relocation error"),
-                            make_string ("symbol ", undef_name, ", version ",
-                                         version->name,
-                                         " not defined in file ",
-                                         version->filename,
-                                         " with link time reference",
-                                         res == -2
-                                         ? " (no version symbols)" : ""));
-         *ref = NULL;
+  /* Determine whether UNDEF_MAP already has a reference to MAP.  First
+     look in the normal dependencies.  */
+  struct link_map **l_initfini = atomic_forced_read (undef_map->l_initfini);
+  if (l_initfini != NULL)
+    {
+      for (i = 0; l_initfini[i] != NULL; ++i)
+       if (l_initfini[i] == map)
          return 0;
-       }
     }
 
-  if (__builtin_expect (current_value.s == NULL, 0))
+  /* No normal dependency.  See whether we already had to add it
+     to the special list of dynamic dependencies.  */
+  unsigned int l_reldepsact = 0;
+  if (l_reldeps != NULL)
     {
-      if (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK)
+      struct link_map **list = &l_reldeps->list[0];
+      l_reldepsact = l_reldeps->act;
+      for (i = 0; i < l_reldepsact; ++i)
+       if (list[i] == map)
+         return 0;
+    }
+
+  /* Save serial number of the target MAP.  */
+  unsigned long long serial = map->l_serial;
+
+  /* Make sure nobody can unload the object while we are at it.  */
+  if (__glibc_unlikely (flags & DL_LOOKUP_GSCOPE_LOCK))
+    {
+      /* We can't just call __rtld_lock_lock_recursive (GL(dl_load_lock))
+        here, that can result in ABBA deadlock.  */
+      THREAD_GSCOPE_RESET_FLAG ();
+      __rtld_lock_lock_recursive (GL(dl_load_lock));
+      /* While MAP value won't change, after THREAD_GSCOPE_RESET_FLAG ()
+        it can e.g. point to unallocated memory.  So avoid the optimizer
+        treating the above read from MAP->l_serial as ensurance it
+        can safely dereference it.  */
+      map = atomic_forced_read (map);
+
+      /* From this point on it is unsafe to dereference MAP, until it
+        has been found in one of the lists.  */
+
+      /* Redo the l_initfini check in case undef_map's l_initfini
+        changed in the mean time.  */
+      if (undef_map->l_initfini != l_initfini
+         && undef_map->l_initfini != NULL)
        {
-         /* We could find no value for a strong reference.  */
-         const char *reference_name = undef_map ? undef_map->l_name : NULL;
+         l_initfini = undef_map->l_initfini;
+         for (i = 0; l_initfini[i] != NULL; ++i)
+           if (l_initfini[i] == map)
+             goto out_check;
+       }
 
-         /* XXX We cannot translate the message.  */
-         _dl_signal_cerror (0, (reference_name[0]
-                                ? reference_name
-                                : (rtld_progname ?: "<main program>")), NULL,
-                            make_string (undefined_msg, undef_name,
-                                         ", version ",
-                                         version->name ?: NULL));
+      /* Redo the l_reldeps check if undef_map's l_reldeps changed in
+        the mean time.  */
+      if (undef_map->l_reldeps != NULL)
+       {
+         if (undef_map->l_reldeps != l_reldeps)
+           {
+             struct link_map **list = &undef_map->l_reldeps->list[0];
+             l_reldepsact = undef_map->l_reldeps->act;
+             for (i = 0; i < l_reldepsact; ++i)
+               if (list[i] == map)
+                 goto out_check;
+           }
+         else if (undef_map->l_reldeps->act > l_reldepsact)
+           {
+             struct link_map **list
+               = &undef_map->l_reldeps->list[0];
+             i = l_reldepsact;
+             l_reldepsact = undef_map->l_reldeps->act;
+             for (; i < l_reldepsact; ++i)
+               if (list[i] == map)
+                 goto out_check;
+           }
        }
-      *ref = NULL;
-      return 0;
     }
+  else
+    __rtld_lock_lock_recursive (GL(dl_load_lock));
 
-  protected = *ref && ELFW(ST_VISIBILITY) ((*ref)->st_other) == STV_PROTECTED;
+  /* The object is not yet in the dependency list.  Before we add
+     it make sure just one more time the object we are about to
+     reference is still available.  There is a brief period in
+     which the object could have been removed since we found the
+     definition.  */
+  runp = GL(dl_ns)[undef_map->l_ns]._ns_loaded;
+  while (runp != NULL && runp != map)
+    runp = runp->l_next;
 
-  if (__builtin_expect (protected != 0, 0))
+  if (runp != NULL)
     {
-      /* It is very tricky.  We need to figure out what value to
-         return for the protected symbol.  */
-      struct sym_val protected_value = { NULL, NULL };
+      /* The object is still available.  */
+
+      /* MAP could have been dlclosed, freed and then some other dlopened
+        library could have the same link_map pointer.  */
+      if (map->l_serial != serial)
+       goto out_check;
+
+      /* Redo the NODELETE check, as when dl_load_lock wasn't held
+        yet this could have changed.  */
+      if (is_nodelete (map, flags))
+       goto out;
+
+      /* If the object with the undefined reference cannot be removed ever
+        just make sure the same is true for the object which contains the
+        definition.  */
+      if (undef_map->l_type != lt_loaded || is_nodelete (map, flags))
+       {
+         if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_BINDINGS)
+             && !is_nodelete (map, flags))
+           {
+             if (undef_map->l_name[0] == '\0')
+               _dl_debug_printf ("\
+marking %s [%lu] as NODELETE due to reference to main program\n",
+                                 map->l_name, map->l_ns);
+             else
+               _dl_debug_printf ("\
+marking %s [%lu] as NODELETE due to reference to %s [%lu]\n",
+                                 map->l_name, map->l_ns,
+                                 undef_map->l_name, undef_map->l_ns);
+           }
+         mark_nodelete (map, flags);
+         goto out;
+       }
 
-      for (scope = symbol_scope; *scope; ++scope)
-       if (_dl_do_lookup_versioned (undef_name, hash, *ref, &protected_value,
-                                    *scope, 0, version, NULL,
-                                    ELF_RTYPE_CLASS_PLT))
-         break;
+      /* Add the reference now.  */
+      if (__glibc_unlikely (l_reldepsact >= undef_map->l_reldepsmax))
+       {
+         /* Allocate more memory for the dependency list.  Since this
+            can never happen during the startup phase we can use
+            `realloc'.  */
+         struct link_map_reldeps *newp;
+         unsigned int max
+           = undef_map->l_reldepsmax ? undef_map->l_reldepsmax * 2 : 10;
 
-      if (protected_value.s != NULL && protected_value.m != undef_map)
+#ifdef RTLD_PREPARE_FOREIGN_CALL
+         RTLD_PREPARE_FOREIGN_CALL;
+#endif
+
+         newp = malloc (sizeof (*newp) + max * sizeof (struct link_map *));
+         if (newp == NULL)
+           {
+             /* If we didn't manage to allocate memory for the list this is
+                no fatal problem.  We simply make sure the referenced object
+                cannot be unloaded.  This is semantically the correct
+                behavior.  */
+             if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_BINDINGS)
+                 && !is_nodelete (map, flags))
+               _dl_debug_printf ("\
+marking %s [%lu] as NODELETE due to memory allocation failure\n",
+                                 map->l_name, map->l_ns);
+             /* In case of non-lazy binding, we could actually report
+                the memory allocation error, but for now, we use the
+                conservative approximation as well.  */
+             mark_nodelete (map, flags);
+             goto out;
+           }
+         else
+           {
+             if (l_reldepsact)
+               memcpy (&newp->list[0], &undef_map->l_reldeps->list[0],
+                       l_reldepsact * sizeof (struct link_map *));
+             newp->list[l_reldepsact] = map;
+             newp->act = l_reldepsact + 1;
+             atomic_write_barrier ();
+             void *old = undef_map->l_reldeps;
+             undef_map->l_reldeps = newp;
+             undef_map->l_reldepsmax = max;
+             if (old)
+               _dl_scope_free (old);
+           }
+       }
+      else
        {
-         current_value.s = *ref;
-         current_value.m = undef_map;
+         undef_map->l_reldeps->list[l_reldepsact] = map;
+         atomic_write_barrier ();
+         undef_map->l_reldeps->act = l_reldepsact + 1;
        }
+
+      /* Display information if we are debugging.  */
+      if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
+       _dl_debug_printf ("\
+\nfile=%s [%lu];  needed by %s [%lu] (relocation dependency)\n\n",
+                         DSO_FILENAME (map->l_name),
+                         map->l_ns,
+                         DSO_FILENAME (undef_map->l_name),
+                         undef_map->l_ns);
     }
+  else
+    /* Whoa, that was bad luck.  We have to search again.  */
+    result = -1;
 
-  /* We have to check whether this would bind UNDEF_MAP to an object
-     in the global scope which was dynamically loaded.  In this case
-     we have to prevent the latter from being unloaded unless the
-     UNDEF_MAP object is also unloaded.  */
-  if (__builtin_expect (current_value.m->l_type == lt_loaded, 0)
-      /* Don't do this for explicit lookups as opposed to implicit
-        runtime lookups.  */
-      && flags != 0
-      /* Add UNDEF_MAP to the dependencies.  */
-      && add_dependency (undef_map, current_value.m) < 0)
-      /* Something went wrong.  Perhaps the object we tried to reference
-        was just removed.  Try finding another definition.  */
-      return INTUSE(_dl_lookup_versioned_symbol) (undef_name, undef_map,
-                                                 ref, symbol_scope,
-                                                 version, type_class, flags);
+ out:
+  /* Release the lock.  */
+  __rtld_lock_unlock_recursive (GL(dl_load_lock));
 
-  if (__builtin_expect (GL(dl_debug_mask)
-                       & (DL_DEBUG_BINDINGS|DL_DEBUG_PRELINK), 0))
-    _dl_debug_bindings (undef_name, undef_map, ref, symbol_scope,
-                       &current_value, version, type_class, protected);
+  if (__glibc_unlikely (flags & DL_LOOKUP_GSCOPE_LOCK))
+    THREAD_GSCOPE_SET_FLAG ();
 
-  *ref = current_value.s;
-  return LOOKUP_VALUE (current_value.m);
+  return result;
+
+ out_check:
+  if (map->l_serial != serial)
+    result = -1;
+  goto out;
 }
-INTDEF (_dl_lookup_versioned_symbol)
 
 
-/* Similar to _dl_lookup_symbol_skip but takes an additional argument
-   with the version we are looking for.  */
+/* Search loaded objects' symbol tables for a definition of the symbol
+   UNDEF_NAME, perhaps with a requested version for the symbol.
+
+   We must never have calls to the audit functions inside this function
+   or in any function which gets called.  If this would happen the audit
+   code might create a thread which can throw off all the scope locking.  */
 lookup_t
-internal_function
-_dl_lookup_versioned_symbol_skip (const char *undef_name,
-                                 struct link_map *undef_map,
-                                 const ElfW(Sym) **ref,
-                                 struct r_scope_elem *symbol_scope[],
-                                 const struct r_found_version *version,
-                                 struct link_map *skip_map)
+_dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map,
+                    const ElfW(Sym) **ref,
+                    struct r_scope_elem *symbol_scope[],
+                    const struct r_found_version *version,
+                    int type_class, int flags, struct link_map *skip_map)
 {
-  const char *reference_name = undef_map->l_name;
-  const unsigned long int hash = _dl_elf_hash (undef_name);
+  const unsigned int new_hash = dl_new_hash (undef_name);
+  unsigned long int old_hash = 0xffffffff;
   struct sym_val current_value = { NULL, NULL };
-  struct r_scope_elem **scope;
-  size_t i;
-  int protected;
+  struct r_scope_elem **scope = symbol_scope;
 
   bump_num_relocations ();
 
+  /* DL_LOOKUP_RETURN_NEWEST does not make sense for versioned
+     lookups.  */
+  assert (version == NULL || !(flags & DL_LOOKUP_RETURN_NEWEST));
+
+  size_t i = 0;
+  if (__glibc_unlikely (skip_map != NULL))
+    /* Search the relevant loaded objects for a definition.  */
+    while ((*scope)->r_list[i] != skip_map)
+      ++i;
+
   /* Search the relevant loaded objects for a definition.  */
-  scope = symbol_scope;
-  for (i = 0; (*scope)->r_list[i] != skip_map; ++i)
-    assert (i < (*scope)->r_nlist);
-
-  if (! _dl_do_lookup_versioned (undef_name, hash, *ref, &current_value,
-                                *scope, i, version, skip_map, 0))
-    while (*++scope)
-      if (_dl_do_lookup_versioned (undef_name, hash, *ref, &current_value,
-                                  *scope, 0, version, skip_map, 0))
-       break;
-
-  if (__builtin_expect (current_value.s == NULL, 0))
+  for (size_t start = i; *scope != NULL; start = 0, ++scope)
+    if (do_lookup_x (undef_name, new_hash, &old_hash, *ref,
+                    &current_value, *scope, start, version, flags,
+                    skip_map, type_class, undef_map) != 0)
+      break;
+
+  if (__glibc_unlikely (current_value.s == NULL))
     {
-      if (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK)
+      if ((*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK)
+         && !(GLRO(dl_debug_mask) & DL_DEBUG_UNUSED))
        {
          /* We could find no value for a strong reference.  */
-         const size_t len = strlen (undef_name);
-         char buf[sizeof undefined_msg + len];
-         __mempcpy (__mempcpy (buf, undefined_msg, sizeof undefined_msg - 1),
-                    undef_name, len + 1);
-         /* XXX We cannot translate the messages.  */
-         _dl_signal_cerror (0, (reference_name[0]
-                                ? reference_name
-                                : (rtld_progname ?: "<main program>")),
-                            NULL, buf);
+         const char *reference_name = undef_map ? undef_map->l_name : "";
+         const char *versionstr = version ? ", version " : "";
+         const char *versionname = (version && version->name
+                                    ? version->name : "");
+         struct dl_exception exception;
+         /* XXX We cannot translate the message.  */
+         _dl_exception_create_format
+           (&exception, DSO_FILENAME (reference_name),
+            "undefined symbol: %s%s%s",
+            undef_name, versionstr, versionname);
+         _dl_signal_cexception (0, &exception, N_("symbol lookup error"));
+         _dl_exception_free (&exception);
        }
       *ref = NULL;
       return 0;
     }
 
-  protected = *ref && ELFW(ST_VISIBILITY) ((*ref)->st_other) == STV_PROTECTED;
-
-  if (__builtin_expect (protected != 0, 0))
+  int protected = (*ref
+                  && ELFW(ST_VISIBILITY) ((*ref)->st_other) == STV_PROTECTED);
+  if (__glibc_unlikely (protected != 0))
     {
       /* It is very tricky.  We need to figure out what value to
-         return for the protected symbol.  */
-      struct sym_val protected_value = { NULL, NULL };
-
-      if (i >= (*scope)->r_nlist
-         || !_dl_do_lookup_versioned (undef_name, hash, *ref,
-                                      &protected_value, *scope, i, version,
-                                      skip_map, ELF_RTYPE_CLASS_PLT))
-       while (*++scope)
-         if (_dl_do_lookup_versioned (undef_name, hash, *ref,
-                                      &protected_value, *scope, 0, version,
-                                      skip_map, ELF_RTYPE_CLASS_PLT))
-           break;
-
-      if (protected_value.s != NULL && protected_value.m != undef_map)
+        return for the protected symbol.  */
+      if (type_class == ELF_RTYPE_CLASS_PLT)
        {
-         current_value.s = *ref;
-         current_value.m = undef_map;
+         if (current_value.s != NULL && current_value.m != undef_map)
+           {
+             current_value.s = *ref;
+             current_value.m = undef_map;
+           }
        }
-    }
-
-  if (__builtin_expect (GL(dl_debug_mask)
-                       & (DL_DEBUG_BINDINGS|DL_DEBUG_PRELINK), 0))
-    _dl_debug_bindings (undef_name, undef_map, ref, symbol_scope,
-                       &current_value, version, 0, protected);
-
-  *ref = current_value.s;
-  return LOOKUP_VALUE (current_value.m);
-}
-
-
-/* Cache the location of MAP's hash table.  */
-
-void
-internal_function
-_dl_setup_hash (struct link_map *map)
-{
-  Elf_Symndx *hash;
-  Elf_Symndx nchain;
-
-  if (!map->l_info[DT_HASH])
-    return;
-  hash = (void *)(map->l_addr + map->l_info[DT_HASH]->d_un.d_ptr);
-
-  map->l_nbuckets = *hash++;
-  nchain = *hash++;
-  map->l_buckets = hash;
-  hash += map->l_nbuckets;
-  map->l_chain = hash;
-}
-
-
-static void
-internal_function
-_dl_debug_bindings (const char *undef_name, struct link_map *undef_map,
-                   const ElfW(Sym) **ref, struct r_scope_elem *symbol_scope[],
-                   struct sym_val *value,
-                   const struct r_found_version *version, int type_class,
-                   int protected)
-{
-  const char *reference_name = undef_map->l_name;
-
-  if (GL(dl_debug_mask) & DL_DEBUG_BINDINGS)
-    {
-      INTUSE(_dl_debug_printf) ("binding file %s to %s: %s symbol `%s'",
-                               (reference_name[0]
-                                ? reference_name
-                                : (rtld_progname ?: "<main program>")),
-                               value->m->l_name[0]
-                               ? value->m->l_name : rtld_progname,
-                               protected ? "protected" : "normal",
-                               undef_name);
-      if (version)
-       _dl_debug_printf_c (" [%s]\n", version->name);
       else
-       _dl_debug_printf_c ("\n");
-    }
-#ifdef SHARED
-  if (GL(dl_debug_mask) & DL_DEBUG_PRELINK)
-    {
-      int conflict = 0;
-      struct sym_val val = { NULL, NULL };
-
-      if ((GL(dl_trace_prelink_map) == NULL
-          || GL(dl_trace_prelink_map) == GL(dl_loaded))
-         && undef_map != GL(dl_loaded))
-       {
-         const unsigned long int hash = _dl_elf_hash (undef_name);
-
-         if (version == 0)
-           _dl_do_lookup (undef_name, hash, *ref, &val,
-                          undef_map->l_local_scope[0], 0, 0, NULL,
-                          type_class);
-         else
-           _dl_do_lookup_versioned (undef_name, hash, *ref, &val,
-                                    undef_map->l_local_scope[0], 0, version,
-                                    NULL, type_class);
-
-         if (val.s != value->s || val.m != value->m)
-           conflict = 1;
-       }
-
-      if (conflict
-         || GL(dl_trace_prelink_map) == undef_map
-         || GL(dl_trace_prelink_map) == NULL)
        {
-         _dl_printf ("%s 0x%0*Zx 0x%0*Zx -> 0x%0*Zx 0x%0*Zx ",
-                     conflict ? "conflict" : "lookup",
-                     (int) sizeof (ElfW(Addr)) * 2, undef_map->l_map_start,
-                     (int) sizeof (ElfW(Addr)) * 2,
-                     ((ElfW(Addr)) *ref) - undef_map->l_map_start,
-                     (int) sizeof (ElfW(Addr)) * 2,
-                     (ElfW(Addr)) (value->s ? value->m->l_map_start : 0),
-                     (int) sizeof (ElfW(Addr)) * 2,
-                     (ElfW(Addr)) (value->s ? value->s->st_value : 0));
-
-         if (conflict)
-           _dl_printf ("x 0x%0*Zx 0x%0*Zx ",
-                       (int) sizeof (ElfW(Addr)) * 2,
-                       (ElfW(Addr)) (val.s ? val.m->l_map_start : 0),
-                       (int) sizeof (ElfW(Addr)) * 2,
-                       (ElfW(Addr)) (val.s ? val.s->st_value : 0));
-
-         _dl_printf ("/%x %s\n", type_class, undef_name);
+         struct sym_val protected_value = { NULL, NULL };
+
+         for (scope = symbol_scope; *scope != NULL; i = 0, ++scope)
+           if (do_lookup_x (undef_name, new_hash, &old_hash, *ref,
+                            &protected_value, *scope, i, version, flags,
+                            skip_map,
+                            (ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA
+                             && ELFW(ST_TYPE) ((*ref)->st_info) == STT_OBJECT
+                             && type_class == ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA)
+                            ? ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA
+                            : ELF_RTYPE_CLASS_PLT, NULL) != 0)
+             break;
+
+         if (protected_value.s != NULL && protected_value.m != undef_map)
+           {
+             current_value.s = *ref;
+             current_value.m = undef_map;
+           }
        }
     }
-#endif
-}
 
-/* These are here so that we only inline do_lookup{,_versioned} in the common
-   case, not everywhere.  */
-static int __attribute_noinline__
-internal_function
-_dl_do_lookup (const char *undef_name, unsigned long int hash,
-              const ElfW(Sym) *ref, struct sym_val *result,
-              struct r_scope_elem *scope, size_t i, int flags,
-              struct link_map *skip, int type_class)
-{
-  return do_lookup (undef_name, hash, ref, result, scope, i, flags, skip,
-                   type_class);
-}
+  /* We have to check whether this would bind UNDEF_MAP to an object
+     in the global scope which was dynamically loaded.  In this case
+     we have to prevent the latter from being unloaded unless the
+     UNDEF_MAP object is also unloaded.  */
+  if (__glibc_unlikely (current_value.m->l_type == lt_loaded)
+      /* Don't do this for explicit lookups as opposed to implicit
+        runtime lookups.  */
+      && (flags & DL_LOOKUP_ADD_DEPENDENCY) != 0
+      /* Add UNDEF_MAP to the dependencies.  */
+      && add_dependency (undef_map, current_value.m, flags) < 0)
+      /* Something went wrong.  Perhaps the object we tried to reference
+        was just removed.  Try finding another definition.  */
+      return _dl_lookup_symbol_x (undef_name, undef_map, ref,
+                                 (flags & DL_LOOKUP_GSCOPE_LOCK)
+                                 ? undef_map->l_scope : symbol_scope,
+                                 version, type_class, flags, skip_map);
 
-static int __attribute_noinline__
-internal_function
-_dl_do_lookup_versioned (const char *undef_name, unsigned long int hash,
-                        const ElfW(Sym) *ref, struct sym_val *result,
-                        struct r_scope_elem *scope, size_t i,
-                        const struct r_found_version *const version,
-                        struct link_map *skip, int type_class)
-{
-  return do_lookup_versioned (undef_name, hash, ref, result, scope, i,
-                             version, skip, type_class);
+  /* The object is used.  */
+  if (__glibc_unlikely (current_value.m->l_used == 0))
+    current_value.m->l_used = 1;
+
+  *ref = current_value.s;
+  return LOOKUP_VALUE (current_value.m);
 }