about summary refs log tree commit diff
path: root/elf
diff options
context:
space:
mode:
Diffstat (limited to 'elf')
-rw-r--r--elf/Makefile13
-rw-r--r--elf/dl-deps.c9
-rw-r--r--elf/dl-error.c2
-rw-r--r--elf/dl-load.c465
-rw-r--r--elf/dl-object.c1
-rw-r--r--elf/dl-support.c31
-rw-r--r--elf/elf.h5
-rw-r--r--elf/genrtldtbl.awk28
-rw-r--r--elf/link.h26
-rw-r--r--elf/rtld.c7
10 files changed, 482 insertions, 105 deletions
diff --git a/elf/Makefile b/elf/Makefile
index 269a872969..e44711471b 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -35,15 +35,18 @@ elide-routines.so = $(dl-routines) dl-support enbl-secure
 # interpreter and operating independent of libc.
 rtld-routines	:= rtld $(dl-routines) dl-sysdep dl-minimal
 distribute	= $(rtld-routines:=.c) dynamic-link.h do-rel.h dl-machine.h \
-		  dl-hash.h soinit.c sofini.c ldd.sh.in ldd.bash.in eval.c
+		  dl-hash.h soinit.c sofini.c ldd.sh.in ldd.bash.in eval.c \
+		  genrtldtbl.awk
 
 extra-libs	 = libdl
 extra-libs-others = $(extra-libs)
 libdl-routines	:= dlopen dlclose dlsym dlvsym dlerror dladdr
 libdl-map	:= libdl.map
 
-before-compile = $(objpfx)trusted-dirs.h
+before-compile = $(objpfx)trusted-dirs.h $(objpfx)rtldtbl.h
 
+# We need GNU awk for the genrtldtbl.awk script.
+GAWK = gawk
 
 all: # Make this the default target; it will be defined in Rules.
 
@@ -51,7 +54,7 @@ include ../Makeconfig
 
 ifeq (yes,$(build-shared))
 extra-objs	= $(rtld-routines:=.so) soinit.so sofini.so eval.so
-generated	= librtld.so dl-allobjs.so
+generated	= librtld.so dl-allobjs.so trusted-dirs.h rtldtbl.h
 install-others	= $(inst_slibdir)/$(rtld-installed-name)
 install-bin	= ldd
 endif
@@ -143,6 +146,10 @@ $(objpfx)trusted-dirs.h: Makefile
 	   echo "  \"$$dir\",";						      \
 	 done;) > $@T
 	mv -f $@T $@
+$(objpfx)rtldtbl.h: Makefile
+	$(make-target-directory)
+	echo "$(default-rpath)" | $(GAWK) -f genrtldtbl.awk > $@T
+	mv -f $@T $@
 CPPFLAGS-dl-load.c = -I$(objdir)/$(subdir)
 CFLAGS-dl-load.c += -Wno-uninitialized
 
diff --git a/elf/dl-deps.c b/elf/dl-deps.c
index 36f5ee0606..e990d6965b 100644
--- a/elf/dl-deps.c
+++ b/elf/dl-deps.c
@@ -57,12 +57,9 @@ openaux (void *a)
 
 
 
