MIPS: IEEE 754-2008 NaN encoding support
[platform/upstream/glibc.git] / elf / ldconfig.c
index 2502f85..c7b9eb9 100644 (file)
@@ -1,21 +1,19 @@
-/* Copyright (C) 1999-2004, 2005 Free Software Foundation, Inc.
+/* Copyright (C) 1999-2013 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 Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; 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,
+   This program 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
-   Lesser General Public License for more details.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
 
 #define PROCINFO_CLASS static
 #include <alloca.h>
 #include <errno.h>
 #include <inttypes.h>
 #include <libintl.h>
+#include <locale.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdio_ext.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <stdint.h>
 #include <sys/fcntl.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <glob.h>
 #include <libgen.h>
 
-#include "ldconfig.h"
-#include "dl-cache.h"
+#include <ldconfig.h>
+#include <dl-cache.h>
 
-#include "dl-procinfo.h"
+#include <dl-procinfo.h>
+
+#ifdef _DL_FIRST_PLATFORM
+# define _DL_FIRST_EXTRA (_DL_FIRST_PLATFORM + _DL_PLATFORMS_COUNT)
+#else
+# define _DL_FIRST_EXTRA _DL_HWCAP_COUNT
+#endif
 
 #ifndef LD_SO_CONF
 # define LD_SO_CONF SYSCONFDIR "/ld.so.conf"
@@ -106,6 +112,9 @@ static char *opt_chroot;
 /* Manually link given shared libraries.  */
 static int opt_manual_link;
 
+/* Should we ignore an old auxiliary cache file?  */
+static int opt_ignore_aux_cache;
+
 /* Cache file to use.  */
 static char *cache_file;
 
@@ -115,11 +124,17 @@ static const char *config_file;
 /* Mask to use for important hardware capabilities.  */
 static unsigned long int hwcap_mask = HWCAP_IMPORTANT;
 
+/* Configuration-defined capabilities defined in kernel vDSOs.  */
+static const char *hwcap_extra[64 - _DL_FIRST_EXTRA];
+
 /* 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;
 
+/* Function to print some extra text in the help message.  */
+static char *more_help (int key, const char *text, void *input);
+
 /* Definitions of arguments for argp functions.  */
 static const struct argp_option options[] =
 {
@@ -133,6 +148,7 @@ static const struct argp_option options[] =
   { 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', N_("FORMAT"), 0, N_("Format to use: new, old or compat (default)"), 0},
+  { "ignore-aux-cache", 'i', NULL, 0, N_("Ignore auxiliary cache file"), 0},
   { NULL, 0, NULL, 0, NULL, 0 }
 };
 
@@ -148,7 +164,7 @@ 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
+  options, parse_opt, NULL, doc, NULL, more_help, NULL
 };
 
 /* Check if string corresponds to an important hardware capability or
@@ -158,17 +174,21 @@ is_hwcap_platform (const char *name)
 {
   int hwcap_idx = _dl_string_hwcap (name);
 
+  /* Is this a normal hwcap for the machine like "fpu?"  */
   if (hwcap_idx != -1 && ((1 << hwcap_idx) & hwcap_mask))
     return 1;
 
+  /* Is this a platform pseudo-hwcap like "i686?"  */
   hwcap_idx = _dl_string_platform (name);
   if (hwcap_idx != -1)
     return 1;
 
-#ifdef USE_TLS
-  if (strcmp (name, "tls") == 0)
-    return 1;
-#endif
+  /* Is this one of the extra pseudo-hwcaps that we map beyond
+     _DL_FIRST_EXTRA like "tls", or "nosegneg?"  */
+  for (hwcap_idx = _DL_FIRST_EXTRA; hwcap_idx < 64; ++hwcap_idx)
+    if (hwcap_extra[hwcap_idx - _DL_FIRST_EXTRA] != NULL
+       && !strcmp (name, hwcap_extra[hwcap_idx - _DL_FIRST_EXTRA]))
+      return 1;
 
   return 0;
 }
