about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog84
-rw-r--r--Makerules2
-rw-r--r--elf/Makefile7
-rw-r--r--elf/dl-close.c107
-rw-r--r--elf/dl-deps.c4
-rw-r--r--elf/dl-load.c314
-rw-r--r--elf/dl-lookup.c125
-rw-r--r--elf/dl-object.c12
-rw-r--r--elf/dl-open.c81
-rw-r--r--elf/dl-reloc.c33
-rw-r--r--elf/dl-runtime.c58
-rw-r--r--elf/dlclose.c70
-rw-r--r--elf/dlopen.c2
-rw-r--r--elf/link.h68
-rw-r--r--elf/rtld.c83
-rw-r--r--sysdeps/i386/dl-machine.h10
-rw-r--r--sysdeps/m68k/dl-machine.h6
17 files changed, 667 insertions, 399 deletions
diff --git a/ChangeLog b/ChangeLog
index 231facf905..02a94226e3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,89 @@
 Mon Jun 10 06:14:03 1996  Roland McGrath  <roland@delasyd.gnu.ai.mit.edu>
 
+	* Makerules ($(common-objpfx)libc.so): Depend on $(elfobjdir)/ld.so.
+
+	* elf/dl-close.c: New file.
+	* elf/link.h: Declare _dl_close.
+	* elf/Makefile (routines): Add dl-close.
+	* elf/dlclose.c (dlclose): Use _dl_close.
+
+	* elf/Makefile ($(objpfx)librtld.so): Remove libelf from deps.
+
+	* elf/dl-runtime.c (_dl_global_scope): New variable.
+	(_dl_object_relocation_scope): New function.
+	(fixup): Use it.
+	* elf/rtld.c (dl_main): Use it.
+	* elf/dl-open.c (_dl_open): Use it.  If (MODE & RTLD_GLOBAL), set the
+	l_global bit and append the new map to _dl_global_scope.
+	* elf/link.h: Declare _dl_global_scope, _dl_global_scope_alloc,
+	and _dl_object_relocation_scope.
+
+	* elf/link.h (struct link_map): Add l_loader member.
+	Remove _dl_map_object_from_fd decl.
+	* elf/dl-load.c (_dl_map_object): Pass LOADER to ...
+	(_dl_map_object_from_fd): Take new arg LOADER and set l_loader member.
+	(_dl_map_object): Try DT_RPATH from all loaders up the chain.
+
+	* elf/dl-object.c (_dl_loaded): Variable removed.
+	(_dl_default_scope): New variable replaces it.
+	* elf/link.h (_dl_loaded): Remove variable decl; instead define as
+	macro for _dl_default_scope[2].
+	(_dl_default_scope): Declare it.
+	* sysdeps/i386/dl-machine.h (RTLD_START): Use _dl_default_scope[2]
+	instead of _dl_loaded.
+	* sysdeps/m68k/dl-machine.h (RTLD_START): Likewise.
+	* elf/rtld.c (dl_main): Use _dl_default_scope for symbol lookups.
+
+	* elf/dl-reloc.c (_dl_relocate_object): Remove check for _dl_rtld_map.
+	* elf/rtld.c (dl_main): Pass 0 for LAZY flag when re-relocating self.
+
+	* elf/link.h (struct link_map.l_type): Remove lt_interpreter.
+ 	(struct link_map): Add new flag member l_global.
+
+	* elf/dl-reloc.c (_dl_relocate_object): Check for _dl_rtld_map
+	directly instead of looking for lt_interpreter.
+	* sysdeps/i386/dl-machine.h (elf_machine_rel): Likewise.
+	* elf/rtld.c (_dl_start): Don't bother setting BOOTSTRAP_MAP.l_type.
+	(dl_main): Set _dl_rtld_map.l_type to lt_library.
+
+	* elf/dl-deps.c (_dl_map_object_deps): Propagate MAP->l_type to
+	dependencies loaded, downgrading lt_executable -> lt_library.
+
+	* elf/dl-load.c (_dl_map_object_from_fd): Take new arg TYPE and set
+	l_type from that, translating lt_library->lt_executable based on the
+	file's ELF type.
+	(_dl_map_object): Likewise.
+	* elf/link.h: Update prototypes.
+	* elf/dl-open.c: Pass type lt_loaded.
+	* elf/rtld.c: Pass type lt_library.
+
+	* elf/dl-load.c (_dl_map_object_from_fd): Handle null return from
+	_dl_new_object.
+	(_dl_map_object_from_fd: lose): Unchain and free L if it's not null.
+	Free REALNAME, and just use NAME in error message.
+	* elf/dl-object.c (_dl_new_object): If malloc fails, return null
+	instead of calling _dl_signal_error.
+
+	* elf/dl-load.c (_dl_map_object_from_fd): Close FD before signalling
+	error for _dl_zerofd setup failure.
+
+	* elf/dl-object.c (_dl_startup_loaded): Variable removed.
+	* elf/link.h: Remove its decl.
+
+	* elf/dl-reloc.c (_dl_relocate_object): Take new SCOPE arg and pass it
+	through to _dl_lookup_symbol.
+	* elf/link.h (_dl_relocate_object): Update comment and prototype.
+	* elf/rtld.c (dl_main): Pass scope vector to _dl_relocate_object.
+
+	* elf/dl-lookup.c (_dl_lookup_symbol): Arg SYMBOL_SCOPE is now a
+	null-terminated vector of pointers, no longer a vector of exactly two.
+	* elf/link.h (_dl_lookup_symbol): Update comment and prototype.
+
+	* elf/dl-runtime.c (fixup): Set up scope for symbol lookup properly as
+	done in _dl_relocate_object.
+
+	* elf/dlopen.c: Pass "" to _dl_open when FILE is null.
+
 	* elf/link.h: Include elfclass.h to define __ELF_NATIVE_CLASS.
 	(ElfW, ELFW): Use it.
 	* elf/Makefile (headers): Add elfclass.h.
diff --git a/Makerules b/Makerules
index d74e5be97a..0310b000c4 100644
--- a/Makerules
+++ b/Makerules
@@ -496,7 +496,7 @@ LDFLAGS-c.so += -e __libc_print_version
 elfobjdir := $(firstword $(objdir) $(..)elf)
 $(common-objpfx)libc.so: $(elfobjdir)/soinit.so \
 			 $(common-objpfx)libc_pic.a \
-			 $(elfobjdir)/sofini.so
+			 $(elfobjdir)/sofini.so $(elfobjdir)/ld.so
 	$(build-shlib)
 
 ifdef libc.so-version
