diff options
-rw-r--r-- | ChangeLog | 27 | ||||
-rw-r--r-- | NEWS | 2 | ||||
-rw-r--r-- | config.make.in | 5 | ||||
-rw-r--r-- | configure.in | 3 | ||||
-rw-r--r-- | elf/Makefile | 14 | ||||
-rw-r--r-- | elf/cache.c | 363 | ||||
-rw-r--r-- | elf/ldconfig.c | 647 | ||||
-rw-r--r-- | elf/ldconfig.h | 63 | ||||
-rw-r--r-- | elf/readlib.c | 160 | ||||
-rw-r--r-- | manual/time.texi | 2 | ||||
-rw-r--r-- | sysdeps/generic/readelflib.c | 180 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/sparc/readelflib.c | 57 |
12 files changed, 1516 insertions, 7 deletions
diff --git a/ChangeLog b/ChangeLog index 4153842209..1dd355f3e4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,30 @@ +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. + 1999-12-03 Ulrich Drepper <drepper@cygnus.com> * sysdeps/generic/bits/stropts.h: Update with LiS types and diff --git a/NEWS b/NEWS index a01e97720c..b5552e8d49 100644 --- a/NEWS +++ b/NEWS @@ -20,6 +20,8 @@ Version 2.2 * Functions feenableexcept and fedisableexcept to control the behaviour of individual exceptions have been added by Andreas Jaeger. +* ldconfig program added by Andreas Jaeger and Jakub Jelinek. + Version 2.1.2 diff --git a/config.make.in b/config.make.in index 4ba8143f4d..06585d2259 100644 --- a/config.make.in +++ b/config.make.in @@ -15,9 +15,8 @@ sysconfdir = @libc_cv_sysconfdir@ libexecdir = @libexecdir@ rootsbindir = @libc_cv_rootsbindir@ -# If ldconfig exists. This will go away as soon as `ldconfig' is available -# in GNU libc. -has-ldconfig = @has_ldconfig@ +# Should we use and build ldconfig? +use-ldconfig = @use_ldconfig@ # Maybe the `ldd' script must be rewritten. ldd-rewrite-script = @ldd_rewrite_script@ diff --git a/configure.in b/configure.in index d7ff75bdd0..6d86b68a8b 100644 --- a/configure.in +++ b/configure.in @@ -1168,6 +1168,7 @@ libc_link_dests= libc_link_sources= # They also can set these variables. +use_ldconfig=no ldd_rewrite_script=no # Iterate over all the sysdep directories we will use, running their @@ -1282,7 +1283,7 @@ AC_SUBST(libc_cv_slibdir) AC_SUBST(libc_cv_sysconfdir) AC_SUBST(libc_cv_rootsbindir) -AC_SUBST(has_ldconfig) +AC_SUBST(use_ldconfig) AC_SUBST(ldd_rewrite_script) AC_SUBST(gnu_ld) AC_SUBST(gnu_as) AC_SUBST(elf) 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" + diff --git a/manual/time.texi b/manual/time.texi index ff393a2c61..9dbe2d1e19 100644 --- a/manual/time.texi +++ b/manual/time.texi @@ -2237,7 +2237,7 @@ the requested time in the @var{requested_time} parameter is rounded up to the next integer multiple of the actual resolution of the system. If the function returns because the time has elapsed the return value is -zero. If the function return @math{-1} the global variable @var{errno} +zero. If the function returns @math{-1} the global variable @var{errno} is set to the following values: @table @code diff --git a/sysdeps/generic/readelflib.c b/sysdeps/generic/readelflib.c new file mode 100644 index 0000000000..645c9124a9 --- /dev/null +++ b/sysdeps/generic/readelflib.c @@ -0,0 +1,180 @@ +/* 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. */ + +/* This code is a heavily simplified version of the readelf program + that's part of the current binutils development version. For architectures + which need to handle both 32bit and 64bit ELF libraries, this file is + included twice for each arch size. */ + +/* Returns 0 if everything is ok, != 0 in case of error. */ +int +process_elf_file (const char *file_name, const char *lib, int *flag, char **soname, + void *file_contents) +{ + int i; + unsigned int j; + int loadaddr; + unsigned int dynamic_addr; + size_t dynamic_size; + char *program_interpreter; + + ElfW(Ehdr) *elf_header; + ElfW(Phdr) *elf_pheader, *segment; + ElfW(Dyn) *dynamic_segment, *dyn_entry; + char *dynamic_strings; + + elf_header = (ElfW(Ehdr) *) file_contents; + + if (elf_header->e_ident [EI_CLASS] != ElfW (CLASS)) + { + if (opt_verbose) + { + if (ElfW (CLASS) == ELFCLASS32) + error (0, 0, _("%s is a 32 bit ELF file.\n"), file_name); + else if (ElfW (CLASS) == ELFCLASS64) + error (0, 0, _("%s is a 64 bit ELF file.\n"), file_name); + else + error (0, 0, _("Unknown ELFCLASS in file %s.\n"), file_name); + } + return 1; + } + + if (elf_header->e_type != ET_DYN) + { + error (0, 0, _("%s is not a shared object file (Type: %d).\n"), file_name, + elf_header->e_type); + return 1; + } + + /* Get information from elf program header. */ + elf_pheader = (ElfW(Phdr) *) (elf_header->e_phoff + file_contents); + + /* The library is an elf library, now search for soname and + libc5/libc6. */ + *flag = FLAG_ELF; + + loadaddr = -1; + dynamic_addr = 0; + dynamic_size = 0; + program_interpreter = NULL; + for (i = 0, segment = elf_pheader; + i < elf_header->e_phnum; i++, segment++) + { + switch (segment->p_type) + { + case PT_LOAD: + if (loadaddr == -1) + loadaddr = (segment->p_vaddr & 0xfffff000) + - (segment->p_offset & 0xfffff000); + break; + + case PT_DYNAMIC: + if (dynamic_addr) + error (0, 0, _("more than one dynamic segment\n")); + + dynamic_addr = segment->p_offset; + dynamic_size = segment->p_filesz; + break; + case PT_INTERP: + program_interpreter = (char *) (file_contents + segment->p_offset); + + /* Check if this is enough to classify the binary. */ + for (j = 0; j < sizeof (interpreters) / sizeof (interpreters [0]); + ++j) + if (strcmp (program_interpreter, interpreters[j].soname) == 0) + { + *flag = interpreters[j].flag; + break; + } + break; + default: + break; + } + + } + if (loadaddr == -1) + { + /* Very strange. */ + loadaddr = 0; + } + + /* Now we can read the dynamic sections. */ + if (dynamic_size == 0) + return 1; + + dynamic_segment = (ElfW(Dyn) *) (file_contents + dynamic_addr); + + /* Find the string table. */ + dynamic_strings = NULL; + for (dyn_entry = dynamic_segment; dyn_entry->d_tag != DT_NULL; + ++dyn_entry) + { + if (dyn_entry->d_tag == DT_STRTAB) + { + dynamic_strings = (char *) (file_contents + dyn_entry->d_un.d_val - loadaddr); + break; + } + } + + if (dynamic_strings == NULL) + return 1; + + /* Now read the DT_NEEDED and DT_SONAME entries. */ + for (dyn_entry = dynamic_segment; dyn_entry->d_tag != DT_NULL; + ++dyn_entry) + { + if (dyn_entry->d_tag == DT_NEEDED || dyn_entry->d_tag == DT_SONAME) + { + char *name = dynamic_strings + dyn_entry->d_un.d_val; + + if (dyn_entry->d_tag == DT_NEEDED) + { + + if (*flag == FLAG_ELF) + { + /* Check if this is enough to classify the binary. */ + for (j = 0; + j < sizeof (known_libs) / sizeof (known_libs [0]); + ++j) + if (strcmp (name, known_libs [j].soname) == 0) + { + *flag = known_libs [j].flag; + break; + } + } + } + + else if (dyn_entry->d_tag == DT_SONAME) + *soname = xstrdup (name); + + /* Do we have everything we need? */ + if (*soname && *flag != FLAG_ELF) + return 0; + } + } + + /* We reach this point only if the file doesn't contain a DT_SONAME + or if we can't classify the library. If it doesn't have a + soname, return the name of the library. */ + if (*soname == NULL) + *soname = xstrdup (lib); + + return 0; +} diff --git a/sysdeps/unix/sysv/linux/sparc/readelflib.c b/sysdeps/unix/sysv/linux/sparc/readelflib.c new file mode 100644 index 0000000000..d956ad90f6 --- /dev/null +++ b/sysdeps/unix/sysv/linux/sparc/readelflib.c @@ -0,0 +1,57 @@ +/* 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. */ + + +int process_elf32_file (const char *file_name, const char *lib, int *flag, + char **soname, void *file_contents); +int process_elf64_file (const char *file_name, const char *lib, int *flag, + char **soname, void *file_contents); + +/* Returns 0 if everything is ok, != 0 in case of error. */ +int +process_elf_file (const char *file_name, const char *lib, int *flag, + char **soname, void *file_contents) +{ + ElfW(Ehdr) *elf_header = (ElfW(Ehdr) *) file_contents; + int ret; + + if (elf_header->e_ident [EI_CLASS] == ELFCLASS32) + return process_elf32_file (file_name, lib, flag, soname, file_contents); + else + { + ret = process_elf64_file (file_name, lib, flag, soname, file_contents); + /* Sparc 64bit libraries are always libc.so.6+. */ + if (!ret) + *flag = FLAG_SPARC_LIB64|FLAG_ELF_LIBC6; + return ret; + } +} + +#undef __ELF_NATIVE_CLASS +#undef process_elf_file +#define process_elf_file process_elf32_file +#define __ELF_NATIVE_CLASS 32 +#include "sysdeps/generic/readelflib.c" + +#undef __ELF_NATIVE_CLASS +#undef process_elf_file +#define process_elf_file process_elf64_file +#define __ELF_NATIVE_CLASS 64 +#include "sysdeps/generic/readelflib.c" |