about summary refs log tree commit diff
path: root/elf
diff options
context:
space:
mode:
Diffstat (limited to 'elf')
-rw-r--r--elf/Versions1
-rw-r--r--elf/dl-close.c35
-rw-r--r--elf/dl-deps.c19
-rw-r--r--elf/dl-error.c83
-rw-r--r--elf/dl-init.c10
-rw-r--r--elf/dl-load.c41
-rw-r--r--elf/dl-lookup.c30
-rw-r--r--elf/dl-object.c42
-rw-r--r--elf/dl-open.c106
-rw-r--r--elf/dl-reloc.c4
-rw-r--r--elf/dl-runtime.c77
-rw-r--r--elf/dl-support.c14
-rw-r--r--elf/dl-symbol.c4
-rw-r--r--elf/dl-version.c6
-rw-r--r--elf/dlfcn.h17
-rw-r--r--elf/dlsym.c16
-rw-r--r--elf/dlvsym.c21
-rw-r--r--elf/ldsodefs.h54
-rw-r--r--elf/link.h41
-rw-r--r--elf/rtld.c115
20 files changed, 387 insertions, 349 deletions
diff --git a/elf/Versions b/elf/Versions
index ed5ba6f830..29795cef4c 100644
--- a/elf/Versions
+++ b/elf/Versions
@@ -19,6 +19,7 @@ libc {
   GLIBC_2.1 {
     # global variables
     _dl_profile; _dl_profile_map; _dl_profile_output; _dl_start_profile;
+    _dl_loaded; _dl_main_searchlist;
 
     # functions used in other libraries
     _dl_mcount; _dl_mcount_wrapper; _dl_mcount_wrapper_check; _dl_unload_cache;
diff --git a/elf/dl-close.c b/elf/dl-close.c
index 3b1e3c9f57..a260539440 100644
--- a/elf/dl-close.c
+++ b/elf/dl-close.c
@@ -57,8 +57,8 @@ _dl_close (struct link_map *map)
       return;
     }
 
-  list = map->l_searchlist;
-  nsearchlist = map->l_nsearchlist;
+  list = map->l_searchlist.r_list;
+  nsearchlist = map->l_searchlist.r_nlist;
 
   /* Call all termination functions at once.  */
   for (i = 0; i < nsearchlist; ++i)
@@ -103,16 +103,18 @@ _dl_close (struct link_map *map)
 	  if (imap->l_global)
 	    {
 	      /* This object is in the global scope list.  Remove it.  */
-	      struct link_map **tail = _dl_global_scope_end;
+	      unsigned int cnt = _dl_main_searchlist->r_nlist;
+
 	      do
-		--tail;
-	      while (*tail != imap);
-	      while (tail < _dl_global_scope_end)
+		--cnt;
+	      while (_dl_main_searchlist->r_list[cnt] != imap);
+	      while (cnt < _dl_main_searchlist->r_nlist)
 		{
-		  tail[0] = tail[1];
-		  ++tail;
+		  _dl_main_searchlist->r_list[0]
+		    = _dl_main_searchlist->r_list[1];
+		  ++cnt;
 		}
-	      --_dl_global_scope_end;
+	      --_dl_main_searchlist->r_nlist;
 	    }
 
 	  /* We can unmap all the maps at once.  We determined the
@@ -135,8 +137,6 @@ _dl_close (struct link_map *map)
 #endif
 	  if (imap->l_next)
 	    imap->l_next->l_prev = imap->l_prev;
-	  if (imap->l_searchlist && imap->l_searchlist != list)
-	    free (imap->l_searchlist);
 
 	  if (imap->l_versions != NULL)
 	    free (imap->l_versions);
@@ -156,15 +156,14 @@ _dl_close (struct link_map *map)
 	  while (lnp != NULL);
 
 	  /* Remove the searchlists.  */
-	  if (imap->l_dupsearchlist != imap->l_searchlist)
+	  if (imap->l_searchlist.r_duplist != imap->l_searchlist.r_list)
 	    {
-	      /* If a l_searchlist object exists there always also is
-		 a l_dupsearchlist object.  */
-	      assert (imap->l_dupsearchlist != NULL);
-	      free (imap->l_dupsearchlist);
+	      /* If a r_list exists there always also is a r_duplist.  */
+	      assert (imap->l_searchlist.r_list != NULL);
+	      free (imap->l_searchlist.r_duplist);
 	    }
-	  if (imap != map && imap->l_searchlist != NULL)
-	    free (imap->l_searchlist);
+	  if (imap != map && imap->l_searchlist.r_list != NULL)
+	    free (imap->l_searchlist.r_list);
 
 	  free (imap);
 	}
diff --git a/elf/dl-deps.c b/elf/dl-deps.c
index 21dcf0dd41..f08f652642 100644
--- a/elf/dl-deps.c
+++ b/elf/dl-deps.c
@@ -372,32 +372,33 @@ _dl_map_object_deps (struct link_map *map,
 
   /* Store the search list we built in the object.  It will be used for
      searches in the scope of this object.  */
-  map->l_searchlist = malloc (nlist * sizeof (struct link_map *));
-  if (map->l_searchlist == NULL)
+  map->l_searchlist.r_list = malloc (nlist * sizeof (struct link_map *));
+  if (map->l_searchlist.r_list == NULL)
     _dl_signal_error (ENOMEM, map->l_name,
 		      "cannot allocate symbol search list");
-  map->l_nsearchlist = nlist;
+  map->l_searchlist.r_nlist = nlist;
 
   for (nlist = 0, runp = known; runp; runp = runp->unique)
     {
-      map->l_searchlist[nlist++] = runp->map;
+      map->l_searchlist.r_list[nlist++] = runp->map;
 
       /* Now clear all the mark bits we set in the objects on the search list
 	 to avoid duplicates, so the next call starts fresh.  */
       runp->map->l_reserved = 0;
     }
 
-  map->l_ndupsearchlist = nduplist;
+  map->l_searchlist.r_nduplist = nduplist;
   if (nlist == nduplist)
-    map->l_dupsearchlist = map->l_searchlist;
+    map->l_searchlist.r_duplist = map->l_searchlist.r_list;
   else
     {
-      map->l_dupsearchlist = malloc (nduplist * sizeof (struct link_map *));
-      if (map->l_dupsearchlist == NULL)
+      map->l_searchlist.r_duplist = malloc (nduplist
+					    * sizeof (struct link_map *));
+      if (map->l_searchlist.r_duplist == NULL)
 	_dl_signal_error (ENOMEM, map->l_name,
 			  "cannot allocate symbol search list");
 
       for (nlist = 0, runp = known; runp; runp = runp->dup)
-	map->l_dupsearchlist[nlist++] = runp->map;
+	map->l_searchlist.r_duplist[nlist++] = runp->map;
     }
 }
diff --git a/elf/dl-error.c b/elf/dl-error.c
index 03292abaf4..cfb6b9ead5 100644
--- a/elf/dl-error.c
+++ b/elf/dl-error.c
@@ -22,6 +22,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <elf/ldsodefs.h>
+#include <bits/libc-lock.h>
 
 /* This structure communicates state between _dl_catch_error and
    _dl_signal_error.  */
@@ -31,14 +32,57 @@ struct catch
     jmp_buf env;		/* longjmp here on error.  */
   };
 
-/* This points to such a structure during a call to _dl_catch_error.
-   During implicit startup and run-time work for needed shared libraries,
-   this is null.  */
+/* Multiple threads at once can use the `_dl_catch_error' function.  The
+   calls can come from the `_dl_map_object_deps', `_dlerror_run', or from
+   any of the libc functionality which loads dynamic objects (NSS, iconv).
+   Therefore we have to be prepared to safe the state in thread-local
+   memory.  `catch' will only be used for the non-threaded case.
+
+   Please note the horrible kludge we have to use to check for the
+   thread functions to be defined.  The problem is that while running
+   ld.so standalone (i.e., before the relocation with the libc symbols
+   available) we do not have a real handling of undefined weak symbols.
+   All symbols are relocated, regardless of the availability.  They are
+   relocated relative to the load address of the dynamic linker.  Adding
+   this start address to zero (the value in the GOT for undefined symbols)
+   leads to an address which is the load address of ld.so.  Once we have
+   relocated with the libc values the value is NULL if the function is
+   not available.  Our "solution" is to regard NULL and the ld.so load
+   address as indicators for unavailable weak symbols.   */
 static struct catch *catch;
 