@@ -203,11 +223,11 @@ path_hwcap (const char *path)
          h = _dl_string_platform (ptr + 1);
          if (h == (uint64_t) -1)
            {
-#ifdef USE_TLS
-             if (strcmp (ptr + 1, "tls") == 0)
-               h = 63;
-             else
-#endif
+             for (h = _DL_FIRST_EXTRA; h < 64; ++h)
+               if (hwcap_extra[h - _DL_FIRST_EXTRA] != NULL
+                   && !strcmp (ptr + 1, hwcap_extra[h - _DL_FIRST_EXTRA]))
+                 break;
+             if (h == 64)
                break;
            }
        }
@@ -229,10 +249,15 @@ parse_opt (int key, char *arg, struct argp_state *state)
     {
     case 'C':
       cache_file = arg;
+      /* Ignore auxiliary cache since we use non-standard cache.  */
+      opt_ignore_aux_cache = 1;
       break;
     case 'f':
       config_file = arg;
       break;
+    case 'i':
+      opt_ignore_aux_cache = 1;
+      break;
     case 'l':
       opt_manual_link = 1;
       break;
@@ -270,16 +295,36 @@ parse_opt (int key, char *arg, struct argp_state *state)
   return 0;
 }
 
+/* Print bug-reporting information in the help message.  */
+static char *
+more_help (int key, const char *text, void *input)
+{
+  char *tp = NULL;
+  switch (key)
+    {
+    case ARGP_KEY_HELP_EXTRA:
+      /* We print some extra information.  */
+      if (asprintf (&tp, gettext ("\
+For bug reporting instructions, please see:\n\
+%s.\n"), REPORT_BUGS_TO) < 0)
+       return NULL;
+      return tp;
+    default:
+      break;
+    }
+  return (char *) text;
+}
+
 /* 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, "ldconfig %s%s\n", PKGVERSION, 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\
-"), "2005");
+"), "2013");
   fprintf (stream, gettext ("Written by %s.\n"),
           "Andreas Jaeger");
 }
@@ -346,14 +391,17 @@ add_dir (const char *line)
     }
 
   /* Canonify path: for now only remove leading and trailing
-     whitespace and the trailing slashes slashes.  */
-  i = strlen (entry->path) - 1;
+     whitespace and the trailing slashes.  */
+  i = strlen (entry->path);
+
+  while (i > 0 && isspace (entry->path[i - 1]))
+    entry->path[--i] = '\0';
 
-  while (isspace (entry->path[i]) && i > 0)
-    entry->path[i--] = '\0';
+  while (i > 0 && entry->path[i - 1] == '/')
+    entry->path[--i] = '\0';
 
-  while (entry->path[i] == '/' && i > 0)
-    entry->path[i--] = '\0';
+  if (i == 0)
+    return;
 
   char *path = entry->path;
   if (opt_chroot)
@@ -509,7 +557,7 @@ manual_link (char *library)
   if (libname)
     {
       /* Successfully split names.  Check if path is just "/" to avoid
-         an empty path.  */
+        an empty path.  */
       if (libname == path)
        {
          libname = library + 1;
@@ -551,7 +599,7 @@ manual_link (char *library)
   /* Do some sanity checks first.  */
   if (lstat64 (real_library, &stat_buf))
     {
-      error (0, errno, _("Can't lstat %s"), library);
+      error (0, errno, _("Cannot lstat %s"), library);
       free (path);
       return;
     }
@@ -563,14 +611,17 @@ manual_link (char *library)
       free (path);
       return;
     }
+
   if (process_file (real_library, library, libname, &flag, &osversion,
-                   &soname, 0))
+                   &soname, 0, &stat_buf))
     {
       error (0, 0, _("No link created since soname could not be found for %s"),
             library);
       free (path);
       return;
     }
+  if (soname == NULL)
+    soname = implicit_soname (libname, flag);
   create_links (real_path, path, libname, soname);
   free (soname);
   free (path);
