about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog65
-rw-r--r--elf/cache.c277
-rw-r--r--elf/ldconfig.c150
-rw-r--r--elf/ldconfig.h11
-rw-r--r--iconv/gconv_open.c28
-rw-r--r--intl/dcigettext.c36
-rw-r--r--intl/tst-gettext.c2
-rw-r--r--linuxthreads/Examples/ex7.c40
-rw-r--r--linuxthreads/Makefile3
-rw-r--r--linuxthreads/internals.h2
-rw-r--r--linuxthreads/join.c8
-rw-r--r--linuxthreads/manager.c34
-rw-r--r--sysdeps/generic/dl-cache.c275
-rw-r--r--sysdeps/generic/dl-cache.h100
14 files changed, 721 insertions, 310 deletions
diff --git a/ChangeLog b/ChangeLog
index 9332cba808..04787bb705 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,68 @@
+2000-05-06  Bruno Haible  <haible@clisp.cons.org>
+
+	* iconv/gconv_open.c (__gconv_open): If __gconv_find_transform
+	returned != __GCONV_OK, there is nothing to clean up.
+
+2000-05-06  Bruno Haible  <haible@clisp.cons.org>
+
+	* intl/tst-gettext.c (main): Disable possibly existing LC_CTYPE and
+	OUTPUT_CHARSET environment variables.
+
+2000-05-06  Andreas Jaeger  <aj@suse.de>
+
+	* sysdeps/generic/dl-cache.h (struct file_entry_new): New.
+	(struct cache_file_new): New.
+	(struct file_entry): New (moved from cache.c).
+	(struct cache_file): New (moved from cache.c).
+
+	* sysdeps/generic/dl-cache.c (SEARCH_CACHE): New macro, broken out
+	from _dl_load_cache_lookup.
+	(_dl_load_cache_lookup): Move search to SEARCH_CACHE macro, handle
+	the different cache formats.
+	New variable cache_new for new format.
+
+	* elf/ldconfig.h: Change according to changes in cache.c and
+	ldconfig.c; remove cache_libcmp; add opt_format.
+
+	* elf/ldconfig.c: Include "dl-cache.h" and "dl-procinfo.h"; remove
+	stuff that's defined in those headers.
+	Add hwcap to struct lib_entry.
+	(opt_format): New variable to select cache format.
+	(options): Add format parameter.
+	(is_hwcap): New function.
+	(path_hwcap): New function.
+	(parse_opt): Handle new format parameter.
+	(search_dir): Handle hwcap, search also subdirectories with hwcap.
+
+	* elf/cache.c (_GNU_SOURCE): Removed.  Not needed anymore since
+	ldconfig is part of glibc.
+	Include dl-cache.h and remove stuff that's defined there.
+	(struct cache_entry): Add new member hwcap.
+	(print_entry): Print hwcap, cleanup a bit.
+	(print_cache): Print new and old formats.
+	(compare): Use _dl_cache_libcmp from dl-cache.h; handle hwcap.
+	(save_cache): Save new and old formats.
+	(add_to_cache): Handle hwcap.
+
+	* sysdeps/generic/dl-cache.c (_dl_cache_libcmp): Moved from here...
+	* sysdeps/generic/dl-cache.h (_dl_cache_libcmp): ...to here.
+	* sysdeps/generic/dl-cache.c (LD_SO_CACHE): Moved from here...
+	* sysdeps/generic/dl-cache.h (LD_SO_CACHE): ...to here.
+	* sysdeps/generic/dl-cache.c (CACHEMAGIC): Moved from here...
+	* sysdeps/generic/dl-cache.h (CACHEMAGIC): ...to here.
+
+2000-05-05  Bruno Haible  <haible@clisp.cons.org>
+
+	* intl/dcigettext.c (alignof): New macro.
+	(_nl_find_msg): Use it instead of __alignof__. Pass correct output
+	buffer length to __gconv/iconv. If malloc (freemem_size) fails, set
+	freemem_size to 0.
+
+2000-05-05  Bruno Haible  <haible@clisp.cons.org>
+
+	* intl/dcigettext.c (dcigettext): Fix interpretation of tsearch
+	return value.
+
 2000-05-06  Ulrich Drepper  <drepper@redhat.com>
 
 	* intl/dcigettext.c (DCIGETTEXT): Always define local variable `index'.
diff --git a/elf/cache.c b/elf/cache.c
index cc75704f54..fbbc426268 100644
--- a/elf/cache.c
+++ b/elf/cache.c
@@ -17,9 +17,6 @@
    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>
@@ -34,32 +31,18 @@
 #include <sys/types.h>
 
 #include "ldconfig.h"
-
-#define CACHEMAGIC "ld.so-1.7.0"
+#include "dl-cache.h"
 
 struct cache_entry
 {
-  char *lib;
-  char *path;
-  int flags;
-  struct cache_entry *next;
+  char *lib;			/* Library name.  */
+  char *path;			/* Path to find library.  */
+  int flags;			/* Flags to indicate kind of library.  */
+  unsigned long int hwcap;	/* Important hardware capabilities.  */
+  int bits_hwcap;		/* Number of bits set in hwcap.  */
+  struct cache_entry *next;	/* Next entry in list.  */
 };
 
-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;
 
@@ -69,7 +52,7 @@ static const char *flag_descr[] =
 
 /* Print a single entry.  */
 static void
-print_entry (const char *lib, int flag, const char *key)
+print_entry (const char *lib, int flag, unsigned long int hwcap, const char *key)
 {
   printf ("\t%s (", lib);
   switch (flag & FLAG_TYPE_MASK)
@@ -93,14 +76,17 @@ print_entry (const char *lib, int flag, const char *key)
     case 0:
       break;
     default:
-      fprintf (stdout, ",%d", flag & FLAG_REQUIRED_MASK);
+      printf (",%d", flag & FLAG_REQUIRED_MASK);
       break;
     }
+  if (hwcap != 0)
+    printf (", hwcap: 0x%lx", hwcap);
   printf (") => %s\n", key);
 }
 
 
-/* Print the whole cache file.  */
+/* Print the whole cache file, if a file contains the new cache format
+   hidden in the old one, print the contents of the new format.  */
 void
 print_cache (const char *cache_name)
 {
@@ -109,7 +95,9 @@ print_cache (const char *cache_name)
   int fd;
   unsigned int i;
   struct cache_file *cache;
+  struct cache_file_new *cache_new = NULL;
   const char *cache_data;
+  int format = 0;
 
   fd = open (cache_name, O_RDONLY);
   if (fd < 0)
@@ -128,20 +116,63 @@ print_cache (const char *cache_name)
     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];
+  if (cache_size < sizeof (struct cache_file))
+    error (EXIT_FAILURE, 0, _("File is not a cache file.\n"));
+
+  if (memcmp (cache->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1))
+    {
+      /* This can only be the new format without the old one.  */
+      cache_new = (struct cache_file_new *) cache;
+
+      if (memcmp (cache_new->magic, CACHEMAGIC_NEW, sizeof CACHEMAGIC_NEW - 1)
+	  || memcmp (cache_new->version, CACHE_VERSION,
+		      sizeof CACHE_VERSION - 1))
+	error (EXIT_FAILURE, 0, _("File is not a cache file.\n"));
+      format = 1;
+      /* This is where the strings start.  */
+      cache_data = (const char *) cache;
+    }
+  else
+    {
+      /* This is where the strings start.  */
+      cache_data = (const char *) &cache->libs[cache->nlibs];
+
+      /* Check for a new cache embedded in the old format.  */
+      if (cache_size >
+	  (sizeof (struct cache_file)
+	   + cache->nlibs * sizeof (struct file_entry)
+	   + sizeof (struct cache_file_new)))
+	{
+	  cache_new = (struct cache_file_new *) cache_data;
 
-  printf (_("%d libs found in cache `%s'\n"), cache->nlibs, cache_name);
+	  if (!memcmp (cache_new->magic, CACHEMAGIC_NEW, sizeof CACHEMAGIC_NEW - 1)
+	      && !memcmp (cache_new->version, CACHE_VERSION,
+			  sizeof CACHE_VERSION - 1))
+	    format = 1;
+	}
+    }
 