-/* We use a very special kind of list to track the three kinds paths
+/* We use a very special kind of list to track the two kinds paths
    through the list of loaded shared objects.  We have to
 
-   - go through all objects in the correct order, which includes the
-     possible recursive loading of auxiliary objects and dependencies
-
    - produce a flat list with unique members of all involved objects
 
    - produce a flat list of all shared objects.
@@ -141,7 +138,7 @@ _dl_map_object_deps (struct link_map *map,
     {
       struct link_map *l = runp->map;
 
-      if (runp->done == 0 && (l->l_info[AUXTAG] || l->l_info[DT_NEEDED]))
+      if (l->l_info[AUXTAG] || l->l_info[DT_NEEDED])
 	{
 	  const char *strtab = ((void *) l->l_addr
 				+ l->l_info[DT_STRTAB]->d_un.d_ptr);
@@ -371,6 +368,6 @@ _dl_map_object_deps (struct link_map *map,
 			  "cannot allocate symbol search list");
 
       for (nlist = 0, runp = head; runp; runp = runp->dup)
-	map->l_searchlist[nlist++] = runp->map;
+	map->l_dupsearchlist[nlist++] = runp->map;
     }
 }
diff --git a/elf/dl-error.c b/elf/dl-error.c
index e2565bb348..7ee803ad9b 100644
--- a/elf/dl-error.c
+++ b/elf/dl-error.c
@@ -75,7 +75,7 @@ _dl_signal_error (int errcode,
       /* Lossage while resolving the program's own symbols is always fatal.  */
       extern char **_dl_argv;	/* Set in rtld.c at startup.  */
       _dl_sysdep_fatal (_dl_argv[0] ?: "<program name unknown>",
-			": error in loading shared libraries\n",
+			": error in loading shared libraries",
 			objname ?: "", objname ? ": " : "",
 			errstring, errcode ? ": " : "",
 			errcode ? strerror (errcode) : "", "\n", NULL);
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 87859219f1..1301e73996 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1,4 +1,4 @@
-/* _dl_map_object -- Map in a shared object's segments from the file.
+/* Map in a shared object's segments from the file.
    Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
@@ -17,14 +17,15 @@
    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.  */
 
+#include <errno.h>
+#include <fcntl.h>
 #include <link.h>
-#include <sys/types.h>
-#include <sys/mman.h>
+#include <stdlib.h>
 #include <string.h>
-#include <fcntl.h>
 #include <unistd.h>
-#include <stdlib.h>
-#include <errno.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 #include "dynamic-link.h"
 
 
@@ -90,6 +91,8 @@ ELF_PREFERRED_ADDRESS_DATA;
 
 size_t _dl_pagesize;
 
+extern const char *_dl_platform;
+extern size_t _dl_platformlen;
 
 /* Local version of `strdup' function.  */
 static inline char *
@@ -105,6 +108,292 @@ local_strdup (const char *s)
 }
 
 
