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