-  /* 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);
+  if (format == 0)
+    {
+      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, 0,
+		     cache_data + cache->libs[i].value);
+    }
+  else if (format == 1)
+    {
+      printf (_("%d libs found in cache `%s'\n"), cache_new->nlibs, cache_name);
+
+      /* Print everything.  */
+      for (i = 0; i < cache_new->nlibs; i++)
+	print_entry (cache_data + cache_new->libs[i].key,
+		     cache_new->libs[i].flags,
+		     cache_new->libs[i].hwcap,
+		     cache_data + cache_new->libs[i].value);
+    }
   /* Cleanup.  */
   munmap (cache, cache_size);
   close (fd);
@@ -155,45 +186,6 @@ init_cache (void)
 }
 
 
-/* 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)
@@ -201,80 +193,142 @@ 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);
+  res = _dl_cache_libcmp (e2->lib, e1->lib);
   if (res == 0)
     {
       if (e1->flags < e2->flags)
 	return 1;
       else if (e1->flags > e2->flags)
 	return -1;
+      /* Sort by most specific hwcap.  */
+      else if (e2->bits_hwcap > e1->bits_hwcap)
+	return 1;
+      else if (e2->bits_hwcap < e1->bits_hwcap)
+	return -1;
+      else if (e2->hwcap > e1->hwcap)
+	return 1;
+      else if (e2->hwcap < e1->hwcap)
+	return -1;
     }
   return res;
 }
 
-
 /* Save the contents of the cache.  */
 void
 save_cache (const char *cache_name)
 {
   struct cache_entry *entry;
-  int i, fd;
+  int fd, idx_old, idx_new;
   size_t total_strlen, len;
   char *strings, *str, *temp_name;
-  struct cache_file *file_entries;
-  size_t file_entries_size;
+  struct cache_file *file_entries = NULL;
+  struct cache_file_new *file_entries_new = NULL;
+  size_t file_entries_size = 0;
+  size_t file_entries_new_size = 0;
   unsigned int str_offset;
   /* Number of cache entries.  */
   int cache_entry_count = 0;
+  /* Number of normal cache entries.  */
+  int cache_entry_old_count = 0;
 
   /* The cache entries are sorted already, save them in this order. */
 
   /* Count the length of all strings.  */
+  /* The old format doesn't contain hwcap entries and doesn't contain
+     libraries in subdirectories with hwcaps entries.  Count therefore
+     also all entries with hwcap == 0.  */
   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;
+      if (entry->hwcap == 0)
+	++cache_entry_old_count;
     }
 
   /* Create the on disk cache structure.  */
   /* First an array for all strings.  */
-  strings = (char *)xmalloc (total_strlen + 1);
+  strings = (char *)xmalloc (total_strlen);
 
-  /* 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);
+  if (opt_format != 2)
+    {
+      /* And the list of all entries in the old format.  */
+      file_entries_size = sizeof (struct cache_file)
+	+ cache_entry_old_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);
 
-  /* 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_old_count;
+    }
 
-  file_entries->nlibs = cache_entry_count;
+  if (opt_format != 0)
+    {
+      /* And the list of all entries in the new format.  */
+      file_entries_new_size = sizeof (struct cache_file_new)
+	+ cache_entry_count * sizeof (struct file_entry_new);
+      file_entries_new = (struct cache_file_new *) xmalloc (file_entries_new_size);
+
+      /* Fill in the header.  */
+      memset (file_entries_new, 0, sizeof (struct cache_file_new));
+      memcpy (file_entries_new->magic, CACHEMAGIC_NEW, sizeof CACHEMAGIC_NEW - 1);
+      memcpy (file_entries_new->version, CACHE_VERSION, sizeof CACHE_VERSION - 1);
+
+      file_entries_new->nlibs = cache_entry_count;
+      file_entries_new->len_strings = total_strlen;
+    }
+
+  /* If we have both formats, we hide the new format in the strings
+     table, we have to adjust all string indices for this so that
+     old libc5/glibc 2 dynamic linkers just ignore them.  */
+  if (opt_format == 1)
+    str_offset = file_entries_new_size;
+  else
+    str_offset = 0;
 
-  str_offset = 0;
   str = strings;