+/* Implement cache for search path lookup.  */
+#if 0
+/* This is how generated should look like.  I'll remove this once I'm
+   sure everything works correctly.  */
+static struct r_search_path_elem rtld_search_dir1 =
+  { "/lib/", 5, unknown, 0, unknown, NULL };
+static struct r_search_path_elem rtld_search_dir2 =
+  { "/usr/lib/", 9, unknown, 0, unknown, &r ld_search_dir1 };
+
+static struct r_search_path_elem *rtld_search_dirs[] =
+{
+  &rtld_search_dir1,
+  &rtld_search_dir2,
+  NULL
+};
+
+static struct r_search_path_elem *all_dirs = &rtld_search_dir2;
+#else
+# include "rtldtbl.h"
+#endif
+
+static size_t max_dirnamelen;
+
+static inline struct r_search_path_elem **
+fillin_rpath (char *rpath, struct r_search_path_elem **result, const char *sep,
+	      const char **trusted)
+{
+  char *cp;
+  size_t nelems = 0;
+
+  while ((cp = __strsep (&rpath, sep)) != NULL)
+    {
+      struct r_search_path_elem *dirp;
+      size_t len = strlen (cp);
+      /* Remove trailing slashes.  */
+      while (len > 1 && cp[len - 1] == '/')
+	--len;
+
+      /* Make sure we don't use untrusted directories if we run SUID.  */
+      if (trusted != NULL)
+	{
+	  const char **trun = trusted;
+
+	  /* All trusted directory must be complete name.  */
+	  if (cp[0] != '/')
+	    continue;
+
+	  while (*trun != NULL
+		 && (memcmp (*trun, cp, len) != 0 || (*trun)[len] != '\0'))
+	    ++trun;
+
+	  if (*trun == NULL)
+	    /* It's no trusted directory, skip it.  */
+	    continue;
+	}
+
+      /* Now add one.  */
+      if (len > 0)
+	cp[len++] = '/';
+
+      /* See if this directory is already known.  */
+      for (dirp = all_dirs; dirp != NULL; dirp = dirp->next)
+	if (dirp->dirnamelen == len && strcmp (cp, dirp->dirname) == 0)
+	  break;
+
+      if (dirp != NULL)
+	{
+	  /* It is available, see whether it's in our own list.  */
+	  size_t cnt;
+	  for (cnt = 0; cnt < nelems; ++cnt)
+	    if (result[cnt] == dirp)
+	      break;
+
+	  if (cnt == nelems)
+	    result[nelems++] = dirp;
+	}
+      else
+	{
+	  /* It's a new directory.  Create an entry and add it.  */
+	  dirp = (struct r_search_path_elem *) malloc (sizeof (*dirp));
+	  if (dirp == NULL)
+	    _dl_signal_error (ENOMEM, NULL,
+			      "cannot create cache for search path");
+
+	  dirp->dirnamelen = len;
+	  dirp->dirstatus = unknown;
+
+	  /* Add the name of the machine dependent directory if a machine
+	     is defined.  */
+	  if (_dl_platform != NULL)
+	    {
+	      char *tmp;
+
+	      dirp->machdirnamelen = len + _dl_platformlen + 1;
+	      tmp = (char *) malloc (len + _dl_platformlen + 2);
+	      if (tmp == NULL)
+		_dl_signal_error (ENOMEM, NULL,
+				  "cannot create cache for search path");
+	      memcpy (tmp, cp, len);
+	      memcpy (tmp + len, _dl_platform, _dl_platformlen);
+	      tmp[len + _dl_platformlen] = '/';
+	      tmp[len + _dl_platformlen + 1] = '\0';
+
+	      dirp->dirname = tmp;
+	      dirp->machdirstatus = unknown;
+
+	      if (max_dirnamelen < dirp->machdirnamelen)
+		max_dirnamelen = dirp->machdirnamelen;
+	    }
+	  else
+	    {
+	      char *tmp;
+
+	      dirp->machdirnamelen = len;
+	      dirp->machdirstatus = nonexisting;
+
+	      tmp = (char *) malloc (len + 1);
+	      if (tmp == NULL)
+		_dl_signal_error (ENOMEM, NULL,
+				  "cannot create cache for search path");
+	      memcpy (tmp, cp, len);
+	      tmp[len] = '\0';
+
+	      if (max_dirnamelen < dirp->dirnamelen)
+		max_dirnamelen = dirp->dirnamelen;
+
+	      dirp->dirname = tmp;
+	    }
+
+	  dirp->next = all_dirs;
+	  all_dirs = dirp;
+
+	  /* Put it in the result array.  */
+	  result[nelems++] = dirp;
+	}
+    }
+
+  /* Terminate the array.  */
+  result[nelems] = NULL;
+
+  return result;
+}
+
+
+static struct r_search_path_elem **
+decompose_rpath (const char *rpath, size_t additional_room)
+{
+  /* Make a copy we can work with.  */
+  char *copy = strdupa (rpath);
+  char *cp;
+  struct r_search_path_elem **result;
+  /* First count the number of necessary elements in the result array.  */
+  size_t nelems = 0;
+
+  for (cp = copy; *cp != '\0'; ++cp)
+    if (*cp == ':')
+      ++nelems;
+
+  /* Allocate room for the result.  NELEMS + 1 + ADDITIONAL_ROOM is an upper
+     limit for the number of necessary entries.  */
+  result = (struct r_search_path_elem **) malloc ((nelems + 1
+						   + additional_room + 1)
+						  * sizeof (*result));
+  if (result == NULL)
+    _dl_signal_error (ENOMEM, NULL, "cannot create cache for search path");
+
+  return fillin_rpath (copy, result, ":", NULL);
+}
+
+
+void
+_dl_init_paths (void)
+{
+  struct r_search_path_elem **pelem;
+
+  /* We have in `search_path' the information about the RPATH of the
+     dynamic loader.  Now fill in the information about the applications
+     RPATH and the directories addressed by the LD_LIBRARY_PATH environment
+     variable.  */
+  struct link_map *l;
+
+  /* First determine how many elements the LD_LIBRARY_PATH contents has.  */
+  const char *llp = getenv ("LD_LIBRARY_PATH");
+  size_t nllp;
+
+  if (llp != NULL && *llp != '\0')
+    {
+      /* Simply count the number of colons.  */
+      const char *cp = llp;
+      nllp = 1;
+      while (*cp)
+	if (*cp++ == ':')
+	  ++nllp;
+    }
+  else
+    nllp = 0;
+
+  l = _dl_loaded;
+  if (l && l->l_type != lt_loaded && l->l_info[DT_RPATH])
+    {
+      /* Allocate room for the search path and fill in information from
+	 RPATH.  */
+      l->l_rpath_dirs =
+	decompose_rpath ((const char *) (l->l_addr
+					 + l->l_info[DT_STRTAB]->d_un.d_ptr
+					 + l->l_info[DT_RPATH]->d_un.d_val),
+			 nllp);
+    }
+  else
+    {
+      /* If we have no LD_LIBRARY_PATH and no RPATH we must tell this
+	 somehow to prevent we look this up again and again.  */
+      if (nllp == 0)
+	 l->l_rpath_dirs = (struct r_search_path_elem **) -1l;
+      else
+	{
+	  l->l_rpath_dirs =
+	    (struct r_search_path_elem **) malloc ((nllp + 1)
+						   * sizeof (*l->l_rpath_dirs));
+	  if (l->l_rpath_dirs == NULL)
+	    _dl_signal_error (ENOMEM, NULL,
+			      "cannot create cache for search path");
+	  l->l_rpath_dirs[0] = NULL;
+	}
+    }
+
+  if (nllp > 0)
+    {
+      static const char *trusted_dirs[] =
+      {
+#include "trusted-dirs.h"
+	NULL
+      };
+      char *copy = strdupa (llp);
+
+      /* Decompose the LD_LIBRARY_PATH and fill in the result.
+         First search for the next place to enter elements.  */
+      struct r_search_path_elem **result = l->l_rpath_dirs;
+      while (*result != NULL)
+	++result;
+
+      /* We need to take care that the LD_LIBRARY_PATH environement
+	 variable can contain a semicolon.  */
+      (void) fillin_rpath (copy, result, ":;",
+			   __libc_enable_secure ? trusted_dirs : NULL);
+    }
+
+  /* Now set up the rest of the rtld_search_dirs.  */
+  for (pelem = rtld_search_dirs; *pelem != NULL; ++pelem)
+    {
+      struct r_search_path_elem *relem = *pelem;
+
+      if (_dl_platform != NULL)
+	{
+	  char *tmp;
+
+	  relem->machdirnamelen = relem->dirnamelen + _dl_platformlen + 1;
+	  tmp = (char *) malloc (relem->machdirnamelen + 1);
+	  if (tmp == NULL)
+	    _dl_signal_error (ENOMEM, NULL,
+			      "cannot create cache for search path");
+
+	  memcpy (tmp, relem->dirname, relem->dirnamelen);
+	  memcpy (tmp + relem->dirnamelen, _dl_platform, _dl_platformlen);
+	  tmp[relem->dirnamelen + _dl_platformlen] = '/';
+	  tmp[relem->dirnamelen + _dl_platformlen + 1] = '\0';
+
+	  relem->dirname = tmp;
+
+	  relem->machdirstatus = unknown;
+
+	  if (max_dirnamelen < relem->machdirnamelen)
+	    max_dirnamelen = relem->machdirnamelen;
+	}
+      else
+	{
+	  relem->machdirnamelen = relem->dirnamelen;
+	  relem->machdirstatus = nonexisting;
+
+	  if (max_dirnamelen < relem->dirnamelen)
+	    max_dirnamelen = relem->dirnamelen;
+	}
+    }
+}
+
+
 /* Map in the shared object NAME, actually located in REALNAME, and already
    opened on FD.  */
 