@@ -616,31 +667,20 @@ struct dlib_entry
 static void
 search_dir (const struct dir_entry *entry)
 {
-  DIR *dir;
-  struct dirent64 *direntry;
-  char *file_name, *dir_name, *real_file_name, *real_name;
-  int file_name_len, real_file_name_len, len;
-  char *soname;
-  struct dlib_entry *dlibs;
-  struct dlib_entry *dlib_ptr;
-  struct stat64 lstat_buf, stat_buf;
-  int is_link, is_dir;
   uint64_t hwcap = path_hwcap (entry->path);
-  unsigned int osversion;
-
-  file_name_len = PATH_MAX;
-  file_name = alloca (file_name_len);
-
-  dlibs = NULL;
-
   if (opt_verbose)
     {
       if (hwcap != 0)
-       printf ("%s: (hwcap: 0x%" PRIx64 ")\n", entry->path, hwcap);
+       printf ("%s: (hwcap: %#.16" PRIx64 ")\n", entry->path, hwcap);
       else
        printf ("%s:\n", entry->path);
     }
 
+  char *dir_name;
+  char *real_file_name;
+  size_t real_file_name_len;
+  size_t file_name_len = PATH_MAX;
+  char *file_name = alloca (file_name_len);
   if (opt_chroot)
     {
       dir_name = chroot_canon (opt_chroot, entry->path);
@@ -654,6 +694,7 @@ search_dir (const struct dir_entry *entry)
       real_file_name = file_name;
     }
 
+  DIR *dir;
   if (dir_name == NULL || (dir = opendir (dir_name)) == NULL)
     {
       if (opt_verbose)
@@ -663,6 +704,8 @@ search_dir (const struct dir_entry *entry)
       return;
     }
 
+  struct dirent64 *direntry;
+  struct dlib_entry *dlibs = NULL;
   while ((direntry = readdir64 (dir)) != NULL)
     {
       int flag;
@@ -686,10 +729,24 @@ search_dir (const struct dir_entry *entry)
 #endif
              !is_hwcap_platform (direntry->d_name)))
        continue;
-      len = strlen (entry->path) + strlen (direntry->d_name);
+
+      size_t len = strlen (direntry->d_name);
+      /* Skip temporary files created by the prelink program.  Files with
+        names like these are never really DSOs we want to look at.  */
+      if (len >= sizeof (".#prelink#") - 1)
+       {
+         if (strcmp (direntry->d_name + len - sizeof (".#prelink#") + 1,
+                     ".#prelink#") == 0)
+           continue;
+         if (len >= sizeof (".#prelink#.XXXXXX") - 1
+             && memcmp (direntry->d_name + len - sizeof (".#prelink#.XXXXXX")
+                        + 1, ".#prelink#.", sizeof (".#prelink#.") - 1) == 0)
+           continue;
+       }
+      len += strlen (entry->path) + 2;
       if (len > file_name_len)
        {
-         file_name_len = len + 1;
+         file_name_len = len;
          file_name = alloca (file_name_len);
          if (!opt_chroot)
            real_file_name = file_name;
@@ -697,15 +754,18 @@ search_dir (const struct dir_entry *entry)
       sprintf (file_name, "%s/%s", entry->path, direntry->d_name);
       if (opt_chroot)
        {
-         len = strlen (dir_name) + strlen (direntry->d_name);
+         len = strlen (dir_name) + strlen (direntry->d_name) + 2;
          if (len > real_file_name_len)
            {
-             real_file_name_len = len + 1;
+             real_file_name_len = len;
              real_file_name = alloca (real_file_name_len);
            }
          sprintf (real_file_name, "%s/%s", dir_name, direntry->d_name);
        }
+
+      struct stat64 lstat_buf;
 #ifdef _DIRENT_HAVE_D_TYPE
+      /* We optimize and try to do the lstat call only if needed.  */
       if (direntry->d_type != DT_UNKNOWN)
        lstat_buf.st_mode = DTTOIF (direntry->d_type);
       else
@@ -716,12 +776,25 @@ search_dir (const struct dir_entry *entry)
            continue;
          }
 
-      is_link = S_ISLNK (lstat_buf.st_mode);
+      struct stat64 stat_buf;
+      int is_dir;
+      int is_link = S_ISLNK (lstat_buf.st_mode);
       if (is_link)
