about summary refs log tree commit diff
path: root/elf
diff options
context:
space:
mode:
Diffstat (limited to 'elf')
-rw-r--r--elf/Makefile2
-rw-r--r--elf/dl-deps.c93
-rw-r--r--elf/dl-lookup.c105
-rw-r--r--elf/dl-minimal.c13
-rw-r--r--elf/dl-reloc.c20
-rw-r--r--elf/dl-runtime.c5
-rw-r--r--elf/dlfcn.h1
-rw-r--r--elf/dlopen.c31
-rw-r--r--elf/dlsym.c3
-rw-r--r--elf/link.h35
-rw-r--r--elf/linux-compat.c7
-rw-r--r--elf/rtld.c55
12 files changed, 221 insertions, 149 deletions
diff --git a/elf/Makefile b/elf/Makefile
index 6124fa95de..8f34a9cfd2 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -29,7 +29,7 @@ libdl-routines	:= dlopen dlclose dlsym dlerror
 libdl-inhibit-o	= $(filter-out .so,$(object-suffixes)) # Build only shared.
 
 rtld-routines	:= rtld $(addprefix dl-,minimal load lookup object reloc \
-				        runtime sysdep error init fini)
+				        deps runtime sysdep error init fini)
 distribute	= $(rtld-routines:=.c) dynamic-link.h do-rel.h \
 		  soinit.c sofini.c ldd.sh.in linux-compat.c
 
diff --git a/elf/dl-deps.c b/elf/dl-deps.c
new file mode 100644
index 0000000000..f87475a670
--- /dev/null
+++ b/elf/dl-deps.c
@@ -0,0 +1,93 @@
+/* Load the dependencies of a mapped object.
+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 <errno.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+
+void
+_dl_map_object_deps (struct link_map *map)
+{
+  unsigned int nlist = 1;
+  struct link_map **list = malloc (sizeof *list);
+  unsigned int done;
+
+  /* Start the search list with one element: MAP itself.  */
+  list[0] = map;
+
+  /* Process each element of the search list, loading each of its immediate
+     dependencies and appending them to the list as we step through it.
+     This produces a flat, ordered list that represents a breadth-first
+     search of the dependency tree.  */
+  for (done = 0; done < nlist; ++done)
+    {
+      struct link_map *l = list[done];
+      if (l->l_info[DT_NEEDED])
+	{
+	  const char *strtab
+	    = ((void *) l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr);
+	  const Elf32_Dyn *d;
+	  for (d = l->l_ld; d->d_tag != DT_NULL; ++d)
+	    if (d->d_tag == DT_NEEDED)
+	      {
+		/* Extend the list and put this object on the end.  */
+		struct link_map **n
+		  = realloc (list, (nlist + 1) * sizeof *list);
+		if (n)
+		  list = n;
+		else
+		  {
+		    free (list);
+		    _dl_signal_error (ENOMEM, map->l_name,
+				      "finding dependencies");
+		  }
+		list[nlist++] = _dl_map_object (l, strtab + d->d_un.d_val);
+	      }
+	}
+    }
+
+  map->l_searchlist = list;
+  map->l_nsearchlist = nlist;
+}
+
+
+struct link_map *
+_dl_open (struct link_map *parent, const char *file, int mode)
+{
+  struct link_map *new, *l;
+  Elf32_Addr init;
+
+  /* Load the named object.  */
+  new = _dl_map_object (parent, file);
+
+  /* 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);
+
+  /* Run the initializer functions of new objects.  */
+  while (init = _dl_init_next ())
+    (*(void (*) (void)) init) ();
+
+  return new;
+}
diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
index b72a6a27d5..26357c2484 100644
--- a/elf/dl-lookup.c
+++ b/elf/dl-lookup.c
@@ -29,77 +29,84 @@ Cambridge, MA 02139, USA.  */
 
 Elf32_Addr
 _dl_lookup_symbol (const char *undef_name, const Elf32_Sym **ref,
-		   struct link_map *symbol_scope,
+		   struct link_map *symbol_scope[2],
 		   const char *reference_name,
 		   Elf32_Addr reloc_addr,
 		   int noplt)
 {
   unsigned long int hash = elf_hash (undef_name);
-  struct link_map *map;
   struct
     {
       Elf32_Addr a;
       const Elf32_Sym *s;
     } weak_value = { 0, NULL };
+  size_t i;
+  struct link_map **scope, *map;
 
   /* Search the relevant loaded objects for a definition.  */
-  for (map = symbol_scope; map; map = map->l_next)
-    {
-      const Elf32_Sym *symtab;
-      const char *strtab;
-      Elf32_Word symidx;
-
-      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])
+  for (scope = symbol_scope; scope < &symbol_scope[2]; ++scope)
+    if (*scope)
+      for (i = 0; i < (*scope)->l_nsearchlist; ++i)
 	{
-	  const Elf32_Sym *sym = &symtab[symidx];
+	  const Elf32_Sym *symtab;
+	  const char *strtab;
+	  Elf32_Word symidx;
+
+	  map = (*scope)->l_searchlist[i];
 
-	  if (sym->st_value == 0 || /* No value.  */
-	      reloc_addr == map->l_addr + sym->st_value || /* Self ref.  */
-	      (noplt && sym->st_shndx == SHN_UNDEF)) /* Unwanted PLT entry.  */
-	    continue;
+	  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);
 