@@ -131,7 +420,7 @@ _dl_map_object_from_fd (char *name, int fd, char *realname,
 	    l->l_next->l_prev = l->l_prev;
 	  free (l);
 	}
-      free (name);
+      free (name);	/* XXX Can this be correct? --drepper */
       free (realname);
       _dl_signal_error (code, name, msg);
     }
@@ -207,9 +496,6 @@ _dl_map_object_from_fd (char *name, int fd, char *realname,
 	return l;
       }
 
-  if (_dl_pagesize == 0)
-    _dl_pagesize = __getpagesize ();
-
   /* Map in the first page to read the header.  */
   header = map (0, sizeof *header);
 
@@ -458,83 +744,87 @@ _dl_map_object_from_fd (char *name, int fd, char *realname,
   return l;
 }
 
-/* Try to open NAME in one of the directories in DIRPATH.
+/* Try to open NAME in one of the directories in DIRS.
    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,
-	   const char *trusted_dirs[])
+	   struct r_search_path_elem **dirs,
+	   char **realname)
 {
   char *buf;
-  const char *p;
-  int fd;
+  int fd = -1;
 
-  p = dirpath;
-  if (p == NULL || *p == '\0')
+  if (dirs == NULL || *dirs == NULL)
     {
       __set_errno (ENOENT);
       return -1;
     }
 
-  buf = __alloca (strlen (dirpath) + 1 + namelen);
+  buf = __alloca (max_dirnamelen + namelen);
   do
     {
-      size_t buflen;
-      size_t this_len;
+      struct r_search_path_elem *this_dir = *dirs;
+      size_t buflen = 0;
 
-      dirpath = p;
-      p = strpbrk (dirpath, ":;");
-      if (p == NULL)
-	p = strchr (dirpath, '\0');
-
-      this_len = p - dirpath;
-
-      /* When we run a setuid program we do not accept any directory.  */
-      if (__libc_enable_secure)
+      if (this_dir->machdirstatus != nonexisting)
 	{
-	  /* All trusted directory must be complete name.  */
-	  if (dirpath[0] != '/')
-	    continue;
+	  /* Construct the pathname to try.  */
+	  (void) memcpy (buf, this_dir->dirname, this_dir->machdirnamelen);
+	  (void) memcpy (buf + this_dir->machdirnamelen, name, namelen);
+	  buflen = this_dir->machdirnamelen + namelen;
+
+	  fd = __open (buf, O_RDONLY);
+	  if (this_dir->machdirstatus == unknown)
+	    if (fd != -1)
+	      this_dir->machdirstatus = existing;
+	    else
+	      {
+		/* We failed to open machine dependent library.  Let's
+		   test whether there is any directory at all.  */
+		struct stat st;
 
-	  /* If we got a list of trusted directories only accept one
-	     of these.  */
-	  if (trusted_dirs != NULL)
-	    {
-	      const char **trust = trusted_dirs;
+		buf[this_dir->machdirnamelen - 1] = '\0';
 
-	      while (*trust !=  NULL)
-		if (memcmp (dirpath, *trust, this_len) == 0
-		    && (*trust)[this_len] == '\0')
-		  break;
+		if (stat (buf, &st) != 0 || ! S_ISDIR (st.st_mode))
+		  /* The directory does not exist ot it is no directory.  */
+		  this_dir->machdirstatus = nonexisting;
 		else
-		  ++trust;
-
-	      /* If directory is not trusted, ignore this directory.  */
-	      if (*trust == NULL)
-		continue;
-	    }
+		  this_dir->machdirstatus = existing;
+	      }
 	}
 
