summary refs log tree commit diff
path: root/elf
diff options
context:
space:
mode:
Diffstat (limited to 'elf')
-rw-r--r--elf/Makefile10
-rw-r--r--elf/dl-deps.c31
-rw-r--r--elf/dl-lookup.c211
-rw-r--r--elf/dlfcn.h6
-rw-r--r--elf/dlsym.c38
-rw-r--r--elf/ldd.bash.in45
-rw-r--r--elf/ldd.sh.in38
-rw-r--r--elf/link.h13
8 files changed, 288 insertions, 104 deletions
diff --git a/elf/Makefile b/elf/Makefile
index 58b3a90f20..4cb8d7e53f 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -99,11 +99,21 @@ $(objpfx)libdl.so: $(objpfx)libdl_pic.a $(common-objpfx)libc.so $(objpfx)ld.so
 
 $(slibdir)/$(rtld-installed-name): $(objpfx)ld.so; $(do-install-program)
 
+ifneq ($(have-bash2),yes)
 $(objpfx)ldd: ldd.sh.in Makefile
 	sed -e 's%@RTLD@%$(slibdir)/$(rtld-installed-name)%g' \
 	    -e 's%@VERSION@%$(version)%g' < $< > $@.new
 	chmod 555 $@.new
 	mv -f $@.new $@
+else
+$(objpfx)ldd: ldd.bash.in Makefile
+	sed -e 's%@BASH@%$(BASH)%g' \
+	    -e 's%@RTLD@%$(slibdir)/$(rtld-installed-name)%g' \
+	    -e 's%@VERSION@%$(version)%g' \
+	    -e 's%@TEXTDOMAINDIR@%$(localedir)%g' < $< > $@.new
+	chmod 555 $@.new
+	mv -f $@.new $@
+endif
 
 # muwahaha
 
diff --git a/elf/dl-deps.c b/elf/dl-deps.c
index 977b3237aa..115982f375 100644
--- a/elf/dl-deps.c
+++ b/elf/dl-deps.c
@@ -33,16 +33,24 @@ _dl_map_object_deps (struct link_map *map,
       struct list *next;
     };
   struct list head[1 + npreloads], *tailp, *scanp;
+  struct list duphead, *duptailp;
   unsigned int nlist;
+  unsigned int nduplist;
 
   /* Start the search list with one element: MAP itself.  */
   head[0].map = map;
 
+  /* We use `l_reserved' as a mark bit to detect objects we have already
+     put in the search list and avoid adding duplicate elements later in
+     the list.  */
+  map->l_reserved = 1;
+
   /* Add the preloaded items after MAP but before any of its dependencies.  */
   for (nlist = 0; nlist < npreloads; ++nlist)
     {
       head[nlist].next = &head[nlist + 1];
       head[nlist + 1].map = preloads[nlist];
+      preloads[nlist]->l_reserved = 1;
     }
 
   /* Terminate the list.  */
@@ -51,10 +59,10 @@ _dl_map_object_deps (struct link_map *map,
   /* Start here for adding dependencies to the list.  */
   tailp = &head[nlist++];
 
-  /* We use `l_reserved' as a mark bit to detect objects we have already
-     put in the search list and avoid adding duplicate elements later in
-     the list.  */
-  map->l_reserved = 1;
+  /* Until now we have the same number of libraries in the normal and
+     the list with duplicates.  */
+  nduplist = nlist;
+  duptailp = &duphead;
 
   /* Process each element of the search list, loading each of its immediate
      dependencies and appending them to the list as we step through it.
@@ -94,6 +102,13 @@ _dl_map_object_deps (struct link_map *map,
 		    /* Set the mark bit that says it's already in the list.  */
 		    dep->l_reserved = 1;
 		  }
+
+		/* In any case Append DEP to the duplicates search list.  */
+		duptailp->next = alloca (sizeof *duptailp);
+		duptailp = duptailp->next;
+		duptailp->map = dep;
+		duptailp->next = NULL;
+		++nduplist;
 	      }
 	}
     }
@@ -112,4 +127,12 @@ _dl_map_object_deps (struct link_map *map,
 	 to avoid duplicates, so the next call starts fresh.  */
       scanp->map->l_reserved = 0;
     }