diff --git a/elf/Makefile b/elf/Makefile
index 06aeb7590a..c2c8f7c5b2 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -21,7 +21,8 @@
 subdir		:= elf
 
 headers		= elf.h elfclass.h link.h dlfcn.h
-routines	= init-first $(dl-routines) dl-open dl-symbol dl-support
+routines	= init-first $(dl-routines) \
+		  dl-open dl-close dl-symbol dl-support
 
 # The core dynamic linking functions are in libc for the static and
 # profiled libraries.
@@ -54,7 +55,7 @@ install-bin	= ldd
 # to run programs during the `make others' pass.
 lib-noranlib: $(objpfx)ld.so $(addprefix $(objpfx),$(extra-objs))
 
-ifneq (,$(filter linux%,$(config-os)))
+ifneq (,$(filter linux% linux,$(config-os)))
 extra-objs	+= linux-compat.so
 install-others	+= $(slibdir)/ld-linux.so.1
 lib-noranlib: $(objpfx)ld-linux.so.1
@@ -75,7 +76,7 @@ $(objpfx)dl-allobjs.so: $(rtld-routines:%=$(objpfx)%.so)
 # dynamic linker shared objects below.
 $(objpfx)librtld.so: $(objpfx)dl-allobjs.so \
 		     $(patsubst %,$(common-objpfx)lib%_pic.a,\
-				elf c $(LDLIBS-c.so:-l%=%))
+				c $(LDLIBS-c.so:-l%=%))
 	$(reloc-link) '-Wl,-(' $^ -lgcc '-Wl,-)'
 
 $(objpfx)ld.so: $(objpfx)librtld.so