-  for (i = 0, entry = entries; entry != NULL; entry = entry->next, ++i)
+  for (idx_old = 0, idx_new = 0, entry = entries; entry != NULL;
+       entry = entry->next, ++idx_new)
     {
-      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;
+      if (opt_format != 2)
+	{
+	  file_entries->libs[idx_old].flags = entry->flags;
+	  /* XXX: Actually we can optimize here and remove duplicates.  */
+	  file_entries->libs[idx_old].key = str_offset;
+	}
+      if (opt_format != 0)
+	{
+	  /* We could subtract file_entries_new_size from str_offset -
+	     not doing so makes the code easier, the string table
+	     always begins at the beginning of the the new cache
+	     struct.  */
+	  file_entries_new->libs[idx_new].flags = entry->flags;
+	  file_entries_new->libs[idx_new].hwcap = entry->hwcap;
+	  file_entries_new->libs[idx_new].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;
+      if (opt_format != 2)
+	file_entries->libs[idx_old].value = str_offset;
+      if (opt_format != 0)
+	file_entries_new->libs[idx_new].value = str_offset;
       len = strlen (entry->path);
       str = stpcpy (str, entry->path);
       /* Account the final NUL.  */
       ++str;
       str_offset += len + 1;
+      /* Ignore entries with hwcap for old format.  */
+      if (entry->hwcap == 0)
+	++idx_old;
     }
-  assert (str_offset == total_strlen);
 
   /* Write out the cache.  */
 
@@ -293,8 +347,17 @@ save_cache (const char *cache_name)
 	   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 (opt_format != 2)
+    {
+      if (write (fd, file_entries, file_entries_size) != (ssize_t)file_entries_size)
+	error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
+    }
+  if (opt_format != 0)
+    {
+      if (write (fd, file_entries_new, file_entries_new_size)
+	  != (ssize_t)file_entries_new_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."));
@@ -325,13 +388,15 @@ save_cache (const char *cache_name)
     }
 }
 
+
 /* Add one library to the cache.  */
 void
-add_to_cache (const char *path, const char *lib, int flags)
+add_to_cache (const char *path, const char *lib, int flags,
+	      unsigned long int hwcap)
 {
   struct cache_entry *new_entry, *ptr, *prev;
   char *full_path;
-  int len;
+  int len, i;
 
   new_entry = (struct cache_entry *) xmalloc (sizeof (struct cache_entry));
 
@@ -343,6 +408,14 @@ add_to_cache (const char *path, const char *lib, int flags)
   new_entry->lib = xstrdup (lib);
   new_entry->path = full_path;
   new_entry->flags = flags;
+  new_entry->hwcap = hwcap;
+  new_entry->bits_hwcap = 0;
+
+  /* Count the number of bits set in the masked value.  */
+  for (i = 0; (~((1UL << i) - 1) & hwcap) != 0; ++i)
+    if ((hwcap & (1UL << i)) != 0)
+      ++new_entry->bits_hwcap;
+
 
   /* Keep the list sorted - search for right place to insert.  */
   ptr = entries;
diff --git a/elf/ldconfig.c b/elf/ldconfig.c
index 98a4817202..ff96682c79 100644
--- a/elf/ldconfig.c
+++ b/elf/ldconfig.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1999 Free Software Foundation, Inc.
+/* Copyright (C) 1999, 2000 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Andreas Jaeger <aj@suse.de>, 1999.
 
@@ -32,10 +32,12 @@
 #include <sys/types.h>
 
 #include "ldconfig.h"
+#include "dl-cache.h"
 
-#ifndef LD_SO_CACHE
-# define LD_SO_CACHE "/etc/ld.so.cache"
-#endif
+/* We don't need this here - silence the compiler.  */
+#define _dl_sysdep_message(string, args...) do {} while (0);
+
+#include "dl-procinfo.h"
 
 #ifndef LD_SO_CONF
 # define LD_SO_CONF "/etc/ld.so.conf"
@@ -49,6 +51,7 @@
 struct lib_entry
   {
     int flags;
+    unsigned long int hwcap;
     char *lib;
     char *path;
   };
@@ -63,7 +66,7 @@ static const struct
   {"libc5", FLAG_ELF_LIBC5},
   {"libc6", FLAG_ELF_LIBC6},
   {"glibc2", FLAG_ELF_LIBC6}
-};  
+};
 
 
 /* List of directories to handle.  */
@@ -85,6 +88,10 @@ static int opt_print_cache = 0;
 /* Be verbose.  */
 int opt_verbose = 0;
 
+/* Format to support.  */
+/* 0: only libc5/glibc2; 1: both; 2: only glibc 2.2.  */
+int opt_format = 1;
+
 /* Build cache.  */
 static int opt_build_cache = 1;
 
@@ -123,6 +130,7 @@ static const struct argp_option options[] =
   { 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, 'l', NULL, 0, N_("Manually link individual libraries."), 0},
+  { "format", 'c', "FORMAT", 0, N_("Format to use: new, old or compat (default)"), 0},
   { NULL, 0, NULL, 0, NULL, 0 }
 };
 
@@ -138,7 +146,53 @@ static struct argp argp =
   options, parse_opt, NULL, doc, NULL, NULL, NULL
 };
 
+/* Check if string corresponds to an important hardware capability.  */
+static int
+is_hwcap (const char *name)
+{
+  int hwcap_idx = _dl_string_hwcap (name);
+  
+  if (hwcap_idx != -1 && ((1 << hwcap_idx) & HWCAP_IMPORTANT))
+    return 1;
+  return 0;
+}
+
+/* Get hwcap encoding of path.  */
+static unsigned long int
+path_hwcap (const char *path)
+{
+  char *str = xstrdup (path);
+  char *ptr;
+  unsigned long int hwcap = 0;
+  unsigned long int h;
+
+  size_t len;
+
+  len = strlen (str);
+  if (str[len] == '/')
+    str[len] = '\0';
+
+  /* Search pathname from the end and check for hwcap strings.  */
+  for (;;)
+    {
+      ptr = strrchr (str, '/');
+
+      if (ptr == NULL)
+	break;
+
+      h = _dl_string_hwcap (ptr+1);
+
+      if (h == -1)
+	break;
+      hwcap += 1 << h;
 
+      /* Search the next part of the path.  */
+      *ptr = '\0';
+    }
+
+  free (str);
+  return hwcap;
+}
 
 /* Handle program arguments.  */
 static error_t
@@ -174,6 +228,14 @@ parse_opt (int key, char *arg, struct argp_state *state)
     case 'X':
       opt_link = 0;
       break;
+    case 'c':
+      if (strcmp (arg, "old") == 0)
+	opt_format = 0;
+      else if (strcmp (arg, "compat") == 0)
+	opt_format = 1;
+      else if (strcmp (arg, "new") == 0)
+	opt_format = 2;
+      break;
     default:
       return ARGP_ERR_UNKNOWN;
     }
