diff options
Diffstat (limited to 'src/ranlib.c')
-rw-r--r-- | src/ranlib.c | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/src/ranlib.c b/src/ranlib.c new file mode 100644 index 00000000..a915e558 --- /dev/null +++ b/src/ranlib.c @@ -0,0 +1,309 @@ +/* Generate an index to speed access to archives. + Copyright (C) 2005, 2006, 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Ulrich Drepper <drepper@redhat.com>, 2005. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <ar.h> +#include <argp.h> +#include <assert.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <gelf.h> +#include <libintl.h> +#include <locale.h> +#include <mcheck.h> +#include <obstack.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdio_ext.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/stat.h> + +#include <system.h> + +#include "arlib.h" + + +/* Prototypes for local functions. */ +static int handle_file (const char *fname); + + +/* 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; + +/* Bug report address. */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { NULL, 0, NULL, 0, NULL, 0 } +}; + +/* Short description of program. */ +static const char doc[] = N_("Generate an index to speed access to archives."); + +/* Strings for arguments in help texts. */ +static const char args_doc[] = N_("ARCHIVE"); + +/* Data structure to communicate with argp functions. */ +static const struct argp argp = +{ + options, NULL, args_doc, doc, NULL, NULL, NULL +}; + + +int +main (int argc, char *argv[]) +{ + /* Make memory leak detection possible. */ + mtrace (); + + /* We use no threads here which can interfere with handling a stream. */ + (void) __fsetlocking (stdin, FSETLOCKING_BYCALLER); + (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER); + (void) __fsetlocking (stderr, FSETLOCKING_BYCALLER); + + /* Set locale. */ + (void) setlocale (LC_ALL, ""); + + /* Make sure the message catalog can be found. */ + (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR); + + /* Initialize the message catalog. */ + (void) textdomain (PACKAGE_TARNAME); + + /* Parse and process arguments. */ + int remaining; + (void) argp_parse (&argp, argc, argv, ARGP_IN_ORDER, &remaining, NULL); + + /* Tell the library which version we are expecting. */ + (void) elf_version (EV_CURRENT); + + /* There must at least be one more parameter specifying the archive. */ + if (remaining == argc) + { + error (0, 0, gettext ("Archive name required")); + argp_help (&argp, stderr, ARGP_HELP_SEE, "ranlib"); + exit (EXIT_FAILURE); + } + + /* We accept the names of multiple archives. */ + int status = 0; + do + status |= handle_file (argv[remaining]); + while (++remaining < argc); + + return status; +} + + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) +{ + fprintf (stream, "ranlib (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); + fprintf (stream, gettext ("\ +Copyright (C) %s Red Hat, 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\ +"), "2008"); + fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); +} + + +static int +copy_content (Elf *elf, int newfd, off_t off, size_t n) +{ + size_t len; + char *rawfile = elf_rawfile (elf, &len); + + assert (off + n <= len); + + /* Tell the kernel we will read all the pages sequentially. */ + size_t ps = sysconf (_SC_PAGESIZE); + if (n > 2 * ps) + posix_madvise (rawfile + (off & ~(ps - 1)), n, POSIX_MADV_SEQUENTIAL); + + return write_retry (newfd, rawfile + off, n) != (ssize_t) n; +} + + +/* Handle a file given on the command line. */ +static int +handle_file (const char *fname) +{ + int fd = open (fname, O_RDONLY); + if (fd == -1) + { + error (0, errno, gettext ("cannot open '%s'"), fname); + return 1; + } + + struct stat st; + if (fstat (fd, &st) != 0) + { + error (0, errno, gettext ("cannot stat '%s'"), fname); + close (fd); + return 1; + } + + /* First we walk through the file, looking for all ELF files to + collect symbols from. */ + Elf *arelf = elf_begin (fd, ELF_C_READ_MMAP, NULL); + if (arelf == NULL) + { + error (0, 0, gettext ("cannot create ELF descriptor for '%s': %s"), + fname, elf_errmsg (-1)); + close (fd); + return 1; + } + + if (elf_kind (arelf) != ELF_K_AR) + { + error (0, 0, gettext ("'%s' is no archive"), fname); + elf_end (arelf); + close (fd); + return 1; + } + + arlib_init (); + + /* Iterate over the content of the archive. */ + off_t index_off = -1; + size_t index_size = 0; + off_t cur_off = SARMAG; + Elf *elf; + Elf_Cmd cmd = ELF_C_READ_MMAP; + while ((elf = elf_begin (fd, cmd, arelf)) != NULL) + { + Elf_Arhdr *arhdr = elf_getarhdr (elf); + assert (arhdr != NULL); + + /* If this is the index, remember the location. */ + if (strcmp (arhdr->ar_name, "/") == 0) + { + index_off = elf_getaroff (elf); + index_size = arhdr->ar_size; + } + else + { + arlib_add_symbols (elf, fname, arhdr->ar_name, cur_off); + cur_off += (((arhdr->ar_size + 1) & ~((off_t) 1)) + + sizeof (struct ar_hdr)); + } + + /* Get next archive element. */ + cmd = elf_next (elf); + if (elf_end (elf) != 0) + error (0, 0, gettext ("error while freeing sub-ELF descriptor: %s"), + elf_errmsg (-1)); + } + + arlib_finalize (); + + /* If the file contains no symbols we need not do anything. */ + int status = 0; + if (symtab.symsnamelen != 0 + /* We have to rewrite the file also if it initially had an index + but now does not need one anymore. */ + || (symtab.symsnamelen == 0 && index_size != 0)) + { + /* Create a new, temporary file in the same directory as the + original file. */ + char tmpfname[strlen (fname) + 7]; + strcpy (stpcpy (tmpfname, fname), "XXXXXX"); + int newfd = mkstemp (tmpfname); + if (unlikely (newfd == -1)) + { + nonew: + error (0, errno, gettext ("cannot create new file")); + status = 1; + } + else + { + /* Create the header. */ + if (unlikely (write_retry (newfd, ARMAG, SARMAG) != SARMAG)) + { + // XXX Use /prof/self/fd/%d ??? + nonew_unlink: + unlink (tmpfname); + if (newfd != -1) + close (newfd); + goto nonew; + } + + /* Create the new file. There are three parts as far we are + concerned: 1. original context before the index, 2. the + new index, 3. everything after the new index. */ + off_t rest_off; + if (index_off != -1) + rest_off = (index_off + sizeof (struct ar_hdr) + + ((index_size + 1) & ~1ul)); + else + rest_off = SARMAG; + + if ((symtab.symsnamelen != 0 + && ((write_retry (newfd, symtab.symsoff, + symtab.symsofflen) + != (ssize_t) symtab.symsofflen) + || (write_retry (newfd, symtab.symsname, + symtab.symsnamelen) + != (ssize_t) symtab.symsnamelen))) + /* Even if the original file had content before the + symbol table, we write it in the correct order. */ + || (index_off > SARMAG + && copy_content (arelf, newfd, SARMAG, index_off - SARMAG)) + || copy_content (arelf, newfd, rest_off, st.st_size - rest_off) + /* Set the mode of the new file to the same values the + original file has. */ + || fchmod (newfd, st.st_mode & ALLPERMS) != 0 + /* Never complain about fchown failing. */ + || (({asm ("" :: "r" (fchown (newfd, st.st_uid, st.st_gid))); }), + close (newfd) != 0) + || (newfd = -1, rename (tmpfname, fname) != 0)) + goto nonew_unlink; + } + } + + elf_end (arelf); + + arlib_fini (); + + close (fd); + + return status; +} + + +#include "debugpred.h" |