diff --git a/elf/dl-close.c b/elf/dl-close.c
new file mode 100644
index 0000000000..69fdefac95
--- /dev/null
+++ b/elf/dl-close.c
@@ -0,0 +1,107 @@
+/* _dl_close -- Close a shared object opened by `_dl_open'.
+Copyright (C) 1996 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
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#include <link.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+
+
+#define LOSE(s) _dl_signal_error (0, map->l_name, s)
+
+void
+_dl_close (struct link_map *map)
+{
+  struct link_map **list;
+  unsigned int i;
+
+  if (map->l_opencount == 0)
+    LOSE ("shared object not open");
+
+  /* Decrement the reference count.  */
+  if (--map->l_opencount > 0 || map->l_type != lt_loaded)
+    /* There are still references to this object.  Do nothing more.  */
+    return;
+
+  list = map->l_searchlist;
+
+  /* The search list contains a counted reference to each object it
+     points to, the 0th elt being MAP itself.  Decrement the reference
+     counts on all the objects MAP depends on.  */
+  for (i = 1; i < map->l_nsearchlist; ++i)
+    --list[i]->l_opencount;
+
+  /* Clear the search list so it doesn't get freed while we are still
+     using it.  We have cached it in LIST and will free it when
+     finished.  */
+  map->l_searchlist = NULL;
+
+  /* Check each element of the search list to see if all references to
+     it are gone.  */
+  for (i = 0; i < map->l_nsearchlist; ++i)
+    {
+      struct link_map *map = list[i];
+      if (map->l_opencount == 0 && map->l_type == lt_loaded)
+	{
+	  /* That was the last reference, and this was a dlopen-loaded
+	     object.  We can unmap it.  */
+	  const ElfW(Phdr) *ph;
+
+	  if (map->l_info[DT_FINI])
+	    /* Call its termination function.  */
+	    (*(void (*) (void)) ((void *) map->l_addr +
+				 map->l_info[DT_FINI]->d_un.d_ptr)) ();
+
+	  if (map->l_global)
+	    {
+	      /* This object is in the global scope list.  Remove it.  */
+	      struct link_map **tail = _dl_global_scope_end;
+	      do
+		--tail;
+	      while (*tail != map);
+	      --_dl_global_scope_end;
+	      memcpy (tail, tail + 1, _dl_global_scope_end - tail);
+	      _dl_global_scope_end[0] = NULL;
+	      _dl_global_scope_end[1] = NULL;
+	    }
+
+	  /* Unmap the segments.  */
+	  for (ph = map->l_phdr; ph < &map->l_phdr[map->l_phnum]; ++ph)
+	    if (ph->p_type == PT_LOAD)
+	      {
+		ElfW(Addr) mapstart = ph->p_vaddr & ~(ph->p_align - 1);
+		ElfW(Addr) mapend = ((ph->p_vaddr + ph->p_memsz
+				      + ph->p_align - 1)
+				     & ~(ph->p_align - 1));
+		__munmap ((caddr_t) mapstart, mapend - mapstart);
+	      }
+
+	  /* Finally, unlink the data structure and free it.  */
+	  map->l_prev->l_next = map->l_next;
+	  if (map->l_next)
+	    map->l_next->l_prev = map->l_prev;
+	  if (map->l_searchlist)
+	    free (map->l_searchlist);
+	  free (map);
+	}
+    }
+
+  free (list);
+}
diff --git a/elf/dl-deps.c b/elf/dl-deps.c
index 7e3b259362..3e49fcfe01 100644
--- a/elf/dl-deps.c
+++ b/elf/dl-deps.c
@@ -62,7 +62,9 @@ _dl_map_object_deps (struct link_map *map)
 	      {
 		/* Map in the needed object.  */
 		struct link_map *dep
-		  = _dl_map_object (l, strtab + d->d_un.d_val);
+		  = _dl_map_object (l, strtab + d->d_un.d_val,
+				    l->l_type == lt_executable ? lt_library :
+				    l->l_type);
 
 		if (dep->l_reserved)
 		  /* This object is already in the search list we are
diff --git a/elf/dl-load.c b/elf/dl-load.c
index c6acc8c222..b9f4aa184b 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -63,152 +63,14 @@ int _dl_zerofd = -1;
 size_t _dl_pagesize;
 
 
-/* Try to open NAME in one of the directories in DIRPATH.
-   Return the fd, or -1.  If successful, fill in *REALNAME
-   with the malloc'd full directory name.  */
-
-static int
-open_path (const char *name, size_t namelen,
-	   const char *dirpath,
-	   char **realname)
-{
-  char *buf;
-  const char *p;
-  int fd;
-
-  p = dirpath;
-  if (p == NULL || *p == '\0')
-    {
-      errno = ENOENT;
-      return -1;
-    }
-
-  buf = __alloca (strlen (dirpath) + 1 + namelen);
-  do
-    {
-      size_t buflen;
-
-      dirpath = p;
-      p = strpbrk (dirpath, ":;");
-      if (p == NULL)
-	p = strchr (dirpath, '\0');
-
-      if (p == dirpath)
-	{
-	  /* Two adjacent colons, or a colon at the beginning or the end of
-	     the path means to search the current directory.  */
-	  (void) memcpy (buf, name, namelen);
-	  buflen = namelen;
-	}
-      else
-	{
-	  /* Construct the pathname to try.  */
-	  (void) memcpy (buf, dirpath, p - dirpath);
-	  buf[p - dirpath] = '/';
-	  (void) memcpy (&buf[(p - dirpath) + 1], name, namelen);
-	  buflen = p - dirpath + 1 + namelen;
-	}
-
-      fd = __open (buf, O_RDONLY);
-      if (fd != -1)
-	{
-	  *realname = malloc (buflen);
-	  if (*realname)
-	    {
-	      memcpy (*realname, buf, buflen);
-	      return fd;
-	    }
-	  else
-	    {
-	      /* No memory for the name, we certainly won't be able
-		 to load and link it.  */
-	      __close (fd);
-	      return -1;
-	    }
-	}
-      if (errno != ENOENT && errno != EACCES)
-	/* The file exists and is readable, but something went wrong.  */
-	return -1;
-    }
-  while (*p++ != '\0');
-
-  return -1;
-}
-
-/* Map in the shared object file NAME.  */
-
-struct link_map *
-_dl_map_object (struct link_map *loader, const char *name)
-{
-  int fd;
-  char *realname;
-  struct link_map *l;
-
-  /* Look for this name among those already loaded.  */
-  for (l = _dl_loaded; l; l = l->l_next)
-    if (! strcmp (name, l->l_libname))
-      {
-	/* The object is already loaded.
-	   Just bump its reference count and return it.  */
-	++l->l_opencount;
-	return l;
-      }
-
-  if (strchr (name, '/') == NULL)
-    {
-      /* Search for NAME in several places.  */
-
-      size_t namelen = strlen (name) + 1;
-
-      inline void trypath (const char *dirpath)
-	{
-	  fd = open_path (name, namelen, dirpath, &realname);
-	}
-
-      fd = -1;
-      if (loader && loader->l_info[DT_RPATH])
-	trypath ((const char *) (loader->l_addr +
-				 loader->l_info[DT_STRTAB]->d_un.d_ptr +
-				 loader->l_info[DT_RPATH]->d_un.d_val));
-      if (fd == -1 && ! _dl_secure)
-	trypath (getenv ("LD_LIBRARY_PATH"));
-      if (fd == -1)
-	{
-	  extern const char *_dl_rpath;	/* Set in rtld.c. */
-	  trypath (_dl_rpath);
-	}
-    }
-  else
-    {
-      fd = __open (name, O_RDONLY);
-      if (fd != -1)
-	{
-	  size_t len = strlen (name) + 1;
-	  realname = malloc (len);
-	  if (realname)
-	    memcpy (realname, name, len);
-	  else
-	    {
-	      __close (fd);
-	      fd = -1;
-	    }
-	}
-    }
-
-  if (fd == -1)
-    _dl_signal_error (errno, name, "cannot open shared object file");
-
-  return _dl_map_object_from_fd (name, fd, realname);
-}
-
-
 /* Map in the shared object NAME, actually located in REALNAME, and already
    opened on FD.  */
 
 struct link_map *
-_dl_map_object_from_fd (const char *name, int fd, char *realname)
+_dl_map_object_from_fd (const char *name, int fd, char *realname,
+			struct link_map *loader, int l_type)
 {
-  struct link_map *l = NULL;
+  struct link_map *l;
   void *file_mapping = NULL;
   size_t mapping_size = 0;
 
@@ -218,7 +80,17 @@ _dl_map_object_from_fd (const char *name, int fd, char *realname)
       (void) __close (fd);
       if (file_mapping)
 	__munmap (file_mapping, mapping_size);
-      _dl_signal_error (code, l ? l->l_name : name, msg);
+      if (l)
+	{
+	  /* Remove the stillborn object from the list and free it.  */
+	  if (l->l_prev)
+	    l->l_prev->l_next = l->l_next;
+	  if (l->l_next)
+	    l->l_next->l_prev = l->l_prev;
+	  free (l);
+	}
+      free (realname);
+      _dl_signal_error (code, name, msg);
     }
 
   inline caddr_t map_segment (ElfW(Addr) mapstart, size_t len,
@@ -304,17 +176,23 @@ _dl_map_object_from_fd (const char *name, int fd, char *realname)
   if (header->e_phentsize != sizeof (ElfW(Phdr)))
     LOSE ("ELF file's phentsize not the expected size");
 
-  /* Enter the new object in the list of loaded objects.  */
-  l = _dl_new_object (realname, name, lt_loaded);
-  l->l_opencount = 1;
-
   if (_dl_zerofd == -1)
     {
       _dl_zerofd = _dl_sysdep_open_zero_fill ();
       if (_dl_zerofd == -1)
-	_dl_signal_error (errno, NULL, "cannot open zero fill device");
+	{
+	  __close (fd);
+	  _dl_signal_error (errno, NULL, "cannot open zero fill device");
+	}
     }
 
+  /* Enter the new object in the list of loaded objects.  */
+  l = _dl_new_object (realname, name, l_type);
+  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 map in the program header table.  */
   l->l_entry = header->e_entry;
@@ -464,7 +342,8 @@ _dl_map_object_from_fd (const char *name, int fd, char *realname)
   /* We are done mapping in the file.  We no longer need the descriptor.  */
   __close (fd);
 
-  l->l_type = type == ET_EXEC ? lt_executable : lt_library;
+  if (l->l_type == lt_library && type == ET_EXEC)
+    l->l_type = lt_executable;
 
   if (l->l_ld == 0)
     {
@@ -484,3 +363,142 @@ _dl_map_object_from_fd (const char *name, int fd, char *realname)
 
   return l;
 }
+
+/* Try to open NAME in one of the directories in DIRPATH.
+   Return the fd, or -1.  If successful, fill in *REALNAME
+   with the malloc'd full directory name.  */
+
+static int
+open_path (const char *name, size_t namelen,
+	   const char *dirpath,
+	   char **realname)
+{
+  char *buf;
+  const char *p;
+  int fd;
+
+  p = dirpath;
+  if (p == NULL || *p == '\0')
+    {
+      errno = ENOENT;
+      return -1;
+    }
+
+  buf = __alloca (strlen (dirpath) + 1 + namelen);
+  do
+    {
+      size_t buflen;
+
+      dirpath = p;
+      p = strpbrk (dirpath, ":;");
+      if (p == NULL)
+	p = strchr (dirpath, '\0');
+
+      if (p == dirpath)
+	{
+	  /* Two adjacent colons, or a colon at the beginning or the end of
+	     the path means to search the current directory.  */
+	  (void) memcpy (buf, name, namelen);
+	  buflen = namelen;
+	}
+      else
+	{
+	  /* Construct the pathname to try.  */
+	  (void) memcpy (buf, dirpath, p - dirpath);
+	  buf[p - dirpath] = '/';
+	  (void) memcpy (&buf[(p - dirpath) + 1], name, namelen);
+	  buflen = p - dirpath + 1 + namelen;
+	}
+
+      fd = __open (buf, O_RDONLY);
+      if (fd != -1)
+	{
+	  *realname = malloc (buflen);
+	  if (*realname)
+	    {
+	      memcpy (*realname, buf, buflen);
+	      return fd;
+	    }
+	  else
+	    {
+	      /* No memory for the name, we certainly won't be able
+		 to load and link it.  */
+	      __close (fd);
+	      return -1;
+	    }
+	}
+      if (errno != ENOENT && errno != EACCES)
+	/* The file exists and is readable, but something went wrong.  */
+	return -1;
+    }
+  while (*p++ != '\0');
+
+  return -1;
+}
+
+/* Map in the shared object file NAME.  */
+
+struct link_map *
+_dl_map_object (struct link_map *loader, const char *name, int type)
+{
+  int fd;
+  char *realname;
+  struct link_map *l;
+
+  /* Look for this name among those already loaded.  */
+  for (l = _dl_loaded; l; l = l->l_next)
+    if (! strcmp (name, l->l_libname))
+      {
+	/* The object is already loaded.
+	   Just bump its reference count and return it.  */
+	++l->l_opencount;
+	return l;
+      }
+
+  if (strchr (name, '/') == NULL)
+    {
+      /* Search for NAME in several places.  */
+
+      size_t namelen = strlen (name) + 1;
+
+      inline void trypath (const char *dirpath)
+	{
+	  fd = open_path (name, namelen, dirpath, &realname);
+	}
+
+      fd = -1;
+      for (l = loader; l; l = l->l_loader)
+	if (l && l->l_info[DT_RPATH])
+	  trypath ((const char *) (l->l_addr +
+				   l->l_info[DT_STRTAB]->d_un.d_ptr +
+				   l->l_info[DT_RPATH]->d_un.d_val));
+      if (fd == -1 && ! _dl_secure)
+	trypath (getenv ("LD_LIBRARY_PATH"));
+      if (fd == -1)
+	{
+	  extern const char *_dl_rpath;	/* Set in rtld.c. */
+	  trypath (_dl_rpath);
+	}
+    }
+  else
+    {
+      fd = __open (name, O_RDONLY);
+      if (fd != -1)
+	{
+	  size_t len = strlen (name) + 1;
+	  realname = malloc (len);
+	  if (realname)
+	    memcpy (realname, name, len);
+	  else
+	    {
+	      __close (fd);
+	      fd = -1;
+	    }
+	}
+    }
+
+  if (fd == -1)
+    _dl_signal_error (errno, name, "cannot open shared object file");
+
+  return _dl_map_object_from_fd (name, fd, realname, loader, type);
+}
diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
index 0674253041..5f1e6d03d9 100644
--- a/elf/dl-lookup.c
+++ b/elf/dl-lookup.c
@@ -50,7 +50,7 @@ _dl_elf_hash (const char *name)
 
 ElfW(Addr)
 _dl_lookup_symbol (const char *undef_name, const ElfW(Sym) **ref,
-		   struct link_map *symbol_scope[2],
+		   struct link_map *symbol_scope[],
 		   const char *reference_name,
 		   ElfW(Addr) reloc_addr,
 		   int noplt)
@@ -65,69 +65,68 @@ _dl_lookup_symbol (const char *undef_name, const ElfW(Sym) **ref,
   struct link_map **scope, *map;
 
   /* Search the relevant loaded objects for a definition.  */
-  for (scope = symbol_scope; scope < &symbol_scope[2]; ++scope)
-    if (*scope)
-      for (i = 0; i < (*scope)->l_nsearchlist; ++i)
-	{
-	  const ElfW(Sym) *symtab;
-	  const char *strtab;
-	  ElfW(Word) symidx;
-
-	  map = (*scope)->l_searchlist[i];
-
-	  symtab = ((void *) map->l_addr + map->l_info[DT_SYMTAB]->d_un.d_ptr);
-	  strtab = ((void *) map->l_addr + map->l_info[DT_STRTAB]->d_un.d_ptr);
-
-	  /* Search the appropriate hash bucket in this object's symbol table
-	     for a definition for the same symbol name.  */
-	  for (symidx = map->l_buckets[hash % map->l_nbuckets];
-	       symidx != STN_UNDEF;
-	       symidx = map->l_chain[symidx])
-	    {
-	      const ElfW(Sym) *sym = &symtab[symidx];
-
-	      if (sym->st_value == 0 || /* No value.  */
-		  /* Cannot resolve to the location being filled in.  */
-		  reloc_addr == map->l_addr + sym->st_value ||
-		  (noplt && sym->st_shndx == SHN_UNDEF)) /* Reject PLT.  */
-		continue;
-
-	      switch (ELFW(ST_TYPE) (sym->st_info))
-		{
-		case STT_NOTYPE:
-		case STT_FUNC:
-		case STT_OBJECT:
-		  break;
-		default:
-		  /* Not a code/data definition.  */
-		  continue;
-		}
-
-	      if (sym != *ref && strcmp (strtab + sym->st_name, undef_name))
-		/* Not the symbol we are looking for.  */
+  for (scope = symbol_scope; *scope; ++scope)
+    for (i = 0; i < (*scope)->l_nsearchlist; ++i)
+      {
+	const ElfW(Sym) *symtab;
+	const char *strtab;
+	ElfW(Word) symidx;
+
+	map = (*scope)->l_searchlist[i];
+
+	symtab = ((void *) map->l_addr + map->l_info[DT_SYMTAB]->d_un.d_ptr);
+	strtab = ((void *) map->l_addr + map->l_info[DT_STRTAB]->d_un.d_ptr);
+
+	/* Search the appropriate hash bucket in this object's symbol table
+	   for a definition for the same symbol name.  */
+	for (symidx = map->l_buckets[hash % map->l_nbuckets];
+	     symidx != STN_UNDEF;
+	     symidx = map->l_chain[symidx])
+	  {
+	    const ElfW(Sym) *sym = &symtab[symidx];
+
+	    if (sym->st_value == 0 || /* No value.  */
+		/* Cannot resolve to the location being filled in.  */
+		reloc_addr == map->l_addr + sym->st_value ||
+		(noplt && sym->st_shndx == SHN_UNDEF)) /* Reject PLT.  */
+	      continue;
+
+	    switch (ELFW(ST_TYPE) (sym->st_info))
+	      {
+	      case STT_NOTYPE:
+	      case STT_FUNC:
+	      case STT_OBJECT:
+		break;
+	      default:
+		/* Not a code/data definition.  */
 		continue;
-
-	      switch (ELFW(ST_BIND) (sym->st_info))
-		{
-		case STB_GLOBAL:
-		  /* Global definition.  Just what we need.  */
-		  *ref = sym;
-		  return map->l_addr;
-		case STB_WEAK:
-		  /* Weak definition.  Use this value if we don't find
-		     another.  */
-		  if (! weak_value.s)
-		    {
-		      weak_value.s = sym;
-		      weak_value.a = map->l_addr;
-		    }
-		  break;
-		default:
-		  /* Local symbols are ignored.  */
-		  break;
-		}
-	    }
-	}
+	      }
+
+	    if (sym != *ref && strcmp (strtab + sym->st_name, undef_name))
+	      /* Not the symbol we are looking for.  */
+	      continue;
+
+	    switch (ELFW(ST_BIND) (sym->st_info))
+	      {
+	      case STB_GLOBAL:
+		/* Global definition.  Just what we need.  */
+		*ref = sym;
+		return map->l_addr;
+	      case STB_WEAK:
+		/* Weak definition.  Use this value if we don't find
+		   another.  */
+		if (! weak_value.s)
+		  {
+		    weak_value.s = sym;
+		    weak_value.a = map->l_addr;
+		  }
+		break;
+	      default:
+		/* Local symbols are ignored.  */
+		break;
+	      }
+	  }
+      }
 
   if (weak_value.s == NULL && ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK)
     {
diff --git a/elf/dl-object.c b/elf/dl-object.c
index 11e9e082d2..851d133df3 100644
--- a/elf/dl-object.c
+++ b/elf/dl-object.c
@@ -1,5 +1,5 @@
 /* Storage management for the chain of loaded shared objects.
-Copyright (C) 1995 Free Software Foundation, Inc.
+Copyright (C) 1995, 1996 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
@@ -24,11 +24,8 @@ Cambridge, MA 02139, USA.  */
 #include <errno.h>
 
 
-/* List of objects currently loaded.  */
-struct link_map *_dl_loaded;
-
-/* Tail of that list which were loaded at startup.  */
-struct link_map *_dl_startup_loaded;
+/* 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.  */
@@ -38,8 +35,7 @@ _dl_new_object (char *realname, const char *libname, int type)
 {
   struct link_map *new = malloc (sizeof *new);
   if (! new)
-    _dl_signal_error (ENOMEM, libname,
-		      "cannot allocate shared object descriptor");
+    return NULL;
 
   memset (new, 0, sizeof *new);
   new->l_name = realname;
diff --git a/elf/dl-open.c b/elf/dl-open.c
index 221abbd35e..9389303b90 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -19,6 +19,10 @@ Cambridge, MA 02139, USA.  */
 
 #include <link.h>
 #include <dlfcn.h>
+#include <stdlib.h>
+#include <errno.h>
+
+size_t _dl_global_scope_alloc;
 
 struct link_map *
 _dl_open (struct link_map *parent, const char *file, int mode)
@@ -26,16 +30,83 @@ _dl_open (struct link_map *parent, const char *file, int mode)
   struct link_map *new, *l;
   ElfW(Addr) init;
 
+
   /* Load the named object.  */
-  new = _dl_map_object (parent, file);
+  new = _dl_map_object (parent, file, lt_loaded);
+  if (new->l_searchlist)
+    /* It was already open.  */
+    return new;
 
   /* Load that object's dependencies.  */
   _dl_map_object_deps (new);
 
-  /* Relocate the objects loaded.  */
-  for (l = new; l; l = l->l_next)
-    if (! l->l_relocated)
-      _dl_relocate_object (l, (mode & RTLD_BINDING_MASK) == RTLD_LAZY);
+
+  /* Relocate the objects loaded.  We do this in reverse order so that copy
+     relocs of earlier objects overwrite the data written by later objects.  */
+
+  l = new;
+  while (l->l_next)
+    l = l->l_next;
+  do
+    {
+      if (! l->l_relocated)
+	{
+	  _dl_relocate_object (l, _dl_object_relocation_scope (l),
+			       (mode & RTLD_BINDING_MASK) == RTLD_LAZY);
+	  *_dl_global_scope_end = NULL;
+	}
+
+      l = l->l_prev;
+    } while (l != new);
+
+  new->l_global = (mode & RTLD_GLOBAL);
+  if (new->l_global)
+    {
+      /* 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.  */
+
+      if (_dl_global_scope_alloc == 0)
+	{
+	  /* This is the first dynamic object given global scope.  */
+	  _dl_global_scope_alloc = 8;
+	  _dl_global_scope = malloc (8 * sizeof (struct link_map *));
+	  if (! _dl_global_scope)
+	    {
+	      _dl_global_scope = _dl_default_scope;
+	    nomem:
+	      _dl_close (new);
+	      _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;
+	}
+      else
+	{
+	  if (_dl_global_scope_alloc <
+	      _dl_global_scope_end - _dl_global_scope + 2)
+	    {
+	      /* Must extend the list.  */
+	      struct link_map **new = realloc (_dl_global_scope,
+					       _dl_global_scope_alloc * 2);
+	      if (! new)
+		goto nomem;
+	      _dl_global_scope_end = new + (_dl_global_scope_end -
+					    _dl_global_scope);
+	      _dl_global_scope = new;
+	      _dl_global_scope_alloc *= 2;
+	    }
+
+	  /* 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;
+	}
+    }
 
   /* Run the initializer functions of new objects.  */
   while (init = _dl_init_next (new))
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index 5da8575da5..e6778e7de3 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -26,7 +26,7 @@ Cambridge, MA 02139, USA.  */
 
 
 void
-_dl_relocate_object (struct link_map *l, int lazy)
+_dl_relocate_object (struct link_map *l, struct link_map *scope[], int lazy)
 {
   if (l->l_relocated)
     return;
@@ -52,44 +52,26 @@ _dl_relocate_object (struct link_map *l, int lazy)
     }
 
   {
-    struct link_map *scope[2];
+    /* Do the actual relocation of the object's GOT and other data.  */
 
-    const char *strtab
+    const char *strtab		/* String table object symbols.  */
       = ((void *) l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr);
-
     ElfW(Addr) resolve (const ElfW(Sym) **ref,
 			ElfW(Addr) reloc_addr, int noplt)
       {
+	/* Look up the referenced symbol in the specified scope.  */
 	return _dl_lookup_symbol (strtab + (*ref)->st_name, ref, scope,
 				  l->l_name, reloc_addr, noplt);
       }
 
-    if (l->l_info[DT_SYMBOLIC])
-      {
-	scope[0] = l;
-	scope[1] = _dl_loaded;
-      }
-    else
-      {
-	scope[0] = _dl_loaded;
-	scope[1] = l;
-      }
-
-    if (l->l_type == lt_interpreter)
-      /* We cannot be lazy when relocating the dynamic linker itself.  It
-	 was previously relocated eagerly (allowing us to be running now),
-	 and needs always to be fully relocated so it can run without the
-	 aid of run-time fixups (because it's the one to do them), so we
-	 must always re-relocate its PLT eagerly.  */
-      lazy = 0;
-
     ELF_DYNAMIC_RELOCATE (l, lazy, resolve);
   }
 
-  /* Set up the PLT so its unrelocated entries will
-     jump to _dl_runtime_resolve, which will relocate them.  */
+  /* Set up the PLT so its unrelocated entries will jump to
+     _dl_runtime_resolve (dl-runtime.c), which will relocate them.  */
   elf_machine_runtime_setup (l, lazy);
 
+  /* Mark the object so we know ths work has been done.  */
   l->l_relocated = 1;
 
   if (l->l_info[DT_TEXTREL])
@@ -114,5 +96,4 @@ _dl_relocate_object (struct link_map *l, int lazy)
 				"can't restore segment prot after reloc");
 	  }
     }
-
 }
diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c
index c306543155..8ad2c0ffa4 100644
--- a/elf/dl-runtime.c
+++ b/elf/dl-runtime.c
@@ -20,6 +20,54 @@ Cambridge, MA 02139, USA.  */
 #include <link.h>
 #include "dynamic-link.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 **
+_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);
+
+      /* The primary scope is this object itself and its
+	 dependencies.  */
+      _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;
+      *_dl_global_scope_end = l;
+      return &_dl_global_scope[2];
+    }
+}
+
 /* Figure out the right type, Rel or Rela.  */
 #define elf_machine_rel 1
 #define elf_machine_rela 2
@@ -67,17 +115,21 @@ fixup (
     = (const void *) (l->l_addr + l->l_info[DT_JMPREL]->d_un.d_ptr +
 		      reloc_offset);
 
+  /* Set up the scope to find symbols referenced by this object.  */
+  struct link_map **scope = _dl_object_relocation_scope (l);
+
+  /* Perform the specified relocation.  */
   ElfW(Addr) resolve (const ElfW(Sym) **ref,
 		      ElfW(Addr) reloc_addr, int noplt)
     {
-      struct link_map *scope[2] = { _dl_loaded, NULL };
       return _dl_lookup_symbol (strtab + (*ref)->st_name, ref,
 				scope, l->l_name, reloc_addr, noplt);
     }
-
-  /* Perform the specified relocation.  */
   elf_machine_relplt (l, reloc, &symtab[ELFW(R_SYM) (reloc->r_info)], resolve);
 
+  *_dl_global_scope_end = NULL;
+
+  /* Return the address that was written by the relocation.  */
   return *(ElfW(Addr) *) (l->l_addr + reloc->r_offset);
 }
 
diff --git a/elf/dlclose.c b/elf/dlclose.c
index 0d2689e01c..a5c8e7c17e 100644
--- a/elf/dlclose.c
+++ b/elf/dlclose.c
@@ -19,81 +19,13 @@ Cambridge, MA 02139, USA.  */
 
 #include <link.h>
 #include <dlfcn.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/mman.h>
-
-
-#define LOSE(s) _dl_signal_error (0, map->l_name, s)
 
 int
 dlclose (void *handle)
 {
   void doit (void)
     {
-      struct link_map *map = handle;
-      struct link_map **list;
-      unsigned int i;
-
-      if (map->l_opencount == 0)
-	LOSE ("shared object not open");
-
-      /* Decrement the reference count.  */
-      if (--map->l_opencount > 0 || map->l_type != lt_loaded)
-	/* There are still references to this object.  Do nothing more.  */
-	return;
-
-      list = map->l_searchlist;
-
-      /* The search list contains a counted reference to each object it
-	 points to, the 0th elt being MAP itself.  Decrement the reference
-	 counts on all the objects MAP depends on.  */
-      for (i = 1; i < map->l_nsearchlist; ++i)
-	--list[i]->l_opencount;
-
-      /* Clear the search list so it doesn't get freed while we are still
-         using it.  We have cached it in LIST and will free it when
-         finished.  */
-      map->l_searchlist = NULL;
-
-      /* Check each element of the search list to see if all references to
-         it are gone.  */
-      for (i = 0; i < map->l_nsearchlist; ++i)
-	{
-	  struct link_map *map = list[i];
-	  if (map->l_opencount == 0 && map->l_type == lt_loaded)
-	    {
-	      /* That was the last reference, and this was a dlopen-loaded
-		 object.  We can unmap it.  */
-	      const ElfW(Phdr) *ph;
-
-	      if (map->l_info[DT_FINI])
-		/* Call its termination function.  */
-		(*(void (*) (void)) ((void *) map->l_addr +
-				     map->l_info[DT_FINI]->d_un.d_ptr)) ();
-
-	      /* Unmap the segments.  */
-	      for (ph = map->l_phdr; ph < &map->l_phdr[map->l_phnum]; ++ph)
-		if (ph->p_type == PT_LOAD)
-		  {
-		    ElfW(Addr) mapstart = ph->p_vaddr & ~(ph->p_align - 1);
-		    ElfW(Addr) mapend = ((ph->p_vaddr + ph->p_memsz
-					  + ph->p_align - 1)
-					 & ~(ph->p_align - 1));
-		    munmap ((caddr_t) mapstart, mapend - mapstart);
-		  }
-
-	      /* Finally, unlink the data structure and free it.  */
-	      map->l_prev->l_next = map->l_next;
-	      if (map->l_next)
-		map->l_next->l_prev = map->l_prev;
-	      if (map->l_searchlist)
-		free (map->l_searchlist);
-	      free (map);
-	    }
-	}
-
-      free (list);
+      _dl_close (handle);
     }
 
   return _dlerror_run (doit) ? -1 : 0;
diff --git a/elf/dlopen.c b/elf/dlopen.c
index 74ab8bb715..e261fcadd9 100644
--- a/elf/dlopen.c
+++ b/elf/dlopen.c
@@ -28,7 +28,7 @@ dlopen (const char *file, int mode)
 
   void doit (void)
     {
-      new = _dl_open (_dl_loaded, file, mode);
+      new = _dl_open (_dl_loaded, file ?: "", mode);
     }
 
   return _dlerror_run (doit) ? NULL : new;
diff --git a/elf/link.h b/elf/link.h
index 6d284cbbde..89dfa2087f 100644
--- a/elf/link.h
+++ b/elf/link.h
@@ -106,6 +106,9 @@ struct link_map
     struct link_map **l_searchlist;
     unsigned int l_nsearchlist;
 
+    /* Dependent object that first caused this object to be loaded.  */
+    struct link_map *l_loader;
+
     /* Symbol hash table.  */
     ElfW(Word) l_nbuckets;
     const ElfW(Word) *l_buckets, *l_chain;
@@ -114,14 +117,14 @@ struct link_map
     enum			/* Where this object came from.  */
       {
 	lt_executable,		/* The main executable program.  */
-	lt_interpreter,		/* The interpreter: the dynamic linker.  */
 	lt_library,		/* Library needed by main executable.  */
 	lt_loaded,		/* Extra run-time loaded shared object.  */
       } l_type:2;
     unsigned int l_relocated:1;	/* Nonzero if object's relocations done.  */
     unsigned int l_init_called:1; /* Nonzero if DT_INIT function called.  */
     unsigned int l_init_running:1; /* Nonzero while DT_INIT function runs.  */
-    unsigned int l_reserved:3;	/* Reserved for internal use.  */
+    unsigned int l_global:1;	/* Nonzero if object in _dl_global_scope.  */
+    unsigned int l_reserved:2;	/* Reserved for internal use.  */
   };
 
 /* Internal functions of the run-time dynamic linker.
@@ -188,12 +191,7 @@ extern int _dlerror_run (void (*operate) (void));
    LOADER's DT_RPATH is used in searching for NAME.
    If the object is already opened, returns its existing map.  */
 extern struct link_map *_dl_map_object (struct link_map *loader,
-					const char *name);
-
-/* Similar, but file found at REALNAME and opened on FD.
-   REALNAME must malloc'd storage and is used in internal data structures.  */
-extern struct link_map *_dl_map_object_from_fd (const char *name,
-						int fd, char *realname);
+					const char *name, int type);
 
 /* Call _dl_map_object on the dependencies of MAP, and
    set up MAP->l_searchlist.  */