@@ -190,7 +252,7 @@ print_version (FILE *stream, struct argp_state *state)
 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");
+"), "2000");
   fprintf (stream, gettext ("Written by %s.\n"),
 	   "Andreas Jaeger");
 }
@@ -202,10 +264,10 @@ 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, '=');
@@ -235,7 +297,7 @@ add_dir (const char *line)
       entry->path [i] = '\0';
       --i;
     }
-  
+
   ptr = dir_entries;
   prev = ptr;
   while (ptr != NULL)
@@ -270,7 +332,7 @@ create_links (const char *path, const char *libname, const char *soname)
   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);
@@ -405,7 +467,7 @@ manual_link (char *library)
    - 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.
 
@@ -416,7 +478,7 @@ manual_link (char *library)
        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.
@@ -424,7 +486,7 @@ manual_link (char *library)
    libxy.so.1.1.  */
 
 /* Information for one library.  */
-struct dlib_entry 
+struct dlib_entry
 {
   char *name;
   char *soname;
@@ -446,12 +508,18 @@ search_dir (const struct dir_entry *entry)
   int nchars;
   struct stat stat_buf;
   int is_link;
-  
+  unsigned long int hwcap = path_hwcap (entry->path);
+
   dlibs = NULL;
 
   if (opt_verbose)
-    printf ("%s:\n", entry->path);
-  
+    {
+      if (hwcap != 0)
+	printf ("%s: (hwcap: 0x%lx)\n", entry->path, hwcap);
+      else
+	printf ("%s:\n", entry->path);
+    }
+
   dir = opendir (entry->path);
   if (dir == NULL)
     {
@@ -459,7 +527,7 @@ search_dir (const struct dir_entry *entry)
 	error (0, errno, _("Can't open directory %s"), entry->path);
       return;
     }
-  
+
 
   while ((direntry = readdir (dir)) != NULL)
     {
@@ -468,15 +536,17 @@ search_dir (const struct dir_entry *entry)
       /* We only look at links and regular files.  */
       if (direntry->d_type != DT_UNKNOWN
 	  && direntry->d_type != DT_LNK
-	  && direntry->d_type != DT_REG)
+	  && direntry->d_type != DT_REG
+	  && direntry->d_type != DT_DIR)
 	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)
+      /* Does this file look like a shared library or is it a hwcap
+	 subdirectory?  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)
+	  && !is_hwcap (direntry->d_name))
 	continue;
       nchars = snprintf (buf, sizeof (buf), "%s/%s", entry->path,
 			 direntry->d_name);
@@ -492,6 +562,16 @@ search_dir (const struct dir_entry *entry)
 	  error (0, errno, _("Can't lstat %s"), buf);
 	  continue;
 	}
+      else if (S_ISDIR (stat_buf.st_mode) && is_hwcap (direntry->d_name))
+	{
+	  /* Handle subdirectory also, make a recursive call.  */
+	  struct dir_entry new_entry;
+	  new_entry.path = buf;
+	  new_entry.flag = entry->flag;
+	  new_entry.next = NULL;
+	  search_dir (&new_entry);
+	  continue;
+	}
       else if (!S_ISREG (stat_buf.st_mode) && !S_ISLNK (stat_buf.st_mode))
 	continue;
 
@@ -506,7 +586,7 @@ search_dir (const struct dir_entry *entry)
 	  free (soname);
 	  soname = xstrdup (direntry->d_name);
 	}
-      
+
       if (flag == FLAG_ELF
 	  && (entry->flag == FLAG_ELF_LIBC5
 	      || entry->flag == FLAG_ELF_LIBC6))
@@ -524,7 +604,7 @@ search_dir (const struct dir_entry *entry)
 	      && 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)
 	{
@@ -535,7 +615,7 @@ search_dir (const struct dir_entry *entry)
 		 is newer.  */
 	      if ((!is_link && dlib_ptr->is_link)
 		  || (is_link == dlib_ptr->is_link
-		      && cache_libcmp (dlib_ptr->name, direntry->d_name) < 0))
+		      && _dl_cache_libcmp (dlib_ptr->name, direntry->d_name) < 0))
 		{
 		  /* It's newer - add it.  */
 		  /* Flag should be the same - sanity check.  */
@@ -586,11 +666,11 @@ search_dir (const struct dir_entry *entry)
       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);
+	add_to_cache (entry->path, dlib_ptr->soname, dlib_ptr->flag, hwcap);
     }
 
   /* Free all resources.  */
