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