@@ -210,21 +208,24 @@ extern void _dl_setup_hash (struct link_map *map);
 extern struct link_map *_dl_open (struct link_map *loader,
 				  const char *name, int mode);
 
+/* Close an object previously opened by _dl_open.  */
+extern void _dl_close (struct link_map *map);
 
 
 /* Search loaded objects' symbol tables for a definition of the symbol
    referred to by UNDEF.  *SYM is the symbol table entry containing the
    reference; it is replaced with the defining symbol, and the base load
-   address of the defining object is returned.  Each of SYMBOL_SCOPE[0] and
-   SYMBOL_SCOPE[1] that is not null and their dependencies are searched to
-   resolve the name.  REFERENCE_NAME should name the object containing the
-   reference; it is used in error messages.  RELOC_ADDR is the address
-   being fixed up and the chosen symbol cannot be one with this value.  If
-   NOPLT is nonzero, then the reference must not be resolved to a PLT
-   entry.  */
+   address of the defining object is returned.  SYMBOL_SCOPE is a
+   null-terminated list of object scopes to search; each object's
+   l_searchlist (i.e. the segment of the dependency tree starting at that
+   object) is searched in turn.  REFERENCE_NAME should name the object
+   containing the reference; it is used in error messages.  RELOC_ADDR is
+   the address being fixed up and the chosen symbol cannot be one with this
+   value.  If NOPLT is nonzero, then the reference must not be resolved to
+   a PLT entry.  */
 extern ElfW(Addr) _dl_lookup_symbol (const char *undef,
 				     const ElfW(Sym) **sym,
-				     struct link_map *symbol_scope[2],
+				     struct link_map *symbol_scope[],
 				     const char *reference_name,
 				     ElfW(Addr) reloc_addr,
 				     int noplt);
