Imported Upstream version 0.153
[platform/upstream/elfutils.git] / src / ranlib.c
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
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 "), "2012");
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"