-  while (dlibs) 
+  while (dlibs)
     {
       dlib_ptr = dlibs;
       free (dlib_ptr->soname);
@@ -627,7 +707,7 @@ parse_conf (const char *filename)
   FILE *file;
   char *line = NULL;
   size_t len = 0;
-  
+
   file = fopen (filename, "r");
 
   if (file == NULL)
@@ -667,7 +747,7 @@ int
 main (int argc, char **argv)
 {
   int remaining;
-  
+
   /* Parse and process arguments.  */
   argp_parse (&argp, argc, argv, 0, &remaining, NULL);
 
@@ -685,7 +765,7 @@ main (int argc, char **argv)
 
   if (config_file == NULL)
     config_file = LD_SO_CONF;
-  
+
   /* Chroot first.  */
   if (opt_chroot)
     {
@@ -713,8 +793,8 @@ main (int argc, char **argv)
 
       exit (0);
     }
-  
-  
+
+
   if (opt_build_cache)
     init_cache ();
 
@@ -726,7 +806,7 @@ main (int argc, char **argv)
 
       parse_conf (config_file);
     }
-  
+
   search_dirs ();
 
   if (opt_build_cache)
diff --git a/elf/ldconfig.h b/elf/ldconfig.h
index 9e40714c05..ecdbb14883 100644
--- a/elf/ldconfig.h
+++ b/elf/ldconfig.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 1999 Free Software Foundation, Inc.
+/* Copyright (C) 1999, 2000 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Andreas Jaeger <aj@suse.de>, 1999.
 
@@ -37,10 +37,8 @@ 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);
-
+extern void add_to_cache (const char *path, const char *lib, int flags,
+			  unsigned long int hwcap);
 
 /* Declared in readlib.c.  */
 extern int process_file (const char *file_name, const char *lib, int *flag,
@@ -54,6 +52,8 @@ extern int process_elf_file (const char *file_name, const char *lib, int *flag,
 /* Declared in ldconfig.c.  */
 extern int opt_verbose;
 
+extern int opt_format;
+
 /* Prototypes for a few program-wide used functions.  */
 extern void *xmalloc (size_t __n);
 extern void *xcalloc (size_t __n, size_t __size);
@@ -61,4 +61,3 @@ extern void *xrealloc (void *__p, size_t __n);
 extern char *xstrdup (const char *__str);
 
 #endif /* ! _LDCONFIG_H  */
-
diff --git a/iconv/gconv_open.c b/iconv/gconv_open.c
index 35ea4782ac..147f5b0e06 100644
--- a/iconv/gconv_open.c
+++ b/iconv/gconv_open.c
@@ -93,25 +93,25 @@ __gconv_open (const char *toset, const char *fromset, __gconv_t *handle,
 	  result->__data[cnt].__internal_use = 0;
 	  result->__data[cnt].__statep = &result->__data[cnt].__state;
 	}
-    }
-
-  if (res != __GCONV_OK)
-    {
-      /* Something went wrong.  Free all the resources.  */
-      int serrno = errno;
 
-      if (result != NULL)
+      if (res != __GCONV_OK)
 	{
-	  while (cnt-- > 0)
-	    free (result->__data[cnt].__outbuf);
+	  /* Something went wrong.  Free all the resources.  */
+	  int serrno = errno;
 
-	  free (result);
-	  result = NULL;
-	}
+	  if (result != NULL)
+	    {
+	      while (cnt-- > 0)
+		free (result->__data[cnt].__outbuf);
+
+	      free (result);
+	      result = NULL;
+	    }
 
-      __gconv_close_transform (steps, nsteps);
+	  __gconv_close_transform (steps, nsteps);
 
-      __set_errno (serrno);
+	  __set_errno (serrno);
+	}
     }
 
   *handle = result;
diff --git a/intl/dcigettext.c b/intl/dcigettext.c
index 3c86b83f28..b52fbd58c4 100644
--- a/intl/dcigettext.c
+++ b/intl/dcigettext.c
@@ -110,6 +110,14 @@ void free ();
 # define __libc_rwlock_unlock(NAME)
 #endif
 
+/* Alignment of types.  */
+#if defined __GNUC__ && __GNUC__ >= 2
+# define alignof(TYPE) __alignof__ (TYPE)
+#else
+# define alignof(TYPE) \
+    ((int) &((struct { char dummy1; TYPE dummy2; } *) 0)->dummy2)
+#endif
+
 /* @@ end of prolog @@ */
 
 #ifdef _LIBC
@@ -785,20 +793,24 @@ _nl_find_msg (domain_file, msgid, index)
 	    resultlen = p - result;
 	  }
 
-	  inbuf = result;
-	  outbuf = freemem + 4;
-
 	  __libc_lock_lock (lock);
 
+	  inbuf = result;
+	  outbuf = freemem + sizeof (nls_uint32);
+
 	  while (1)
 	    {
 # ifdef _LIBC
 	      size_t non_reversible;
 	      int res;
 
+	      if (freemem_size < 4)
+		goto resize_freemem;
+
 	      res = __gconv (domain->conv,
 			     &inbuf, inbuf + resultlen,
-			     &outbuf, outbuf + freemem_size,
+			     &outbuf,
+			     outbuf + freemem_size - sizeof (nls_uint32),
 			     &non_reversible);
 
 	      if (res == __GCONV_OK || res == __GCONV_EMPTY_INPUT)
@@ -816,8 +828,12 @@ _nl_find_msg (domain_file, msgid, index)
 	      const char *inptr = (const char *) inbuf;
 	      size_t inleft = resultlen;
 	      char *outptr = (char *) outbuf;
-	      size_t outleft = freemem_size;
+	      size_t outleft;
+
+	      if (freemem_size < 4)
+		goto resize_freemem;
 
+	      outleft = freemem_size - 4;
 	      if (iconv (domain->conv, &inptr, &inleft, &outptr, &outleft)
 		  != (size_t) (-1))
 		{
@@ -832,6 +848,7 @@ _nl_find_msg (domain_file, msgid, index)
 #  endif
 # endif
 
+	    resize_freemem:
 	      /* We must resize the buffer.  */
 	      freemem_size = 2 * freemem_size;
 	      if (freemem_size < 4064)
@@ -839,11 +856,12 @@ _nl_find_msg (domain_file, msgid, index)
 	      freemem = (char *) malloc (freemem_size);
 	      if (__builtin_expect (freemem == NULL, 0))
 		{
+		  freemem_size = 0;
 		  __libc_lock_unlock (lock);
 		  goto converted;
 		}
 
-	      outbuf = freemem + 4;
+	      outbuf = freemem + sizeof (nls_uint32);
 	    }
 
 	  /* We have now in our buffer a converted string.  Put this
@@ -853,15 +871,15 @@ _nl_find_msg (domain_file, msgid, index)
 	  /* Shrink freemem, but keep it aligned.  */
 	  freemem_size -= outbuf - freemem;
 	  freemem = outbuf;
-	  freemem += freemem_size & (__alignof__ (nls_uint32) - 1);
-	  freemem_size = freemem_size & ~ (__alignof__ (nls_uint32) - 1);
+	  freemem += freemem_size & (alignof (nls_uint32) - 1);
+	  freemem_size = freemem_size & ~ (alignof (nls_uint32) - 1);
 
 	  __libc_lock_unlock (lock);
 	}
 
       /* Now domain->conv_tab[act] contains the translation of at least
 	 the variants 0 .. INDEX.  */
-      result = domain->conv_tab[act] + 4;
+      result = domain->conv_tab[act] + sizeof (nls_uint32);
     }
 
  converted:
diff --git a/intl/tst-gettext.c b/intl/tst-gettext.c
index 9ce11903aa..b897d71582 100644
--- a/intl/tst-gettext.c
+++ b/intl/tst-gettext.c
@@ -68,7 +68,9 @@ main (int argc, char *argv[])
   setenv ("LANGUAGE", "existing-locale", 1);
   setenv ("LC_ALL", "non-existing-locale", 1);
   setenv ("LC_MESSAGES", "non-existing-locale", 1);
+  setenv ("LC_CTYPE", "non-existing-locale", 1);
   setenv ("LANG", "non-existing-locale", 1);