+#ifdef PIC
+# define tsd_setspecific(data) \
+  if (__libc_internal_tsd_set != (void *) _dl_rtld_map.l_addr		      \
+      && __libc_internal_tsd_set != NULL)				      \
+    __libc_internal_tsd_set (_LIBC_TSD_KEY_DL_ERROR, data);		      \
+  else									      \
+    catch = (data)
+# define tsd_getspecific() \
+  (__libc_internal_tsd_set != (void *) _dl_rtld_map.l_addr		      \
+   && __libc_internal_tsd_set != NULL					      \
+   ? (struct catch *) __libc_internal_tsd_get (_LIBC_TSD_KEY_DL_ERROR)	      \
+   : catch)
+#else
+# define tsd_setspecific(data) \
+  if (__libc_internal_tsd_set != NULL)					      \
+    __libc_internal_tsd_set (_LIBC_TSD_KEY_DL_ERROR, data);		      \
+  else									      \
+    catch = (data)
+# define tsd_getspecific() \
+  (__libc_internal_tsd_set != NULL					      \
+   ? (struct catch *) __libc_internal_tsd_get (_LIBC_TSD_KEY_DL_ERROR)	      \
+   : catch)
+#endif
+
+
 /* This points to a function which is called when an error is
    received.  Unlike the handling of `catch' this function may return.
-   The arguments will be the `errstring' and `objname'.  */
+   The arguments will be the `errstring' and `objname'.
+
+   Since this functionality is not used in normal programs (only in ld.so)
+   we do not care about multi-threaded programs here.  We keep this as a
+   global variable.  */
 static receiver_fct receiver;
 
 
