• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Generate an index to speed access to archives.
2    Copyright (C) 2005-2012 Red Hat, Inc.
3    This file is part of Red Hat elfutils.
4    Written by Ulrich Drepper <drepper@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 ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
62 
63 /* Bug report address.  */
64 ARGP_PROGRAM_BUG_ADDRESS_DEF = 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, arlib_argp_children, NULL, NULL
83 };
84 
85 
86 int
main(int argc,char * argv[])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
print_version(FILE * stream,struct argp_state * state)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 "), "2012");
141   fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
142 }
143 
144 
145 static int
copy_content(Elf * elf,int newfd,off_t off,size_t n)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
handle_file(const char * fname)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