summaryrefslogtreecommitdiff
path: root/libc/elf/ldconfig.c
diff options
context:
space:
mode:
Diffstat (limited to 'libc/elf/ldconfig.c')
-rw-r--r--libc/elf/ldconfig.c1297
1 files changed, 1297 insertions, 0 deletions
diff --git a/libc/elf/ldconfig.c b/libc/elf/ldconfig.c
new file mode 100644
index 000000000..5b9a5b174
--- /dev/null
+++ b/libc/elf/ldconfig.c
@@ -0,0 +1,1297 @@
+/* Copyright (C) 1999-2004, 2005, 2006 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Andreas Jaeger <aj@suse.de>, 1999.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 as
+ published by the Free Software Foundation.
+
+ 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#define PROCINFO_CLASS static
+#include <alloca.h>
+#include <argp.h>
+#include <dirent.h>
+#include <elf.h>
+#include <error.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <libintl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdio_ext.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 <glob.h>
+#include <libgen.h>
+
+#include <ldconfig.h>
+#include <dl-cache.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"
+#endif
+
+/* Get libc version number. */
+#include <version.h>
+
+#define PACKAGE _libc_intl_domainname
+
+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;
+ ino64_t ino;
+ dev_t dev;
+ 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;
+
+/* Be verbose. */
+int opt_verbose;
+
+/* 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;
+
+/* Generate links. */
+static int opt_link = 1;
+
+/* Only process directories specified on the command line. */
+static int opt_only_cline;
+
+/* Path to root for chroot. */
+static char *opt_chroot;
+
+/* Manually link given shared libraries. */
+static int opt_manual_link;
+
+/* Cache file to use. */
+static char *cache_file;
+
+/* Configuration file. */
+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;
+
+/* 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', N_("ROOT"), 0, N_("Change to and use ROOT as root directory"), 0},
+ { NULL, 'C', N_("CACHE"), 0, N_("Use CACHE as cache file"), 0},
+ { NULL, 'f', N_("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', N_("FORMAT"), 0, N_("Format to use: new, old or compat (default)"), 0},
+ { NULL, 0, NULL, 0, NULL, 0 }
+};
+
+#define PROCINFO_CLASS static
+#include <dl-procinfo.c>
+
+/* 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
+};
+
+/* Check if string corresponds to an important hardware capability or
+ a platform. */
+static int
+is_hwcap_platform (const char *name)
+{
+ int hwcap_idx = _dl_string_hwcap (name);
+
+ if (hwcap_idx != -1 && ((1 << hwcap_idx) & hwcap_mask))
+ return 1;
+
+ hwcap_idx = _dl_string_platform (name);
+ if (hwcap_idx != -1)
+ return 1;
+
+ 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;
+}
+
+/* Get hwcap (including platform) encoding of path. */
+static uint64_t
+path_hwcap (const char *path)
+{
+ char *str = xstrdup (path);
+ char *ptr;
+ uint64_t hwcap = 0;
+ uint64_t 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 == (uint64_t) -1)
+ {
+ h = _dl_string_platform (ptr + 1);
+ if (h == (uint64_t) -1)
+ {
+ 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;
+ }
+ }
+ hwcap += 1ULL << h;
+
+ /* Search the next part of the path. */
+ *ptr = '\0';
+ }
+
+ free (str);
+ return hwcap;
+}
+
+/* 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 'l':
+ opt_manual_link = 1;
+ 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;
+ 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;
+ }
+
+ 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\
+"), "2006");
+ fprintf (stream, gettext ("Written by %s.\n"),
+ "Andreas Jaeger");
+}
+
+/* Add a single directory entry. */
+static void
+add_single_dir (struct dir_entry *entry, int verbose)
+{
+ struct dir_entry *ptr, *prev;
+
+ ptr = dir_entries;
+ prev = ptr;
+ while (ptr != NULL)
+ {
+ /* Check for duplicates. */
+ if (ptr->ino == entry->ino && ptr->dev == entry->dev)
+ {
+ if (opt_verbose && verbose)
+ error (0, 0, _("Path `%s' given more than once"), entry->path);
+ /* Use the newer information. */
+ ptr->flag = entry->flag;
+ free (entry->path);
+ 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;
+}
+
+/* Add one directory to the list of directories to process. */
+static void
+add_dir (const char *line)
+{
+ unsigned int i;
+ struct dir_entry *entry = xmalloc (sizeof (struct dir_entry));
+ entry->next = NULL;
+
+ /* Search for an '=' sign. */
+ entry->path = xstrdup (line);
+ char *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 leading and trailing
+ whitespace and the trailing slashes slashes. */
+ i = strlen (entry->path) - 1;
+
+ while (isspace (entry->path[i]) && i > 0)
+ entry->path[i--] = '\0';
+
+ while (entry->path[i] == '/' && i > 0)
+ entry->path[i--] = '\0';
+
+ char *path = entry->path;
+ if (opt_chroot)
+ path = chroot_canon (opt_chroot, path);
+
+ struct stat64 stat_buf;
+ if (path == NULL || stat64 (path, &stat_buf))
+ {
+ if (opt_verbose)
+ error (0, errno, _("Can't stat %s"), entry->path);
+ free (entry->path);
+ free (entry);
+ }
+ else
+ {
+ entry->ino = stat_buf.st_ino;
+ entry->dev = stat_buf.st_dev;
+
+ add_single_dir (entry, 1);
+ }
+
+ if (opt_chroot)
+ free (path);
+}
+
+
+static int
+chroot_stat (const char *real_path, const char *path, struct stat64 *st)
+{
+ int ret;
+ char *canon_path;
+
+ if (!opt_chroot)
+ return stat64 (real_path, st);
+
+ ret = lstat64 (real_path, st);
+ if (ret || !S_ISLNK (st->st_mode))
+ return ret;
+
+ canon_path = chroot_canon (opt_chroot, path);
+ if (canon_path == NULL)
+ return -1;
+
+ ret = stat64 (canon_path, st);
+ free (canon_path);
+ return ret;
+}
+
+/* Create a symbolic link from soname to libname in directory path. */
+static void
+create_links (const char *real_path, const char *path, const char *libname,
+ const char *soname)
+{
+ char *full_libname, *full_soname;
+ char *real_full_libname, *real_full_soname;
+ struct stat64 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. */
+ full_libname = alloca (strlen (path) + strlen (libname) + 2);
+ full_soname = alloca (strlen (path) + strlen (soname) + 2);
+ sprintf (full_libname, "%s/%s", path, libname);
+ sprintf (full_soname, "%s/%s", path, soname);
+ if (opt_chroot)
+ {
+ real_full_libname = alloca (strlen (real_path) + strlen (libname) + 2);
+ real_full_soname = alloca (strlen (real_path) + strlen (soname) + 2);
+ sprintf (real_full_libname, "%s/%s", real_path, libname);
+ sprintf (real_full_soname, "%s/%s", real_path, soname);
+ }
+ else
+ {
+ real_full_libname = full_libname;
+ real_full_soname = full_soname;
+ }
+
+ /* Does soname already exist and point to the right library? */
+ if (chroot_stat (real_full_soname, full_soname, &stat_so) == 0)
+ {
+ if (chroot_stat (real_full_libname, 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 (lstat64 (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 (lstat64 (real_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 (real_full_soname))
+ {
+ error (0, 0, _("Can't unlink %s"), full_soname);
+ do_link = 0;
+ }
+ /* Create symbolic link. */
+ if (do_link && symlink (libname, real_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);
+}
+
+/* Manually link the given library. */
+static void
+manual_link (char *library)
+{
+ char *path;
+ char *real_path;
+ char *real_library;
+ char *libname;
+ char *soname;
+ struct stat64 stat_buf;
+ int flag;
+ unsigned int osversion;
+
+ /* Prepare arguments for create_links call. Split library name in
+ directory and filename first. Since path is allocated, we've got
+ to be careful to free at the end. */
+ path = xstrdup (library);
+ libname = strrchr (path, '/');
+
+ if (libname)
+ {
+ /* Successfully split names. Check if path is just "/" to avoid
+ an empty path. */
+ if (libname == path)
+ {
+ libname = library + 1;
+ path = xrealloc (path, 2);
+ strcpy (path, "/");
+ }
+ else
+ {
+ *libname = '\0';
+ ++libname;
+ }
+ }
+ else
+ {
+ /* There's no path, construct one. */
+ libname = library;
+ path = xrealloc (path, 2);
+ strcpy (path, ".");
+ }
+
+ if (opt_chroot)
+ {
+ real_path = chroot_canon (opt_chroot, path);
+ if (real_path == NULL)
+ {
+ error (0, errno, _("Can't find %s"), path);
+ free (path);
+ return;
+ }
+ real_library = alloca (strlen (real_path) + strlen (libname) + 2);
+ sprintf (real_library, "%s/%s", real_path, libname);
+ }
+ else
+ {
+ real_path = path;
+ real_library = library;
+ }
+
+ /* Do some sanity checks first. */
+ if (lstat64 (real_library, &stat_buf))
+ {
+ error (0, errno, _("Can't lstat %s"), library);
+ free (path);
+ return;
+ }
+ /* We don't want links here! */
+ else if (!S_ISREG (stat_buf.st_mode))
+ {
+ error (0, 0, _("Ignored file %s since it is not a regular file."),
+ library);
+ free (path);
+ return;
+ }
+ if (process_file (real_library, library, libname, &flag, &osversion,
+ &soname, 0))
+ {
+ error (0, 0, _("No link created since soname could not be found for %s"),
+ library);
+ free (path);
+ return;
+ }
+ create_links (real_path, path, libname, soname);
+ free (soname);
+ free (path);
+}
+
+
+/* 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;
+ unsigned int osversion;
+ struct dlib_entry *next;
+};
+
+
+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: %#.16" PRIx64 ")\n", entry->path, hwcap);
+ else
+ printf ("%s:\n", entry->path);
+ }
+
+ if (opt_chroot)
+ {
+ dir_name = chroot_canon (opt_chroot, entry->path);
+ real_file_name_len = PATH_MAX;
+ real_file_name = alloca (real_file_name_len);
+ }
+ else
+ {
+ dir_name = entry->path;
+ real_file_name_len = 0;
+ real_file_name = file_name;
+ }
+
+ if (dir_name == NULL || (dir = opendir (dir_name)) == NULL)
+ {
+ if (opt_verbose)
+ error (0, errno, _("Can't open directory %s"), entry->path);
+ if (opt_chroot && dir_name)
+ free (dir_name);
+ return;
+ }
+
+ while ((direntry = readdir64 (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
+ && direntry->d_type != DT_DIR)
+ continue;
+#endif /* _DIRENT_HAVE_D_TYPE */
+ /* 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)
+ && (
+#ifdef _DIRENT_HAVE_D_TYPE
+ direntry->d_type == DT_REG ||
+#endif
+ !is_hwcap_platform (direntry->d_name)))
+ continue;
+ 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);
+ if (len > file_name_len)
+ {
+ file_name_len = len + 1;
+ file_name = alloca (file_name_len);
+ if (!opt_chroot)
+ real_file_name = file_name;
+ }
+ sprintf (file_name, "%s/%s", entry->path, direntry->d_name);
+ if (opt_chroot)
+ {
+ len = strlen (dir_name) + strlen (direntry->d_name);
+ if (len > real_file_name_len)
+ {
+ real_file_name_len = len + 1;
+ real_file_name = alloca (real_file_name_len);
+ }
+ sprintf (real_file_name, "%s/%s", dir_name, direntry->d_name);
+ }
+#ifdef _DIRENT_HAVE_D_TYPE
+ if (direntry->d_type != DT_UNKNOWN)
+ lstat_buf.st_mode = DTTOIF (direntry->d_type);
+ else
+#endif
+ if (__builtin_expect (lstat64 (real_file_name, &lstat_buf), 0))
+ {
+ error (0, errno, _("Cannot lstat %s"), file_name);
+ continue;
+ }
+
+ 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))
+ {
+ if (opt_verbose)
+ error (0, errno, _("Cannot stat %s"), file_name);
+
+ /* Remove stale symlinks. */
+ if (strstr (direntry->d_name, ".so."))
+ unlink (real_file_name);
+ continue;
+ }
+ is_dir = S_ISDIR (stat_buf.st_mode);
+ }
+ else
+ is_dir = S_ISDIR (lstat_buf.st_mode);
+
+ if (is_dir && is_hwcap_platform (direntry->d_name))
+ {
+ /* Handle subdirectory later. */
+ struct dir_entry *new_entry;
+
+ new_entry = xmalloc (sizeof (struct dir_entry));
+ new_entry->path = xstrdup (file_name);
+ new_entry->flag = entry->flag;
+ new_entry->next = NULL;
+ if (is_link)
+ {
+ new_entry->ino = stat_buf.st_ino;
+ new_entry->dev = stat_buf.st_dev;
+ }
+ 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;
+ }
+ add_single_dir (new_entry, 0);
+ continue;
+ }
+ else if (!S_ISREG (lstat_buf.st_mode) && !is_link)
+ continue;
+
+ if (opt_chroot && is_link)
+ {
+ real_name = chroot_canon (opt_chroot, file_name);
+ if (real_name == NULL)
+ {
+ if (strstr (file_name, ".so") == NULL)
+ error (0, 0, _("Input file %s not found.\n"), file_name);
+ continue;
+ }
+ }
+ else
+ real_name = real_file_name;
+
+ if (process_file (real_name, file_name, direntry->d_name, &flag,
+ &osversion, &soname, is_link))
+ {
+ if (real_name != real_file_name)
+ free (real_name);
+ continue;
+ }
+
+
+ /* A link may just point to itself. */
+ if (is_link)
+ {
+ /* If the path the link points to isn't its soname and it is not
+ .so symlink for ld(1) only, we treat it as a normal file. */
+ const char *real_base_name = basename (real_file_name);
+
+ if (strcmp (real_base_name, soname) != 0)
+ {
+ len = strlen (real_base_name);
+ if (len < strlen (".so")
+ || strcmp (real_base_name + len - strlen (".so"), ".so") != 0
+ || strncmp (real_base_name, soname, len) != 0)
+ is_link = 0;
+ }
+ }
+
+ if (real_name != real_file_name)
+ free (real_name);
+
+ 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"), file_name);
+ if (flag == FLAG_ELF_LIBC6 && entry->flag != FLAG_ELF_LIBC6
+ && entry->flag != FLAG_ANY)
+ error (0, 0, _("libc6 library %s in wrong directory"), file_name);
+ if (flag == FLAG_LIBC4 && entry->flag != FLAG_LIBC4
+ && entry->flag != FLAG_ANY)
+ error (0, 0, _("libc4 library %s in wrong directory"), file_name);
+ }
+
+ /* 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
+ && _dl_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->osversion = osversion;
+ 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->osversion = osversion;
+ 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 (dir_name, entry->path, dlib_ptr->name,
+ dlib_ptr->soname);
+ if (opt_build_cache)
+ add_to_cache (entry->path, dlib_ptr->soname, dlib_ptr->flag,
+ dlib_ptr->osversion, hwcap);
+ }
+
+ /* Free all resources. */
+ while (dlibs)
+ {
+ dlib_ptr = dlibs;
+ free (dlib_ptr->soname);
+ free (dlib_ptr->name);
+ dlibs = dlibs->next;
+ free (dlib_ptr);
+ }
+
+ if (opt_chroot && dir_name)
+ free (dir_name);
+}
+
+/* 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);
+ }
+}
+
+
+static void parse_conf_include (const char *config_file, unsigned int lineno,
+ bool do_chroot, const char *pattern);
+
+/* Parse configuration file. */
+static void
+parse_conf (const char *filename, bool do_chroot)
+{
+ FILE *file = NULL;
+ char *line = NULL;
+ const char *canon;
+ size_t len = 0;
+ unsigned int lineno;
+
+ if (do_chroot && opt_chroot)
+ {
+ canon = chroot_canon (opt_chroot, filename);
+ if (canon)
+ file = fopen (canon, "r");
+ else
+ canon = filename;
+ }
+ else
+ {
+ canon = filename;
+ file = fopen (filename, "r");
+ }
+
+ if (file == NULL)
+ {
+ error (0, errno, _("Can't open configuration file %s"), canon);
+ if (canon != filename)
+ free ((char *) canon);
+ return;
+ }
+
+ /* No threads use this stream. */
+ __fsetlocking (file, FSETLOCKING_BYCALLER);
+
+ if (canon != filename)
+ free ((char *) canon);
+
+ lineno = 0;
+ do
+ {
+ ssize_t n = getline (&line, &len, file);
+ if (n < 0)
+ break;
+
+ ++lineno;
+ 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';
+
+ /* Remove leading whitespace. NUL is no whitespace character. */
+ char *cp = line;
+ while (isspace (*cp))
+ ++cp;
+
+ /* If the line is blank it is ignored. */
+ if (cp[0] == '\0')
+ continue;
+
+ if (!strncmp (cp, "include", 7) && isblank (cp[7]))
+ {
+ char *dir;
+ cp += 8;
+ while ((dir = strsep (&cp, " \t")) != NULL)
+ 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);
+ }
+ while (!feof_unlocked (file));
+
+ /* Free buffer and close file. */
+ free (line);
+ fclose (file);
+}
+
+/* Handle one word in an `include' line, a glob pattern of additional
+ config files to read. */
+static void
+parse_conf_include (const char *config_file, unsigned int lineno,
+ bool do_chroot, const char *pattern)
+{
+ if (opt_chroot && pattern[0] != '/')
+ error (EXIT_FAILURE, 0,
+ _("need absolute file name for configuration file when using -r"));
+
+ char *copy = NULL;
+ if (pattern[0] != '/' && strchr (config_file, '/') != NULL)
+ {
+ if (asprintf (&copy, "%s/%s", dirname (strdupa (config_file)),
+ pattern) < 0)
+ error (EXIT_FAILURE, 0, _("memory exhausted"));
+ pattern = copy;
+ }
+
+ glob64_t gl;
+ int result;
+ if (do_chroot && opt_chroot)
+ {
+ char *canon = chroot_canon (opt_chroot, pattern);
+ result = glob64 (canon ?: pattern, 0, NULL, &gl);
+ free (canon);
+ }
+ else
+ result = glob64 (pattern, 0, NULL, &gl);
+
+ switch (result)
+ {
+ case 0:
+ for (size_t i = 0; i < gl.gl_pathc; ++i)
+ parse_conf (gl.gl_pathv[i], false);
+ globfree64 (&gl);
+ break;
+
+ case GLOB_NOMATCH:
+ break;
+
+ case GLOB_NOSPACE:
+ errno = ENOMEM;
+ case GLOB_ABORTED:
+ if (opt_verbose)
+ error (0, errno, _("%s:%u: cannot read directory %s"),
+ config_file, lineno, pattern);
+ break;
+
+ default:
+ abort ();
+ break;
+ }
+
+ if (copy)
+ free (copy);
+}
+
+/* Honour LD_HWCAP_MASK. */
+static void
+set_hwcap (void)
+{
+ char *mask = getenv ("LD_HWCAP_MASK");
+
+ if (mask)
+ hwcap_mask = strtoul (mask, NULL, 0);
+}
+
+
+int
+main (int argc, char **argv)
+{
+ int remaining;
+
+ /* Parse and process arguments. */
+ argp_parse (&argp, argc, argv, 0, &remaining, NULL);
+
+ /* Remaining arguments are additional directories if opt_manual_link
+ is not set. */
+ if (remaining != argc && !opt_manual_link)
+ {
+ int i;
+ for (i = remaining; i < argc; ++i)
+ if (opt_build_cache && argv[i][0] != '/')
+ error (EXIT_FAILURE, 0,
+ _("relative path `%s' used to build cache"),
+ argv[i]);
+ else
+ add_dir (argv[i]);
+ }
+
+#ifdef USE_TLS
+ hwcap_extra[63 - _DL_FIRST_EXTRA] = "tls";
+#endif
+
+ set_hwcap ();
+
+ if (opt_chroot)
+ {
+ /* Normalize the path a bit, we might need it for printing later. */
+ char *endp = rawmemchr (opt_chroot, '\0');
+ while (endp > opt_chroot && endp[-1] == '/')
+ --endp;
+ *endp = '\0';
+ if (endp == opt_chroot)
+ opt_chroot = NULL;
+
+ if (opt_chroot)
+ {
+ /* It is faster to use chroot if we can. */
+ if (!chroot (opt_chroot))
+ {
+ if (chdir ("/"))
+ error (EXIT_FAILURE, errno, _("Can't chdir to /"));
+ opt_chroot = NULL;
+ }
+ }
+ }
+
+ if (cache_file == NULL)
+ {
+ cache_file = alloca (strlen (LD_SO_CACHE) + 1);
+ strcpy (cache_file, LD_SO_CACHE);
+ }
+
+ if (config_file == NULL)
+ config_file = LD_SO_CONF;
+
+ if (opt_print_cache)
+ {
+ if (opt_chroot)
+ {
+ char *p = chroot_canon (opt_chroot, cache_file);
+ if (p == NULL)
+ error (EXIT_FAILURE, errno, _("Can't open cache file %s\n"),
+ cache_file);
+ cache_file = p;
+ }
+ print_cache (cache_file);
+ if (opt_chroot)
+ free (cache_file);
+ exit (0);
+ }
+
+ if (opt_chroot)
+ {
+ /* Canonicalize the directory name of cache_file, not cache_file,
+ 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 : "/");
+ }
+
+ if (p)
+ ++p;
+ else
+ p = cache_file;
+
+ cache_file = alloca (strlen (canon) + strlen (p) + 2);
+ sprintf (cache_file, "%s/%s", canon, p);
+ free (canon);
+ }
+
+ if (opt_manual_link)
+ {
+ /* Link all given libraries manually. */
+ int i;
+
+ for (i = remaining; i < argc; ++i)
+ manual_link (argv[i]);
+
+ exit (0);
+ }
+
+
+ if (opt_build_cache)
+ init_cache ();
+
+ if (!opt_only_cline)
+ {
+ parse_conf (config_file, true);
+
+ /* Always add the standard search paths. */
+ add_system_dir (SLIBDIR);
+ if (strcmp (SLIBDIR, LIBDIR))
+ add_system_dir (LIBDIR);
+ }
+
+ search_dirs ();
+
+ if (opt_build_cache)
+ save_cache (cache_file);
+
+ return 0;
+}