-        {
+       {
          /* In case of symlink, we check if the symlink refers to
             a directory. */
-         if (__builtin_expect (stat64 (real_file_name, &stat_buf), 0))
+         char *target_name = real_file_name;
+         if (opt_chroot)
+           {
+             target_name = chroot_canon (opt_chroot, file_name);
+             if (target_name == NULL)
+               {
+                 if (strstr (file_name, ".so") == NULL)
+                   error (0, 0, _("Input file %s not found.\n"), file_name);
+                 continue;
+               }
+           }
+         if (__builtin_expect (stat64 (target_name, &stat_buf), 0))
            {
              if (opt_verbose)
                error (0, errno, _("Cannot stat %s"), file_name);
@@ -732,6 +805,12 @@ search_dir (const struct dir_entry *entry)
              continue;
            }
          is_dir = S_ISDIR (stat_buf.st_mode);
+
+         /* lstat_buf is later stored, update contents.  */
+         lstat_buf.st_dev = stat_buf.st_dev;
+         lstat_buf.st_ino = stat_buf.st_ino;
+         lstat_buf.st_size = stat_buf.st_size;
+         lstat_buf.st_ctime = stat_buf.st_ctime;
        }
       else
        is_dir = S_ISDIR (lstat_buf.st_mode);
@@ -745,36 +824,28 @@ search_dir (const struct dir_entry *entry)
          new_entry->path = xstrdup (file_name);
          new_entry->flag = entry->flag;
          new_entry->next = NULL;
-         if (is_link)
+#ifdef _DIRENT_HAVE_D_TYPE
+         /* We have filled in lstat only #ifndef
+            _DIRENT_HAVE_D_TYPE.  Fill it in if needed.  */
+         if (!is_link
+             && direntry->d_type != DT_UNKNOWN
+             && __builtin_expect (lstat64 (real_file_name, &lstat_buf), 0))
            {
-             new_entry->ino = stat_buf.st_ino;
-             new_entry->dev = stat_buf.st_dev;
+             error (0, errno, _("Cannot lstat %s"), file_name);
+             free (new_entry->path);
+             free (new_entry);
+             continue;
            }
-         else
-           {
-#ifdef _DIRENT_HAVE_D_TYPE
-             /* We have filled in lstat only #ifndef
-                _DIRENT_HAVE_D_TYPE.  Fill it in if needed.  */
-             if (direntry->d_type != DT_UNKNOWN
-                 && __builtin_expect (lstat64 (real_file_name, &lstat_buf),
-                                      0))
-               {
-                 error (0, errno, _("Cannot lstat %s"), file_name);
-                 free (new_entry->path);
-                 free (new_entry);
-                 continue;
-               }
 #endif
-
-             new_entry->ino = lstat_buf.st_ino;
-             new_entry->dev = lstat_buf.st_dev;
-           }
+         new_entry->ino = lstat_buf.st_ino;
+         new_entry->dev = lstat_buf.st_dev;
          add_single_dir (new_entry, 0);
          continue;
        }
       else if (!S_ISREG (lstat_buf.st_mode) && !is_link)
        continue;
 
+      char *real_name;
       if (opt_chroot && is_link)
        {
          real_name = chroot_canon (opt_chroot, file_name);
@@ -788,14 +859,36 @@ search_dir (const struct dir_entry *entry)
       else
        real_name = real_file_name;
 
-      if (process_file (real_name, file_name, direntry->d_name, &flag,
-                       &osversion, &soname, is_link))
+#ifdef _DIRENT_HAVE_D_TYPE
+      /* Call lstat64 if not done yet.  */
+      if (!is_link
+         && direntry->d_type != DT_UNKNOWN
+         && __builtin_expect (lstat64 (real_file_name, &lstat_buf), 0))
        {
-         if (real_name != real_file_name)
-           free (real_name);
+         error (0, errno, _("Cannot lstat %s"), file_name);
          continue;
        }
+#endif
+
+      /* First search whether the auxiliary cache contains this
+        library already and it's not changed.  */
+      char *soname;
+      unsigned int osversion;
+      if (!search_aux_cache (&lstat_buf, &flag, &osversion, &soname))
+       {
+         if (process_file (real_name, file_name, direntry->d_name, &flag,
+                           &osversion, &soname, is_link, &lstat_buf))
+           {
+             if (real_name != real_file_name)
+               free (real_name);
+             continue;
+           }
+         else if (opt_build_cache)
+           add_to_aux_cache (&lstat_buf, flag, osversion, soname);
+       }
 
+      if (soname == NULL)
+       soname = implicit_soname (direntry->d_name, flag);
 
       /* A link may just point to itself.  */
       if (is_link)
@@ -812,7 +905,7 @@ search_dir (const struct dir_entry *entry)
                  || strncmp (real_base_name, soname, len) != 0)
                is_link = 0;
            }
