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