@@ -236,11 +237,33 @@ 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;
 
-/* List of objects currently loaded.  */
-extern struct link_map *_dl_loaded;
+/* 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);
 
-/* Tail of that list which were loaded at startup.  */
-extern struct link_map *_dl_startup_loaded;
 
 /* Allocate a `struct link_map' for a new object being loaded,
    and enter it into the _dl_loaded list.  */
@@ -248,8 +271,11 @@ extern struct link_map *_dl_new_object (char *realname, const char *libname,
 					int type);
 
 /* 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, int lazy);
+extern void _dl_relocate_object (struct link_map *map,
+				 struct link_map *scope[],
+				 int lazy);
 
 /* Return the address of the next initializer function for MAP or one of
    its dependencies that has not yet been run.  When there are no more
diff --git a/elf/rtld.c b/elf/rtld.c
index bc1f71bd4e..66477274e5 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -75,13 +75,6 @@ _dl_start (void *arg)
   /* Relocate ourselves so we can do normal function calls and
      data access using the global offset table.  */
 
-  /* We must initialize `l_type' to make sure it is not `lt_interpreter'.
-     That is the type to describe us, but not during bootstrapping--it
-     indicates to elf_machine_rel{,a} that we were already relocated during
-     bootstrapping, so it must anti-perform each bootstrapping relocation
-     before applying the final relocation when ld.so is linked in as
-     normal a shared library.  */
-  bootstrap_map.l_type = lt_library;
   ELF_DYNAMIC_RELOCATE (&bootstrap_map, 0, NULL);
 
 