-	  switch (ELF32_ST_TYPE (sym->st_info))
+	  /* 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])
 	    {
-	    case STT_NOTYPE:
-	    case STT_FUNC:
-	    case STT_OBJECT:
-	      break;
-	    default:
-	      /* Not a code/data definition.  */
-	      continue;
-	    }
+	      const Elf32_Sym *sym = &symtab[symidx];
 
-	  if (sym != *ref && strcmp (strtab + sym->st_name, undef_name))
-	    /* Not the symbol we are looking for.  */
-	    continue;
+	      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 (ELF32_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)
+	      switch (ELF32_ST_TYPE (sym->st_info))
 		{
-		  weak_value.s = sym;
-		  weak_value.a = map->l_addr;
+		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.  */
+		continue;
+
+	      switch (ELF32_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;
 		}
-	      break;
-	    default:
-	      /* Local symbols are ignored.  */
-	      break;
 	    }
 	}
-    }
 
   if (weak_value.s == NULL && ELF32_ST_BIND ((*ref)->st_info) != STB_WEAK)
     {
diff --git a/elf/dl-minimal.c b/elf/dl-minimal.c
index 9e0a77032d..c61b51532b 100644
--- a/elf/dl-minimal.c
+++ b/elf/dl-minimal.c
@@ -1,5 +1,5 @@
 /* Minimal replacements for basic facilities used in the dynamic linker.
-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
@@ -84,10 +84,17 @@ free (void *ptr)
 }
 weak_symbol (free)
 
-/* This is never called.  */
+/* This is only called with the most recent block returned by malloc.  */
 void *
 realloc (void *ptr, size_t n)
-{ ptr += n; abort (); }
+{
+  void *new;
+  assert (ptr == alloc_last_block);
+  alloc_ptr = alloc_last_block;
+  new = malloc (n);
+  assert (new == ptr);
+  return new;
+}
 weak_symbol (realloc)
 
 /* Avoid signal frobnication in setjmp/longjmp.  Keeps things smaller.  */
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index 424d303800..bfa0174444 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -54,12 +54,11 @@ _dl_relocate_object (struct link_map *l, int lazy)
     }
 
   {
-    struct link_map *real_next, *scope;
+    struct link_map *scope[2];
 
     const char *strtab
       = ((void *) l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr);
 
-
     Elf32_Addr resolve (const Elf32_Sym **ref,
 			Elf32_Addr reloc_addr, int noplt)
       {
@@ -67,16 +66,16 @@ _dl_relocate_object (struct link_map *l, int lazy)
 				  l->l_name, reloc_addr, noplt);
       }
 
-    real_next = l->l_next;
     if (l->l_info[DT_SYMBOLIC])
       {
-	if (l->l_prev)
-	  l->l_prev->l_next = real_next;
-	l->l_next = _dl_loaded;
-	scope = l;
+	scope[0] = l;
+	scope[1] = _dl_loaded;
       }
     else
-      scope = _dl_loaded;
+      {
+	scope[0] = _dl_loaded;
+	scope[1] = l;
+      }
 
     if (l->l_type == lt_interpreter)
       /* We cannot be lazy when relocating the dynamic linker itself.  It
@@ -87,11 +86,6 @@ _dl_relocate_object (struct link_map *l, int lazy)
       lazy = 0;
 
     ELF_DYNAMIC_RELOCATE (l, lazy, resolve);
-
-    /* Restore list frobnication done above for DT_SYMBOLIC.  */
-    l->l_next = real_next;
-    if (l->l_prev)
-      l->l_prev->l_next = l;
   }
 
   /* Set up the PLT so its unrelocated entries will
diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c
index 0beba769fa..dce34f8a89 100644
--- a/elf/dl-runtime.c
+++ b/elf/dl-runtime.c
@@ -70,8 +70,9 @@ fixup (
   Elf32_Addr resolve (const Elf32_Sym **ref,
 		      Elf32_Addr reloc_addr, int noplt)
     {
-      return _dl_lookup_symbol (strtab + (*ref)->st_name, ref, _dl_loaded,
-				l->l_name, reloc_addr, 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.  */
diff --git a/elf/dlfcn.h b/elf/dlfcn.h
index a405baa473..8d19b8b847 100644
--- a/elf/dlfcn.h
+++ b/elf/dlfcn.h
@@ -24,6 +24,7 @@ Cambridge, MA 02139, USA.  */
 /* The MODE argument to `dlopen' contains one of the following: */
 #define RTLD_LAZY	0x001	/* Lazy function call binding.  */
 #define RTLD_NOW	0x002	/* Immediate function call binding.  */