@@ -48,27 +92,30 @@ _dl_signal_error (int errcode,
 		  const char *objname,
 		  const char *errstring)
 {
+  struct catch *lcatch;
+
   if (! errstring)
     errstring = "DYNAMIC LINKER BUG!!!";
 
-  if (catch)
+  lcatch = tsd_getspecific ();
+  if (lcatch != NULL)
     {
       /* We are inside _dl_catch_error.  Return to it.  We have to
 	 duplicate the error string since it might be allocated on the
 	 stack.  */
       size_t objname_len = objname ? strlen (objname) + 2 : 0;
       size_t errstring_len = strlen (errstring) + 1;
-      catch->errstring = malloc (objname_len + errstring_len);
-      if (catch->errstring != NULL)
+      lcatch->errstring = malloc (objname_len + errstring_len);
+      if (lcatch->errstring != NULL)
 	{
 	  if (objname_len > 0)
 	    {
-	      memcpy (catch->errstring, objname, objname_len - 2);
-	      memcpy (catch->errstring + objname_len - 2, ": ", 2);
+	      memcpy (lcatch->errstring, objname, objname_len - 2);
+	      memcpy (lcatch->errstring + objname_len - 2, ": ", 2);
 	    }
-	  memcpy (catch->errstring + objname_len, errstring, errstring_len);
+	  memcpy (lcatch->errstring + objname_len, errstring, errstring_len);
 	}
-      longjmp (catch->env, errcode ?: -1);
+      longjmp (lcatch->env, errcode ?: -1);
     }
   else if (receiver)
     {
@@ -106,19 +153,19 @@ _dl_catch_error (char **errstring,
      inefficient.  So we initialize `c' by hand.  */
   c.errstring = NULL;
 
-  old = catch;
+  old = tsd_getspecific ();
   errcode = setjmp (c.env);
   if (errcode == 0)
     {
-      catch = &c;
+      tsd_setspecific (&c);
       (*operate) (args);
-      catch = old;
+      tsd_setspecific (old);
       *errstring = NULL;
       return 0;
     }
 
   /* We get here only if we longjmp'd out of OPERATE.  */
-  catch = old;
+  tsd_setspecific (old);
   *errstring = c.errstring;
   return errcode == -1 ? 0 : errcode;
 }
@@ -130,15 +177,15 @@ _dl_receive_error (receiver_fct fct, void (*operate) (void *), void *args)
   struct catch *old_catch;
   receiver_fct old_receiver;
 
-  old_catch = catch;
+  old_catch = tsd_getspecific ();
   old_receiver = receiver;
 
   /* Set the new values.  */
-  catch = NULL;
+  tsd_setspecific (NULL);
   receiver = fct;
 
   (*operate) (args);
 
-  catch = old_catch;
+  tsd_setspecific (old_catch);
   receiver = old_receiver;
 }
diff --git a/elf/dl-init.c b/elf/dl-init.c
index f93f8f1cb5..64aa1ce0bf 100644
--- a/elf/dl-init.c
+++ b/elf/dl-init.c
@@ -26,7 +26,7 @@
 
 ElfW(Addr)
 internal_function
-_dl_init_next (struct link_map *map)
+_dl_init_next (struct r_scope_elem *searchlist)
 {
   unsigned int i;
 
@@ -34,10 +34,10 @@ _dl_init_next (struct link_map *map)
      dependency order, so processing that list from back to front gets us
      breadth-first leaf-to-root order.  */
 
-  i = map->l_nsearchlist;
+  i = searchlist->r_nlist;
   while (i-- > 0)
     {
-      struct link_map *l = map->l_searchlist[i];
+      struct link_map *l = searchlist->r_list[i];
 
       if (l->l_init_called)
 	/* This object is all done.  */
@@ -53,8 +53,8 @@ _dl_init_next (struct link_map *map)
 	  continue;
 	}
 
-      if (l->l_info[DT_INIT] &&
-	  !(l->l_name[0] == '\0' && l->l_type == lt_executable))
+      if (l->l_info[DT_INIT]
+	  && (l->l_name[0] != '\0' || l->l_type != lt_executable))
 	{
 	  /* Run this object's initializer.  */
 	  l->l_init_running = 1;
diff --git a/elf/dl-load.c b/elf/dl-load.c
index c9a39c01a2..35c34e0305 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -529,7 +529,12 @@ _dl_init_paths (const char *llp)
   l = _dl_loaded;
   if (l != NULL)
     {
-      if (l->l_type != lt_loaded && l->l_info[DT_RPATH])
+      /* We should never get here when initializing in a static application.
+	 If this is a dynamically linked application _dl_loaded always
+	 points to the main map which is not dlopen()ed.  */
+      assert (l->l_type != lt_loaded);
+
+      if (l->l_info[DT_RPATH])
 	{
 	  /* Allocate room for the search path and fill in information
 	     from RPATH.  */
@@ -727,11 +732,10 @@ _dl_map_object_from_fd (const char *name, int fd, char *realname,
 #endif
 
   /* Enter the new object in the list of loaded objects.  */
-  l = _dl_new_object (realname, name, l_type);
+  l = _dl_new_object (realname, name, l_type, loader);
   if (! l)
     lose (ENOMEM, "cannot create shared object descriptor");
   l->l_opencount = 1;
-  l->l_loader = loader;
 
   /* Extract the remaining details we need from the ELF header
      and then read in the program header table.  */
@@ -973,6 +977,35 @@ _dl_map_object_from_fd (const char *name, int fd, char *realname,
   if (l->l_info[DT_HASH])
     _dl_setup_hash (l);
 
+  /* If this object has DT_SYMBOLIC set modify now its scope.  We don't
+     have to do this for the main map.  */
+  if (l->l_info[DT_SYMBOLIC] && &l->l_searchlist != l->l_scope[0])
+    {
+      /* Create an appropriate searchlist.  It contains only this map.
+
+	 XXX This is the definition of DT_SYMBOLIC in SysVr4.  The old
+	 GNU ld.so implementation had a different interpretation which
+	 is more reasonable.  We are prepared to add this possibility
+	 back as part of a GNU extension of the ELF format.  */
+      l->l_symbolic_searchlist.r_list =
+	(struct link_map **) malloc (sizeof (struct link_map *));
+
+      if (l->l_symbolic_searchlist.r_list == NULL)
+	lose (ENOMEM, "cannot create searchlist");
+
+      l->l_symbolic_searchlist.r_list[0] = l;
+      l->l_symbolic_searchlist.r_nlist = 1;
+      l->l_symbolic_searchlist.r_duplist = l->l_symbolic_searchlist.r_list;
+      l->l_symbolic_searchlist.r_nduplist = 1;
+
+      /* Now move the existing entries one back.  */
+      memmove (&l->l_scope[1], &l->l_scope[0],
+	       3 * sizeof (struct r_scope_elem *));
+
+      /* Now add the new entry.  */
+      l->l_scope[0] = &l->l_symbolic_searchlist;
+    }
+
   return l;
 }
 
@@ -1280,7 +1313,7 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded,
 
 	  /* Enter the new object in the list of loaded objects.  */
 	  if ((name_copy = local_strdup (name)) == NULL
-	      || (l = _dl_new_object (name_copy, name, type)) == NULL)
+	      || (l = _dl_new_object (name_copy, name, type, loader)) == NULL)
 	    _dl_signal_error (ENOMEM, name,
 			      "cannot create shared object descriptor");
 	  /* We use an opencount of 0 as a sign for the faked entry.  */
diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
index 7badf86328..5245c628d2 100644
--- a/elf/dl-lookup.c
+++ b/elf/dl-lookup.c
@@ -63,12 +63,12 @@ struct sym_val
 static inline int
 do_lookup (const char *undef_name, unsigned long int hash,
 	   const ElfW(Sym) *ref, struct sym_val *result,
-	   struct link_map *scope, size_t i, const char *reference_name,
+	   struct r_scope_elem *scope, size_t i, const char *reference_name,
 	   const struct r_found_version *version, struct link_map *skip,
 	   int reloc_type)
 {
-  struct link_map **list = scope->l_searchlist;
-  size_t n = scope->l_nsearchlist;
+  struct link_map **list = scope->r_list;
+  size_t n = scope->r_nlist;
   struct link_map *map;
 
   for (; i < n; ++i)
@@ -212,13 +212,13 @@ do_lookup (const char *undef_name, unsigned long int hash,
 ElfW(Addr)
 internal_function
 _dl_lookup_symbol (const char *undef_name, const ElfW(Sym) **ref,
-		   struct link_map *symbol_scope[],
+		   struct r_scope_elem *symbol_scope[],
 		   const char *reference_name,
 		   int reloc_type)
 {
   const unsigned long int hash = _dl_elf_hash (undef_name);
   struct sym_val current_value = { NULL, NULL };
-  struct link_map **scope;
+  struct r_scope_elem **scope;
 
   /* Search the relevant loaded objects for a definition.  */
   for (scope = symbol_scope; *scope; ++scope)
@@ -260,19 +260,19 @@ _dl_lookup_symbol (const char *undef_name, const ElfW(Sym) **ref,
 ElfW(Addr)
 internal_function
 _dl_lookup_symbol_skip (const char *undef_name, const ElfW(Sym) **ref,
-			struct link_map *symbol_scope[],
+			struct r_scope_elem *symbol_scope[],
 			const char *reference_name,
 			struct link_map *skip_map)
 {
   const unsigned long int hash = _dl_elf_hash (undef_name);
   struct sym_val current_value = { NULL, NULL };
-  struct link_map **scope;
+  struct r_scope_elem **scope;
   size_t i;
 
   /* 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);
+  for (i = 0; (*scope)->r_duplist[i] != skip_map; ++i)
+    assert (i < (*scope)->r_nduplist);
 
   if (! do_lookup (undef_name, hash, *ref, &current_value,
 		   *scope, i, reference_name, NULL, skip_map, 0))
@@ -309,14 +309,14 @@ _dl_lookup_symbol_skip (const char *undef_name, const ElfW(Sym) **ref,
 ElfW(Addr)
 internal_function
 _dl_lookup_versioned_symbol (const char *undef_name, const ElfW(Sym) **ref,
-			     struct link_map *symbol_scope[],
+			     struct r_scope_elem *symbol_scope[],
 			     const char *reference_name,
 			     const struct r_found_version *version,
 			     int reloc_type)
 {
   const unsigned long int hash = _dl_elf_hash (undef_name);
   struct sym_val current_value = { NULL, NULL };
-  struct link_map **scope;
+  struct r_scope_elem **scope;
 
   /* Search the relevant loaded objects for a definition.  */
   for (scope = symbol_scope; *scope; ++scope)
@@ -375,20 +375,20 @@ ElfW(Addr)
 internal_function
 _dl_lookup_versioned_symbol_skip (const char *undef_name,
 				  const ElfW(Sym) **ref,
-				  struct link_map *symbol_scope[],
+				  struct r_scope_elem *symbol_scope[],
 				  const char *reference_name,
 				  const struct r_found_version *version,
 				  struct link_map *skip_map)
 {
   const unsigned long int hash = _dl_elf_hash (undef_name);
   struct sym_val current_value = { NULL, NULL };
-  struct link_map **scope;
+  struct r_scope_elem **scope;
   size_t i;
 
   /* 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);
+  for (i = 0; (*scope)->r_duplist[i] != skip_map; ++i)
+    assert (i < (*scope)->r_nduplist);
 
   if (! do_lookup (undef_name, hash, *ref, &current_value,
 		   *scope, i, reference_name, version, skip_map, 0))
diff --git a/elf/dl-object.c b/elf/dl-object.c
index db6e81c11e..d9dd692be2 100644
--- a/elf/dl-object.c
+++ b/elf/dl-object.c
@@ -25,16 +25,17 @@
 
 #include <assert.h>
 
-/* List of objects currently loaded is [2] of this, aka _dl_loaded.  */
-struct link_map *_dl_default_scope[5];
 
 /* Allocate a `struct link_map' for a new object being loaded,
    and enter it into the _dl_loaded list.  */
 
 struct link_map *
 internal_function
-_dl_new_object (char *realname, const char *libname, int type)
+_dl_new_object (char *realname, const char *libname, int type,
+		struct link_map *loader)
 {
+  struct link_map *l;
+  int idx;
   size_t libname_len = strlen (libname) + 1;
   struct link_map *new = calloc (sizeof *new, 1);
   struct libname_list *newname = malloc (sizeof *newname + libname_len);
@@ -46,26 +47,37 @@ _dl_new_object (char *realname, const char *libname, int type)
   newname->next = NULL;
   new->l_libname = newname;
   new->l_type = type;
+  new->l_loader = loader;
 
-  if (_dl_loaded == NULL)
-    {
-      new->l_prev = new->l_next = NULL;
-      _dl_loaded = new;
-    }
-  else
+  /* Counter for the scopes we have to handle.  */
+  idx = 0;
+
+  if (_dl_loaded != NULL)
     {
-      struct link_map *l = _dl_loaded;
+      l = _dl_loaded;
       while (l->l_next)
 	l = l->l_next;
       new->l_prev = l;
-      new->l_next = NULL;
+      /* new->l_next = NULL;	Would be necesary but we use calloc.  */
       l->l_next = new;
-    }
 
-  /* Don't try to find the origin for the main map.  */
-  if (realname[0] == '\0')
-    new->l_origin = NULL;
+      /* Add the global scope.  */
+      new->l_scope[idx++] = &_dl_loaded->l_searchlist;
+    }
+  /* This is our local scope.  */
+  if (loader != NULL)
+    {
+      while (loader->l_loader != NULL)
+	loader = loader->l_loader;
+      new->l_scope[idx] = &loader->l_searchlist;
+    }
   else
+    new->l_scope[idx] = &new->l_searchlist;
+
+  new->l_local_scope[0] = new->l_scope[idx];
+
+  /* Don't try to find the origin for the main map which has the name "".  */
+  if (realname[0] != '\0')
     {
       char *origin;
 
diff --git a/elf/dl-open.c b/elf/dl-open.c
index fa36fcb32e..6765b6ce08 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -77,7 +77,7 @@ dl_open_worker (void *a)
 
   /* Load the named object.  */
   args->map = new = _dl_map_object (NULL, file, 0, lt_loaded, 0);
-  if (new->l_searchlist)
+  if (new->l_searchlist.r_list)
     /* It was already open.  */
     return;
 
@@ -97,21 +97,6 @@ dl_open_worker (void *a)
     {
       if (! l->l_relocated)
 	{
-	  /* We use an indirect call call for _dl_relocate_object because
-	     we must avoid using the PLT in the call.  If our PLT entry for
-	     _dl_relocate_object hasn't been used yet, then the dynamic
-	     linker fixup routine will clobber _dl_global_scope during its
-	     work.  We must be sure that nothing will require a PLT fixup
-	     between when _dl_object_relocation_scope returns and when we
-	     enter the dynamic linker's code (_dl_relocate_object).  */
-	  __typeof (_dl_relocate_object) *reloc = &_dl_relocate_object;
-
-	  /* GCC is very clever.  If we wouldn't add some magic it would
-	     simply optimize away our nice little variable `reloc' and we
-	     would result in a not working binary.  So let's swing the
-	     magic ward.  */
-	  asm ("" : "=r" (reloc) : "0" (reloc));
-
 #ifdef PIC
 	  if (_dl_profile != NULL)
 	    {
@@ -122,7 +107,7 @@ dl_open_worker (void *a)
 		 start the profiling.  */
 	      struct link_map *old_profile_map = _dl_profile_map;
 
-	      (*reloc) (l, _dl_object_relocation_scope (l), 1, 1);
+	      _dl_relocate_object (l, l->l_scope, 1, 1);
 
 	      if (old_profile_map == NULL && _dl_profile_map != NULL)
 		/* We must prepare the profiling.  */
@@ -130,10 +115,8 @@ dl_open_worker (void *a)
 	    }
 	  else
 #endif
-	    (*reloc) (l, _dl_object_relocation_scope (l),
-		      (mode & RTLD_BINDING_MASK) == RTLD_LAZY, 0);
-
-	  *_dl_global_scope_end = NULL;
+	    _dl_relocate_object (l, l->l_scope,
+				 (mode & RTLD_BINDING_MASK) == RTLD_LAZY, 0);
 	}
 
       if (l == new)
@@ -146,50 +129,58 @@ dl_open_worker (void *a)
     {
       /* The symbols of the new object and its dependencies are to be
 	 introduced into the global scope that will be used to resolve
-	 references from other dynamically-loaded objects.  */
-
+	 references from other dynamically-loaded objects.
+
+	 The global scope is the searchlist in the main link map.  We
+	 extend this list if necessary.  There is one problem though:
+	 since this structure was allocated very early (before the libc
+	 is loaded) the memory it uses is allocated by the malloc()-stub
+	 in the ld.so.  When we come here these functions are not used
+	 anymore.  Instead the malloc() implementation of the libc is
+	 used.  But this means the block from the main map cannot be used
+	 in an realloc() call.  Therefore we allocate a completely new
+	 array the first time we have to add something to the locale scope.  */
       if (_dl_global_scope_alloc == 0)
 	{
 	  /* This is the first dynamic object given global scope.  */
-	  _dl_global_scope_alloc = 8;
-	  _dl_global_scope = malloc (_dl_global_scope_alloc
-				     * sizeof (struct link_map *));
-	  if (! _dl_global_scope)
+	  struct link_map **new_global;
+
+	  _dl_global_scope_alloc = _dl_main_searchlist->r_nlist + 8;
+	  new_global = (struct link_map **)
+	    malloc (_dl_global_scope_alloc * sizeof (struct link_map *));
+	  if (new_global == NULL)
 	    {
-	      _dl_global_scope = _dl_default_scope;
+	      _dl_global_scope_alloc = 0;
 	    nomem:
 	      new->l_global = 0;
 	      _dl_signal_error (ENOMEM, file, "cannot extend global scope");
 	    }
-	  _dl_global_scope[2] = _dl_default_scope[2];
-	  _dl_global_scope[3] = new;
-	  _dl_global_scope[4] = NULL;
-	  _dl_global_scope[5] = NULL;
-	  _dl_global_scope_end = &_dl_global_scope [4];
+
+	  /* Copy over the old entries.  */
+	  memcpy (new_global, _dl_main_searchlist->r_list,
+		  (_dl_main_searchlist->r_nlist * sizeof (struct link_map *)));
+
+	  _dl_main_searchlist->r_list = new_global;
 	}
-      else
+      else if (_dl_main_searchlist->r_nlist == _dl_global_scope_alloc)
 	{
-	  if (_dl_global_scope_end + 3
-	      > _dl_global_scope + _dl_global_scope_alloc)
-	    {
-	      /* Must extend the list.  */
-	      struct link_map **new = realloc (_dl_global_scope,
-					       _dl_global_scope_alloc * 2
-					       * sizeof (struct link_map *));
-	      if (! new)
-		goto nomem;
-	      _dl_global_scope = new;
-	      _dl_global_scope_end = new + _dl_global_scope_alloc - 2;
-	      _dl_global_scope_alloc *= 2;
-	    }
+	  /* We have to extend the existing array of link maps in the
+	     main map.  */
+	  struct link_map **new_global;
+
+	  new_global = (struct link_map **)
+	    malloc ((_dl_global_scope_alloc + 8) * sizeof (struct link_map *));
+	  if (new_global == NULL)
+	    goto nomem;
 
-	  /* Append the new object and re-terminate the list.  */
-	  *_dl_global_scope_end++ = new;
-	  /* We keep the list double-terminated so the last element
-	     can be filled in for symbol lookups.  */
-	  _dl_global_scope_end[0] = NULL;
-	  _dl_global_scope_end[1] = NULL;
+	  _dl_global_scope_alloc += 8;
+	  _dl_main_searchlist->r_list = new_global;
 	}
+
+      /* Now add the new entry.  */
+      _dl_main_searchlist->r_list[_dl_main_searchlist->r_nlist] = new;
+
+      /* XXX Do we have to add something to r_dupsearchlist???  --drepper */
     }
 
 
@@ -201,10 +192,14 @@ dl_open_worker (void *a)
   _dl_debug_state ();
 
   /* Run the initializer functions of new objects.  */
-  while (init = _dl_init_next (new))
+  while (init = _dl_init_next (&new->l_searchlist))
     (*(void (*) (int, char **, char **)) init) (__libc_argc, __libc_argv,
 						__environ);
 
+  if (new->l_global)
+    /* Now we can make the new map available in the global scope.  */
+    ++_dl_main_searchlist->r_nlist;
+
   if (_dl_sysdep_start == NULL)
     /* We must be the static _dl_open in libc.a.  A static program that
        has loaded a dynamic object now has competition.  */
@@ -241,9 +236,6 @@ _dl_open (const char *file, int mode)
       /* Some error occured during loading.  */
       char *local_errstring;
 
-      /* Reset the global scope.  */
-      *_dl_global_scope_end = NULL;
-
       /* Remove the object from memory.  It may be in an inconsistent
 	 state if relocation failed, for example.  */
       if (args.map)
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index 55e62b6705..35ef88ab71 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -27,8 +27,8 @@
 
 
 void
-_dl_relocate_object (struct link_map *l, struct link_map *scope[], int lazy,
-		     int consider_profiling)
+_dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
+		     int lazy, int consider_profiling)
 {
   if (l->l_relocated)
     return;
diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c
index a5e8592154..38ecf96525 100644
--- a/elf/dl-runtime.c
+++ b/elf/dl-runtime.c
@@ -19,63 +19,6 @@
 
 #include <unistd.h>
 #include <elf/ldsodefs.h>
-
-
-/* The global scope we will use for symbol lookups.
-   This will be modified by _dl_open if RTLD_GLOBAL is used.  */
-struct link_map **_dl_global_scope = _dl_default_scope;
-struct link_map **_dl_global_scope_end = &_dl_default_scope[3];
-
-
-/* Hack _dl_global_scope[0] and [1] as necessary, and return a pointer into
-   _dl_global_scope that should be passed to _dl_lookup_symbol for symbol
-   references made in the object L's relocations.  */
-inline struct link_map **
-internal_function
-_dl_object_relocation_scope (struct link_map *l)
-{
-  if (l->l_info[DT_SYMBOLIC])
-    {
-      /* This object's global references are to be resolved first
-	 in the object itself, and only secondarily in more global
-	 scopes.  */
-
-      if (! l->l_searchlist)
-	/* We must construct the searchlist for this object.  */
-	_dl_map_object_deps (l, NULL, 0, 0);
-
-      /* The primary scope is this object itself and its
-	 dependencies.
-
-	 XXX This is wrong.  Only the object must be searched, not
-	 the dependencies. --drepper  */
-      _dl_global_scope[0] = l;
-
-      /* Secondary is the dependency tree that reached L; the object
-	 requested directly by the user is at the root of that tree.  */
-      while (l->l_loader)
-	l = l->l_loader;
-      _dl_global_scope[1] = l;
-
-      /* Finally, the global scope follows.  */
-
-      return _dl_global_scope;
-    }
-  else
-    {
-      /* Use first the global scope, and then the scope of the root of the
-	 dependency tree that first caused this object to be loaded.  */
-      while (l->l_loader)
-	l = l->l_loader;
-      /* There is no point in searching the same list twice.  This isn't
-	 guaranteed to always find all duplicates if new objects are added
-	 to the global scope, but is good enough most of the time.  */
-      if (_dl_global_scope[2] != l)
-	*_dl_global_scope_end = l;
-      return &_dl_global_scope[2];
-    }
-}
-
 #include "dynamic-link.h"
 
 #if !defined ELF_MACHINE_NO_RELA || ELF_MACHINE_NO_REL
@@ -115,9 +58,6 @@ fixup (
   void *const rel_addr = (void *)(l->l_addr + reloc->r_offset);
   ElfW(Addr) value;
 
-  /* Set up the scope to find symbols referenced by this object.  */
-  struct link_map **scope = _dl_object_relocation_scope (l);
-
   /* Sanity check that we're really looking at a PLT relocation.  */
   assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);
 
@@ -134,13 +74,13 @@ fixup (
 	if (version->hash != 0)
 	  {
 	    value = _dl_lookup_versioned_symbol(strtab + sym->st_name,
-						&sym, scope, l->l_name,
+						&sym, l->l_scope, l->l_name,
 						version, ELF_MACHINE_JMP_SLOT);
 	    break;
 	  }
       }
     case 0:
-      value = _dl_lookup_symbol (strtab + sym->st_name, &sym, scope,
+      value = _dl_lookup_symbol (strtab + sym->st_name, &sym, l->l_scope,
 				 l->l_name, ELF_MACHINE_JMP_SLOT);
     }
 
@@ -154,8 +94,6 @@ fixup (
   /* Finally, fix up the plt itself.  */
   elf_machine_fixup_plt (l, reloc, rel_addr, value);
 
-  *_dl_global_scope_end = NULL;
-
   return value;
 }
 
@@ -191,9 +129,6 @@ profile_fixup (
 			  reloc_offset);
       const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
 
-      /* Set up the scope to find symbols referenced by this object.  */
-      struct link_map **scope = _dl_object_relocation_scope (l);
-
       /* Sanity check that we're really looking at a PLT relocation.  */
       assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);
 
@@ -210,14 +145,14 @@ profile_fixup (
 	    if (version->hash != 0)
 	      {
 		value = _dl_lookup_versioned_symbol(strtab + sym->st_name,
-						    &sym, scope, l->l_name,
-						    version,
+						    &sym, l->l_scope,
+						    l->l_name, version,
 						    ELF_MACHINE_JMP_SLOT);
 		break;
 	      }
 	  }
 	case 0:
-	  value = _dl_lookup_symbol (strtab + sym->st_name, &sym, scope,
+	  value = _dl_lookup_symbol (strtab + sym->st_name, &sym, l->l_scope,
 				     l->l_name, ELF_MACHINE_JMP_SLOT);
 	}
 
@@ -228,8 +163,6 @@ profile_fixup (
       /* And now perhaps the relocation addend.  */
       value = elf_machine_plt_value (l, reloc, value);
 
-      *_dl_global_scope_end = NULL;
-
       /* Store the result for later runs.  */
       *resultp = value;
     }
diff --git a/elf/dl-support.c b/elf/dl-support.c
index 9b94907940..76af1883ac 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
@@ -66,6 +66,20 @@ void *__libc_stack_end;
 /* Path where the binary is found.  */
 const char *_dl_origin_path;
 
+/* Initially empty list of loaded objects.  */
+struct link_map *_dl_loaded;
+
+/* Fake scope.  In dynamically linked binaries this is the scope of the
+   main application but here we don't have  something like this.  So
+   create a fake scope containing nothing.  */
+static struct r_scope_elem fake_scope;
+/* Variable which can be used in lookup to process the global scope.  */
+struct r_scope_elem *_dl_global_scope[2] = { &fake_scope, NULL };
+/* This is a global pointer to this structure which is public.  It is
+   used by dlopen/dlclose to add and remove objects from what is regarded
+   to be the global scope.  */
+struct r_scope_elem *_dl_main_searchlist = &fake_scope;
+
 
 static void non_dynamic_init (void) __attribute__ ((unused));
 
diff --git a/elf/dl-symbol.c b/elf/dl-symbol.c
index 22adf4351a..3ae44d6bea 100644
--- a/elf/dl-symbol.c
+++ b/elf/dl-symbol.c
@@ -28,7 +28,7 @@ _dl_symbol_value (struct link_map *map, const char *name)
 {
   ElfW(Addr) loadbase;
   const ElfW(Sym) *ref = NULL;
-  struct link_map *scope[2] = { map, NULL };
-  loadbase = _dl_lookup_symbol (name, &ref, scope, map->l_name, 0);
+  loadbase = _dl_lookup_symbol (name, &ref, map->l_local_scope, map->l_name,
+				0);
   return loadbase + ref->st_value;
 }
diff --git a/elf/dl-version.c b/elf/dl-version.c
index 413b3bc0df..4ba7f1ab3a 100644
--- a/elf/dl-version.c
+++ b/elf/dl-version.c
@@ -61,9 +61,9 @@ find_needed (const char *name, struct link_map *map)
 
   /* The required object is not in the global scope, look to see if it is
      a dependency of the current object.  */
-  for (n = 0; n < map->l_nsearchlist; n++)
-    if (_dl_name_match_p (name, map->l_searchlist[n]))
-      return map->l_searchlist[n];
+  for (n = 0; n < map->l_searchlist.r_nlist; n++)
+    if (_dl_name_match_p (name, map->l_searchlist.r_list[n]))
+      return map->l_searchlist.r_list[n];
 
   /* Should never happen.  */
   return NULL;
diff --git a/elf/dlfcn.h b/elf/dlfcn.h
index 715e1783cf..d2a27b85a2 100644
--- a/elf/dlfcn.h
+++ b/elf/dlfcn.h
@@ -21,16 +21,23 @@
 #define	_DLFCN_H 1
 
 #include <features.h>
+#define __need_NULL
+#include <stddef.h>
 
 /* Collect various system dependand definitions and declarations.  */
 #include <bits/dlfcn.h>
 
-/* If the first argument of `dlsym' is set to RTLD_NEXT the run-time
-   address of the symbol called NAME in the next shared object is
-   returned.  The "next" relation is defined by the order the shared
-   objects were loaded.  */
+/* If the first argument of `dlsym' or `dlvsym' is set to RTLD_NEXT
+   the run-time address of the symbol called NAME in the next shared
+   object is returned.  The "next" relation is defined by the order
+   the shared objects were loaded.  */
 #define RTLD_NEXT	((void *) -1l)
 
+/* If the first argument to `dlsym' or `dlvsym' is set to RTLD_DEFAULT
+   the run-time address of the symbol called NAME in the global scope
+   is returned.  */
+#define RTLD_DEFAULT	NULL
+
 __BEGIN_DECLS
 
 /* Open the shared object FILE and map it in; return a handle that can be
@@ -57,6 +64,7 @@ extern void *dlvsym __P ((void *__handle, __const char *__name,
    the error string so that a following call returns null.  */
 extern char *dlerror __P ((void));
 
+#ifdef __USE_GNU
 /* Fill in *INFO with the following information about ADDRESS.
    Returns 0 iff no shared object's segments contain that address.  */
 typedef struct
@@ -68,7 +76,6 @@ typedef struct
   } Dl_info;
 extern int dladdr __P ((const void *__address, Dl_info *__info));
 
-#ifdef __USE_GNU
 /* To support profiling of shared objects it is a good idea to call
    the function found using `dlsym' using the following macro since
    these calls do not use the PLT.  But this would mean the dynamic
diff --git a/elf/dlsym.c b/elf/dlsym.c
index 8b20dcf90d..ed786a2625 100644
--- a/elf/dlsym.c
+++ b/elf/dlsym.c
@@ -44,9 +44,7 @@ dlsym_doit (void *a)
   if (args->handle == NULL)
     /* Search the global scope.  */
     args->loadbase = _dl_lookup_symbol (args->name, &args->ref,
-					&(_dl_global_scope
-					  ?: _dl_default_scope)[2],
-					NULL, 0);
+					_dl_global_scope, NULL, 0);
   else if (args->handle == RTLD_NEXT)
     {
       struct link_map *l, *match;
@@ -65,19 +63,15 @@ RTLD_NEXT used in code not dynamically loaded"));
       while (l->l_loader)
 	l = l->l_loader;
 
-      {
-	struct link_map *mapscope[2] = { l, NULL };
-	args->loadbase = _dl_lookup_symbol_skip (args->name, &args->ref,
-						 mapscope, NULL, match);
-      }
+      args->loadbase = _dl_lookup_symbol_skip (args->name, &args->ref,
+					       l->l_local_scope, NULL, match);
     }
   else
     {
       /* Search the scope of the given object.  */
       struct link_map *map = args->handle;
-      struct link_map *mapscope[2] = { map, NULL };
-      args->loadbase = _dl_lookup_symbol (args->name, &args->ref, mapscope,
-					  map->l_name, 0);
+      args->loadbase = _dl_lookup_symbol (args->name, &args->ref,
+					  map->l_local_scope, map->l_name, 0);
     }
 }
 
diff --git a/elf/dlvsym.c b/elf/dlvsym.c
index a1ba9504e8..989e87b9fa 100644
--- a/elf/dlvsym.c
+++ b/elf/dlvsym.c
@@ -46,8 +46,7 @@ dlvsym_doit (void *a)
   if (args->handle == NULL)
     /* Search the global scope.  */
     args->loadbase = _dl_lookup_versioned_symbol (args->name, &args->ref,
-						  &(_dl_global_scope
-						    ?: _dl_default_scope)[2],
+						  _dl_global_scope,
 						  NULL, &args->version, 0);
   else if (args->handle == RTLD_NEXT)
     {
@@ -67,23 +66,19 @@ RTLD_NEXT used in code not dynamically loaded"));
       while (l->l_loader)
 	l = l->l_loader;
 
-      {
-	struct link_map *mapscope[2] = { l, NULL };
-	args->loadbase = _dl_lookup_versioned_symbol_skip (args->name,
-							   &args->ref,
-							   mapscope,
-							   NULL,
-							   &args->version,
-							   match);
-      }
+      args->loadbase = _dl_lookup_versioned_symbol_skip (args->name,
+							 &args->ref,
+							 l->l_local_scope,
+							 NULL, &args->version,
+							 match);
     }
   else
     {
       /* Search the scope of the given object.  */
       struct link_map *map = args->handle;
-      struct link_map *mapscope[2] = { map, NULL };
       args->loadbase = _dl_lookup_versioned_symbol (args->name, &args->ref,
-						    mapscope, map->l_name,
+						    map->l_local_scope,
+						    map->l_name,
 						    &args->version, 0);
     }
 }