@@ -178,7 +171,7 @@ of this helper program; chances are you did not intend to run this program.\n",
       --_dl_argc;
       ++_dl_argv;
 
-      l = _dl_map_object (NULL, _dl_argv[0]);
+      l = _dl_map_object (NULL, _dl_argv[0], lt_library);
       phdr = l->l_phdr;
       phent = l->l_phnum;
       l->l_name = (char *) "";
@@ -188,7 +181,7 @@ of this helper program; chances are you did not intend to run this program.\n",
     {
       /* Create a link_map for the executable itself.
 	 This will be what dlopen on "" returns.  */
-      l = _dl_new_object ((char *) "", "", lt_executable);
+      l = _dl_new_object ((char *) "", "", lt_library);
       l->l_phdr = phdr;
       l->l_phnum = phent;
       interpreter_name = 0;
@@ -245,7 +238,7 @@ 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.  */
   _dl_rtld_map.l_name = (char *) _dl_rtld_map.l_libname = interpreter_name;
-  _dl_rtld_map.l_type = lt_interpreter;
+  _dl_rtld_map.l_type = lt_library;
   while (l->l_next)
     l = l->l_next;
   l->l_next = &_dl_rtld_map;
@@ -293,9 +286,9 @@ of this helper program; chances are you did not intend to run this program.\n",
       for (i = 1; i < _dl_argc; ++i)
 	{
 	  const ElfW(Sym) *ref = NULL;
-	  struct link_map *scope[2] ={ _dl_loaded, NULL };
-	  ElfW(Addr) loadbase
-	    = _dl_lookup_symbol (_dl_argv[i], &ref, scope, "argument", 0, 0);
+	  ElfW(Addr) loadbase = _dl_lookup_symbol (_dl_argv[i], &ref,
+						   &_dl_default_scope[2],
+						   "argument", 0, 0);
 	  char buf[20], *bp;
 	  buf[sizeof buf - 1] = '\0';
 	  bp = _itoa (ref->st_value, &buf[sizeof buf - 1], 16, 0);
@@ -314,35 +307,41 @@ of this helper program; chances are you did not intend to run this program.\n",
 
   lazy = !_dl_secure && *(getenv ("LD_BIND_NOW") ?: "") == '\0';
 
-  /* Now we have all the objects loaded.  Relocate them all except for
-     the dynamic linker itself.  We do this in reverse order so that
-     copy relocs of earlier objects overwrite the data written by later
-     objects.  We do not re-relocate the dynamic linker itself in this
-     loop because that could result in the GOT entries for functions we
-     call being changed, and that would break us.  It is safe to
-     relocate the dynamic linker out of order because it has no copy
-     relocs (we know that because it is self-contained).  */
-  l = _dl_loaded;
-  while (l->l_next)
-    l = l->l_next;
-  do
-    {
-      if (l != &_dl_rtld_map)
-	_dl_relocate_object (l, lazy);
-      l = l->l_prev;
-    } while (l);
-
-  /* Do any necessary cleanups for the startup OS interface code.
-     We do these now so that no calls are made after rtld re-relocation
-     which might be resolved to different functions than we expect.
-     We cannot do this before relocating the other objects because
-     _dl_relocate_object might need to call `mprotect' for DT_TEXTREL.  */
-  _dl_sysdep_start_cleanup ();
-
-  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, lazy);
+  {
+    /* Now we have all the objects loaded.  Relocate them all except for
+       the dynamic linker itself.  We do this in reverse order so that copy
+       relocs of earlier objects overwrite the data written by later
+       objects.  We do not re-relocate the dynamic linker itself in this
+       loop because that could result in the GOT entries for functions we
+       call being changed, and that would break us.  It is safe to relocate
+       the dynamic linker out of order because it has no copy relocs (we
+       know that because it is self-contained).  */
+
+    l = _dl_loaded;
+    while (l->l_next)
+      l = l->l_next;
+    do
+      {
+	if (l != &_dl_rtld_map)
+	  {
+	    _dl_relocate_object (l, _dl_object_relocation_scope (l), lazy);
+	    *_dl_global_scope_end = NULL;
+	  }
+	l = l->l_prev;
+      } while (l);
+
+    /* Do any necessary cleanups for the startup OS interface code.
+       We do these now so that no calls are made after rtld re-relocation
+       which might be resolved to different functions than we expect.
+       We cannot do this before relocating the other objects because
+       _dl_relocate_object might need to call `mprotect' for DT_TEXTREL.  */
+    _dl_sysdep_start_cleanup ();
+
+    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);
+  }
 
   /* Tell the debugger where to find the map of loaded objects.  */
   _dl_r_debug.r_version = 1	/* R_DEBUG_VERSION XXX */;