+#define	RTLD_BINDING_MASK 0x3	/* Mask of binding time value.  */
 
 /* If the following bit is set in the MODE argument to `dlopen',
    the symbols of the loaded object and its dependencies are made
diff --git a/elf/dlopen.c b/elf/dlopen.c
index 26ed35ace6..74ab8bb715 100644
--- a/elf/dlopen.c
+++ b/elf/dlopen.c
@@ -24,38 +24,11 @@ Cambridge, MA 02139, USA.  */
 void *
 dlopen (const char *file, int mode)
 {
-  struct link_map *new, *l;
+  struct link_map *new;
 
   void doit (void)
     {
-      Elf32_Addr init;
-
-      new = _dl_map_object (_dl_loaded, file);
-
-      /* Map in any dependencies.  */
-      for (l = new; l; l = l->l_next)
-	if (! l->l_deps_loaded)
-	  {
-	    if (l->l_info[DT_NEEDED])
-	      {
-		const char *strtab
-		  = (void *) l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr;
-		const Elf32_Dyn *d;
-		for (d = l->l_ld; d->d_tag != DT_NULL; ++d)
-		  if (d->d_tag == DT_NEEDED)
-		    _dl_map_object (l, strtab + d->d_un.d_val);
-	      }
-	    l->l_deps_loaded = 1;
-	  }
-
-      /* Relocate the objects loaded.  */
-      for (l = new; l; l = l->l_next)
-	if (! l->l_relocated)
-	  _dl_relocate_object (l, mode == RTLD_LAZY);
-
-      /* Run the initializer functions of new objects.  */
-      while (init = _dl_init_next ())
-	(*(void (*) (void)) init) ();
+      new = _dl_open (_dl_loaded, file, mode);
     }
 
   return _dlerror_run (doit) ? NULL : new;
diff --git a/elf/dlsym.c b/elf/dlsym.c
index cc50650fc9..804d404bb3 100644
--- a/elf/dlsym.c
+++ b/elf/dlsym.c
@@ -33,7 +33,8 @@ dlsym (void *handle, const char *name)
   int lose;
   void doit (void)
     {
-      loadbase = _dl_lookup_symbol (name, &ref, map, map->l_name, 0);
+      struct link_map *scope[2] = { map, NULL };
+      loadbase = _dl_lookup_symbol (name, &ref, scope, map->l_name, 0, 0);
     }
 
   /* Confine the symbol scope to just this map.  */
diff --git a/elf/link.h b/elf/link.h
index a19a2ce0b3..73782d8ac3 100644
--- a/elf/link.h
+++ b/elf/link.h
@@ -90,6 +90,12 @@ struct link_map
     Elf32_Word l_phnum;		/* Number of program header entries.  */
     Elf32_Addr l_entry;		/* Entry point location.  */
 
+    /* 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;
+
     /* Symbol hash table.  */
     Elf32_Word l_nbuckets;
     const Elf32_Word *l_buckets, *l_chain;
@@ -102,7 +108,6 @@ struct link_map
 	lt_library,		/* Library needed by main executable.  */
 	lt_loaded,		/* Extra run-time loaded shared object.  */
       } l_type:2;
-    unsigned int l_deps_loaded:1; /* Nonzero if DT_NEEDED items loaded.  */
     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.  */