-        }
+       }
 
       if (real_name != real_file_name)
        free (real_name);
@@ -827,6 +920,7 @@ search_dir (const struct dir_entry *entry)
          && (entry->flag == FLAG_ELF_LIBC5
              || entry->flag == FLAG_ELF_LIBC6))
        flag = entry->flag;
+
       /* Some sanity checks to print warnings.  */
       if (opt_verbose)
        {
@@ -842,6 +936,7 @@ search_dir (const struct dir_entry *entry)
        }
 
       /* Add library to list.  */
+      struct dlib_entry *dlib_ptr;
       for (dlib_ptr = dlibs; dlib_ptr != NULL; dlib_ptr = dlib_ptr->next)
        {
          /* Is soname already in list?  */
@@ -866,12 +961,13 @@ search_dir (const struct dir_entry *entry)
                        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);
+                              dlib_ptr->name, direntry->d_name,
+                              entry->path);
                    }
                  free (dlib_ptr->name);
-                 dlib_ptr->osversion = osversion;
                  dlib_ptr->name = xstrdup (direntry->d_name);
                  dlib_ptr->is_link = is_link;
+                 dlib_ptr->osversion = osversion;
                }
              /* Don't add this library, abort loop.  */
              /* Also free soname, since it's dynamically allocated.  */