+  unsetenv ("OUTPUT_CHARSET");
   /* This is the name of the existing domain with a catalog for the
      LC_MESSAGES category.  */
   textdomain ("existing-domain");
diff --git a/linuxthreads/Examples/ex7.c b/linuxthreads/Examples/ex7.c
new file mode 100644
index 0000000000..94b708b56b
--- /dev/null
+++ b/linuxthreads/Examples/ex7.c
@@ -0,0 +1,40 @@
+/* This is a test of the special shutdown that occurs
+   when all threads, including the main one, call
+   pthread_exit(). It demonstrates that atexit
+   handlers are properly called, and that the
+   output is properly flushed even when stdout is
+   redirected to a file, and therefore fully buffered. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+
+#define NTHREADS 20	/* number of threads */
+
+static void *thread(void *arg)
+{
+  printf("thread terminating\n");
+  return 0;
+}
+
+void cleanup(void)
+{
+  printf("atexit handler called\n");
+}
+
+int main(void)
+{
+  int i;
+
+  atexit(cleanup);
+
+  for (i = 0; i < NTHREADS; i++) {
+    pthread_t id;
+    if (pthread_create(&id, 0, thread, 0) != 0) {
+      fprintf(stderr, "pthread_create failed\n");
+      abort();
+    }
+  }
+
+  pthread_exit(0);
+}
diff --git a/linuxthreads/Makefile b/linuxthreads/Makefile
index b51ea84f33..6e443631c3 100644
--- a/linuxthreads/Makefile
+++ b/linuxthreads/Makefile
@@ -38,7 +38,7 @@ libpthread-routines := attr cancel condvar join manager mutex ptfork \
 		       oldsemaphore events getcpuclockid
 
 vpath %.c Examples
-tests = ex1 ex2 ex3 ex4 ex5 ex6
+tests = ex1 ex2 ex3 ex4 ex5 ex6 ex7
 
 include ../Rules
 
@@ -66,3 +66,4 @@ $(objpfx)ex3: $(libpthread)
 $(objpfx)ex4: $(libpthread)
 $(objpfx)ex5: $(libpthread)
 $(objpfx)ex6: $(libpthread)
+$(objpfx)ex7: $(libpthread)
diff --git a/linuxthreads/internals.h b/linuxthreads/internals.h
index b257be0279..e4cda4b66c 100644
--- a/linuxthreads/internals.h
+++ b/linuxthreads/internals.h
@@ -199,7 +199,7 @@ struct pthread_request {
   pthread_descr req_thread;     /* Thread doing the request */
   enum {                        /* Request kind */
     REQ_CREATE, REQ_FREE, REQ_PROCESS_EXIT, REQ_MAIN_THREAD_EXIT,
-    REQ_POST, REQ_DEBUG
+    REQ_POST, REQ_DEBUG, REQ_KICK
   } req_kind;
   union {                       /* Arguments for request */
     struct {                    /* For REQ_CREATE: */
diff --git a/linuxthreads/join.c b/linuxthreads/join.c
index 2716d799c1..7c9b6c5fd3 100644
--- a/linuxthreads/join.c
+++ b/linuxthreads/join.c
@@ -73,9 +73,13 @@ void pthread_exit(void * retval)
     request.req_kind = REQ_MAIN_THREAD_EXIT;
     __libc_write(__pthread_manager_request, (char *)&request, sizeof(request));
     suspend(self);
+    /* Main thread flushes stdio streams and runs atexit functions.
+       It also calls a handler within LinuxThreads which sends a process exit
+       request to the thread manager. */
+    exit(0); 
   }
-  /* Exit the process (but don't flush stdio streams, and don't run
-     atexit functions). */
+  /* Threads other than the main one  terminate without flushing stdio streams
+     or running atexit functions. */
   _exit(0);
 }
 
diff --git a/linuxthreads/manager.c b/linuxthreads/manager.c
index 0c781dea6e..2d3e227f23 100644
--- a/linuxthreads/manager.c
+++ b/linuxthreads/manager.c
@@ -162,13 +162,22 @@ int __pthread_manager(void *arg)
       case REQ_PROCESS_EXIT:
         pthread_handle_exit(request.req_thread,
                             request.req_args.exit.code);
+	/* NOTREACHED */
         break;
       case REQ_MAIN_THREAD_EXIT:
         main_thread_exiting = 1;
+	/* Reap children in case all other threads died and the signal handler
+	   went off before we set main_thread_exiting to 1, and therefore did
+	   not do REQ_KICK. */
+	pthread_reap_children();
+
         if (__pthread_main_thread->p_nextlive == __pthread_main_thread) {
           restart(__pthread_main_thread);
-          return 0;
-        }
+	  /* The main thread will now call exit() which will trigger an
+	     __on_exit handler, which in turn will send REQ_PROCESS_EXIT
+	     to the thread manager. In case you are wondering how the
+	     manager terminates from its loop here. */
+	}	      
         break;
       case REQ_POST:
         __new_sem_post(request.req_args.post);
@@ -179,6 +188,10 @@ int __pthread_manager(void *arg)
 	if (__pthread_threads_debug && __pthread_sig_debug > 0)
 	  raise(__pthread_sig_debug);
         break;
+      case REQ_KICK:
+	/* This is just a prod to get the manager to reap some
+	   threads right away, avoiding a potential delay at shutdown. */
+	break;
       }
     }
   }
@@ -591,7 +604,7 @@ static void pthread_exited(pid_t pid)
   if (main_thread_exiting &&
       __pthread_main_thread->p_nextlive == __pthread_main_thread) {
     restart(__pthread_main_thread);
-    _exit(0);
+    /* Same logic as REQ_MAIN_THREAD_EXIT. */
   }
 }
 
