summaryrefslogtreecommitdiff
path: root/elf
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>1999-12-04 08:00:00 +0000
committerUlrich Drepper <drepper@redhat.com>1999-12-04 08:00:00 +0000
commit591e1ffbf8db5e059b9bcf3bde6d7758f018a46d (patch)
treeb5e9db8cae0b20da5dfceb7c8e4bd91f84e3ac74 /elf
parent589328764d60283e16442c9c9dcf818346356776 (diff)
downloadglibc-591e1ffbf8db5e059b9bcf3bde6d7758f018a46d.tar.gz
Update.
1999-11-30 Andreas Jaeger <aj@suse.de> Add ldconfig: * elf/Makefile (extra-objs): Added ldconfig-modules. (ldconfig-modules): New. Added vpath for xstrdup and xmalloc. Check for use-ldconfig instead of has-ldconfig. ($(objpfx)ldconfig): New rule with dependencies. (distribute): Add new files. * sysdeps/unix/sysv/linux/sparc/readelflib.c: New file, developed together with Jakub Jelinek <jakub@redhat.com>. * sysdeps/generic/readelflib.c: Likewise. * elf/cache.c: New file. * elf/ldconfig.c: New file. * elf/ldconfig.h: New file. * elf/readlib.c: New file. * Makefile (install): Remove flag -d in ldconfig call. * configure.in: Rename has_ldconfig to use_ldconfig, set it to no by default. * config.make.in (has-ldconfig): Renamed to use-ldconfig, changed comment.
Diffstat (limited to 'elf')
-rw-r--r--elf/Makefile14
-rw-r--r--elf/cache.c363
-rw-r--r--elf/ldconfig.c647
-rw-r--r--elf/ldconfig.h63
-rw-r--r--elf/readlib.c160
5 files changed, 1245 insertions, 2 deletions
diff --git a/elf/Makefile b/elf/Makefile
index ff5f44a819..422d278ce2 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -41,7 +41,8 @@ distribute := $(rtld-routines:=.c) dynamic-link.h do-rel.h dl-machine.h \
dl-librecon.h interp.c sln.c dl-dst.h hp-timing.h \
do-lookup.h sprof.c gen-trusted-dirs.awk \
testobj1.c testobj2.c testobj3.c testobj4.c testobj5.c \
- testobj6.c testobj1_1.c failobj.c
+ testobj6.c testobj1_1.c failobj.c \
+ ldconfig.h ldconfig.c cache.c readlib.c readelflib.c
include ../Makeconfig
@@ -64,10 +65,17 @@ install-bin += sprof
others-static = sln
install-rootsbin = sln
-ifeq (yes,$(has-ldconfig))
+ifeq (yes,$(use-ldconfig))
others-static += ldconfig
others += ldconfig
install-rootsbin += ldconfig
+
+ldconfig-modules := cache readlib xmalloc xstrdup
+extra-objs += $(ldconfig-modules:=.o)
+
+# To find xmalloc.c and xstrdup.c
+vpath %.c ../locale/programs
+
endif
ifeq (yes,$(build-shared))
@@ -191,6 +199,8 @@ $(objpfx)ldd: ldd.bash.in $(common-objpfx)soversions.mk \
$(objpfx)sprof: $(libdl)
+$(objpfx)ldconfig: $(ldconfig-modules:%=$(objpfx)%.o)
+
test-modules = $(addprefix $(objpfx),$(addsuffix .so,$(modules-names)))
generated += $(addsuffix .so,$(modules-names))
diff --git a/elf/cache.c b/elf/cache.c
new file mode 100644
index 0000000000..d13e4dbbd7
--- /dev/null
+++ b/elf/cache.c
@@ -0,0 +1,363 @@
+/* Copyright (C) 1999 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Andreas Jaeger <aj@suse.de>, 1999.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#define _GNU_SOURCE 1
+
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <dirent.h>
+#include <libintl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "ldconfig.h"
+
+#define CACHEMAGIC "ld.so-1.7.0"
+
+struct cache_entry
+{
+ char *lib;
+ char *path;
+ int flags;
+ struct cache_entry *next;
+};
+
+struct file_entry
+{
+ int flags; /* This is 1 for an ELF library. */
+ unsigned int key, value; /* String table indices. */
+};
+
+
+struct cache_file
+{
+ char magic[sizeof CACHEMAGIC - 1];
+ unsigned int nlibs;
+ struct file_entry libs[0];
+};
+
+
+/* List of all cache entries. */
+static struct cache_entry *entries;
+
+static const char *flag_descr[] =
+{ "libc4", "ELF", "libc5", "libc6"};
+
+
+/* Print a single entry. */
+static void
+print_entry (const char *lib, int flag, const char *key)
+{
+ printf ("\t%s (", lib);
+ switch (flag)
+ {
+ case FLAG_LIBC4:
+ case FLAG_ELF:
+ case FLAG_ELF_LIBC5:
+ case FLAG_ELF_LIBC6:
+ fputs (flag_descr [flag & FLAG_TYPE_MASK], stdout);
+ break;
+ default:
+ fputs ("unknown", stdout);
+ break;
+ }
+ switch (flag & FLAG_REQUIRED_MASK)
+ {
+#ifdef __sparc__
+ case FLAG_SPARC_LIB64:
+ fputs (",64bit", stdout);
+#endif
+ case 0:
+ break;
+ default:
+ fprintf (stdout, ",%d", flag & FLAG_REQUIRED_MASK);
+ break;
+ }
+ printf (") => %s\n", key);
+}
+
+
+/* Print the whole cache file. */
+void
+print_cache (const char *cache_name)
+{
+ size_t cache_size;
+ struct stat st;
+ int fd;
+ unsigned int i;
+ struct cache_file *cache;
+ const char *cache_data;
+
+ fd = open (cache_name, O_RDONLY);
+ if (fd < 0)
+ error (EXIT_FAILURE, errno, _("Can't open cache file %s\n"), cache_name);
+
+ if (fstat (fd, &st) < 0
+ /* No need to map the file if it is empty. */
+ || st.st_size == 0)
+ {
+ close (fd);
+ return;
+ }
+
+ cache = mmap (0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (cache == MAP_FAILED)
+ error (EXIT_FAILURE, errno, _("mmap of cache file failed.\n"));
+ cache_size = st.st_size;
+
+ if (cache_size < sizeof (struct cache_file)
+ || memcmp (cache->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1))
+ return;
+ /* This is where the strings start. */
+ cache_data = (const char *) &cache->libs[cache->nlibs];
+
+ printf (_("%d libs found in cache `%s'\n"), cache->nlibs, cache_name);
+
+ /* Print everything. */
+ for (i = 0; i < cache->nlibs; i++)
+ print_entry (cache_data + cache->libs[i].key,
+ cache->libs[i].flags,
+ cache_data + cache->libs[i].value);
+
+ /* Cleanup. */
+ munmap (cache, cache_size);
+ close (fd);
+}
+
+/* Initialize cache data structures. */
+void
+init_cache (void)
+{
+ entries = NULL;
+}
+
+
+/* Helper function which must match the one in the dynamic linker, so that
+ we rely on the same sort order. */
+int
+cache_libcmp (const char *p1, const char *p2)
+{
+ while (*p1 != '\0')
+ {
+ if (*p1 >= '0' && *p1 <= '9')
+ {
+ if (*p2 >= '0' && *p2 <= '9')
+ {
+ /* Must compare this numerically. */
+ int val1;
+ int val2;
+
+ val1 = *p1++ - '0';
+ val2 = *p2++ - '0';
+ while (*p1 >= '0' && *p1 <= '9')
+ val1 = val1 * 10 + *p1++ - '0';
+ while (*p2 >= '0' && *p2 <= '9')
+ val2 = val2 * 10 + *p2++ - '0';
+ if (val1 != val2)
+ return val1 - val2;
+ }
+ else
+ return 1;
+ }
+ else if (*p2 >= '0' && *p2 <= '9')
+ return -1;
+ else if (*p1 != *p2)
+ return *p1 - *p2;
+ else
+ {
+ ++p1;
+ ++p2;
+ }
+ }
+ return *p1 - *p2;
+}
+
+static
+int compare (const struct cache_entry *e1, const struct cache_entry *e2)
+{
+ int res;
+
+ /* We need to swap entries here to get the correct sort order. */
+ res = cache_libcmp (e2->lib, e1->lib);
+ if (res == 0)
+ {
+ if (e1->flags < e2->flags)
+ return 1;
+ else if (e1->flags > e2->flags)
+ return -1;
+ }
+ return res;
+}
+
+
+/* Save the contents of the cache. */
+void
+save_cache (const char *cache_name)
+{
+ struct cache_entry *entry;
+ int i, fd;
+ size_t total_strlen, len;
+ char *strings, *str, *temp_name;
+ struct cache_file *file_entries;
+ size_t file_entries_size;
+ unsigned int str_offset;
+ /* Number of cache entries. */
+ int cache_entry_count = 0;
+
+ /* The cache entries are sorted already, save them in this order. */
+
+ /* Count the length of all strings. */
+ total_strlen = 0;
+ for (entry = entries; entry != NULL; entry = entry->next)
+ {
+ /* Account the final NULs. */
+ total_strlen += strlen (entry->lib) + strlen (entry->path) + 2;
+ ++cache_entry_count;
+ }
+
+ /* Create the on disk cache structure. */
+ /* First an array for all strings. */
+ strings = (char *)xmalloc (total_strlen + 1);
+
+ /* And the list of all entries. */
+ file_entries_size = sizeof (struct cache_file)
+ + cache_entry_count * sizeof (struct file_entry);
+ file_entries = (struct cache_file *) xmalloc (file_entries_size);
+
+ /* Fill in the header. */
+ memset (file_entries, 0, sizeof (struct cache_file));
+ memcpy (file_entries->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1);
+
+ file_entries->nlibs = cache_entry_count;
+
+ str_offset = 0;
+ str = strings;
+ for (i = 0, entry = entries; entry != NULL; entry = entry->next, ++i)
+ {
+ file_entries->libs[i].flags = entry->flags;
+ /* First the library. */
+ /* XXX: Actually we can optimize here and remove duplicates. */
+ file_entries->libs[i].key = str_offset;
+ len = strlen (entry->lib);
+ str = stpcpy (str, entry->lib);
+ /* Account the final NUL. */
+ ++str;
+ str_offset += len + 1;
+ /* Then the path. */
+ file_entries->libs[i].value = str_offset;
+ len = strlen (entry->path);
+ str = stpcpy (str, entry->path);
+ /* Account the final NUL. */
+ ++str;
+ str_offset += len + 1;
+ }
+ assert (str_offset == total_strlen);
+
+ /* Write out the cache. */
+
+ /* Write cache first to a temporary file and rename it later. */
+ temp_name = xmalloc (strlen (cache_name) + 2);
+ sprintf (temp_name, "%s~", cache_name);
+ /* First remove an old copy if it exists. */
+ if (unlink (temp_name) && errno != ENOENT)
+ error (EXIT_FAILURE, errno, _("Can't remove old temporary cache file %s"),
+ temp_name);
+
+ /* Create file. */
+ fd = open (temp_name, O_CREAT|O_WRONLY|O_TRUNC|O_NOFOLLOW, 0644);
+ if (fd < 0)
+ error (EXIT_FAILURE, errno, _("Can't create temporary cache file %s"),
+ temp_name);
+
+ /* Write contents. */
+ if (write (fd, file_entries, file_entries_size) != (ssize_t)file_entries_size)
+ error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
+
+ if (write (fd, strings, total_strlen) != (ssize_t)total_strlen)
+ error (EXIT_FAILURE, errno, _("Writing of cache data failed."));
+
+ close (fd);
+
+ /* Move temporary to its final location. */
+ if (rename (temp_name, cache_name))
+ error (EXIT_FAILURE, errno, _("Renaming of %s to %s failed"), temp_name,
+ cache_name);
+
+ /* Free all allocated memory. */
+ free (file_entries);
+ free (strings);
+
+ while (entries)
+ {
+ entry = entries;
+ free (entry->path);
+ free (entry->lib);
+ entries = entries->next;
+ free (entry);
+ }
+}
+
+/* Add one library to the cache. */
+void
+add_to_cache (const char *path, const char *lib, int flags)
+{
+ struct cache_entry *new_entry, *ptr, *prev;
+ char *full_path;
+ int len;
+
+ new_entry = (struct cache_entry *) xmalloc (sizeof (struct cache_entry));
+
+ len = strlen (lib) + strlen (path) + 2;
+
+ full_path = (char *) xmalloc (len);
+ snprintf (full_path, len, "%s/%s", path, lib);
+
+ new_entry->lib = xstrdup (lib);
+ new_entry->path = full_path;
+ new_entry->flags = flags;
+
+ /* Keep the list sorted - search for right place to insert. */
+ ptr = entries;
+ prev = entries;
+ while (ptr != NULL)
+ {
+ if (compare (ptr, new_entry) > 0)
+ break;
+ prev = ptr;
+ ptr = ptr->next;
+ }
+ /* Is this the first entry? */
+ if (ptr == entries)
+ {
+ new_entry->next = entries;
+ entries = new_entry;
+ }
+ else
+ {
+ new_entry->next = prev->next;
+ prev->next = new_entry;
+ }
+}
diff --git a/elf/ldconfig.c b/elf/ldconfig.c
new file mode 100644
index 0000000000..6c2bcb2d4f
--- /dev/null
+++ b/elf/ldconfig.c
@@ -0,0 +1,647 @@
+/* Copyright (C) 1999 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Andreas Jaeger <aj@suse.de>, 1999.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <argp.h>
+#include <dirent.h>
+#include <error.h>
+#include <errno.h>
+#include <libintl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "ldconfig.h"
+
+#ifndef LD_SO_CACHE
+# define LD_SO_CACHE "/etc/ld.so.cache"
+#endif
+
+#ifndef LD_SO_CONF
+# define LD_SO_CONF "/etc/ld.so.conf"
+#endif
+
+/* Get libc version number. */
+#include <version.h>
+
+#define PACKAGE _libc_intl_domainname
+
+struct lib_entry
+ {
+ int flags;
+ char *lib;
+ char *path;
+ };
+
+static const struct
+{
+ const char *name;
+ int flag;
+} lib_types [] =
+{
+ {"libc4", FLAG_LIBC4},
+ {"libc5", FLAG_ELF_LIBC5},
+ {"libc6", FLAG_ELF_LIBC6},
+ {"glibc2", FLAG_ELF_LIBC6}
+};
+
+
+/* List of directories to handle. */
+struct dir_entry
+{
+ char *path;
+ int flag;
+ struct dir_entry *next;
+};
+
+/* The list is unsorted, contains no duplicates. Entries are added at
+ the end. */
+static struct dir_entry *dir_entries;
+
+/* Flags for different options. */
+/* Print Cache. */
+static int opt_print_cache = 0;
+
+/* Be verbose. */
+int opt_verbose = 0;
+
+/* Build cache. */
+static int opt_build_cache = 1;
+
+/* Generate links. */
+static int opt_link = 1;
+
+/* Only process directories specified on the command line. */
+static int opt_only_cline = 0;
+
+/* Path to root for chroot. */
+static char *opt_chroot;
+
+/* Cache file to use. */
+static const char *cache_file;
+
+/* Configuration file. */
+static const char *config_file;
+
+/* Name and version of program. */
+static void print_version (FILE *stream, struct argp_state *state);
+void (*argp_program_version_hook) (FILE *, struct argp_state *)
+ = print_version;
+
+/* Definitions of arguments for argp functions. */
+static const struct argp_option options[] =
+{
+ { "print-cache", 'p', NULL, 0, N_("Print cache"), 0},
+ { "verbose", 'v', NULL, 0, N_("Generate verbose messages"), 0},
+ { NULL, 'N', NULL, 0, N_("Don't build cache"), 0},
+ { NULL, 'X', NULL, 0, N_("Don't generate links"), 0},
+ { NULL, 'r', "ROOT", 0, N_("Change to and use ROOT as root directory"), 0},
+ { NULL, 'C', "CACHE", 0, N_("Use CACHE as cache file"), 0},
+ { NULL, 'f', "CONF", 0, N_("Use CONF as configuration file"), 0},
+ { NULL, 'n', NULL, 0, N_("Only process directories specified on the command line. Don't build cache."), 0},
+ { NULL, 0, NULL, 0, NULL, 0 }
+};
+
+/* Short description of program. */
+static const char doc[] = N_("Configure Dynamic Linker Run Time Bindings.");
+
+/* Prototype for option handler. */
+static error_t parse_opt (int key, char *arg, struct argp_state *state);
+
+/* Data structure to communicate with argp functions. */
+static struct argp argp =
+{
+ options, parse_opt, NULL, doc, NULL, NULL, NULL
+};
+
+
+
+/* Handle program arguments. */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case 'C':
+ cache_file = arg;
+ break;
+ case 'f':
+ config_file = arg;
+ break;
+ case 'N':
+ opt_build_cache = 0;
+ break;
+ case 'n':
+ opt_build_cache = 0;
+ opt_only_cline = 1;
+ break;
+ case 'p':
+ opt_print_cache = 1;
+ break;
+ case 'r':
+ opt_chroot = arg;
+ break;
+ case 'v':
+ opt_verbose = 1;
+ break;
+ case 'X':
+ opt_link = 0;
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+/* Print the version information. */
+static void
+print_version (FILE *stream, struct argp_state *state)
+{
+ fprintf (stream, "ldconfig (GNU %s) %s\n", PACKAGE, VERSION);
+ fprintf (stream, gettext ("\
+Copyright (C) %s Free Software Foundation, Inc.\n\
+This is free software; see the source for copying conditions. There is NO\n\
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
+"), "1999");
+ fprintf (stream, gettext ("Written by %s.\n"),
+ "Andreas Jaeger");
+}
+
+/* Add one directory to the list of directories to process. */
+static void
+add_dir (const char *line)
+{
+ char *equal_sign;
+ struct dir_entry *entry, *ptr, *prev;
+ unsigned int i;
+
+ entry = xmalloc (sizeof (struct dir_entry));
+ entry->next = NULL;
+
+ /* Search for an '=' sign. */
+ entry->path = xstrdup (line);
+ equal_sign = strchr (entry->path, '=');
+ if (equal_sign)
+ {
+ *equal_sign = '\0';
+ ++equal_sign;
+ entry->flag = FLAG_ANY;
+ for (i = 0; i < sizeof (lib_types) / sizeof (lib_types [0]); ++i)
+ if (strcmp (equal_sign, lib_types[i].name) == 0)
+ {
+ entry->flag = lib_types[i].flag;
+ break;
+ }
+ if (entry->flag == FLAG_ANY)
+ error (0, 0, _("%s is not a known library type"), equal_sign);
+ }
+ else
+ {
+ entry->flag = FLAG_ANY;
+ }
+
+ /* Canonify path: for now only remove trailing slashes. */
+ i = strlen (entry->path) - 1;
+ while (entry->path[i] == '/' && i > 0)
+ {
+ entry->path [i] = '\0';
+ --i;
+ }
+
+ ptr = dir_entries;
+ prev = ptr;
+ while (ptr != NULL)
+ {
+ /* Check for duplicates. */
+ if (strcmp (ptr->path, entry->path) == 0)
+ {
+ if (opt_verbose)
+ error (0, 0, _("Path `%s' given more than once"), entry->path);
+ /* Use the newer information. */
+ ptr->flag = entry->flag;
+ free (entry);
+ break;
+ }
+ prev = ptr;
+ ptr = ptr->next;
+ }
+ /* Is this the first entry? */
+ if (ptr == NULL && dir_entries == NULL)
+ dir_entries = entry;
+ else if (ptr == NULL)
+ prev->next = entry;
+}
+
+
+/* Create a symbolic link from soname to libname in directory path. */
+static void
+create_links (const char *path, const char *libname, const char *soname)
+{
+ char full_libname [PATH_MAX], full_soname [PATH_MAX];
+ struct stat stat_lib, stat_so, lstat_so;
+ int do_link = 1;
+ int do_remove = 1;
+ /* XXX: The logics in this function should be simplified. */
+
+ /* Get complete path. */
+ snprintf (full_libname, sizeof full_libname, "%s/%s", path, libname);
+ snprintf (full_soname, sizeof full_soname, "%s/%s", path, soname);
+
+ /* Does soname already exist and point to the right library? */
+ if (stat (full_soname, &stat_so) == 0)
+ {
+ if (stat (full_libname, &stat_lib))
+ {
+ error (0, 0, _("Can't stat %s\n"), full_libname);
+ return;
+ }
+ if (stat_lib.st_dev == stat_so.st_dev
+ && stat_lib.st_ino == stat_so.st_ino)
+ /* Link is already correct. */
+ do_link = 0;
+ else if (lstat (full_soname, &lstat_so) == 0
+ && !S_ISLNK (lstat_so.st_mode))
+ {
+ error (0, 0, _("%s is not a symbolic link\n"), full_soname);
+ do_link = 0;
+ do_remove = 0;
+ }
+ }
+ else if (lstat (full_soname, &lstat_so) != 0
+ || !S_ISLNK (lstat_so.st_mode))
+ /* Unless it is a stale symlink, there is no need to remove. */
+ do_remove = 0;
+
+ if (opt_verbose)
+ printf ("\t%s -> %s", soname, libname);
+
+ if (do_link && opt_link)
+ {
+ /* Remove old link. */
+ if (do_remove)
+ if (unlink (full_soname))
+ {
+ error (0, 0, _("Can't unlink %s"), full_soname);
+ do_link = 0;
+ }
+ /* Create symbolic link. */
+ if (do_link && symlink (libname, full_soname))
+ {
+ error (0, 0, _("Can't link %s to %s"), full_soname, libname);
+ do_link = 0;
+ }
+ if (opt_verbose)
+ {
+ if (do_link)
+ fputs (_(" (changed)\n"), stdout);
+ else
+ fputs (_(" (SKIPPED)\n"), stdout);
+ }
+ }
+ else if (opt_verbose)
+ fputs ("\n", stdout);
+}
+
+/* Read a whole directory and search for libraries.
+ The purpose is two-fold:
+ - search for libraries which will be added to the cache
+ - create symbolic links to the soname for each library
+
+ This has to be done separatly for each directory.
+
+ To keep track of which libraries to add to the cache and which
+ links to create, we save a list of all libraries.
+
+ The algorithm is basically:
+ for all libraries in the directory do
+ get soname of library
+ if soname is already in list
+ if new library is newer, replace entry
+ otherwise ignore this library
+ otherwise add library to list
+
+ For example, if the two libraries libxy.so.1.1 and libxy.so.1.2
+ exist and both have the same soname, e.g. libxy.so, a symbolic link
+ is created from libxy.so.1.2 (the newer one) to libxy.so.
+ libxy.so.1.2 and libxy.so are added to the cache - but not
+ libxy.so.1.1. */
+
+/* Information for one library. */
+struct dlib_entry
+{
+ char *name;
+ char *soname;
+ int flag;
+ int is_link;
+ struct dlib_entry *next;
+};
+
+
+static void
+search_dir (const struct dir_entry *entry)
+{
+ DIR *dir;
+ struct dirent *direntry;
+ char buf [PATH_MAX];
+ char *soname;
+ struct dlib_entry *dlibs;
+ struct dlib_entry *dlib_ptr;
+ int nchars;
+ struct stat stat_buf;
+ int is_link;
+
+ dlibs = NULL;
+
+ if (opt_verbose)
+ printf ("%s:\n", entry->path);
+
+ dir = opendir (entry->path);
+ if (dir == NULL)
+ {
+ if (opt_verbose)
+ error (0, errno, _("Can't open directory %s"), entry->path);
+ return;
+ }
+
+
+ while ((direntry = readdir (dir)) != NULL)
+ {
+ int flag;
+#ifdef _DIRENT_HAVE_D_TYPE
+ /* We only look at links and regular files. */
+ if (direntry->d_type != DT_UNKNOWN
+ && direntry->d_type != DT_LNK
+ && direntry->d_type != DT_REG)
+ continue;
+#endif /* _DIRENT_HAVE_D_TYPE */
+
+ /* Does this file look like a shared library? The dynamic
+ linker is also considered as shared library. */
+ if ((strncmp (direntry->d_name, "lib", 3) != 0
+ && strncmp (direntry->d_name, "ld-", 3) != 0)
+ || strstr (direntry->d_name, ".so") == NULL)
+ continue;
+ nchars = snprintf (buf, sizeof (buf), "%s/%s", entry->path,
+ direntry->d_name);
+ /* Check for overflow. */
+ if (nchars >= (int) sizeof (buf))
+ {
+ error (0, 0, _("buffer for snprintf too small for %s/%s--file is ignored\n"),
+ entry->path, direntry->d_name);
+ continue;
+ }
+ if (lstat (buf, &stat_buf))
+ {
+ error (0, errno, _("Can't lstat %s"), buf);
+ continue;
+ }
+ else if (!S_ISREG (stat_buf.st_mode) && !S_ISLNK (stat_buf.st_mode))
+ continue;
+
+ is_link = S_ISLNK (stat_buf.st_mode);
+
+ if (process_file (buf, direntry->d_name, &flag, &soname, is_link))
+ continue;
+
+ /* Links will just point to itself. */
+ if (is_link)
+ {
+ free (soname);
+ soname = xstrdup (direntry->d_name);
+ }
+
+ if (flag == FLAG_ELF
+ && (entry->flag == FLAG_ELF_LIBC5
+ || entry->flag == FLAG_ELF_LIBC6))
+ flag = entry->flag;
+ /* Some sanity checks to print warnings. */
+ if (opt_verbose)
+ {
+ if (flag == FLAG_ELF_LIBC5 && entry->flag != FLAG_ELF_LIBC5
+ && entry->flag != FLAG_ANY)
+ error (0, 0, _("libc5 library %s in wrong directory"), buf);
+ if (flag == FLAG_ELF_LIBC6 && entry->flag != FLAG_ELF_LIBC6
+ && entry->flag != FLAG_ANY)
+ error (0, 0, _("libc6 library %s in wrong directory"), buf);
+ if (flag == FLAG_LIBC4 && entry->flag != FLAG_LIBC4
+ && entry->flag != FLAG_ANY)
+ error (0, 0, _("libc4 library %s in wrong directory"), buf);
+ }
+
+ /* Add library to list. */
+ for (dlib_ptr = dlibs; dlib_ptr != NULL; dlib_ptr = dlib_ptr->next)
+ {
+ /* Is soname already in list? */
+ if (strcmp (dlib_ptr->soname, soname) == 0)
+ {
+ /* Prefer a file to a link, otherwise check which one
+ is newer. */
+ if ((!is_link && dlib_ptr->is_link)
+ || (is_link == dlib_ptr->is_link
+ && cache_libcmp (dlib_ptr->name, direntry->d_name) < 0))
+ {
+ /* It's newer - add it. */
+ /* Flag should be the same - sanity check. */
+ if (dlib_ptr->flag != flag)
+ {
+ if (dlib_ptr->flag == FLAG_ELF
+ && (flag == FLAG_ELF_LIBC5 || flag == FLAG_ELF_LIBC6))
+ dlib_ptr->flag = flag;
+ else if ((dlib_ptr->flag == FLAG_ELF_LIBC5
+ || dlib_ptr->flag == FLAG_ELF_LIBC6)
+ && flag == FLAG_ELF)
+ dlib_ptr->flag = flag;
+ else
+ error (0, 0, _("libraries %s and %s in directory %s have same soname but different type."),
+ dlib_ptr->name, direntry->d_name, entry->path);
+ }
+ free (dlib_ptr->name);
+ dlib_ptr->name = xstrdup (direntry->d_name);
+ dlib_ptr->is_link = is_link;
+ }
+ /* Don't add this library, abort loop. */
+ /* Also free soname, since it's dynamically allocated. */
+ free (soname);
+ break;
+ }
+ }
+ /* Add the library if it's not already in. */
+ if (dlib_ptr == NULL)
+ {
+ dlib_ptr = (struct dlib_entry *)xmalloc (sizeof (struct dlib_entry));
+ dlib_ptr->name = xstrdup (direntry->d_name);
+ dlib_ptr->flag = flag;
+ dlib_ptr->soname = soname;
+ dlib_ptr->is_link = is_link;
+ /* Add at head of list. */
+ dlib_ptr->next = dlibs;
+ dlibs = dlib_ptr;
+ }
+ }
+
+ closedir (dir);
+
+ /* Now dlibs contains a list of all libs - add those to the cache
+ and created all symbolic links. */
+ for (dlib_ptr = dlibs; dlib_ptr != NULL; dlib_ptr = dlib_ptr->next)
+ {
+ /* Don't create links to links. */
+ if (dlib_ptr->is_link == 0)
+ create_links (entry->path, dlib_ptr->name, dlib_ptr->soname);
+ if (opt_build_cache)
+ add_to_cache (entry->path, dlib_ptr->soname, dlib_ptr->flag);
+ }
+
+ /* Free all resources. */
+ while (dlibs)
+ {
+ dlib_ptr = dlibs;
+ free (dlib_ptr->soname);
+ free (dlib_ptr->name);
+ dlibs = dlibs->next;
+ free (dlib_ptr);
+ }
+}
+
+/* Search through all libraries. */
+static void
+search_dirs (void)
+{
+ struct dir_entry *entry;
+
+ for (entry = dir_entries; entry != NULL; entry = entry->next)
+ search_dir (entry);
+
+ /* Free all allocated memory. */
+ while (dir_entries)
+ {
+ entry = dir_entries;
+ dir_entries = dir_entries->next;
+ free (entry->path);
+ free (entry);
+ }
+}
+
+
+/* Parse configuration file. */
+static void
+parse_conf (const char *filename)
+{
+ FILE *file;
+ char *line = NULL;
+ size_t len = 0;
+
+ file = fopen (filename, "r");
+
+ if (file == NULL)
+ {
+ error (0, errno, _("Can't open configuration file %s"), filename);
+ return;
+ }
+
+ do
+ {
+ ssize_t n = getline (&line, &len, file);
+ if (n < 0)
+ break;
+
+ if (line[n - 1] == '\n')
+ line[n - 1] = '\0';
+
+ /* Because the file format does not know any form of quoting we
+ can search forward for the next '#' character and if found
+ make it terminating the line. */
+ *strchrnul (line, '#') = '\0';
+
+ /* If the line is blank it is ignored. */
+ if (line[0] == '\0')
+ continue;
+
+ add_dir (line);
+ } while (!feof (file));
+
+ /* Free buffer and close file. */
+ free (line);
+ fclose (file);
+}
+
+
+int
+main (int argc, char **argv)
+{
+ int remaining;
+
+ /* Parse and process arguments. */
+ argp_parse (&argp, argc, argv, 0, &remaining, NULL);
+
+ /* Remaining arguments are additional libraries. */
+ if (remaining != argc)
+ {
+ int i;
+ for (i = remaining; i < argc; ++i)
+ add_dir (argv [i]);
+ }
+
+ if (cache_file == NULL)
+ cache_file = LD_SO_CACHE;
+
+ if (config_file == NULL)
+ config_file = LD_SO_CONF;
+
+ /* Chroot first. */
+ if (opt_chroot)
+ {
+ if (chroot (opt_chroot))
+ /* Report failure and exit program. */
+ error (EXIT_FAILURE, errno, _("Can't chroot to %s"), opt_chroot);
+ /* chroot doesn't change the working directory, let's play safe. */
+ if (chdir ("/"))
+ error (EXIT_FAILURE, errno, _("Can't chdir to /"));
+ }
+
+ if (opt_print_cache)
+ {
+ print_cache (cache_file);
+ exit (0);
+ }
+
+ if (opt_build_cache)
+ init_cache ();
+
+ if (!opt_only_cline)
+ {
+ /* Always add the standard search paths. */
+ add_dir ("/lib");
+ add_dir ("/usr/lib");
+
+ parse_conf (config_file);
+ }
+
+ search_dirs ();
+
+ if (opt_build_cache)
+ save_cache (cache_file);
+
+ return 0;
+}
diff --git a/elf/ldconfig.h b/elf/ldconfig.h
new file mode 100644
index 0000000000..3344876857
--- /dev/null
+++ b/elf/ldconfig.h
@@ -0,0 +1,63 @@
+/* Copyright (C) 1999 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Andreas Jaeger <aj@suse.de>, 1999.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#ifndef _LDCONFIG_H
+#define _LDCONFIG_H
+
+#define FLAG_ANY -1
+#define FLAG_TYPE_MASK 0x00ff
+#define FLAG_LIBC4 0x0000
+#define FLAG_ELF 0x0001
+#define FLAG_ELF_LIBC5 0x0002
+#define FLAG_ELF_LIBC6 0x0003
+#define FLAG_REQUIRED_MASK 0xff00
+#define FLAG_SPARC_LIB64 0x0100
+
+/* Declared in cache.c. */
+extern void print_cache (const char *cache_name);
+
+extern void init_cache (void);
+
+extern void save_cache (const char *cache_name);
+
+extern void add_to_cache (const char *path, const char *lib, int flags);
+
+extern int cache_libcmp (const char *p1, const char *p2);
+
+
+/* Declared in readlib.c. */
+extern int process_file (const char *file_name, const char *lib, int *flag,
+ char **soname, int is_link);
+
+/* Declared in readelflib.c. */
+extern int process_elf_file (const char *file_name, const char *lib, int *flag,
+ char **soname, void *file_contents);
+
+
+/* Declared in ldconfig.c. */
+extern int opt_verbose;
+
+/* Prototypes for a few program-wide used functions. */
+extern void *xmalloc (size_t __n);
+extern void *xcalloc (size_t __n, size_t __size);
+extern void *xrealloc (void *__p, size_t __n);
+extern char *xstrdup (const char *__str);
+
+#endif /* ! _LDCONFIG_H */
+
diff --git a/elf/readlib.c b/elf/readlib.c
new file mode 100644
index 0000000000..e6d0619091
--- /dev/null
+++ b/elf/readlib.c
@@ -0,0 +1,160 @@
+/* Copyright (C) 1999 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Andreas Jaeger <aj@suse.de>, 1999 and
+ Jakub Jelinek <jakub@redhat.com>, 1999.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/* The code in this file and in readelflib is a heavily simplified
+ version of the readelf program that's part of the current binutils
+ development version. Besides the simplification, it has also been
+ modified to read some other file formats. */
+
+
+#include <a.out.h>
+#include <elf.h>
+#include <error.h>
+#include <link.h>
+#include <libintl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include "ldconfig.h"
+
+#define Elf32_CLASS ELFCLASS32
+#define Elf64_CLASS ELFCLASS64
+
+struct known_names
+{
+ const char *soname;
+ int flag;
+};
+
+static struct known_names interpreters [] =
+{
+ {"/lib/ld-linux.so.2", FLAG_ELF_LIBC6},
+ {"/lib/ld-linux.so.1", FLAG_ELF_LIBC5}
+};
+
+static struct known_names known_libs [] =
+{
+ {"libc.so.6", FLAG_ELF_LIBC6},
+ {"libc.so.5", FLAG_ELF_LIBC5},
+ {"libm.so.6", FLAG_ELF_LIBC6},
+ {"libm.so.5", FLAG_ELF_LIBC5}
+};
+
+
+
+/* Returns 0 if everything is ok, != 0 in case of error. */
+int
+process_file (const char *file_name, const char *lib, int *flag, char **soname, int is_link)
+{
+ FILE *file;
+ struct stat statbuf;
+ void *file_contents;
+ int ret;
+
+ ElfW(Ehdr) *elf_header;
+ struct exec *aout_header;
+
+ ret = 0;
+ *flag = FLAG_ANY;
+ *soname = NULL;
+
+ file = fopen (file_name, "rb");
+ if (file == NULL)
+ {
+ /* No error for stale symlink. */
+ if (is_link && strstr (file_name, ".so.") != NULL)
+ return 1;
+ error (0, 0, _("Input file %s not found.\n"), file_name);
+ return 1;
+ }
+
+ if (fstat (fileno (file), &statbuf) < 0)
+ {
+ error (0, 0, _("Cannot fstat file %s.\n"), file_name);
+ return 1;
+ }
+
+ file_contents = mmap (0, statbuf.st_size, PROT_READ, MAP_SHARED, fileno (file), 0);
+ if (file_contents == MAP_FAILED)
+ {
+ error (0, 0, _("Cannot mmap file %s.\n"), file_name);
+ fclose (file);
+ return 1;
+ }
+
+ /* First check if this is an aout file. */
+ aout_header = (struct exec *) file_contents;
+ if (N_MAGIC (*aout_header) == ZMAGIC
+ || N_MAGIC (*aout_header) == QMAGIC)
+ {
+ /* Aout files don't have a soname, just return the the name
+ including the major number. */
+ char *copy, *major, *dot;
+ copy = xstrdup (lib);
+ major = strstr (copy, ".so.");
+ if (major)
+ {
+ dot = strstr (major + 4, ".");
+ if (dot)
+ *dot = '\0';
+ }
+ *soname = copy;
+ *flag = FLAG_LIBC4;
+ goto done;
+ }
+
+ elf_header = (ElfW(Ehdr) *) file_contents;
+ if (elf_header->e_ident [EI_MAG0] != ELFMAG0
+ || elf_header->e_ident [EI_MAG1] != ELFMAG1
+ || elf_header->e_ident [EI_MAG2] != ELFMAG2
+ || elf_header->e_ident [EI_MAG3] != ELFMAG3)
+ {
+ /* The file is neither ELF nor aout. Check if it's a linker script,
+ like libc.so - otherwise complain. */
+ int len = statbuf.st_size;
+ /* Only search the beginning of the file. */
+ if (len > 512)
+ len = 512;
+ if (memmem (file_contents, len, "GROUP", 5) == NULL
+ && memmem (file_contents, len, "GNU ld script", 13) == NULL)
+ error (0, 0, _("%s is not an ELF file - it has the wrong magic bytes at the start.\n"),
+ file_name);
+ ret = 1;
+ goto done;
+ }
+
+ if (process_elf_file (file_name, lib, flag, soname, file_contents))
+ ret = 1;
+
+ done:
+ /* Clean up allocated memory and resources. */
+ munmap (file_contents, statbuf.st_size);
+ fclose (file);
+
+ return ret;
+}
+
+/* Get architecture specific version of process_elf_file. */
+#include "readelflib.c"
+