@@ -884,10 +980,10 @@ search_dir (const struct dir_entry *entry)
        {
          dlib_ptr = (struct dlib_entry *)xmalloc (sizeof (struct dlib_entry));
          dlib_ptr->name = xstrdup (direntry->d_name);
-         dlib_ptr->flag = flag;
-         dlib_ptr->osversion = osversion;
          dlib_ptr->soname = soname;
+         dlib_ptr->flag = flag;
          dlib_ptr->is_link = is_link;
+         dlib_ptr->osversion = osversion;
          /* Add at head of list.  */
          dlib_ptr->next = dlibs;
          dlibs = dlib_ptr;
@@ -898,6 +994,7 @@ search_dir (const struct dir_entry *entry)
 
   /* Now dlibs contains a list of all libs - add those to the cache
      and created all symbolic links.  */
+  struct dlib_entry *dlib_ptr;
   for (dlib_ptr = dlibs; dlib_ptr != NULL; dlib_ptr = dlib_ptr->next)
     {
       /* Don't create links to links.  */
@@ -972,7 +1069,9 @@ parse_conf (const char *filename, bool do_chroot)
 
   if (file == NULL)
     {
-      error (0, errno, _("Can't open configuration file %s"), canon);
+      error (0, errno, _("\
+Warning: ignoring configuration file that cannot be opened: %s"),
+            canon);
       if (canon != filename)
        free ((char *) canon);
       return;
@@ -1017,6 +1116,53 @@ parse_conf (const char *filename, bool do_chroot)
            if (dir[0] != '\0')
              parse_conf_include (filename, lineno, do_chroot, dir);
        }
+      else if (!strncasecmp (cp, "hwcap", 5) && isblank (cp[5]))
+       {
+         cp += 6;
+         char *p, *name = NULL;
+         unsigned long int n = strtoul (cp, &cp, 0);
+         if (cp != NULL && isblank (*cp))
+           while ((p = strsep (&cp, " \t")) != NULL)
+             if (p[0] != '\0')
+               {
+                 if (name == NULL)
+                   name = p;
+                 else
+                   {
+                     name = NULL;
+                     break;
+                   }
+               }
+         if (name == NULL)
+           {
+             error (EXIT_FAILURE, 0, _("%s:%u: bad syntax in hwcap line"),
+                    filename, lineno);
+             break;
+           }
+         if (n >= (64 - _DL_FIRST_EXTRA))
+           error (EXIT_FAILURE, 0,
+                  _("%s:%u: hwcap index %lu above maximum %u"),
+                  filename, lineno, n, 64 - _DL_FIRST_EXTRA - 1);
+         if (hwcap_extra[n] == NULL)
+           {
+             for (unsigned long int h = 0; h < (64 - _DL_FIRST_EXTRA); ++h)
+               if (hwcap_extra[h] != NULL && !strcmp (name, hwcap_extra[h]))
+                 error (EXIT_FAILURE, 0,
+                        _("%s:%u: hwcap index %lu already defined as %s"),
+                        filename, lineno, h, name);
+             hwcap_extra[n] = xstrdup (name);
+           }
+         else
+           {
+             if (strcmp (name, hwcap_extra[n]))
+               error (EXIT_FAILURE, 0,
+                      _("%s:%u: hwcap index %lu already defined as %s"),
+                      filename, lineno, n, hwcap_extra[n]);
+             if (opt_verbose)
+               error (0, 0, _("%s:%u: duplicate hwcap %lu %s"),
+                      filename, lineno, n, name);
+           }
+       }
       else
        add_dir (cp);
     }
@@ -1051,7 +1197,9 @@ parse_conf_include (const char *config_file, unsigned int lineno,
   if (do_chroot && opt_chroot)
     {
       char *canon = chroot_canon (opt_chroot, pattern);
-      result = glob64 (canon ?: pattern, 0, NULL, &gl);
+      if (canon == NULL)
+       return;
+      result = glob64 (canon, 0, NULL, &gl);
       free (canon);
     }
   else
@@ -1081,8 +1229,7 @@ parse_conf_include (const char *config_file, unsigned int lineno,
       break;
     }
 
-  if (copy)
-    free (copy);
+  free (copy);
 }
 
 /* Honour LD_HWCAP_MASK.  */
@@ -1099,9 +1246,14 @@ set_hwcap (void)
 int
 main (int argc, char **argv)
 {
-  int remaining;
+  /* Set locale via LC_ALL.  */
+  setlocale (LC_ALL, "");
+
+  /* Set the text message domain.  */
+  textdomain (_libc_intl_domainname);
 
   /* Parse and process arguments.  */
+  int remaining;
   argp_parse (&argp, argc, argv, 0, &remaining, NULL);
 
   /* Remaining arguments are additional directories if opt_manual_link
@@ -1118,12 +1270,18 @@ main (int argc, char **argv)
          add_dir (argv[i]);
     }
 
+  /* The last entry in hwcap_extra is reserved for the "tls" pseudo-hwcap which
+     indicates support for TLS.  This pseudo-hwcap is only used by old versions
+     under which TLS support was optional.  The entry is no longer needed, but
+     must remain for compatibility.  */
+  hwcap_extra[63 - _DL_FIRST_EXTRA] = "tls";
+
   set_hwcap ();
 
   if (opt_chroot)
     {
       /* Normalize the path a bit, we might need it for printing later.  */
-      char *endp = strchr (opt_chroot, '\0');
+      char *endp = rawmemchr (opt_chroot, '\0');
       while (endp > opt_chroot && endp[-1] == '/')
        --endp;
       *endp = '\0';
@@ -1170,17 +1328,15 @@ main (int argc, char **argv)
   if (opt_chroot)
     {
       /* Canonicalize the directory name of cache_file, not cache_file,
-         because we'll rename a temporary cache file to it.  */
+        because we'll rename a temporary cache file to it.  */
       char *p = strrchr (cache_file, '/');
       char *canon = chroot_canon (opt_chroot,
                                  p ? (*p = '\0', cache_file) : "/");
 
       if (canon == NULL)
-       {
-         error (EXIT_FAILURE, errno,
-                _("Can't open cache file directory %s\n"),
-                p ? cache_file : "/");
-       }
+       error (EXIT_FAILURE, errno,
+              _("Can't open cache file directory %s\n"),
+              p ? cache_file : "/");
 
       if (p)
        ++p;
@@ -1217,10 +1373,23 @@ main (int argc, char **argv)
        add_system_dir (LIBDIR);
     }
 
+  const char *aux_cache_file = _PATH_LDCONFIG_AUX_CACHE;
+  if (opt_chroot)
+    aux_cache_file = chroot_canon (opt_chroot, aux_cache_file);
+
+  if (! opt_ignore_aux_cache && aux_cache_file)
+    load_aux_cache (aux_cache_file);
+  else
+    init_aux_cache ();
+
   search_dirs ();
 
   if (opt_build_cache)
-    save_cache (cache_file);
+    {
+      save_cache (cache_file);
+      if (aux_cache_file)
+       save_aux_cache (aux_cache_file);
+    }
 
   return 0;
 }