+
+  map->l_dupsearchlist = malloc (nduplist * sizeof (struct link_map *));
+  map->l_ndupsearchlist = nduplist;
+
+  for (nlist = 0; nlist < npreloads + 1; ++nlist)
+    map->l_dupsearchlist[nlist] = head[nlist].map;
+  for (scanp = duphead.next; scanp; scanp = scanp->next)
+    map->l_dupsearchlist[nlist++] = scanp->map;
 }
diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
index f35bbbe967..717ac83f7a 100644
--- a/elf/dl-lookup.c
+++ b/elf/dl-lookup.c
@@ -23,6 +23,13 @@
 #include <string.h>
 
 
+struct sym_val
+  {
+    ElfW(Addr) a;
+    const ElfW(Sym) *s;
+  };
+
+
 /* This is the hashing function specified by the ELF ABI.  */
 static inline unsigned
 _dl_elf_hash (const char *name)
@@ -44,6 +51,90 @@ _dl_elf_hash (const char *name)
   return hash;
 }
 
+
+/* Inner part of the lookup functions.  */
+static inline ElfW(Addr)
+do_lookup (const char *undef_name, unsigned long int hash,
+	   const ElfW(Sym) **ref, struct sym_val *result,
+	   struct link_map *list[], size_t i, size_t n,
+	   const char *reference_name, struct link_map *skip, int flags)
+{
+  struct link_map *map;
+
+  for (; i < n; ++i)
+    {
+      const ElfW(Sym) *symtab;
+      const char *strtab;
+      ElfW(Symndx) symidx;
+
+      map = list[i];
+
+      /* Here come the extra test needed for `_dl_lookup_symbol_skip'.  */
+      if (skip != NULL && map == skip)
+	continue;
+
+      /* Don't search the executable when resolving a copy reloc.  */
+      if (flags & DL_LOOKUP_NOEXEC && map->l_type == lt_executable)
+	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);
+
+      /* 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.  */
+	      ((flags & DL_LOOKUP_NOPLT) != 0 /* Reject PLT entry.  */
+	       && sym->st_shndx == SHN_UNDEF))
+	    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.  */
+	    continue;
+
+	  switch (ELFW(ST_BIND) (sym->st_info))
+	    {
+	    case STB_GLOBAL:
+	      /* Global definition.  Just what we need.  */
+	      result->s = sym;
+	      result->a = map->l_addr;
+	      return 1;
+	    case STB_WEAK:
+	      /* Weak definition.  Use this value if we don't find
+		 another.  */
+	      if (! result->s)
+		{
+		  result->s = sym;
+		  result->a = map->l_addr;
+		}
+	      break;
+	    default:
+	      /* Local symbols are ignored.  */
+	      break;
+	    }
+	}
+    }
+
+  /* We have not found anything until now.  */
+  return 0;
+}
+
 /* Search loaded objects' symbol tables for a definition of the symbol
    UNDEF_NAME.  FLAGS is a set of flags.  If DL_LOOKUP_NOEXEC is set,
    then don't search the executable for a definition; this used for
@@ -57,82 +148,17 @@ _dl_lookup_symbol (const char *undef_name, const ElfW(Sym) **ref,
 		   int flags)
 {
   const unsigned long int hash = _dl_elf_hash (undef_name);
-  struct
-    {
-      ElfW(Addr) a;
-      const ElfW(Sym) *s;
-    } weak_value = { 0, NULL };
-  size_t i;
-  struct link_map **scope, *map;
+  struct sym_val current_value = { 0, NULL };
+  struct link_map **scope;
 
   /* Search the relevant loaded objects for a definition.  */
   for (scope = symbol_scope; *scope; ++scope)
-    for (i = 0; i < (*scope)->l_nsearchlist; ++i)
-      {
-	const ElfW(Sym) *symtab;
-	const char *strtab;
-	ElfW(Symndx) symidx;
-
-	map = (*scope)->l_searchlist[i];
-
-	/* Don't search the executable when resolving a copy reloc.  */
-	if (flags & DL_LOOKUP_NOEXEC && map->l_type == lt_executable)
-	  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);
-
-	/* 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.  */
-		((flags & DL_LOOKUP_NOPLT) != 0 /* Reject PLT entry.  */
-		 && sym->st_shndx == SHN_UNDEF))
-	      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.  */
-	      continue;
+    if (do_lookup (undef_name, hash, ref, &current_value,
+		   (*scope)->l_searchlist, 0, (*scope)->l_nsearchlist,
+		   reference_name, NULL, flags))
+      break;
 