@@ -685,7 +698,22 @@ static void pthread_handle_exit(pthread_descr issuing_thread, int exitcode)
 
 void __pthread_manager_sighandler(int sig)
 {
+  int kick_manager = terminated_children == 0 && main_thread_exiting;
   terminated_children = 1;
+
+  /* If the main thread is terminating, kick the thread manager loop
+     each time some threads terminate. This eliminates a two second
+     shutdown delay caused by the thread manager sleeping in the
+     call to __poll(). Instead, the thread manager is kicked into
+     action, reaps the outstanding threads and resumes the main thread
+     so that it can complete the shutdown. */
+
+  if (kick_manager) {
+    struct pthread_request request;
+    request.req_thread = 0;
+    request.req_kind = REQ_KICK;
+    __libc_write(__pthread_manager_request, (char *) &request, sizeof(request));
+  }
 }
 
 /* Adjust priority of thread manager so that it always run at a priority
diff --git a/sysdeps/generic/dl-cache.c b/sysdeps/generic/dl-cache.c
index 2ab13d8d3f..28fab0b19e 100644
--- a/sysdeps/generic/dl-cache.c
+++ b/sysdeps/generic/dl-cache.c
@@ -22,31 +22,16 @@
 #include <sys/mman.h>
 #include <dl-cache.h>
 
+
 /* System-dependent function to read a file's whole contents
    in the most convenient manner available.  */
 extern void *_dl_sysdep_read_whole_file (const char *filename,
 					 size_t *filesize_ptr,
 					 int mmap_prot);
 
-#ifndef LD_SO_CACHE
-# define LD_SO_CACHE "/etc/ld.so.cache"
-#endif
-
-#define CACHEMAGIC "ld.so-1.7.0"
-
-struct cache_file
-  {
-    char magic[sizeof CACHEMAGIC - 1];
-    unsigned int nlibs;
-    struct
-      {
-	int flags;		/* This is 1 for an ELF library.  */
-	unsigned int key, value; /* String table indices.  */
-      } libs[0];
-  };
-
 /* This is the starting address and the size of the mmap()ed file.  */
 static struct cache_file *cache;
+static struct cache_file_new *cache_new;
 static size_t cachesize;
 
 /* 1 if cache_data + PTR points into the cache.  */
@@ -56,45 +41,100 @@ static size_t cachesize;
    binaries.  */
 int _dl_correct_cache_id = _DL_CACHE_DEFAULT_ID;
 
-/* Helper function which must match the one in ldconfig, so that
-   we rely on the same sort order.  */
-static int
-_dl_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;
-}
+#define SEARCH_CACHE(cache)						  \
+/* We use binary search since the table is sorted in the cache file.	  \
+   The first matching entry in the table is returned.			  \
+   It is important to use the same algorithm as used while generating	  \
+   the cache file.  */							  \
+do									  \
+  {									  \
+    left = 0;								  \
+    right = cache->nlibs - 1;						  \
+    middle = (left + right) / 2;					  \
+    cmpres = 1;								  \
+    									  \
+    while (left <= right)						  \
+      {									  \
+	/* Make sure string table indices are not bogus before using	  \
+	   them.  */							  \
+	if (! _dl_cache_verify_ptr (cache->libs[middle].key))		  \
+	  {								  \
+	    cmpres = 1;							  \
+	    break;							  \
+	  }								  \
+									  \
+	/* Actually compare the entry with the key.  */			  \
+	cmpres = _dl_cache_libcmp (name,				  \
+				   cache_data + cache->libs[middle].key); \
+	if (cmpres == 0)						  \
+	  /* Found it.  */						  \
+	  break;							  \
+									  \
+	if (cmpres < 0)							  \
+	  left = middle + 1;						  \
+	else								  \
+	  right = middle - 1;						  \
+									  \
+	middle = (left + right) / 2;					  \
+      }									  \
+									  \
+    if (cmpres == 0)							  \
+      {									  \
+	/* LEFT now marks the last entry for which we know the name is	  \
+	   correct.  */							  \
+	left = middle;							  \
+									  \
+	/* There might be entries with this name before the one we	  \
+	   found.  So we have to find the beginning.  */		  \
+	while (middle > 0						  \
+	       /* Make sure string table indices are not bogus before	  \
+		  using them.  */					  \
+	       && _dl_cache_verify_ptr (cache->libs[middle - 1].key)	  \
+	       /* Actually compare the entry.  */			  \
+	       && (_dl_cache_libcmp (name,				  \
+				     cache_data				  \
+				     + cache->libs[middle - 1].key)	  \
+		   == 0))						  \
+	  --middle;							  \
+									  \
+	do								  \
+	  {								  \
+	    int flags;							  \
+									  \
+	    /* Only perform the name test if necessary.  */		  \
+	    if (middle > left						  \
+		/* We haven't seen this string so far.  Test whether the  \
+		   index is ok and whether the name matches.  Otherwise	  \
+		   we are done.  */					  \
+		&& (! _dl_cache_verify_ptr (cache->libs[middle].key)	  \
+		    || (_dl_cache_libcmp (name,				  \
+					  cache_data			  \
+					  + cache->libs[middle].key)	  \
+			!= 0)))						  \
+	      break;							  \
+									  \
+	    flags = cache->libs[middle].flags;				  \
+	    if (_dl_cache_check_flags (flags)				  \
+		&& _dl_cache_verify_ptr (cache->libs[middle].value))	  \
+	      {								  \
+		if (best == NULL || flags == _dl_correct_cache_id)	  \
+		  {							  \
+		    HWCAP_CHECK;					  \
+		    best = cache_data + cache->libs[middle].value;	  \
+		    							  \
+		    if (flags == _dl_correct_cache_id)			  \
+		      /* We've found an exact match for the shared	  \
+			 object and no general `ELF' release.  Stop	  \
+			 searching.  */					  \
+		      break;						  \
+		  }							  \
+	      }								  \
+	  }								  \
+	while (++middle <= right);					  \
+      }									  \
+  }									  \
+while (0)
+
 
 
 /* Look up NAME in ld.so.cache and return the file name stored there,
@@ -117,10 +157,38 @@ _dl_load_cache_lookup (const char *name)
       /* Read the contents of the file.  */
       void *file = _dl_sysdep_read_whole_file (LD_SO_CACHE, &cachesize,
 					       PROT_READ);
+
+      /* We can handle three different cache file formats here:
+	 - the old libc5/glibc2.0/2.1 format
+	 - the old format with the new format in it
+	 - only the new format
+	 The following checks if the cache contains any of these formats.  */
       if (file && cachesize > sizeof *cache &&
 	  !memcmp (file, CACHEMAGIC, sizeof CACHEMAGIC - 1))