-      if (this_len == 0)
-	{
-	  /* 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
+      if (fd == -1 && this_dir->dirstatus != nonexisting)
 	{
 	  /* Construct the pathname to try.  */
-	  (void) memcpy (buf, dirpath, this_len);
-	  buf[this_len] = '/';
-	  (void) memcpy (&buf[this_len + 1], name, namelen);
-	  buflen = this_len + 1 + namelen;
+	  (void) memcpy (buf, this_dir->dirname, this_dir->dirnamelen);
+	  (void) memcpy (buf + this_dir->dirnamelen, name, namelen);
+	  buflen = this_dir->dirnamelen + namelen;
+
+	  fd = __open (buf, O_RDONLY);
+	  if (this_dir->dirstatus == unknown)
+	    if (fd != -1)
+	      this_dir->dirstatus = existing;
+	    else
+	      /* We failed to open library.  Let's test whether there
+		 is any directory at all.  */
+	      if (this_dir->dirnamelen <= 1)
+		this_dir->dirstatus = existing;
+	      else
+		{
+		  struct stat st;
+
+		  buf[this_dir->dirnamelen - 1] = '\0';
+
+		  if (stat (buf, &st) != 0 || ! S_ISDIR (st.st_mode))
+		    /* The directory does not exist ot it is no directory.  */
+		    this_dir->dirstatus = nonexisting;
+		  else
+		    this_dir->dirstatus = existing;
+		}
 	}
 