@@ -176,22 +181,36 @@ extern struct link_map *_dl_map_object (struct link_map *loader,
 extern struct link_map *_dl_map_object_from_fd (const char *name,
 						int fd, char *realname);
 
+/* Call _dl_map_object on the dependencies of MAP, and
+   set up MAP->l_searchlist.  */
+extern void _dl_map_object_deps (struct link_map *map);
+
 /* Cache the locations of MAP's hash table.  */
 extern void _dl_setup_hash (struct link_map *map);
 
 
+/* Open the shared object NAME, relocate it, and run its initializer if it
+   hasn't already been run.  LOADER's DT_RPATH is used in searching for
+   NAME.  MODE is as for `dlopen' (see <dlfcn.h>).  If the object is
+   already opened, returns its existing map.  */
+extern struct link_map *_dl_open (struct link_map *loader,
+				  const char *name, int mode);
+
+
+
 /* 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.  SYMBOL_SCOPE is the head of
-   the chain used for searching.  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.  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.  */
 extern Elf32_Addr _dl_lookup_symbol (const char *undef,
 				     const Elf32_Sym **sym,
-				     struct link_map *symbol_scope,
+				     struct link_map *symbol_scope[2],
 				     const char *reference_name,
 				     Elf32_Addr reloc_addr,
 				     int noplt);
diff --git a/elf/linux-compat.c b/elf/linux-compat.c
index ed1595e247..8821a140e0 100644
--- a/elf/linux-compat.c
+++ b/elf/linux-compat.c
@@ -1,5 +1,5 @@
 /* Initializer for Linux-compatible dynamic linker `/lib/ld-linux.so.1'.
-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
@@ -33,8 +33,9 @@ void
 _init (void)
 {
   const Elf32_Sym *ref = NULL;
-  Elf32_Addr loadbase = _dl_lookup_symbol ("atexit", &ref, _dl_loaded,
+  struct link_map *scope[2] = { _dl_loaded, NULL };
+  Elf32_Addr loadbase = _dl_lookup_symbol ("atexit", &ref, scope,
 					   "<ld-linux.so.1 initialization>",
-					   1);
+					   0, 1);
   (*(__typeof (atexit) *) (loadbase + ref->st_value)) (&_dl_fini);
 }
diff --git a/elf/rtld.c b/elf/rtld.c
index 511d19cb15..6dc68226db 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -124,7 +124,7 @@ dl_main (const Elf32_Phdr *phdr,
 	 Elf32_Addr *user_entry)
 {
   const Elf32_Phdr *ph;
-  struct link_map *l, *last, *before_rtld;
+  struct link_map *l;
   const char *interpreter_name;
   int lazy;
   int list_only = 0;
@@ -250,44 +250,19 @@ of this helper program; chances are you did not intend to run this program.\n",
   l->l_next = &rtld_map;
   rtld_map.l_prev = l;
 
-  /* Now process all the DT_NEEDED entries and map in the objects.
-     Each new link_map will go on the end of the chain, so we will
-     come across it later in the loop to map in its dependencies.  */
-  before_rtld = NULL;
-  for (l = _dl_loaded; l; l = l->l_next)
-    {
-      if (l->l_info[DT_NEEDED])
-	{
-	  const char *strtab
-	    = (void *) l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr;
-	  const Elf32_Dyn *d;
-	  last = l;
-	  for (d = l->l_ld; d->d_tag != DT_NULL; ++d)
-	    if (d->d_tag == DT_NEEDED)
-	      {
-		struct link_map *new;
-		new = _dl_map_object (l, strtab + d->d_un.d_val);
-		if (!before_rtld && new == &rtld_map)
-		  before_rtld = last;
-		last = new;
-	      }
-	}
-      l->l_deps_loaded = 1;
-    }
+  /* Load all the libraries specified by DT_NEEDED entries.  */
+  _dl_map_object_deps (l);
 
-  /* If any DT_NEEDED entry referred to the interpreter object itself,
-     reorder the list so it appears after its dependent.  If not,
-     remove it from the maps we will use for symbol resolution.  */
-  rtld_map.l_prev->l_next = rtld_map.l_next;
-  if (rtld_map.l_next)
-    rtld_map.l_next->l_prev = rtld_map.l_prev;
-  if (before_rtld)
+  /* XXX if kept, move it so l_next list is in dep order because
+     it will determine gdb's search order.
+     Perhaps do this always, so later dlopen by name finds it?
+     XXX But then gdb always considers it present.  */
+  if (rtld_map.l_opencount == 0)
     {
-      rtld_map.l_prev = before_rtld;
-      rtld_map.l_next = before_rtld->l_next;
-      before_rtld->l_next = &rtld_map;
-      if (rtld_map.l_next)
-	rtld_map.l_next->l_prev = &rtld_map;
+      /* No DT_NEEDED entry referred to the interpreter object itself,
+	 so remove it from the list of visible objects.  */
+      rtld_map.l_prev->l_next = rtld_map.l_next;
+      rtld_map.l_next->l_prev = rtld_map.l_prev;
     }
 
   if (list_only)
@@ -316,9 +291,9 @@ of this helper program; chances are you did not intend to run this program.\n",
       for (i = 1; i < _dl_argc; ++i)
 	{
 	  const Elf32_Sym *ref = NULL;
-	  Elf32_Addr loadbase = _dl_lookup_symbol (_dl_argv[i], &ref,
-						   _dl_loaded, "argument",
-						   0, 0);
+	  struct link_map *scope[2] ={ _dl_loaded, NULL };
+	  Elf32_Addr loadbase
+	    = _dl_lookup_symbol (_dl_argv[i], &ref, scope, "argument", 0, 0);
 	  char buf[20], *bp;
 	  buf[sizeof buf - 1] = '\0';
 	  bp = _itoa (ref->st_value, &buf[sizeof buf - 1], 16, 0);