about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog27
-rw-r--r--NEWS2
-rw-r--r--config.make.in5
-rw-r--r--configure.in3
-rw-r--r--elf/Makefile14
-rw-r--r--elf/cache.c363
-rw-r--r--elf/ldconfig.c647
-rw-r--r--elf/ldconfig.h63
-rw-r--r--elf/readlib.c160
-rw-r--r--manual/time.texi2
-rw-r--r--sysdeps/generic/readelflib.c180
-rw-r--r--sysdeps/unix/sysv/linux/sparc/readelflib.c57
12 files changed, 1516 insertions, 7 deletions
diff --git a/ChangeLog b/ChangeLog
index 4153842209..1dd355f3e4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,30 @@
+1999-11-30  Andreas Jaeger  <aj@suse.de>
+
+	Add ldconfig:
+	* elf/Makefile (extra-objs): Added ldconfig-modules.
+	(ldconfig-modules): New.
+	Added vpath for xstrdup and xmalloc.
+	Check for use-ldconfig instead of has-ldconfig.
+	($(objpfx)ldconfig): New rule with dependencies.
+	(distribute): Add new files.
+
+	* sysdeps/unix/sysv/linux/sparc/readelflib.c: New file,
+	developed together with Jakub Jelinek <jakub@redhat.com>.
+	* sysdeps/generic/readelflib.c: Likewise.
+
+	* elf/cache.c: New file.
+	* elf/ldconfig.c: New file.
+	* elf/ldconfig.h: New file.
+	* elf/readlib.c: New file.
+
+	* Makefile (install): Remove flag -d in ldconfig call.
+
+	* configure.in: Rename has_ldconfig to use_ldconfig, set it to no
+	by default.
+
+	* config.make.in (has-ldconfig): Renamed to use-ldconfig, changed
+	comment.
+
 1999-12-03  Ulrich Drepper  <drepper@cygnus.com>
 
 	* sysdeps/generic/bits/stropts.h: Update with LiS types and
diff --git a/NEWS b/NEWS
index a01e97720c..b5552e8d49 100644
--- a/NEWS
+++ b/NEWS
@@ -20,6 +20,8 @@ Version 2.2
 * Functions feenableexcept and fedisableexcept to control the
   behaviour of individual exceptions have been added by Andreas Jaeger.
 
+* ldconfig program added by Andreas Jaeger and Jakub Jelinek.
+
 
 Version 2.1.2
 
diff --git a/config.make.in b/config.make.in
index 4ba8143f4d..06585d2259 100644
--- a/config.make.in
+++ b/config.make.in
@@ -15,9 +15,8 @@ sysconfdir = @libc_cv_sysconfdir@
 libexecdir = @libexecdir@
 rootsbindir = @libc_cv_rootsbindir@
 
-# If ldconfig exists.  This will go away as soon as `ldconfig' is available
-# in GNU libc.
-has-ldconfig = @has_ldconfig@
+# Should we use and build ldconfig?
+use-ldconfig = @use_ldconfig@
 
 # Maybe the `ldd' script must be rewritten.
 ldd-rewrite-script = @ldd_rewrite_script@
diff --git a/configure.in b/configure.in
index d7ff75bdd0..6d86b68a8b 100644
--- a/configure.in
+++ b/configure.in
@@ -1168,6 +1168,7 @@ libc_link_dests=
 libc_link_sources=
 
 # They also can set these variables.
+use_ldconfig=no
 ldd_rewrite_script=no
 
 # Iterate over all the sysdep directories we will use, running their
@@ -1282,7 +1283,7 @@ AC_SUBST(libc_cv_slibdir)
 AC_SUBST(libc_cv_sysconfdir)
 AC_SUBST(libc_cv_rootsbindir)
 
-AC_SUBST(has_ldconfig)
+AC_SUBST(use_ldconfig)
 AC_SUBST(ldd_rewrite_script)
 
 AC_SUBST(gnu_ld) AC_SUBST(gnu_as) AC_SUBST(elf)
diff --git a/elf/Makefile b/elf/Makefile
index ff5f44a819..422d278ce2 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -41,7 +41,8 @@ distribute	:= $(rtld-routines:=.c) dynamic-link.h do-rel.h dl-machine.h \
 		   dl-librecon.h interp.c sln.c dl-dst.h hp-timing.h \
 		   do-lookup.h sprof.c gen-trusted-dirs.awk \
 		   testobj1.c testobj2.c testobj3.c testobj4.c testobj5.c \
-		   testobj6.c testobj1_1.c failobj.c
+		   testobj6.c testobj1_1.c failobj.c \
+		   ldconfig.h ldconfig.c cache.c readlib.c readelflib.c
 
 include ../Makeconfig
 
@@ -64,10 +65,17 @@ install-bin	+= sprof
 others-static   = sln
 install-rootsbin = sln
 
-ifeq (yes,$(has-ldconfig))
+ifeq (yes,$(use-ldconfig))
 others-static	+= ldconfig
 others		+= ldconfig
 install-rootsbin += ldconfig
+
+ldconfig-modules := cache readlib xmalloc xstrdup
+extra-objs	+= $(ldconfig-modules:=.o)
+
+# To find xmalloc.c and xstrdup.c
+vpath %.c ../locale/programs
+
 endif
 
 ifeq (yes,$(build-shared))
@@ -191,6 +199,8 @@ $(objpfx)ldd: ldd.bash.in $(common-objpfx)soversions.mk \
 
 $(objpfx)sprof: $(libdl)
 
+$(objpfx)ldconfig: $(ldconfig-modules:%=$(objpfx)%.o)
+
 test-modules = $(addprefix $(objpfx),$(addsuffix .so,$(modules-names)))
 generated += $(addsuffix .so,$(modules-names))
 