diff --git a/sysdeps/i386/dl-machine.h b/sysdeps/i386/dl-machine.h
index b52e604d4a..bcd388fd20 100644
--- a/sysdeps/i386/dl-machine.h
+++ b/sysdeps/i386/dl-machine.h
@@ -100,7 +100,7 @@ elf_machine_rel (struct link_map *map,
       *reloc_addr = sym ? (loadbase + sym->st_value) : 0;
       break;
     case R_386_32:
-      if (map->l_type == lt_interpreter)
+      if (resolve && map == &_dl_rtld_map)
 	{
 	  /* Undo the relocation done here during bootstrapping.  Now we will
 	     relocate it anew, possibly using a binding found in the user
@@ -117,7 +117,7 @@ elf_machine_rel (struct link_map *map,
       *reloc_addr += sym ? (loadbase + sym->st_value) : 0;
       break;
     case R_386_RELATIVE:
-      if (map->l_type != lt_interpreter) /* Already done in dynamic linker.  */
+      if (!resolve || map != &_dl_rtld_map) /* Already done in rtld itself.  */
 	*reloc_addr += map->l_addr;
       break;
     case R_386_PC32:
@@ -229,9 +229,9 @@ _dl_start_user:\n\
 	leal (%esp,%eax,4), %esp\n\
 	# Push back the modified argument count.\n\
 	pushl %ecx\n\
-	# Push _dl_loaded as argument in _dl_init_next call below.\n\
-	movl _dl_loaded@GOT(%ebx), %eax\n\
-	movl (%eax), %esi\n\
+	# Push _dl_default_scope[2] as argument in _dl_init_next call below.\n\
+	movl _dl_default_scope@GOT(%ebx), %eax\n\
+	movl 8(%eax), %esi\n\
 0:	pushl %esi\n\
 	# Call _dl_init_next to return the address of an initializer\n\
 	# function to run.\n\
diff --git a/sysdeps/m68k/dl-machine.h b/sysdeps/m68k/dl-machine.h
index 74e88749e6..415216b14a 100644
--- a/sysdeps/m68k/dl-machine.h
+++ b/sysdeps/m68k/dl-machine.h
@@ -252,9 +252,9 @@ _dl_start_user:
 	lea (%sp, %d0*4), %sp
 	| Push back the modified argument count.
 	move.l %d1, -(%sp)
-0:	| Push _dl_loaded as argument in _dl_init_next call below.
-	move.l ([_dl_loaded@GOT, %a5]), %d2
-0:	move.l %d2, -(%sp)
+0:	| Push _dl_default_scope[2] as argument in _dl_init_next call below.
+	move.l ([_dl_default_scope@GOT, %a5]), %d2
+0:	move.l (%d2, 8), -(%sp)
 	| Call _dl_init_next to return the address of an initializer
 	| function to run.
 	bsr.l _dl_init_next@PLTPC