-	    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 &&
+  if (current_value.s == NULL &&
       (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK))
     {
       /* We could find no value for a strong reference.  */
@@ -144,8 +170,45 @@ _dl_lookup_symbol (const char *undef_name, const ElfW(Sym) **ref,
       _dl_signal_error (0, reference_name, buf);
     }
 
-  *ref = weak_value.s;
-  return weak_value.a;
+  *ref = current_value.s;
+  return current_value.a;
+}
+
+
+/* This function is nearly the same as `_dl_lookup_symbol' but it
+   skips in the first list all objects until SKIP_MAP is found.  I.e.,
+   it only considers objects which were loaded after the described
+   object.  If there are more search lists the object described by
+   SKIP_MAP is only skipped.  */
+ElfW(Addr)
+_dl_lookup_symbol_skip (const char *undef_name, const ElfW(Sym) **ref,
+			struct link_map *symbol_scope[],
+			const char *reference_name,
+			struct link_map *skip_map,
+			int flags)
+{
+  int found_entry = 0;
+  const unsigned long int hash = _dl_elf_hash (undef_name);
+  struct sym_val current_value = { 0, NULL };
+  struct link_map **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);
+
+  if (! do_lookup (undef_name, hash, ref, &current_value,
+		   (*scope)->l_dupsearchlist, i, (*scope)->l_ndupsearchlist,
+		   reference_name, skip_map, flags))
+    while (*++scope)
+      if (do_lookup (undef_name, hash, ref, &current_value,
+		     (*scope)->l_dupsearchlist, 0, (*scope)->l_ndupsearchlist,
+		     reference_name, skip_map, flags))
+	break;
+
+  *ref = current_value.s;
+  return current_value.a;
 }
 
 
diff --git a/elf/dlfcn.h b/elf/dlfcn.h
index b59bebd5b1..c2b66a7797 100644
--- a/elf/dlfcn.h
+++ b/elf/dlfcn.h
@@ -32,6 +32,12 @@
    visible as if the object were linked directly into the program.  */
 #define RTLD_GLOBAL	0x100
 
+/* 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.  */
+#define RTLD_NEXT	((void *) -1l)
+
 /* Open the shared object FILE and map it in; return a handle that can be
    passed to `dlsym' to get symbol values from it.  */
 extern void *dlopen __P ((__const char *__file, int __mode));
diff --git a/elf/dlsym.c b/elf/dlsym.c
index edfe1c6cbb..12a29e4b4c 100644
--- a/elf/dlsym.c
+++ b/elf/dlsym.c
@@ -26,25 +26,43 @@
 void *
 dlsym (void *handle, const char *name)
 {
+  ElfW(Addr) caller = (ElfW(Addr)) __builtin_return_address (0);
   ElfW(Addr) loadbase;
   const ElfW(Sym) *ref = NULL;
   void doit (void)
     {
-      struct link_map *map = handle, **scope, *mapscope[2] = { map, NULL };
-      const char *owner;
-
-      if (map)
+      if (handle == NULL)
+	/* Search the global scope.  */
+	loadbase = _dl_lookup_symbol
+	  (name, &ref, &(_dl_global_scope ?: _dl_default_scope)[2], NULL, 0);
+      else if (handle == RTLD_NEXT)
 	{
-	  /* Search the scope of the given object.  */
-	  scope = mapscope;
-	  owner = map->l_name;
+	  struct link_map *l, *match;
+
+	  /* Find the highest-addressed object that CALLER is not below.  */
+	  match = NULL;
+	  for (l = _dl_loaded; l; l = l->l_next)
+	    if (caller >= l->l_addr && (!match || match->l_addr < l->l_addr))
+	      match = l;
+
+	  if (! match)
+	    _dl_signal_error (0, NULL, _("\
+RTLD_NEXT used in code not dynamically loaded"));
+
+	  l = match;
+	  while (l->l_loader)
+	    l = l->l_loader;
+
+	  loadbase = _dl_lookup_symbol_skip
+	    (name, &ref, &_dl_loaded, NULL, l, 0);
 	}
       else
 	{
-	  scope = &(_dl_global_scope ?: _dl_default_scope)[2];
-	  owner = NULL;
+	  /* Search the scope of the given object.  */
+	  struct link_map *map = handle;
+	  struct link_map *mapscope[2] = { map, NULL };
+	  loadbase = _dl_lookup_symbol (name, &ref, mapscope, map->l_name, 0);
 	}
-      loadbase = _dl_lookup_symbol (name, &ref, scope, owner, 0);
     }
 
   return _dlerror_run (doit) ? NULL : (void *) (loadbase + ref->st_value);
diff --git a/elf/ldd.bash.in b/elf/ldd.bash.in
index e7ad21fa9d..5269708f43 100644
--- a/elf/ldd.bash.in
+++ b/elf/ldd.bash.in
@@ -1,24 +1,50 @@
 #! @BASH@
 
+# 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., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+
 # This is the `ldd' command, which lists what shared libraries are
 # used by given dynamically-linked executables.  It works by invoking the
 # run-time dynamic linker as a command and setting the environment
 # variable LD_TRACE_LOADED_OBJECTS to a non-empty value.
 
+# We should be able to find the translation right at the beginning.
+TEXTDOMAIN=libc
+TEXTDOMAINDIR=@TEXTDOMAINDIR@
+
 RTLD=@RTLD@
-usage=$"\
-ldd [OPTION]... FILE...
-  --help           print this help and exit
-  --version        print version information and exit
-Report bugs to <bug-glibc@prep.ai.mit.edu>."
 
 while test $# -gt 0; do
   case "$1" in
   --v*)