-      fd = __open (buf, O_RDONLY);
       if (fd != -1)
 	{
 	  *realname = malloc (buflen);
@@ -555,7 +845,7 @@ open_path (const char *name, size_t namelen,
 	/* The file exists and is readable, but something went wrong.  */
 	return -1;
     }
-  while (*p++ != '\0');
+  while (*++dirs != NULL);
 
   return -1;
 }
@@ -593,39 +883,34 @@ _dl_map_object (struct link_map *loader, const char *name, int type,
 
       size_t namelen = strlen (name) + 1;
 
-      inline void trypath (const char *dirpath, const char *trusted[])
-	{
-	  fd = open_path (name, namelen, dirpath, &realname, trusted);
-	}
-
       fd = -1;
 
       /* First try the DT_RPATH of the dependent object that caused NAME
 	 to be loaded.  Then that object's dependent, and on up.  */
       for (l = loader; fd == -1 && 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), NULL);
-      /* If dynamically linked, try the DT_RPATH of the executable itself.  */
-      l = _dl_loaded;
-      if (fd == -1 && l && l->l_type != lt_loaded && 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), NULL);
-      /* Try an environment variable (unless setuid).  */
-      if (fd == -1)
-	{
-	  static const char *trusted_dirs[] =
 	  {
-#include "trusted-dirs.h"
-	    NULL
-	  };
-	  const char *ld_library_path = getenv ("LD_LIBRARY_PATH");
+	    /* Make sure the cache information is available.  */
+	    if (l->l_rpath_dirs == NULL)
+	      {
+		size_t ptrval = (l->l_addr
+				 + l->l_info[DT_STRTAB]->d_un.d_ptr
+				 + l->l_info[DT_RPATH]->d_un.d_val);
+		l->l_rpath_dirs =
+		  decompose_rpath ((const char *) ptrval, 0);
+	      }
+
+	    if (l->l_rpath_dirs != (struct r_search_path_elem **) -1l)
+	      fd = open_path (name, namelen, l->l_rpath_dirs, &realname);
+	  }
+
+      /* If dynamically linked, try the DT_RPATH of the executable itself
+	 and the LD_LIBRARY_PATH environment variable.  */
+      l = _dl_loaded;
+      if (fd == -1 && l && l->l_type != lt_loaded
+	  && l->l_rpath_dirs != (struct r_search_path_elem **) -1l)
+	fd = open_path (name, namelen, l->l_rpath_dirs, &realname);
 
