aboutsummaryrefslogtreecommitdiff
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.xz
glibc-591e1ffbf8db5e059b9bcf3bde6d7758f018a46d.zip
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
+