-    echo 'ldd (GNU libc) @VERSION@'; exit 0 ;;
+    echo $"ldd (GNU libc) @VERSION@
+Copyright (C) 1996 Free Software Foundation, Inc.
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+    exit 0 ;;
   --h*)
-    echo "$usage"; exit 0 ;;
-  --)		# Stop option prcessing
+    echo $"ldd [OPTION]... FILE...
+  --help           print this help and exit
+  --version        print version information and exit
+Report bugs to <bug-glibc@prep.ai.mit.edu>."
+    exit 0 ;;
+  --)		# Stop option prcessing.
     shift; break ;;
   *)
     break ;;
@@ -64,3 +90,6 @@ Try \`ldd --help' for more information."
 esac
 
 exit 0
+# Local Variables:
+#  mode:ksh
+# End:
diff --git a/elf/ldd.sh.in b/elf/ldd.sh.in
index 16d3fd8b79..f8df62d223 100644
--- a/elf/ldd.sh.in
+++ b/elf/ldd.sh.in
@@ -1,24 +1,46 @@
 #! /bin/sh
 
+# 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., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+
 # This is the `ldd' command, which lists what shared libraries are
 # used by given dynamically-linked executables.  It works by invoking the
 # run-time dynamic linker as a command and setting the environment
 # variable LD_TRACE_LOADED_OBJECTS to a non-empty value.
 
 RTLD=@RTLD@
-usage="\
-ldd [OPTION]... FILE...
-  --help           print this help and exit
-  --version        print version information and exit
-Report bugs to <bug-glibc@prep.ai.mit.edu>."
 
 while test $# -gt 0; do
   case "$1" in
   --v*)
-    echo 'ldd (GNU libc) @VERSION@'; exit 0 ;;
+    echo 'ldd (GNU libc) @VERSION@
+Copyright (C) 1996 Free Software Foundation, Inc.
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.'
+    exit 0 ;;
   --h*)
-    echo "$usage"; exit 0 ;;
-  --)		# Stop option prcessing
+    echo 'ldd [OPTION]... FILE...
+  --help           print this help and exit
+  --version        print version information and exit
+Report bugs to <bug-glibc@prep.ai.mit.edu>.'
+    exit 0 ;;
+  --)		# Stop option prcessing.
     shift; break ;;
   *)
     break ;;
diff --git a/elf/link.h b/elf/link.h
index 17fea305ed..b277bf75c1 100644
--- a/elf/link.h
+++ b/elf/link.h
@@ -109,6 +109,11 @@ struct link_map
     struct link_map **l_searchlist;
     unsigned int l_nsearchlist;
 
+    /* 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;
+
     /* Dependent object that first caused this object to be loaded.  */
     struct link_map *l_loader;
 
@@ -239,6 +244,14 @@ extern ElfW(Addr) _dl_lookup_symbol (const char *undef,
 				     const char *reference_name,
 				     int flags);
 
+/* 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[],
+					  const char *reference_name,
+					  struct link_map *skip_this,
+					  int flags);
+
 /* Look up symbol NAME in MAP's scope and return its run-time address.  */
 extern ElfW(Addr) _dl_symbol_value (struct link_map *map, const char *name);