-	/* Looks ok.  */
-	cache = file;
+	{
+	  /* Looks ok.  */
+	  cache = file;
+
+	  /* Check for new version.  */
+	  cache_new = (struct cache_file_new *) &cache->libs[cache->nlibs];
+	  if (cachesize <
+	      (sizeof (struct cache_file) + cache->nlibs * sizeof (struct file_entry)
+	       + sizeof (struct cache_file_new))
+	      || memcmp (cache_new->magic, CACHEMAGIC_NEW,
+			  sizeof CACHEMAGIC_NEW - 1)
+	      || memcmp (cache_new->version, CACHE_VERSION,
+			 sizeof CACHE_VERSION - 1))
+	    cache_new = (void *) -1;
+	}
+      else if (file && cachesize > sizeof *cache_new)
+	{
+	  cache_new = (struct cache_file_new *) file;
+	  if (memcmp (cache_new->magic, CACHEMAGIC_NEW,
+		      sizeof CACHEMAGIC_NEW - 1)
+	      || memcmp (cache_new->version, CACHE_VERSION,
+			 sizeof CACHE_VERSION - 1))
+	    cache_new = (void *) -1;
+	}
       else
 	{
 	  if (file)
@@ -139,88 +207,23 @@ _dl_load_cache_lookup (const char *name)
 
   best = NULL;
 
-  /* We use binary search since the table is sorted in the cache file.
-     It is important to use the same algorithm as used while generating
-     the cache file.  */
-  left = 0;
-  right = cache->nlibs - 1;
-  middle = (left + right) / 2;
-  cmpres = 1;
-
-  while (left <= right)
+  if (cache_new != (void *) -1)
     {
-      /* Make sure string table indices are not bogus before using them.  */
-      if (! _dl_cache_verify_ptr (cache->libs[middle].key))
-	{
-	  cmpres = 1;
-	  break;
-	}
+      /* This file ends in static libraries where we don't have a hwcap.  */
+      unsigned long int *hwcap;
+      weak_extern (_dl_hwcap);
 
-      /* Actually compare the entry with the key.  */
-      cmpres = _dl_cache_libcmp (name, cache_data + cache->libs[middle].key);
-      if (cmpres == 0)
-	/* Found it.  */
-	break;
+      hwcap = &_dl_hwcap;
 
-      if (cmpres < 0)
-	left = middle + 1;
-      else
-	right = middle - 1;
-
-      middle = (left + right) / 2;
-    }
-
-  if (cmpres == 0)
-    {
-      /* LEFT now marks the last entry for which we know the name is
-	 correct.  */
-      left = middle;
-
-      /* There might be entries with this name before the one we
-         found.  So we have to find the beginning.  */
-      while (middle > 0
-	     /* Make sure string table indices are not bogus before
-                using them.  */
-	     && _dl_cache_verify_ptr (cache->libs[middle - 1].key)
-	     /* Actually compare the entry.  */
-	     && (_dl_cache_libcmp (name,
-				   cache_data + cache->libs[middle - 1].key)
-		 == 0))
-	--middle;
-
-      do
-	{
-	  int flags;
-
-	  /* Only perform the name test if necessary.  */
-	  if (middle > left
-	      /* We haven't seen this string so far.  Test whether the
-		 index is ok and whether the name matches.  Otherwise
-		 we are done.  */
-	      && (! _dl_cache_verify_ptr (cache->libs[middle].key)
-		  || (_dl_cache_libcmp (name,
-					cache_data + cache->libs[middle].key)
-		      != 0)))
-	    break;
-
-	  flags = cache->libs[middle].flags;
-	  if (_dl_cache_check_flags (flags)
-	      && _dl_cache_verify_ptr (cache->libs[middle].value))
-	    {
-	      if (best == NULL || flags == _dl_correct_cache_id)
-		{
-		  best = cache_data + cache->libs[middle].value;
-
-		  if (flags == _dl_correct_cache_id)
-		    /* We've found an exact match for the shared
-		       object and no general `ELF' release.  Stop
-		       searching.  */
-		    break;
-		}
-	    }
-	}
-      while (++middle <= right);
+#define HWCAP_CHECK							     \
+      if (hwcap && (cache_new->libs[middle].hwcap & *hwcap) > _dl_hwcap)     \
+	continue
+      SEARCH_CACHE (cache_new);
     }
+  else
+#undef HWCAP_CHECK
+#define HWCAP_CHECK do {} while (0)
+    SEARCH_CACHE (cache);
 
   /* Print our result if wanted.  */
   if (_dl_debug_libs && best != NULL)
diff --git a/sysdeps/generic/dl-cache.h b/sysdeps/generic/dl-cache.h
index 579c261af1..4eb64cf557 100644
--- a/sysdeps/generic/dl-cache.h
+++ b/sysdeps/generic/dl-cache.h
@@ -1,5 +1,5 @@
 /* Support for reading /etc/ld.so.cache files written by Linux ldconfig.
-   Copyright (C) 1999 Free Software Foundation, Inc.
+   Copyright (C) 1999, 2000 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
@@ -21,3 +21,101 @@
 
 #define _dl_cache_check_flags(flags)			\
   ((flags) == 1 || (flags) == _DL_CACHE_DEFAULT_ID)
+
+
+#ifndef LD_SO_CACHE
+# define LD_SO_CACHE "/etc/ld.so.cache"
+#endif
+
+#define CACHEMAGIC "ld.so-1.7.0"
+
+/* libc5 and glibc 2.0/2.1 use the same format.  For glibc 2.2 another
+   format has been added in a compatible way:
+   The beginning of the string table is used for the new table:
+   	old_magic
+	nlibs
+	libs[0]
+	...
+	libs[nlibs-1]
+	new magic
+	newnlibs
+	...
+	newlibs[0]
+	...
+	newlibs[newnlibs-1]
+	string 1
+	string 2
+	...
+*/
+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];
+};
+
+#define CACHEMAGIC_NEW "glibc-ld.so.cache"
+#define CACHE_VERSION "1.0"
+
+
+struct file_entry_new
+{
+  int flags;			/* This is 1 for an ELF library.  */
+  unsigned int key, value;	/* String table indices.  */
+  unsigned long hwcap;		/* Hwcap entry.  */
+};
+
+struct cache_file_new
+{
+  char magic[sizeof CACHEMAGIC_NEW - 1];
+  char version[sizeof CACHE_VERSION - 1];
+  unsigned int nlibs;		/* Number of entries.  */
+  unsigned int len_strings;	/* Size of string table. */
+  unsigned int unused[4];	/* Leave space for future extensions.  */
+  struct file_entry_new libs[0]; /* Entries describing libraries.  */
+  /* After this the string table of size len_strings is found.  */
+};
+
+static int
+_dl_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;
+}