diff --git a/elf/cache.c b/elf/cache.c
new file mode 100644
index 0000000000..d13e4dbbd7
--- /dev/null
+++ b/elf/cache.c
@@ -0,0 +1,363 @@
+/* Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Andreas Jaeger <aj@suse.de>, 1999.
+
+   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.  */
+
+#define _GNU_SOURCE 1
+
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <dirent.h>
+#include <libintl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "ldconfig.h"
+
+#define CACHEMAGIC "ld.so-1.7.0"
+
+struct cache_entry
+{
+  char *lib;
+  char *path;
+  int flags;
+  struct cache_entry *next;
+};
+
+struct file_entry
+{
+  int flags;		/* This is 1 for an ELF library.  */
+  unsigned int key, value; /* String table indices.  */
+};
+
+
+struct cache_file
+{
+  char magic[sizeof CACHEMAGIC - 1];
+  unsigned int nlibs;
+  struct file_entry libs[0];
+};
+
+
+/* List of all cache entries.  */
+static struct cache_entry *entries;
+
+static const char *flag_descr[] =
+{ "libc4", "ELF", "libc5", "libc6"};
+
+
+/* Print a single entry.  */
+static void
+print_entry (const char *lib, int flag, const char *key)
+{
+  printf ("\t%s (", lib);
+  switch (flag)
+    {
+    case FLAG_LIBC4:
+    case FLAG_ELF:
+    case FLAG_ELF_LIBC5:
+    case FLAG_ELF_LIBC6:
+      fputs (flag_descr [flag & FLAG_TYPE_MASK], stdout);
+      break;
+    default:
+      fputs ("unknown", stdout);
+      break;
+    }
+  switch (flag & FLAG_REQUIRED_MASK)
+    {
+#ifdef __sparc__
+    case FLAG_SPARC_LIB64:
+      fputs (",64bit", stdout);
+#endif
+    case 0:
+      break;
+    default:
+      fprintf (stdout, ",%d", flag & FLAG_REQUIRED_MASK);
+      break;
+    }
+  printf (") => %s\n", key);
+}
+
+
+/* Print the whole cache file.  */
+void
+print_cache (const char *cache_name)
+{
+  size_t cache_size;
+  struct stat st;
+  int fd;
+  unsigned int i;
+  struct cache_file *cache;
+  const char *cache_data;
+  
+  fd = open (cache_name, O_RDONLY);
+  if (fd < 0)
+    error (EXIT_FAILURE, errno, _("Can't open cache file %s\n"), cache_name);
+
+  if (fstat (fd, &st) < 0
+      /* No need to map the file if it is empty.  */
+      || st.st_size == 0)
+    {
+      close (fd);
+      return;
+    }
+  
+  cache = mmap (0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+  if (cache == MAP_FAILED)
+    error (EXIT_FAILURE, errno, _("mmap of cache file failed.\n"));
+  cache_size = st.st_size;
+
+  if (cache_size < sizeof (struct cache_file)
+      || memcmp (cache->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1))
+    return;
+  /* This is where the strings start.  */
+  cache_data = (const char *) &cache->libs[cache->nlibs];
+
+  printf (_("%d libs found in cache `%s'\n"), cache->nlibs, cache_name);
+  
+  /* Print everything.  */
+  for (i = 0; i < cache->nlibs; i++)
+    print_entry (cache_data + cache->libs[i].key,
+		 cache->libs[i].flags,
+		 cache_data + cache->libs[i].value);
+
+  /* Cleanup.  */
+  munmap (cache, cache_size);
+  close (fd);
+}
+
+/* Initialize cache data structures.  */
+void
+init_cache (void)
+{
+  entries = NULL;
+}
+
+
+/* Helper function which must match the one in the dynamic linker, so that
+   we rely on the same sort order.  */
+int
+cache_libcmp (const char *p1, const char *p2)
+{
+  while (*p1 != '\0')
+    {
+      if (*p1 >= '0' && *p1 <= '9')
+        {
+          if (*p2 >= '0' && *p2 <= '9')
+            {
+	      /* Must compare this numerically.  */
+	      int val1;
+	      int val2;
+
+	      val1 = *p1++ - '0';
+	      val2 = *p2++ - '0';
+	      while (*p1 >= '0' && *p1 <= '9')
+	        val1 = val1 * 10 + *p1++ - '0';
+	      while (*p2 >= '0' && *p2 <= '9')
+	        val2 = val2 * 10 + *p2++ - '0';
+	      if (val1 != val2)
+		return val1 - val2;
+	    }
+	  else
+            return 1;
+        }
+      else if (*p2 >= '0' && *p2 <= '9')
+        return -1;
+      else if (*p1 != *p2)
+        return *p1 - *p2;
+      else
+	{
+	  ++p1;
+	  ++p2;
+	}
+    }
+  return *p1 - *p2;
+}
+
+static
+int compare (const struct cache_entry *e1, const struct cache_entry *e2)
+{
+  int res;
+  
+  /* We need to swap entries here to get the correct sort order.  */
+  res = cache_libcmp (e2->lib, e1->lib);
+  if (res == 0)
+    {
+      if (e1->flags < e2->flags)
+	return 1;
+      else if (e1->flags > e2->flags)
+	return -1;
+    }
+  return res;
+}
+
+
+/* Save the contents of the cache.  */
+void
+save_cache (const char *cache_name)
+{
+  struct cache_entry *entry;
+  int i, fd;
+  size_t total_strlen, len;
+  char *strings, *str, *temp_name;
+  struct cache_file *file_entries;
+  size_t file_entries_size;
+  unsigned int str_offset;
+  /* Number of cache entries.  */
+  int cache_entry_count = 0;
+
+  /* The cache entries are sorted already, save them in this order. */
+
+  /* Count the length of all strings.  */
+  total_strlen = 0;
+  for (entry = entries; entry != NULL; entry = entry->next)
+    {
+      /* Account the final NULs.  */
+      total_strlen += strlen (entry->lib) + strlen (entry->path) + 2;
+      ++cache_entry_count;
+    }
+  
+  /* Create the on disk cache structure.  */
+  /* First an array for all strings.  */
+  strings = (char *)xmalloc (total_strlen + 1);
+
+  /* And the list of all entries.  */
+  file_entries_size = sizeof (struct cache_file)
+    + cache_entry_count * sizeof (struct file_entry);
+  file_entries = (struct cache_file *) xmalloc (file_entries_size);
+
+  /* Fill in the header.  */
+  memset (file_entries, 0, sizeof (struct cache_file));
+  memcpy (file_entries->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1);
+  
+  file_entries->nlibs = cache_entry_count;
+
+  str_offset = 0;
+  str = strings;
+  for (i = 0, entry = entries; entry != NULL; entry = entry->next, ++i)
+    {
+      file_entries->libs[i].flags = entry->flags;
+      /* First the library.  */
+      /* XXX: Actually we can optimize here and remove duplicates.  */
+      file_entries->libs[i].key = str_offset;
+      len = strlen (entry->lib);
+      str = stpcpy (str, entry->lib);
+      /* Account the final NUL.  */
+      ++str;
+      str_offset += len + 1;
+      /* Then the path.  */
+      file_entries->libs[i].value = str_offset;
+      len = strlen (entry->path);
+      str = stpcpy (str, entry->path);
+      /* Account the final NUL.  */
+      ++str;
+      str_offset += len + 1;
+    }
+  assert (str_offset == total_strlen);
+
+  /* Write out the cache.  */
+
+  /* Write cache first to a temporary file and rename it later.  */
+  temp_name = xmalloc (strlen (cache_name) + 2);
+  sprintf (temp_name, "%s~", cache_name);
+  /* First remove an old copy if it exists.  */
+  if (unlink (temp_name) && errno != ENOENT)
+    error (EXIT_FAILURE, errno, _("Can't remove old temporary cache file %s"),
+	   temp_name);
+
+  /* Create file.  */
+  fd = open (temp_name, O_CREAT|O_WRONLY|O_TRUNC|O_NOFOLLOW, 0644);
+  if (fd < 0)
+    error (EXIT_FAILURE, errno, _("Can't create temporary cache file %s"),
+	   temp_name);
+
+  /* Write contents.  */
+  if (write (fd, file_entries, file_entries_size) != (ssize_t)file_entries_size)
+    error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
+
+  if (write (fd, strings, total_strlen) != (ssize_t)total_strlen)
+    error (EXIT_FAILURE, errno, _("Writing of cache data failed."));
+
+  close (fd);
+
+  /* Move temporary to its final location.  */
+  if (rename (temp_name, cache_name))
+    error (EXIT_FAILURE, errno, _("Renaming of %s to %s failed"), temp_name,
+	   cache_name);
+  
+  /* Free all allocated memory.  */
+  free (file_entries);
+  free (strings);
+
+  while (entries)
+    {
+      entry = entries;
+      free (entry->path);
+      free (entry->lib);
+      entries = entries->next;
+      free (entry);
+    }
+}
+
+/* Add one library to the cache.  */
+void
+add_to_cache (const char *path, const char *lib, int flags)
+{
+  struct cache_entry *new_entry, *ptr, *prev;
+  char *full_path;
+  int len;
+
+  new_entry = (struct cache_entry *) xmalloc (sizeof (struct cache_entry));
+
+  len = strlen (lib) + strlen (path) + 2;
+
+  full_path = (char *) xmalloc (len);
+  snprintf (full_path, len, "%s/%s", path, lib);
+  
+  new_entry->lib = xstrdup (lib);
+  new_entry->path = full_path;
+  new_entry->flags = flags;
+
+  /* Keep the list sorted - search for right place to insert.  */
+  ptr = entries;
+  prev = entries;
+  while (ptr != NULL)
+    {
+      if (compare (ptr, new_entry) > 0)
+	break;
+      prev = ptr;
+      ptr = ptr->next;
+    }
+  /* Is this the first entry?  */
+  if (ptr == entries)
+    {
+      new_entry->next = entries;
+      entries = new_entry;
+    }
+  else
+    {
+      new_entry->next = prev->next;
+      prev->next = new_entry;
+    }
+}
diff --git a/elf/ldconfig.c b/elf/ldconfig.c
new file mode 100644
index 0000000000..6c2bcb2d4f
--- /dev/null
+++ b/elf/ldconfig.c
@@ -0,0 +1,647 @@
+/* Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Andreas Jaeger <aj@suse.de>, 1999.
+
+   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.  */
+
+#include <argp.h>
+#include <dirent.h>
+#include <error.h>
+#include <errno.h>
+#include <libintl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "ldconfig.h"
+
+#ifndef LD_SO_CACHE
+# define LD_SO_CACHE "/etc/ld.so.cache"
+#endif
+
+#ifndef LD_SO_CONF
+# define LD_SO_CONF "/etc/ld.so.conf"
+#endif
+
+/* Get libc version number.  */
+#include <version.h>
+
+#define PACKAGE _libc_intl_domainname
+
+struct lib_entry
+  {
+    int flags;
+    char *lib;
+    char *path;
+  };
+
+static const struct
+{
+  const char *name;
+  int flag;
+} lib_types [] =
+{
+  {"libc4", FLAG_LIBC4},
+  {"libc5", FLAG_ELF_LIBC5},
+  {"libc6", FLAG_ELF_LIBC6},
+  {"glibc2", FLAG_ELF_LIBC6}
+};  
+
+
+/* List of directories to handle.  */
+struct dir_entry
+{
+  char *path;
+  int flag;
+  struct dir_entry *next;
+};
+
+/* The list is unsorted, contains no duplicates.  Entries are added at
+   the end.  */
+static struct dir_entry *dir_entries;
+
+/* Flags for different options.  */
+/* Print Cache.  */
+static int opt_print_cache = 0;
+
+/* Be verbose.  */
+int opt_verbose = 0;
+
+/* Build cache.  */
+static int opt_build_cache = 1;
+
+/* Generate links.  */
+static int opt_link = 1;
+
+/* Only process directories specified on the command line.  */
+static int opt_only_cline = 0;
+
+/* Path to root for chroot.  */
+static char *opt_chroot;
+
+/* Cache file to use.  */
+static const char *cache_file;
+
+/* Configuration file.  */
+static const char *config_file;
+
+/* Name and version of program.  */
+static void print_version (FILE *stream, struct argp_state *state);
+void (*argp_program_version_hook) (FILE *, struct argp_state *)
+     = print_version;
+
+/* Definitions of arguments for argp functions.  */
+static const struct argp_option options[] =
+{
+  { "print-cache", 'p', NULL, 0, N_("Print cache"), 0},
+  { "verbose", 'v', NULL, 0, N_("Generate verbose messages"), 0},
+  { NULL, 'N', NULL, 0, N_("Don't build cache"), 0},
+  { NULL, 'X', NULL, 0, N_("Don't generate links"), 0},
+  { NULL, 'r', "ROOT", 0, N_("Change to and use ROOT as root directory"), 0},
+  { NULL, 'C', "CACHE", 0, N_("Use CACHE as cache file"), 0},
+  { NULL, 'f', "CONF", 0, N_("Use CONF as configuration file"), 0},
+  { NULL, 'n', NULL, 0, N_("Only process directories specified on the command line.  Don't build cache."), 0},
+  { NULL, 0, NULL, 0, NULL, 0 }
+};
+
+/* Short description of program.  */
+static const char doc[] = N_("Configure Dynamic Linker Run Time Bindings.");
+
+/* Prototype for option handler.  */
+static error_t parse_opt (int key, char *arg, struct argp_state *state);
+
+/* Data structure to communicate with argp functions.  */
+static struct argp argp =
+{
+  options, parse_opt, NULL, doc, NULL, NULL, NULL
+};
+
+
+
+/* Handle program arguments.  */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+  switch (key)
+    {
+    case 'C':
+      cache_file = arg;
+      break;
+    case 'f':
+      config_file = arg;
+      break;
+    case 'N':
+      opt_build_cache = 0;
+      break;
+    case 'n':
+      opt_build_cache = 0;
+      opt_only_cline = 1;
+      break;
+    case 'p':
+      opt_print_cache = 1;
+      break;
+    case 'r':
+      opt_chroot = arg;
+      break;
+    case 'v':
+      opt_verbose = 1;
+      break;
+    case 'X':
+      opt_link = 0;
+      break;
+    default:
+      return ARGP_ERR_UNKNOWN;
+    }
+
+  return 0;
+}
+
+/* Print the version information.  */
+static void
+print_version (FILE *stream, struct argp_state *state)
+{
+  fprintf (stream, "ldconfig (GNU %s) %s\n", PACKAGE, VERSION);
+  fprintf (stream, gettext ("\
+Copyright (C) %s Free Software Foundation, Inc.\n\
+This is free software; see the source for copying conditions.  There is NO\n\
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
+"), "1999");
+  fprintf (stream, gettext ("Written by %s.\n"),
+	   "Andreas Jaeger");
+}
+
+/* Add one directory to the list of directories to process.  */
+static void
+add_dir (const char *line)
+{
+  char *equal_sign;
+  struct dir_entry *entry, *ptr, *prev;
+  unsigned int i;
+  
+  entry = xmalloc (sizeof (struct dir_entry));
+  entry->next = NULL;
+  
+  /* Search for an '=' sign.  */
+  entry->path = xstrdup (line);
+  equal_sign = strchr (entry->path, '=');
+  if (equal_sign)
+    {
+      *equal_sign = '\0';
+      ++equal_sign;
+      entry->flag = FLAG_ANY;
+      for (i = 0; i < sizeof (lib_types) / sizeof (lib_types [0]); ++i)
+	if (strcmp (equal_sign, lib_types[i].name) == 0)
+	  {
+	    entry->flag = lib_types[i].flag;
+	    break;
+	  }
+      if (entry->flag == FLAG_ANY)
+	error (0, 0, _("%s is not a known library type"), equal_sign);
+    }
+  else
+    {
+      entry->flag = FLAG_ANY;
+    }
+
+  /* Canonify path: for now only remove trailing slashes.  */
+  i = strlen (entry->path) - 1;
+  while (entry->path[i] == '/' && i > 0)
+    {
+      entry->path [i] = '\0';
+      --i;
+    }
+  
+  ptr = dir_entries;
+  prev = ptr;
+  while (ptr != NULL)
+    {
+      /* Check for duplicates.  */
+      if (strcmp (ptr->path, entry->path) == 0)
+	{
+	  if (opt_verbose)
+	    error (0, 0, _("Path `%s' given more than once"), entry->path);
+	  /* Use the newer information.  */
+	  ptr->flag = entry->flag;
+	  free (entry);
+	  break;
+	}
+      prev = ptr;
+      ptr = ptr->next;
+    }
+  /* Is this the first entry?  */
+  if (ptr == NULL && dir_entries == NULL)
+    dir_entries = entry;
+  else if (ptr == NULL)
+    prev->next = entry;
+}
+
+
+/* Create a symbolic link from soname to libname in directory path.  */
+static void
+create_links (const char *path, const char *libname, const char *soname)
+{
+  char full_libname [PATH_MAX], full_soname [PATH_MAX];
+  struct stat stat_lib, stat_so, lstat_so;
+  int do_link = 1;
+  int do_remove = 1;
+  /* XXX: The logics in this function should be simplified.  */
+  
+  /* Get complete path.  */
+  snprintf (full_libname, sizeof full_libname, "%s/%s", path, libname);
+  snprintf (full_soname, sizeof full_soname, "%s/%s", path, soname);
+
+  /* Does soname already exist and point to the right library?  */
+  if (stat (full_soname, &stat_so) == 0)
+    {
+      if (stat (full_libname, &stat_lib))
+	{
+	  error (0, 0, _("Can't stat %s\n"), full_libname);
+	  return;
+	}
+      if (stat_lib.st_dev == stat_so.st_dev
+	  && stat_lib.st_ino == stat_so.st_ino)
+	/* Link is already correct.  */
+	do_link = 0;
+      else if (lstat (full_soname, &lstat_so) == 0
+	       && !S_ISLNK (lstat_so.st_mode))
+	{
+	  error (0, 0, _("%s is not a symbolic link\n"), full_soname);
+	  do_link = 0;
+	  do_remove = 0;
+	}
+    }
+  else if (lstat (full_soname, &lstat_so) != 0
+	   || !S_ISLNK (lstat_so.st_mode))
+    /* Unless it is a stale symlink, there is no need to remove.  */
+    do_remove = 0;
+
+  if (opt_verbose)
+    printf ("\t%s -> %s", soname, libname);
+
+  if (do_link && opt_link)
+    {
+      /* Remove old link.  */
+      if (do_remove)
+	if (unlink (full_soname))
+	  {
+	    error (0, 0, _("Can't unlink %s"), full_soname);
+	    do_link = 0;
+	  }
+      /* Create symbolic link.  */
+      if (do_link && symlink (libname, full_soname))
+	{
+	  error (0, 0, _("Can't link %s to %s"), full_soname, libname);
+	  do_link = 0;
+	}
+      if (opt_verbose)
+	{
+	  if (do_link)
+	    fputs (_(" (changed)\n"), stdout);
+	  else
+	    fputs (_(" (SKIPPED)\n"), stdout);
+	}
+    }
+  else if (opt_verbose)
+    fputs ("\n", stdout);
+}
+
+/* Read a whole directory and search for libraries.
+   The purpose is two-fold:
+   - search for libraries which will be added to the cache
+   - create symbolic links to the soname for each library
+
+   This has to be done separatly for each directory.
+   
+   To keep track of which libraries to add to the cache and which
+   links to create, we save a list of all libraries.
+
+   The algorithm is basically:
+   for all libraries in the directory do
+     get soname of library
+     if soname is already in list
+       if new library is newer, replace entry
+       otherwise ignore this library
+     otherwise add library to list
+   
+   For example, if the two libraries libxy.so.1.1 and libxy.so.1.2
+   exist and both have the same soname, e.g. libxy.so, a symbolic link
+   is created from libxy.so.1.2 (the newer one) to libxy.so.
+   libxy.so.1.2 and libxy.so are added to the cache - but not
+   libxy.so.1.1.  */
+
+/* Information for one library.  */
+struct dlib_entry 
+{
+  char *name;
+  char *soname;
+  int flag;
+  int is_link;
+  struct dlib_entry *next;
+};
+
+
+static void
+search_dir (const struct dir_entry *entry)
+{
+  DIR *dir;
+  struct dirent *direntry;
+  char buf [PATH_MAX];
+  char *soname;
+  struct dlib_entry *dlibs;
+  struct dlib_entry *dlib_ptr;
+  int nchars;
+  struct stat stat_buf;
+  int is_link;
+  
+  dlibs = NULL;
+
+  if (opt_verbose)
+    printf ("%s:\n", entry->path);
+  
+  dir = opendir (entry->path);
+  if (dir == NULL)
+    {
+      if (opt_verbose)
+	error (0, errno, _("Can't open directory %s"), entry->path);
+      return;
+    }
+  
+
+  while ((direntry = readdir (dir)) != NULL)
+    {
+      int flag;
+#ifdef _DIRENT_HAVE_D_TYPE
+      /* We only look at links and regular files.  */
+      if (direntry->d_type != DT_UNKNOWN
+	  && direntry->d_type != DT_LNK
+	  && direntry->d_type != DT_REG)
+	continue;
+#endif /* _DIRENT_HAVE_D_TYPE  */
+
+      /* Does this file look like a shared library?  The dynamic
+	 linker is also considered as shared library.  */
+      if ((strncmp (direntry->d_name, "lib", 3) != 0
+	   && strncmp (direntry->d_name, "ld-", 3) != 0)
+	  || strstr (direntry->d_name, ".so") == NULL)
+	continue;
+      nchars = snprintf (buf, sizeof (buf), "%s/%s", entry->path,
+			 direntry->d_name);
+      /* Check for overflow.  */
+      if (nchars >= (int) sizeof (buf))
+	{
+	  error (0, 0, _("buffer for snprintf too small for %s/%s--file is ignored\n"),
+		 entry->path, direntry->d_name);
+	  continue;
+	}
+      if (lstat (buf, &stat_buf))
+	{
+	  error (0, errno, _("Can't lstat %s"), buf);
+	  continue;
+	}
+      else if (!S_ISREG (stat_buf.st_mode) && !S_ISLNK (stat_buf.st_mode))
+	continue;
+
+      is_link = S_ISLNK (stat_buf.st_mode);
+
+      if (process_file (buf, direntry->d_name, &flag, &soname, is_link))
+	continue;
+
+      /* Links will just point to itself.  */
+      if (is_link)
+	{
+	  free (soname);
+	  soname = xstrdup (direntry->d_name);
+	}
+      
+      if (flag == FLAG_ELF
+	  && (entry->flag == FLAG_ELF_LIBC5
+	      || entry->flag == FLAG_ELF_LIBC6))
+	flag = entry->flag;
+      /* Some sanity checks to print warnings.  */
+      if (opt_verbose)
+	{
+	  if (flag == FLAG_ELF_LIBC5 && entry->flag != FLAG_ELF_LIBC5
+	      && entry->flag != FLAG_ANY)
+	    error (0, 0, _("libc5 library %s in wrong directory"), buf);
+	  if (flag == FLAG_ELF_LIBC6 && entry->flag != FLAG_ELF_LIBC6
+	      && entry->flag != FLAG_ANY)
+	    error (0, 0, _("libc6 library %s in wrong directory"), buf);
+	  if (flag == FLAG_LIBC4 && entry->flag != FLAG_LIBC4
+	      && entry->flag != FLAG_ANY)
+	    error (0, 0, _("libc4 library %s in wrong directory"), buf);
+	}
+      
+      /* Add library to list.  */
+      for (dlib_ptr = dlibs; dlib_ptr != NULL; dlib_ptr = dlib_ptr->next)
+	{
+	  /* Is soname already in list?  */
+	  if (strcmp (dlib_ptr->soname, soname) == 0)
+	    {
+	      /* Prefer a file to a link, otherwise check which one
+		 is newer.  */
+	      if ((!is_link && dlib_ptr->is_link)
+		  || (is_link == dlib_ptr->is_link
+		      && cache_libcmp (dlib_ptr->name, direntry->d_name) < 0))
+		{
+		  /* It's newer - add it.  */
+		  /* Flag should be the same - sanity check.  */
+		  if (dlib_ptr->flag != flag)
+		    {
+		      if (dlib_ptr->flag == FLAG_ELF
+			  && (flag == FLAG_ELF_LIBC5 || flag == FLAG_ELF_LIBC6))
+			dlib_ptr->flag = flag;
+		      else if ((dlib_ptr->flag == FLAG_ELF_LIBC5
+				|| dlib_ptr->flag == FLAG_ELF_LIBC6)
+			       && flag == FLAG_ELF)
+			dlib_ptr->flag = flag;
+		      else
+			error (0, 0, _("libraries %s and %s in directory %s have same soname but different type."),
+			       dlib_ptr->name, direntry->d_name, entry->path);
+		    }
+		  free (dlib_ptr->name);
+		  dlib_ptr->name = xstrdup (direntry->d_name);
+		  dlib_ptr->is_link = is_link;
+		}
+	      /* Don't add this library, abort loop.  */
+	      /* Also free soname, since it's dynamically allocated.  */
+	      free (soname);
+	      break;
+	    }
+	}
+      /* Add the library if it's not already in.  */
+      if (dlib_ptr == NULL)
+	{
+	  dlib_ptr = (struct dlib_entry *)xmalloc (sizeof (struct dlib_entry));
+	  dlib_ptr->name = xstrdup (direntry->d_name);
+	  dlib_ptr->flag = flag;
+	  dlib_ptr->soname = soname;
+	  dlib_ptr->is_link = is_link;
+	  /* Add at head of list.  */
+	  dlib_ptr->next = dlibs;
+	  dlibs = dlib_ptr;
+	}
+    }
+
+  closedir (dir);
+
+  /* Now dlibs contains a list of all libs - add those to the cache
+     and created all symbolic links.  */
+  for (dlib_ptr = dlibs; dlib_ptr != NULL; dlib_ptr = dlib_ptr->next)
+    {
+      /* Don't create links to links.  */
+      if (dlib_ptr->is_link == 0)
+	create_links (entry->path, dlib_ptr->name, dlib_ptr->soname);
+      if (opt_build_cache)
+	add_to_cache (entry->path, dlib_ptr->soname, dlib_ptr->flag);
+    }
+
+  /* Free all resources.  */
+  while (dlibs) 
+    {
+      dlib_ptr = dlibs;
+      free (dlib_ptr->soname);
+      free (dlib_ptr->name);
+      dlibs = dlibs->next;
+      free (dlib_ptr);
+    }
+}
+
+/* Search through all libraries.  */
+static void
+search_dirs (void)
+{
+  struct dir_entry *entry;
+
+  for (entry = dir_entries; entry != NULL; entry = entry->next)
+    search_dir (entry);
+
+  /* Free all allocated memory.  */
+  while (dir_entries)
+    {
+      entry = dir_entries;
+      dir_entries = dir_entries->next;
+      free (entry->path);
+      free (entry);
+    }
+}
+
+
+/* Parse configuration file.  */
+static void
+parse_conf (const char *filename)
+{
+  FILE *file;
+  char *line = NULL;
+  size_t len = 0;
+  
+  file = fopen (filename, "r");
+
+  if (file == NULL)
+    {
+      error (0, errno, _("Can't open configuration file %s"), filename);
+      return;
+    }
+
+  do
+    {
+      ssize_t n = getline (&line, &len, file);
+      if (n < 0)
+	break;
+
+      if (line[n - 1] == '\n')
+	line[n - 1] = '\0';
+
+      /* Because the file format does not know any form of quoting we
+	 can search forward for the next '#' character and if found
+	 make it terminating the line.  */
+      *strchrnul (line, '#') = '\0';
+
+      /* If the line is blank it is ignored.  */
+      if (line[0] == '\0')
+	continue;
+
+      add_dir (line);
+    } while (!feof (file));
+
+  /* Free buffer and close file.  */
+  free (line);
+  fclose (file);
+}
+
+
+int
+main (int argc, char **argv)
+{
+  int remaining;
+  
+  /* Parse and process arguments.  */
+  argp_parse (&argp, argc, argv, 0, &remaining, NULL);
+
+  /* Remaining arguments are additional libraries.  */
+  if (remaining != argc)
+    {
+      int i;
+      for (i = remaining; i < argc; ++i)
+	add_dir (argv [i]);
+    }
+
+  if (cache_file == NULL)
+    cache_file = LD_SO_CACHE;
+
+  if (config_file == NULL)
+    config_file = LD_SO_CONF;
+  
+  /* Chroot first.  */
+  if (opt_chroot)
+    {
+      if (chroot (opt_chroot))
+	/* Report failure and exit program.  */
+	error (EXIT_FAILURE, errno, _("Can't chroot to %s"), opt_chroot);
+      /* chroot doesn't change the working directory, let's play safe.  */
+      if (chdir ("/"))
+	error (EXIT_FAILURE, errno, _("Can't chdir to /"));
+    }
+
+  if (opt_print_cache)
+    {
+      print_cache (cache_file);
+      exit (0);
+    }
+
+  if (opt_build_cache)
+    init_cache ();
+
+  if (!opt_only_cline)
+    {
+      /* Always add the standard search paths.  */
+      add_dir ("/lib");
+      add_dir ("/usr/lib");
+
+      parse_conf (config_file);
+    }
+  
+  search_dirs ();
+
+  if (opt_build_cache)
+    save_cache (cache_file);
+
+  return 0;
+}
diff --git a/elf/ldconfig.h b/elf/ldconfig.h
new file mode 100644
index 0000000000..3344876857
--- /dev/null
+++ b/elf/ldconfig.h
@@ -0,0 +1,63 @@
+/* Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Andreas Jaeger <aj@suse.de>, 1999.
+
+   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.  */
+
+#ifndef _LDCONFIG_H
+#define _LDCONFIG_H
+
+#define FLAG_ANY		-1
+#define FLAG_TYPE_MASK		0x00ff
+#define FLAG_LIBC4		0x0000
+#define FLAG_ELF		0x0001
+#define FLAG_ELF_LIBC5		0x0002
+#define FLAG_ELF_LIBC6		0x0003
+#define FLAG_REQUIRED_MASK	0xff00
+#define FLAG_SPARC_LIB64	0x0100
+
+/* Declared in cache.c.  */
+extern void print_cache (const char *cache_name);
+
+extern void init_cache (void);
+
+extern void save_cache (const char *cache_name);
+
+extern void add_to_cache (const char *path, const char *lib, int flags);
+
+extern int cache_libcmp (const char *p1, const char *p2);
+
+
+/* Declared in readlib.c.  */
+extern int process_file (const char *file_name, const char *lib, int *flag,
+			 char **soname, int is_link);
+
+/* Declared in readelflib.c.  */
+extern int process_elf_file (const char *file_name, const char *lib, int *flag,
+			     char **soname, void *file_contents);
+
+
+/* Declared in ldconfig.c.  */
+extern int opt_verbose;
+
+/* Prototypes for a few program-wide used functions.  */
+extern void *xmalloc (size_t __n);
+extern void *xcalloc (size_t __n, size_t __size);
+extern void *xrealloc (void *__p, size_t __n);
+extern char *xstrdup (const char *__str);
+
+#endif /* ! _LDCONFIG_H  */
+
diff --git a/elf/readlib.c b/elf/readlib.c
new file mode 100644
index 0000000000..e6d0619091
--- /dev/null
+++ b/elf/readlib.c
@@ -0,0 +1,160 @@
+/* Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Andreas Jaeger <aj@suse.de>, 1999 and
+		  Jakub Jelinek <jakub@redhat.com>, 1999.
+
+   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.  */
+
+/* The code in this file and in readelflib is a heavily simplified
+   version of the readelf program that's part of the current binutils
+   development version.  Besides the simplification, it has also been
+   modified to read some other file formats.  */
+
+
+#include <a.out.h>
+#include <elf.h>
+#include <error.h>
+#include <link.h>
+#include <libintl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include "ldconfig.h"
+
+#define Elf32_CLASS ELFCLASS32
+#define Elf64_CLASS ELFCLASS64
+
+struct known_names
+{
+  const char *soname;
+  int flag;
+};
+
+static struct known_names interpreters [] =
+{
+  {"/lib/ld-linux.so.2", FLAG_ELF_LIBC6},
+  {"/lib/ld-linux.so.1", FLAG_ELF_LIBC5}
+};
+
+static struct known_names known_libs [] =
+{
+  {"libc.so.6", FLAG_ELF_LIBC6},
+  {"libc.so.5", FLAG_ELF_LIBC5},
+  {"libm.so.6", FLAG_ELF_LIBC6},
+  {"libm.so.5", FLAG_ELF_LIBC5}
+};
+
+
+
+/* Returns 0 if everything is ok, != 0 in case of error.  */
+int
+process_file (const char *file_name, const char *lib, int *flag, char **soname, int is_link)
+{
+  FILE *file;
+  struct stat statbuf;
+  void *file_contents;
+  int ret;
+  
+  ElfW(Ehdr) *elf_header;
+  struct exec *aout_header;
+
+  ret = 0;
+  *flag = FLAG_ANY;
+  *soname = NULL;
+
+  file = fopen (file_name, "rb");
+  if (file == NULL)
+    {
+      /* No error for stale symlink.  */
+      if (is_link && strstr (file_name, ".so.") != NULL)
+	return 1;
+      error (0, 0, _("Input file %s not found.\n"), file_name);
+      return 1;
+    }
+
+  if (fstat (fileno (file), &statbuf) < 0)
+    {
+      error (0, 0, _("Cannot fstat file %s.\n"), file_name);
+      return 1;
+    }
+
+  file_contents = mmap (0, statbuf.st_size, PROT_READ, MAP_SHARED, fileno (file), 0);
+  if (file_contents == MAP_FAILED)
+    {
+      error (0, 0, _("Cannot mmap file %s.\n"), file_name);
+      fclose (file);
+      return 1;
+    }
+
+  /* First check if this is an aout file.  */
+  aout_header = (struct exec *) file_contents;
+  if (N_MAGIC (*aout_header) == ZMAGIC
+      || N_MAGIC (*aout_header) == QMAGIC)
+    {
+      /* Aout files don't have a soname, just return the the name
+         including the major number.  */
+      char *copy, *major, *dot;
+      copy = xstrdup (lib);
+      major = strstr (copy, ".so.");
+      if (major)
+	{
+	  dot = strstr (major + 4, ".");
+	  if (dot)
+	    *dot = '\0';
+	}
+      *soname = copy;
+      *flag = FLAG_LIBC4;
+      goto done;
+    }
+  
+  elf_header = (ElfW(Ehdr) *) file_contents;
+  if (elf_header->e_ident [EI_MAG0] != ELFMAG0
+      || elf_header->e_ident [EI_MAG1] != ELFMAG1
+      || elf_header->e_ident [EI_MAG2] != ELFMAG2
+      || elf_header->e_ident [EI_MAG3] != ELFMAG3)
+    {
+      /* The file is neither ELF nor aout.  Check if it's a linker script,
+	 like libc.so - otherwise complain.  */
+      int len = statbuf.st_size;
+      /* Only search the beginning of the file.  */
+      if (len > 512)
+	len = 512;
+      if (memmem (file_contents, len, "GROUP", 5) == NULL
+	  && memmem (file_contents, len, "GNU ld script", 13) == NULL)
+	error (0, 0, _("%s is not an ELF file - it has the wrong magic bytes at the start.\n"),
+	       file_name);
+      ret = 1;
+      goto done;
+    }
+
+  if (process_elf_file (file_name, lib, flag, soname, file_contents))
+    ret = 1;
+
+ done:
+  /* Clean up allocated memory and resources.  */
+  munmap (file_contents, statbuf.st_size);
+  fclose (file);
+
+  return ret;
+}
+
+/* Get architecture specific version of process_elf_file.  */
+#include "readelflib.c"
+
diff --git a/manual/time.texi b/manual/time.texi
index ff393a2c61..9dbe2d1e19 100644
--- a/manual/time.texi
+++ b/manual/time.texi
@@ -2237,7 +2237,7 @@ the requested time in the @var{requested_time} parameter is rounded up
 to the next integer multiple of the actual resolution of the system.
 
 If the function returns because the time has elapsed the return value is
-zero.  If the function return @math{-1} the global variable @var{errno}
+zero.  If the function returns @math{-1} the global variable @var{errno}
 is set to the following values:
 
 @table @code
diff --git a/sysdeps/generic/readelflib.c b/sysdeps/generic/readelflib.c
new file mode 100644
index 0000000000..645c9124a9
--- /dev/null
+++ b/sysdeps/generic/readelflib.c
@@ -0,0 +1,180 @@
+/* Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Andreas Jaeger <aj@suse.de>, 1999 and
+		  Jakub Jelinek <jakub@redhat.com>, 1999.
+
+   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 code is a heavily simplified version of the readelf program
+   that's part of the current binutils development version.  For architectures
+   which need to handle both 32bit and 64bit ELF libraries,  this file is
+   included twice for each arch size.  */
+
+/* Returns 0 if everything is ok, != 0 in case of error.  */
+int
+process_elf_file (const char *file_name, const char *lib, int *flag, char **soname,
+		  void *file_contents)
+{
+  int i;
+  unsigned int j;
+  int loadaddr;
+  unsigned int dynamic_addr;
+  size_t dynamic_size;
+  char *program_interpreter;
+  
+  ElfW(Ehdr) *elf_header;
+  ElfW(Phdr) *elf_pheader, *segment;
+  ElfW(Dyn) *dynamic_segment, *dyn_entry;
+  char *dynamic_strings;  
+
+  elf_header = (ElfW(Ehdr) *) file_contents;
+
+  if (elf_header->e_ident [EI_CLASS] != ElfW (CLASS))
+    {
+      if (opt_verbose)
+	{
+	  if (ElfW (CLASS) == ELFCLASS32)
+	    error (0, 0, _("%s is a 32 bit ELF file.\n"), file_name);
+	  else if (ElfW (CLASS) == ELFCLASS64)
+	    error (0, 0, _("%s is a 64 bit ELF file.\n"), file_name);
+	  else
+	    error (0, 0, _("Unknown ELFCLASS in file %s.\n"), file_name);
+	}
+      return 1;
+    }
+
+  if (elf_header->e_type != ET_DYN)
+    {
+      error (0, 0, _("%s is not a shared object file (Type: %d).\n"), file_name,
+	     elf_header->e_type);
+      return 1;
+    }
+  
+  /* Get information from elf program header.  */
+  elf_pheader = (ElfW(Phdr) *) (elf_header->e_phoff + file_contents);
+
+  /* The library is an elf library, now search for soname and
+     libc5/libc6.  */
+  *flag = FLAG_ELF;
+  
+  loadaddr = -1;
+  dynamic_addr = 0;
+  dynamic_size = 0;
+  program_interpreter = NULL;
+  for (i = 0, segment = elf_pheader;
+       i < elf_header->e_phnum; i++, segment++)
+    {
+      switch (segment->p_type)
+	{
+	case PT_LOAD:
+	  if (loadaddr == -1)
+	    loadaddr = (segment->p_vaddr & 0xfffff000)
+	      - (segment->p_offset & 0xfffff000);
+	  break;
+
+	case PT_DYNAMIC:
+	  if (dynamic_addr)
+	    error (0, 0, _("more than one dynamic segment\n"));
+
+	  dynamic_addr = segment->p_offset;
+	  dynamic_size = segment->p_filesz;
+	  break;
+	case PT_INTERP:
+	  program_interpreter = (char *) (file_contents + segment->p_offset);
+
+	  /* Check if this is enough to classify the binary.  */
+	  for (j = 0; j < sizeof (interpreters) / sizeof (interpreters [0]);
+	       ++j)
+	    if (strcmp (program_interpreter, interpreters[j].soname) == 0)
+	      {
+		*flag = interpreters[j].flag;
+		break;
+	      }
+	  break;
+	default:
+	  break;
+	}
+      
+    }
+  if (loadaddr == -1)
+    {
+      /* Very strange. */
+      loadaddr = 0;
+    }
+
+  /* Now we can read the dynamic sections.  */
+  if (dynamic_size == 0)
+    return 1;
+  
+  dynamic_segment = (ElfW(Dyn) *) (file_contents + dynamic_addr);
+
+  /* Find the string table.  */
+  dynamic_strings = NULL;
+  for (dyn_entry = dynamic_segment; dyn_entry->d_tag != DT_NULL;
+       ++dyn_entry)
+    {
+      if (dyn_entry->d_tag == DT_STRTAB)
+	{
+	  dynamic_strings = (char *) (file_contents + dyn_entry->d_un.d_val - loadaddr);
+	  break;
+	}
+    }
+
+  if (dynamic_strings == NULL)
+    return 1;
+
+  /* Now read the DT_NEEDED and DT_SONAME entries.  */
+  for (dyn_entry = dynamic_segment; dyn_entry->d_tag != DT_NULL;
+       ++dyn_entry)
+    {
+      if (dyn_entry->d_tag == DT_NEEDED || dyn_entry->d_tag == DT_SONAME)
+	{
+	  char *name = dynamic_strings + dyn_entry->d_un.d_val;
+
+	  if (dyn_entry->d_tag == DT_NEEDED)
+	    {
+	      
+	      if (*flag == FLAG_ELF)
+		{
+		  /* Check if this is enough to classify the binary.  */
+		  for (j = 0;
+		       j < sizeof (known_libs) / sizeof (known_libs [0]);
+		       ++j)
+		    if (strcmp (name, known_libs [j].soname) == 0)
+		      {
+			*flag = known_libs [j].flag;
+			break;
+		      }
+		}
+	    }
+
+	  else if (dyn_entry->d_tag == DT_SONAME)
+	    *soname = xstrdup (name);
+	  
+	  /* Do we have everything we need?  */
+	  if (*soname && *flag != FLAG_ELF)
+	    return 0;
+	}
+    }
+
+  /* We reach this point only if the file doesn't contain a DT_SONAME
+     or if we can't classify the library.  If it doesn't have a
+     soname, return the name of the library.  */
+  if (*soname == NULL)
+    *soname = xstrdup (lib);
+
+  return 0;
+}
diff --git a/sysdeps/unix/sysv/linux/sparc/readelflib.c b/sysdeps/unix/sysv/linux/sparc/readelflib.c
new file mode 100644
index 0000000000..d956ad90f6
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/sparc/readelflib.c
@@ -0,0 +1,57 @@
+/* Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Andreas Jaeger <aj@suse.de>, 1999 and
+		  Jakub Jelinek <jakub@redhat.com>, 1999.
+
+   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.  */
+
+
+int process_elf32_file (const char *file_name, const char *lib, int *flag,
+			char **soname, void *file_contents);
+int process_elf64_file (const char *file_name, const char *lib, int *flag,
+			char **soname, void *file_contents);
+
+/* Returns 0 if everything is ok, != 0 in case of error.  */
+int
+process_elf_file (const char *file_name, const char *lib, int *flag,
+		  char **soname, void *file_contents)
+{
+  ElfW(Ehdr) *elf_header = (ElfW(Ehdr) *) file_contents;
+  int ret;
+
+  if (elf_header->e_ident [EI_CLASS] == ELFCLASS32)
+    return process_elf32_file (file_name, lib, flag, soname, file_contents);
+  else
+    {
+      ret = process_elf64_file (file_name, lib, flag, soname, file_contents);
+      /* Sparc 64bit libraries are always libc.so.6+.  */
+      if (!ret)
+	*flag = FLAG_SPARC_LIB64|FLAG_ELF_LIBC6;
+      return ret;
+    }
+}
+
+#undef __ELF_NATIVE_CLASS
+#undef process_elf_file
+#define process_elf_file process_elf32_file
+#define __ELF_NATIVE_CLASS 32
+#include "sysdeps/generic/readelflib.c"
+
+#undef __ELF_NATIVE_CLASS
+#undef process_elf_file
+#define process_elf_file process_elf64_file
+#define __ELF_NATIVE_CLASS 64
+#include "sysdeps/generic/readelflib.c"