Home | History | Annotate | Download | only in src
      1 /* Generate an index to speed access to archives.
      2    Copyright (C) 2005, 2006, 2007 Red Hat, Inc.
      3    This file is part of Red Hat elfutils.
      4    Written by Ulrich Drepper <drepper (at) redhat.com>, 2005.
      5 
      6    Red Hat elfutils is free software; you can redistribute it and/or modify
      7    it under the terms of the GNU General Public License as published by the
      8    Free Software Foundation; version 2 of the License.
      9 
     10    Red Hat elfutils is distributed in the hope that it will be useful, but
     11    WITHOUT ANY WARRANTY; without even the implied warranty of
     12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13    General Public License for more details.
     14 
     15    You should have received a copy of the GNU General Public License along
     16    with Red Hat elfutils; if not, write to the Free Software Foundation,
     17    Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
     18 
     19    Red Hat elfutils is an included package of the Open Invention Network.
     20    An included package of the Open Invention Network is a package for which
     21    Open Invention Network licensees cross-license their patents.  No patent
     22    license is granted, either expressly or impliedly, by designation as an
     23    included package.  Should you wish to participate in the Open Invention
     24    Network licensing program, please visit www.openinventionnetwork.com
     25    <http://www.openinventionnetwork.com>.  */
     26 
     27 #ifdef HAVE_CONFIG_H
     28 # include <config.h>
     29 #endif
     30 
     31 #include <ar.h>
     32 #include <argp.h>
     33 #include <assert.h>
     34 #include <errno.h>
     35 #include <error.h>
     36 #include <fcntl.h>
     37 #include <gelf.h>
     38 #include <libintl.h>
     39 #include <locale.h>
     40 #include <mcheck.h>
     41 #include <obstack.h>
     42 #include <stdlib.h>
     43 #include <stdio.h>
     44 #include <stdio_ext.h>
     45 #include <unistd.h>
     46 #include <sys/mman.h>
     47 #include <sys/param.h>
     48 #include <sys/stat.h>
     49 
     50 #include <system.h>
     51 
     52 #include "arlib.h"
     53 
     54 
     55 /* Prototypes for local functions.  */
     56 static int handle_file (const char *fname);
     57 
     58 
     59 /* Name and version of program.  */
     60 static void print_version (FILE *stream, struct argp_state *state);
     61 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
     62 
     63 /* Bug report address.  */
     64 const char *argp_program_bug_address = PACKAGE_BUGREPORT;
     65 
     66 
     67 /* Definitions of arguments for argp functions.  */
     68 static const struct argp_option options[] =
     69 {
     70   { NULL, 0, NULL, 0, NULL, 0 }
     71 };
     72 
     73 /* Short description of program.  */
     74 static const char doc[] = N_("Generate an index to speed access to archives.");
     75 
     76 /* Strings for arguments in help texts.  */
     77 static const char args_doc[] = N_("ARCHIVE");
     78 
     79 /* Data structure to communicate with argp functions.  */
     80 static const struct argp argp =
     81 {
     82   options, NULL, args_doc, doc, NULL, NULL, NULL
     83 };
     84 
     85 
     86 int
     87 main (int argc, char *argv[])
     88 {
     89   /* Make memory leak detection possible.  */
     90   mtrace ();
     91 
     92   /* We use no threads here which can interfere with handling a stream.  */
     93   (void) __fsetlocking (stdin, FSETLOCKING_BYCALLER);
     94   (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER);
     95   (void) __fsetlocking (stderr, FSETLOCKING_BYCALLER);
     96 
     97   /* Set locale.  */
     98   (void) setlocale (LC_ALL, "");
     99 
    100   /* Make sure the message catalog can be found.  */
    101   (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
    102 
    103   /* Initialize the message catalog.  */
    104   (void) textdomain (PACKAGE_TARNAME);
    105 
    106   /* Parse and process arguments.  */
    107   int remaining;
    108   (void) argp_parse (&argp, argc, argv, ARGP_IN_ORDER, &remaining, NULL);
    109 
    110   /* Tell the library which version we are expecting.  */
    111   (void) elf_version (EV_CURRENT);
    112 
    113   /* There must at least be one more parameter specifying the archive.   */
    114   if (remaining == argc)
    115     {
    116       error (0, 0, gettext ("Archive name required"));
    117       argp_help (&argp, stderr, ARGP_HELP_SEE, "ranlib");
    118       exit (EXIT_FAILURE);
    119     }
    120 
    121   /* We accept the names of multiple archives.  */
    122   int status = 0;
    123   do
    124     status |= handle_file (argv[remaining]);
    125   while (++remaining < argc);
    126 
    127   return status;
    128 }
    129 
    130 
    131 /* Print the version information.  */
    132 static void
    133 print_version (FILE *stream, struct argp_state *state __attribute__ ((unused)))
    134 {
    135   fprintf (stream, "ranlib (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
    136   fprintf (stream, gettext ("\
    137 Copyright (C) %s Red Hat, Inc.\n\
    138 This is free software; see the source for copying conditions.  There is NO\n\
    139 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
    140 "), "2008");
    141   fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
    142 }
    143 
    144 
    145 static int
    146 copy_content (Elf *elf, int newfd, off_t off, size_t n)
    147 {
    148   size_t len;
    149   char *rawfile = elf_rawfile (elf, &len);
    150 
    151   assert (off + n <= len);
    152 
    153   /* Tell the kernel we will read all the pages sequentially.  */
    154   size_t ps = sysconf (_SC_PAGESIZE);
    155   if (n > 2 * ps)
    156     posix_madvise (rawfile + (off & ~(ps - 1)), n, POSIX_MADV_SEQUENTIAL);
    157 
    158   return write_retry (newfd, rawfile + off, n) != (ssize_t) n;
    159 }
    160 
    161 
    162 /* Handle a file given on the command line.  */
    163 static int
    164 handle_file (const char *fname)
    165 {
    166   int fd = open (fname, O_RDONLY);
    167   if (fd == -1)
    168     {
    169       error (0, errno, gettext ("cannot open '%s'"), fname);
    170       return 1;
    171     }
    172 
    173   struct stat st;
    174   if (fstat (fd, &st) != 0)
    175     {
    176       error (0, errno, gettext ("cannot stat '%s'"), fname);
    177       close (fd);
    178       return 1;
    179     }
    180 
    181   /* First we walk through the file, looking for all ELF files to
    182      collect symbols from.  */
    183   Elf *arelf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
    184   if (arelf == NULL)
    185     {
    186       error (0, 0, gettext ("cannot create ELF descriptor for '%s': %s"),
    187 	     fname, elf_errmsg (-1));
    188       close (fd);
    189       return 1;
    190     }
    191 
    192   if (elf_kind (arelf) != ELF_K_AR)
    193     {
    194       error (0, 0, gettext ("'%s' is no archive"), fname);
    195       elf_end (arelf);
    196       close (fd);
    197       return 1;
    198     }
    199 
    200   arlib_init ();
    201 
    202   /* Iterate over the content of the archive.  */
    203   off_t index_off = -1;
    204   size_t index_size = 0;
    205   off_t cur_off = SARMAG;
    206   Elf *elf;
    207   Elf_Cmd cmd = ELF_C_READ_MMAP;
    208   while ((elf = elf_begin (fd, cmd, arelf)) != NULL)
    209     {
    210       Elf_Arhdr *arhdr = elf_getarhdr (elf);
    211       assert (arhdr != NULL);
    212 
    213       /* If this is the index, remember the location.  */
    214       if (strcmp (arhdr->ar_name, "/") == 0)
    215 	{
    216 	  index_off = elf_getaroff (elf);
    217 	  index_size = arhdr->ar_size;
    218 	}
    219       else
    220 	{
    221 	  arlib_add_symbols (elf, fname, arhdr->ar_name, cur_off);
    222 	  cur_off += (((arhdr->ar_size + 1) & ~((off_t) 1))
    223 		      + sizeof (struct ar_hdr));
    224 	}
    225 
    226       /* Get next archive element.  */
    227       cmd = elf_next (elf);
    228       if (elf_end (elf) != 0)
    229 	error (0, 0, gettext ("error while freeing sub-ELF descriptor: %s"),
    230 	       elf_errmsg (-1));
    231     }
    232 
    233   arlib_finalize ();
    234 
    235   /* If the file contains no symbols we need not do anything.  */
    236   int status = 0;
    237   if (symtab.symsnamelen != 0
    238       /* We have to rewrite the file also if it initially had an index
    239 	 but now does not need one anymore.  */
    240       || (symtab.symsnamelen == 0 && index_size != 0))
    241     {
    242       /* Create a new, temporary file in the same directory as the
    243 	 original file.  */
    244       char tmpfname[strlen (fname) + 7];
    245       strcpy (stpcpy (tmpfname, fname), "XXXXXX");
    246       int newfd = mkstemp (tmpfname);
    247       if (unlikely (newfd == -1))
    248 	{
    249 	nonew:
    250 	  error (0, errno, gettext ("cannot create new file"));
    251 	  status = 1;
    252 	}
    253       else
    254 	{
    255 	  /* Create the header.  */
    256 	  if (unlikely (write_retry (newfd, ARMAG, SARMAG) != SARMAG))
    257 	    {
    258 	      // XXX Use /prof/self/fd/%d ???
    259 	    nonew_unlink:
    260 	      unlink (tmpfname);
    261 	      if (newfd != -1)
    262 		close (newfd);
    263 	      goto nonew;
    264 	    }
    265 
    266 	  /* Create the new file.  There are three parts as far we are
    267 	     concerned: 1. original context before the index, 2. the
    268 	     new index, 3. everything after the new index.  */
    269 	  off_t rest_off;
    270 	  if (index_off != -1)
    271 	    rest_off = (index_off + sizeof (struct ar_hdr)
    272 			+ ((index_size + 1) & ~1ul));
    273 	  else
    274 	    rest_off = SARMAG;
    275 
    276 	  if ((symtab.symsnamelen != 0
    277 	       && ((write_retry (newfd, symtab.symsoff,
    278 				 symtab.symsofflen)
    279 		    != (ssize_t) symtab.symsofflen)
    280 		   || (write_retry (newfd, symtab.symsname,
    281 				    symtab.symsnamelen)
    282 		       != (ssize_t) symtab.symsnamelen)))
    283 	      /* Even if the original file had content before the
    284 		 symbol table, we write it in the correct order.  */
    285 	      || (index_off > SARMAG
    286 		  && copy_content (arelf, newfd, SARMAG, index_off - SARMAG))
    287 	      || copy_content (arelf, newfd, rest_off, st.st_size - rest_off)
    288 	      /* Set the mode of the new file to the same values the
    289 		 original file has.  */
    290 	      || fchmod (newfd, st.st_mode & ALLPERMS) != 0
    291 	      /* Never complain about fchown failing.  */
    292 	      || (({asm ("" :: "r" (fchown (newfd, st.st_uid, st.st_gid))); }),
    293 		  close (newfd) != 0)
    294 	      || (newfd = -1, rename (tmpfname, fname) != 0))
    295 	    goto nonew_unlink;
    296 	}
    297     }
    298 
    299   elf_end (arelf);
    300 
    301   arlib_fini ();
    302 
    303   close (fd);
    304 
    305   return status;
    306 }
    307 
    308 
    309 #include "debugpred.h"
    310