-	  if (ld_library_path != NULL && *ld_library_path != '\0')
-	    trypath (ld_library_path, trusted_dirs);
-	}
       if (fd == -1)
 	{
 	  /* Check the list of libraries in the file /etc/ld.so.cache,
@@ -646,12 +931,10 @@ _dl_map_object (struct link_map *loader, const char *name, int type,
 		}
 	    }
 	}
+
       /* Finally, try the default path.  */
       if (fd == -1)
-	{
-	  extern const char *_dl_rpath;	/* Set in rtld.c. */
-	  trypath (_dl_rpath, NULL);
-	}
+	fd = open_path (name, namelen, rtld_search_dirs, &realname);
     }
   else
     {
diff --git a/elf/dl-object.c b/elf/dl-object.c
index 941bfa34cc..65f80d1a7e 100644
--- a/elf/dl-object.c
+++ b/elf/dl-object.c
@@ -44,6 +44,7 @@ _dl_new_object (char *realname, const char *libname, int type)
   newname->next = NULL;
   new->l_libname = newname;
   new->l_type = type;
+  new->l_rpath_dirs = NULL;
 
   if (_dl_loaded == NULL)
     {
diff --git a/elf/dl-support.c b/elf/dl-support.c
index 21cd13e5cf..3333bf10fb 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
@@ -20,8 +20,10 @@
 /* This file defines some things that for the dynamic linker are defined in
    rtld.c and dl-sysdep.c in ways appropriate to bootstrap dynamic linking.  */
 
+#include <link.h>
 #include <stdlib.h>
-
+#include <unistd.h>
+#include <dl-machine.h>
 
 extern char *__progname;
 char **_dl_argv = &__progname;	/* This is checked for some error messages.  */
@@ -30,15 +32,36 @@ char **_dl_argv = &__progname;	/* This is checked for some error messages.  */
    For the dynamic linker it is set by -rpath when linking.  */
 const char *_dl_rpath = DEFAULT_RPATH;
 
+/* Name of the architecture.  */
+const char *_dl_platform;
+size_t _dl_platformlen;
+
 /* If nonzero print warnings about problematic situations.  */
 int _dl_verbose;
 
+/* Structure to store information about search paths.  */
+struct r_search_path *_dl_search_paths;
 
-static void init_verbose (void) __attribute__ ((unused));
+
+static void non_dynamic_init (void) __attribute__ ((unused));
 
 static void
-init_verbose (void)
+non_dynamic_init (void)
 {
   _dl_verbose = *(getenv ("LD_WARN") ?: "") == '\0' ? 0 : 1;
+
+  /* Initialize the data structures for the search paths for shared
+     objects.  */
+  _dl_init_paths ();
+
+#ifdef DL_PLATFORM_INIT
+  DL_PLATFORM_INIT;
+#endif
+
+  /* Now determine the length of the platform string.  */
+  if (_dl_platform != NULL)
+    _dl_platformlen = strlen (_dl_platform);
+
+  _dl_pagesize = __getpagesize ();
 }
-text_set_element (__libc_subinit, init_verbose);
+text_set_element (__libc_subinit, non_dynamic_init);
diff --git a/elf/elf.h b/elf/elf.h
index d01bc90c90..02d092ffac 100644
--- a/elf/elf.h
+++ b/elf/elf.h
@@ -654,6 +654,11 @@ typedef struct
 #define AT_GID		13		/* Real gid */
 #define AT_EGID		14		/* Effective gid */
 
+/* Some more special a_type values describing the hardware.  */
+#define AT_PLATFORM	15		/* String identifying platform.  */
+#define AT_HWCAP	16		/* Machine dependent hints about
+					   processor capabilities.  */
+
 /* Motorola 68k specific definitions.  */
 
 /* m68k relocs.  */
diff --git a/elf/genrtldtbl.awk b/elf/genrtldtbl.awk
new file mode 100644
index 0000000000..50f81c8c34
--- /dev/null
+++ b/elf/genrtldtbl.awk
@@ -0,0 +1,28 @@
+#! /usr/bin/awk
+BEGIN {
+  FS=":";
+  count=0;
+}
+{
+  for (i = 1; i <= NF; ++i) {
+    dir[count++] = gensub(/((.*)[^/])?[/]*/, "\\1", "", $i);
+  }
+}
+END {
+  for (i = 0; i < count; ++i) {
+    printf ("static struct r_search_path_elem rtld_search_dir%d =\n", i+1);
+    printf ("  { \"%s/\", %d, unknown, 0, unknown, ",
+	    dir[i], length (dir[i]) + 1);
+    if (i== 0)
+      printf ("NULL };\n");
+    else
+      printf ("&rtld_search_dir%d };\n", i);
+  }
+  printf ("\nstatic struct r_search_path_elem *rtld_search_dirs[] =\n{\n");
+  for (i = 0; i < count; ++i) {
+    printf ("  &rtld_search_dir%d,\n", i + 1);
+  }
+  printf ("  NULL\n};\n\n");
+  printf ("static struct r_search_path_elem *all_dirs = &rtld_search_dir%d;\n",
+	  count);
+}
diff --git a/elf/link.h b/elf/link.h
index f457174df5..e0fccd0fca 100644
--- a/elf/link.h
+++ b/elf/link.h
@@ -91,6 +91,25 @@ struct r_found_version
     const char *filename;
   };
 
+/* We want to cache information about the searches for shared objects.  */
+
+enum r_dir_status { unknown, nonexisting, existing };
+
+struct r_search_path_elem
+  {
+    const char *dirname;
+
+    size_t dirnamelen;
+    enum r_dir_status dirstatus;
+
+    size_t machdirnamelen;
+    enum r_dir_status machdirstatus;
+
+    /* This link is only used in the `all_dirs' member of `r_search_path'.  */
+    struct r_search_path_elem *next;
+  };
+
+
 /* Structure describing a loaded shared object.  The `l_next' and `l_prev'
    members form a chain of all the shared objects loaded at startup.
 
@@ -163,6 +182,9 @@ struct link_map
     /* Array with version names.  */
     unsigned int l_nversions;
     struct r_found_version *l_versions;
+
+    /* Collected information about own RPATH directories.  */
+    struct r_search_path_elem **l_rpath_dirs;
   };
 
 
@@ -407,6 +429,10 @@ extern void _dl_debug_state (void);
    in the `r_ldbase' member.  Returns the address of the structure.  */
 extern struct r_debug *_dl_debug_initialize (ElfW(Addr) ldbase);
 
+/* Initialize the basic data structure for the search paths.  */
+void _dl_init_paths (void);
+
+
 __END_DECLS
 
 #endif /* link.h */
diff --git a/elf/rtld.c b/elf/rtld.c
index a025757e05..7189ca6b2b 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -58,6 +58,9 @@ int _dl_argc;
 char **_dl_argv;
 const char *_dl_rpath;
 int _dl_verbose;
+const char *_dl_platform;
+size_t _dl_platformlen;
+struct r_search_path *_dl_search_paths;
 
 /* Set nonzero during loading and initialization of executable and
    libraries, cleared before the executable's entry point runs.  This
@@ -526,6 +529,10 @@ of this helper program; chances are you did not intend to run this program.\n",
       assert (i == npreloads);
     }
 
+  /* Initialize the data structures for the search paths for shared
+     objects.  */
+  _dl_init_paths ();
+
   /* 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.  */