diff --git a/elf/ldsodefs.h b/elf/ldsodefs.h
index 51fecc6eb7..807613e4b4 100644
--- a/elf/ldsodefs.h
+++ b/elf/ldsodefs.h
@@ -282,7 +282,7 @@ extern void _dl_close (struct link_map *map)
    symbols can be chosen.  */
 extern ElfW(Addr) _dl_lookup_symbol (const char *undef,
 				     const ElfW(Sym) **sym,
-				     struct link_map *symbol_scope[],
+				     struct r_scope_elem *symbol_scope[],
 				     const char *reference_name,
 				     int reloc_type)
      internal_function;
@@ -290,7 +290,7 @@ extern ElfW(Addr) _dl_lookup_symbol (const char *undef,
 /* Lookup versioned symbol.  */
 extern ElfW(Addr) _dl_lookup_versioned_symbol (const char *undef,
 					       const ElfW(Sym) **sym,
-					       struct link_map *symbol_scope[],
+					       struct r_scope_elem *symbol_scope[],
 					       const char *reference_name,
 					       const struct r_found_version *version,
 					       int reloc_type)
@@ -299,7 +299,7 @@ extern ElfW(Addr) _dl_lookup_versioned_symbol (const char *undef,
 /* For handling RTLD_NEXT we must be able to skip shared objects.  */
 extern ElfW(Addr) _dl_lookup_symbol_skip (const char *undef,
 					  const ElfW(Sym) **sym,
-					  struct link_map *symbol_scope[],
+					  struct r_scope_elem *symbol_scope[],
 					  const char *reference_name,
 					  struct link_map *skip_this)
      internal_function;
@@ -308,7 +308,7 @@ extern ElfW(Addr) _dl_lookup_symbol_skip (const char *undef,
    skip shared objects.  */
 extern ElfW(Addr) _dl_lookup_versioned_symbol_skip (const char *undef,
 						    const ElfW(Sym) **sym,
-						    struct link_map *symbol_scope[],
+						    struct r_scope_elem *symbol_scope[],
 						    const char *reference_name,
 						    const struct r_found_version *version,
 						    struct link_map *skip_this)
@@ -325,46 +325,24 @@ extern ElfW(Addr) _dl_symbol_value (struct link_map *map, const char *name)
 
 /* Structure describing the dynamic linker itself.  */
 extern struct link_map _dl_rtld_map;
-
-/* The list of objects currently loaded is the third element of the
-   `_dl_default_scope' array, and the fourth element is always null.
-   This leaves two slots before it that are used when resolving
-   DT_SYMBOLIC objects' references one after it for normal references
-   (see below).  */
-#define _dl_loaded	(_dl_default_scope[2])
-extern struct link_map *_dl_default_scope[5];
-
-/* Null-terminated list of objects in the dynamic `global scope'.  The
-   list starts at [2]; i.e. &_dl_global_scope[2] is the argument
-   passed to _dl_lookup_symbol to search the global scope.  To search
-   a specific object and its dependencies in preference to the global
-   scope, fill in the [1] slot and pass its address; for two specific
-   object scopes, fill [0] and [1].  The list is double-terminated; to
-   search the global scope and then a specific object and its
-   dependencies, set *_dl_global_scope_end.  This variable initially
-   points to _dl_default_scope, and _dl_loaded is always kept in [2]
-   of this list.  A new list is malloc'd when new objects are loaded
-   with RTLD_GLOBAL.  */
-extern struct link_map **_dl_global_scope, **_dl_global_scope_end;
-extern size_t _dl_global_scope_alloc; /* Number of slots malloc'd.  */
-
-/* Hack _dl_global_scope[0] and [1] as necessary, and return a pointer into
-   _dl_global_scope that should be passed to _dl_lookup_symbol for symbol
-   references made in the object MAP's relocations.  */
-extern struct link_map **_dl_object_relocation_scope (struct link_map *map)
-     internal_function;
-
+/* And a pointer to the map for the main map.  */
+extern struct link_map *_dl_loaded;
+/* Array representing global scope.  */
+extern struct r_scope_elem *_dl_global_scope[2];
+/* Direct pointer to the searchlist of the main object.  */
+extern struct r_scope_elem *_dl_main_searchlist;
 
 /* Allocate a `struct link_map' for a new object being loaded,
-   and enter it into the _dl_loaded list.  */
+   and enter it into the _dl_main_map list.  */
 extern struct link_map *_dl_new_object (char *realname, const char *libname,
-					int type) internal_function;
+					int type, struct link_map *loader)
+     internal_function;
 
 /* Relocate the given object (if it hasn't already been).
    SCOPE is passed to _dl_lookup_symbol in symbol lookups.
    If LAZY is nonzero, don't relocate its PLT.  */
 extern void _dl_relocate_object (struct link_map *map,
-				 struct link_map *scope[],
+				 struct r_scope_elem *scope[],
 				 int lazy, int consider_profiling);
 
 /* Check the version dependencies of all objects available through
@@ -377,11 +355,11 @@ extern int _dl_check_all_versions (struct link_map *map, int verbose)
 extern int _dl_check_map_versions (struct link_map *map, int verbose)
      internal_function;
 
-/* Return the address of the next initializer function for MAP or one of
+/* Return the address of the next initializer function for SCOPE or one of
    its dependencies that has not yet been run.  When there are no more
    initializers to be run, this returns zero.  The functions are returned
    in the order they should be called.  */
-extern ElfW(Addr) _dl_init_next (struct link_map *map) internal_function;
+extern ElfW(Addr) _dl_init_next (struct r_scope_elem *scope) internal_function;
 
 /* Call the finalizer functions of all shared objects whose
    initializer functions have completed.  */
diff --git a/elf/link.h b/elf/link.h
index e31dd2dd65..ecf0469b7a 100644
--- a/elf/link.h
+++ b/elf/link.h
@@ -80,6 +80,24 @@ struct libname_list;
 struct r_found_version;
 struct r_search_path_elem;
 
+/* Forward declaration.  */
+struct link_map;
+
+/* Structure to describe a single list of scope elements.  The lookup
+   functions get passed an array of pointers to such structures.  */
+struct r_scope_elem
+{
+  /* Array of maps for the scope.  */
+  struct link_map **r_list;
+  /* Number of entries in the scope.  */
+  unsigned int r_nlist;
+
+  /* Array of maps which also includes duplicates.  */
+  struct link_map **r_duplist;
+  /* Number of elements in this list.  */
+  unsigned int r_nduplist;
+};
+
 
 /* Structure describing a loaded shared object.  The `l_next' and `l_prev'
    members form a chain of all the shared objects loaded at startup.
@@ -119,15 +137,14 @@ struct link_map
     ElfW(Half) l_phnum;		/* Number of program header entries.  */
 
     /* Array of DT_NEEDED dependencies and their dependencies, in
-       dependency order for symbol lookup.  This is null before the
-       dependencies have been loaded.  */
-    struct link_map **l_searchlist;
-    unsigned int l_nsearchlist;
+       dependency order for symbol lookup (with and without
+       duplicates).  There is no entry before the dependencies have
+       been loaded.  */
+    struct r_scope_elem l_searchlist;
 
-    /* We keep another list in which we keep duplicates.  This is
-       needed in _dl_lookup_symbol_skip to implemented RTLD_NEXT.  */
-    struct link_map **l_dupsearchlist;
-    unsigned int l_ndupsearchlist;
+    /* We need a special searchlist to process objects marked with
+       DT_SYMBOLIC.  */
+    struct r_scope_elem l_symbolic_searchlist;
 
     /* Dependent object that first caused this object to be loaded.  */
     struct link_map *l_loader;
@@ -168,6 +185,14 @@ struct link_map
     /* Start and finish of memory map for this object.  l_map_start
        need not be the same as l_addr.  */
     ElfW(Addr) l_map_start, l_map_end;
+
+    /* This is an array defining the lookup scope for this link map.
+     There are at most three different scope lists.  */
+    struct r_scope_elem *l_scope[4];
+
+    /* A similar array, this time only with the local scope.  This is
+       used occasionally.  */
+    struct r_scope_elem *l_local_scope[2];
   };
 
 #endif /* link.h */
diff --git a/elf/rtld.c b/elf/rtld.c
index 66e5d6c59d..52295deb2c 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -89,6 +89,14 @@ const char *_dl_inhibit_rpath;		/* RPATH values which should be
 					   ignored.  */
 const char *_dl_origin_path;
 
+/* This is a pointer to the map for the main object and through it to
+   all loaded objects.  */
+struct link_map *_dl_loaded;
+/* Pointer to the l_searchlist element of the link map of the main object.  */
+struct r_scope_elem *_dl_main_searchlist;
+/* Array which is used when looking up in the global scope.  */
+struct r_scope_elem *_dl_global_scope[2];
+
 /* Set nonzero during loading and initialization of executable and
    libraries, cleared before the executable's entry point runs.  This
    must not be initialized to nonzero, because the unused dynamic
@@ -201,7 +209,6 @@ struct map_args
 /* Arguments to version_check_doit.  */
 struct version_check_args
 {
-  struct link_map *main_map;
   int doexit;
 };
 
@@ -210,22 +217,22 @@ relocate_doit (void *a)
 {
   struct relocate_args *args = (struct relocate_args *) a;
 
-  _dl_relocate_object (args->l, _dl_object_relocation_scope (args->l),
+  _dl_relocate_object (args->l, args->l->l_scope,
 		       args->lazy, 0);
 }
 
 static void
 map_doit (void *a)
 {
-  struct map_args *args = (struct map_args *)a;
+  struct map_args *args = (struct map_args *) a;
   args->main_map = _dl_map_object (NULL, args->str, 0, lt_library, 0);
 }
 
 static void
 version_check_doit (void *a)
 {
-  struct version_check_args *args = (struct version_check_args *)a;
-  if (_dl_check_all_versions (args->main_map, 1) && args->doexit)
+  struct version_check_args *args = (struct version_check_args *) a;
+  if (_dl_check_all_versions (_dl_loaded, 1) && args->doexit)
     /* We cannot start the application.  Abort now.  */
     _exit (1);
 }
@@ -234,11 +241,11 @@ version_check_doit (void *a)
 static inline struct link_map *
 find_needed (const char *name)
 {
-  unsigned int n;
+  unsigned int n = _dl_loaded->l_searchlist.r_nlist;
 
-  for (n = 0; n < _dl_loaded->l_nsearchlist; ++n)
-    if (_dl_name_match_p (name, _dl_loaded->l_searchlist[n]))
-      return _dl_loaded->l_searchlist[n];
+  while (n-- > 0)
+    if (_dl_name_match_p (name, _dl_loaded->l_searchlist.r_list[n]))
+      return _dl_loaded->l_searchlist.r_list[n];
 
   /* Should never happen.  */
   return NULL;
@@ -289,7 +296,6 @@ dl_main (const ElfW(Phdr) *phdr,
 	 ElfW(Addr) *user_entry)
 {
   const ElfW(Phdr) *ph;
-  struct link_map *main_map;
   int lazy;
   enum mode mode;
   struct link_map **preloads;
@@ -405,7 +411,7 @@ of this helper program; chances are you did not intend to run this program.\n\
 
 	  args.str = _dl_argv[0];
 	  (void) _dl_catch_error (&err_str, map_doit, &args);
-	  main_map = args.main_map;
+	  _dl_loaded = args.main_map;
 	  if (err_str != NULL)
 	    {
 	      free (err_str);
@@ -413,37 +419,37 @@ of this helper program; chances are you did not intend to run this program.\n\
 	    }
 	}
       else
-	main_map = _dl_map_object (NULL, _dl_argv[0], 0, lt_library, 0);
+	_dl_loaded = _dl_map_object (NULL, _dl_argv[0], 0, lt_library, 0);
 
-      phdr = main_map->l_phdr;
-      phent = main_map->l_phnum;
+      phdr = _dl_loaded->l_phdr;
+      phent = _dl_loaded->l_phnum;
       /* We overwrite here a pointer to a malloc()ed string.  But since
 	 the malloc() implementation used at this point is the dummy
 	 implementations which has no real free() function it does not
 	 makes sense to free the old string first.  */
-      main_map->l_name = (char *) "";
-      *user_entry = main_map->l_entry;
+      _dl_loaded->l_name = (char *) "";
+      *user_entry = _dl_loaded->l_entry;
     }
   else
     {
       /* Create a link_map for the executable itself.
 	 This will be what dlopen on "" returns.  */
-      main_map = _dl_new_object ((char *) "", "", lt_executable);
-      if (main_map == NULL)
+      _dl_loaded = _dl_new_object ((char *) "", "", lt_executable, NULL);
+      if (_dl_loaded == NULL)
 	_dl_sysdep_fatal ("cannot allocate memory for link map\n", NULL);
-      main_map->l_phdr = phdr;
-      main_map->l_phnum = phent;
-      main_map->l_entry = *user_entry;
-      main_map->l_opencount = 1;
+      _dl_loaded->l_phdr = phdr;
+      _dl_loaded->l_phnum = phent;
+      _dl_loaded->l_entry = *user_entry;
+      _dl_loaded->l_opencount = 1;
 
       /* We delay initializing the path structure until we got the dynamic
 	 information for the program.  */
     }
 
   /* It is not safe to load stuff after the main program.  */
-  main_map->l_map_end = ~0;
+  _dl_loaded->l_map_end = ~0;
   /* Perhaps the executable has no PT_LOAD header entries at all.  */
-  main_map->l_map_start = ~0;
+  _dl_loaded->l_map_start = ~0;
 
   /* Scan the program header table for the dynamic section.  */
   for (ph = phdr; ph < &phdr[phent]; ++ph)
@@ -451,12 +457,12 @@ of this helper program; chances are you did not intend to run this program.\n\
       {
       case PT_PHDR:
 	/* Find out the load address.  */
-	main_map->l_addr = (ElfW(Addr)) phdr - ph->p_vaddr;
+	_dl_loaded->l_addr = (ElfW(Addr)) phdr - ph->p_vaddr;
 	break;
       case PT_DYNAMIC:
 	/* This tells us where to find the dynamic section,
 	   which tells us everything we need to do.  */
-	main_map->l_ld = (void *) main_map->l_addr + ph->p_vaddr;
+	_dl_loaded->l_ld = (void *) _dl_loaded->l_addr + ph->p_vaddr;
 	break;
       case PT_INTERP:
 	/* This "interpreter segment" was used by the program loader to
@@ -465,7 +471,8 @@ of this helper program; chances are you did not intend to run this program.\n\
 	   dlopen call or DT_NEEDED entry, for something that wants to link
 	   against the dynamic linker as a shared library, will know that
 	   the shared object is already loaded.  */
-	_dl_rtld_libname.name = (const char *) main_map->l_addr + ph->p_vaddr;
+	_dl_rtld_libname.name = ((const char *) _dl_loaded->l_addr
+				 + ph->p_vaddr);
 	_dl_rtld_libname.next = NULL;
 	_dl_rtld_map.l_libname = &_dl_rtld_libname;
 
@@ -491,9 +498,9 @@ of this helper program; chances are you did not intend to run this program.\n\
 	/* Remember where the main program starts in memory.  */
 	{
 	  ElfW(Addr) mapstart;
-	  mapstart = main_map->l_addr + (ph->p_vaddr & ~(ph->p_align - 1));
-	  if (main_map->l_map_start > mapstart)
-	    main_map->l_map_start = mapstart;
+	  mapstart = _dl_loaded->l_addr + (ph->p_vaddr & ~(ph->p_align - 1));
+	  if (_dl_loaded->l_map_start > mapstart)
+	    _dl_loaded->l_map_start = mapstart;
 	}
 	break;
       }
@@ -509,10 +516,10 @@ of this helper program; chances are you did not intend to run this program.\n\
     assert (_dl_rtld_map.l_libname); /* How else did we get here?  */
 
   /* Extract the contents of the dynamic section for easy access.  */
-  elf_get_dynamic_info (main_map->l_ld, main_map->l_info);
-  if (main_map->l_info[DT_HASH])
+  elf_get_dynamic_info (_dl_loaded->l_ld, _dl_loaded->l_info);
+  if (_dl_loaded->l_info[DT_HASH])
     /* Set up our cache of pointers into the hash table.  */
-    _dl_setup_hash (main_map);
+    _dl_setup_hash (_dl_loaded);
 
   if (mode == verify)
     {
@@ -520,7 +527,7 @@ of this helper program; chances are you did not intend to run this program.\n\
 	 executable using us as the program interpreter.  Exit with an
 	 error if we were not able to load the binary or no interpreter
 	 is specified (i.e., this is no dynamically linked binary.  */
-      if (main_map->l_ld == NULL)
+      if (_dl_loaded->l_ld == NULL)
 	_exit (1);
 
       /* We allow here some platform specific code.  */
@@ -537,14 +544,14 @@ of this helper program; chances are you did not intend to run this program.\n\
 
   /* Put the link_map for ourselves on the chain so it can be found by
      name.  Note that at this point the global chain of link maps contains
-     exactly one element, which is pointed to by main_map.  */
+     exactly one element, which is pointed to by _dl_loaded.  */
   if (! _dl_rtld_map.l_name)
     /* If not invoked directly, the dynamic linker shared object file was
        found by the PT_INTERP name.  */
     _dl_rtld_map.l_name = (char *) _dl_rtld_map.l_libname->name;
   _dl_rtld_map.l_type = lt_library;
-  main_map->l_next = &_dl_rtld_map;
-  _dl_rtld_map.l_prev = main_map;
+  _dl_loaded->l_next = &_dl_rtld_map;
+  _dl_rtld_map.l_prev = _dl_loaded;
 
   /* We have two ways to specify objects to preload: via environment
      variable and via the file /etc/ld.so.preload.  The later can also
@@ -565,7 +572,7 @@ of this helper program; chances are you did not intend to run this program.\n\
 	if (p[0] != '\0'
 	    && (! __libc_enable_secure || strchr (p, '/') == NULL))
 	  {
-	    struct link_map *new_map = _dl_map_object (main_map, p, 1,
+	    struct link_map *new_map = _dl_map_object (_dl_loaded, p, 1,
 						       lt_library, 0);
 	    if (new_map->l_opencount == 1)
 	      /* It is no duplicate.  */
@@ -627,7 +634,7 @@ of this helper program; chances are you did not intend to run this program.\n\
 	  while ((p = strsep (&runp, ": \t\n")) != NULL)
 	    if (p[0] != '\0')
 	      {
-		struct link_map *new_map = _dl_map_object (main_map, p, 1,
+		struct link_map *new_map = _dl_map_object (_dl_loaded, p, 1,
 							   lt_library, 0);
 		if (new_map->l_opencount == 1)
 		  /* It is no duplicate.  */
@@ -638,7 +645,7 @@ of this helper program; chances are you did not intend to run this program.\n\
       if (problem != NULL)
 	{
 	  char *p = strndupa (problem, file_size - (problem - file));
-	  struct link_map *new_map = _dl_map_object (main_map, p, 1,
+	  struct link_map *new_map = _dl_map_object (_dl_loaded, p, 1,
 						     lt_library, 0);
 	  if (new_map->l_opencount == 1)
 	    /* It is no duplicate.  */
@@ -667,7 +674,7 @@ of this helper program; chances are you did not intend to run this program.\n\
   /* Load all the libraries specified by DT_NEEDED entries.  If LD_PRELOAD
      specified some libraries to load, these are inserted before the actual
      dependencies in the executable's searchlist for symbol resolution.  */
-  _dl_map_object_deps (main_map, preloads, npreloads, mode == trace);
+  _dl_map_object_deps (_dl_loaded, preloads, npreloads, mode == trace);
 
 #ifndef MAP_ANON
   /* We are done mapping things, so close the zero-fill descriptor.  */
@@ -687,11 +694,12 @@ of this helper program; chances are you did not intend to run this program.\n\
 	 chain in symbol search order because gdb uses the chain's order as
 	 its symbol search order.  */
       i = 1;
-      while (main_map->l_searchlist[i] != &_dl_rtld_map)
+      while (_dl_loaded->l_searchlist.r_list[i] != &_dl_rtld_map)
 	++i;
-      _dl_rtld_map.l_prev = main_map->l_searchlist[i - 1];
-      _dl_rtld_map.l_next = (i + 1 < main_map->l_nsearchlist ?
-			     main_map->l_searchlist[i + 1] : NULL);
+      _dl_rtld_map.l_prev = _dl_loaded->l_searchlist.r_list[i - 1];
+      _dl_rtld_map.l_next = (i + 1 < _dl_loaded->l_searchlist.r_nlist
+			     ? _dl_loaded->l_searchlist.r_list[i + 1]
+			     : NULL);
       assert (_dl_rtld_map.l_prev->l_next == _dl_rtld_map.l_next);
       _dl_rtld_map.l_prev->l_next = &_dl_rtld_map;
       if (_dl_rtld_map.l_next)
@@ -706,7 +714,6 @@ of this helper program; chances are you did not intend to run this program.\n\
   {
     struct version_check_args args;
     args.doexit = mode == normal;
-    args.main_map = main_map;
     _dl_receive_error (print_missing_version, version_check_doit, &args);
   }
 
@@ -745,7 +752,7 @@ of this helper program; chances are you did not intend to run this program.\n\
 	  {
 	    const ElfW(Sym) *ref = NULL;
 	    ElfW(Addr) loadbase = _dl_lookup_symbol (_dl_argv[i], &ref,
-						     &_dl_default_scope[2],
+						     _dl_loaded->l_scope,
 						     "argument",
 						     ELF_MACHINE_JMP_SLOT);
 	    char buf[20], *bp;
@@ -780,7 +787,6 @@ of this helper program; chances are you did not intend to run this program.\n\
 		      args.l = l;
 		      _dl_receive_error (print_unresolved, relocate_doit,
 					 &args);
-		      *_dl_global_scope_end = NULL;
 		    }
 		  l = l->l_prev;
 		} while (l);
@@ -890,11 +896,8 @@ of this helper program; chances are you did not intend to run this program.\n\
     do
       {
 	if (l != &_dl_rtld_map)
-	  {
-	    _dl_relocate_object (l, _dl_object_relocation_scope (l), lazy,
-				 consider_profiling);
-	    *_dl_global_scope_end = NULL;
-	  }
+	  _dl_relocate_object (l, l->l_scope, lazy, consider_profiling);
+
 	l = l->l_prev;
       } while (l);
 
@@ -908,9 +911,13 @@ of this helper program; chances are you did not intend to run this program.\n\
     if (_dl_rtld_map.l_opencount > 0)
       /* There was an explicit ref to the dynamic linker as a shared lib.
 	 Re-relocate ourselves with user-controlled symbol definitions.  */
-      _dl_relocate_object (&_dl_rtld_map, &_dl_default_scope[2], 0, 0);
+      _dl_relocate_object (&_dl_rtld_map, _dl_loaded->l_scope, 0, 0);
   }
 
+  /* Now set up the variable which helps the assembler startup code.  */
+  _dl_main_searchlist = &_dl_loaded->l_searchlist;
+  _dl_global_scope[0] = &_dl_loaded->l_searchlist;
+
   {
     /* Initialize _r_debug.  */
     struct r_debug *r = _dl_debug_initialize (_dl_rtld_map.l_addr);