libelf_ar_util.c revision 276524
1/*- 2 * Copyright (c) 2006,2009,2010 Joseph Koshy 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS `AS IS' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28 29#include <assert.h> 30#include <libelf.h> 31#include <stdlib.h> 32#include <string.h> 33 34#include "_libelf.h" 35#include "_libelf_ar.h" 36 37ELFTC_VCSID("$Id: libelf_ar_util.c 3013 2014-03-23 06:16:59Z jkoshy $"); 38 39/* 40 * Convert a string bounded by `start' and `start+sz' (exclusive) to a 41 * number in the specified base. 42 */ 43int 44_libelf_ar_get_number(const char *src, size_t sz, unsigned int base, 45 size_t *ret) 46{ 47 size_t r; 48 unsigned int c, v; 49 const unsigned char *e, *s; 50 51 assert(base <= 10); 52 53 s = (const unsigned char *) src; 54 e = s + sz; 55 56 /* skip leading blanks */ 57 for (;s < e && (c = *s) == ' '; s++) 58 ; 59 60 r = 0L; 61 for (;s < e; s++) { 62 if ((c = *s) == ' ') 63 break; 64 if (c < '0' || c > '9') 65 return (0); 66 v = c - '0'; 67 if (v >= base) /* Illegal digit. */ 68 break; 69 r *= base; 70 r += v; 71 } 72 73 *ret = r; 74 75 return (1); 76} 77 78/* 79 * Return the translated name for an archive member. 80 */ 81char * 82_libelf_ar_get_translated_name(const struct ar_hdr *arh, Elf *ar) 83{ 84 char *s; 85 unsigned char c; 86 size_t len, offset; 87 const unsigned char *buf, *p, *q, *r; 88 const size_t bufsize = sizeof(arh->ar_name); 89 90 assert(arh != NULL); 91 assert(ar->e_kind == ELF_K_AR); 92 assert((const unsigned char *) arh >= ar->e_rawfile && 93 (const unsigned char *) arh < ar->e_rawfile + ar->e_rawsize); 94 95 buf = (const unsigned char *) arh->ar_name; 96 97 /* 98 * Check for extended naming. 99 * 100 * If the name matches the pattern "^/[0-9]+", it is an 101 * SVR4-style extended name. If the name matches the pattern 102 * "#1/[0-9]+", the entry uses BSD style extended naming. 103 */ 104 if (buf[0] == '/' && (c = buf[1]) >= '0' && c <= '9') { 105 /* 106 * The value in field ar_name is a decimal offset into 107 * the archive string table where the actual name 108 * resides. 109 */ 110 if (_libelf_ar_get_number((const char *) (buf + 1), 111 bufsize - 1, 10, &offset) == 0) { 112 LIBELF_SET_ERROR(ARCHIVE, 0); 113 return (NULL); 114 } 115 116 if (offset > ar->e_u.e_ar.e_rawstrtabsz) { 117 LIBELF_SET_ERROR(ARCHIVE, 0); 118 return (NULL); 119 } 120 121 p = q = ar->e_u.e_ar.e_rawstrtab + offset; 122 r = ar->e_u.e_ar.e_rawstrtab + ar->e_u.e_ar.e_rawstrtabsz; 123 124 for (; p < r && *p != '/'; p++) 125 ; 126 len = (size_t) (p - q + 1); /* space for the trailing NUL */ 127 128 if ((s = malloc(len)) == NULL) { 129 LIBELF_SET_ERROR(RESOURCE, 0); 130 return (NULL); 131 } 132 133 (void) strncpy(s, (const char *) q, len - 1); 134 s[len - 1] = '\0'; 135 136 return (s); 137 } else if (IS_EXTENDED_BSD_NAME(buf)) { 138 r = buf + LIBELF_AR_BSD_EXTENDED_NAME_PREFIX_SIZE; 139 140 if (_libelf_ar_get_number((const char *) r, bufsize - 141 LIBELF_AR_BSD_EXTENDED_NAME_PREFIX_SIZE, 10, 142 &len) == 0) { 143 LIBELF_SET_ERROR(ARCHIVE, 0); 144 return (NULL); 145 } 146 147 /* 148 * Allocate space for the file name plus a 149 * trailing NUL. 150 */ 151 if ((s = malloc(len + 1)) == NULL) { 152 LIBELF_SET_ERROR(RESOURCE, 0); 153 return (NULL); 154 } 155 156 /* 157 * The file name follows the archive header. 158 */ 159 q = (const unsigned char *) (arh + 1); 160 161 (void) strncpy(s, (const char *) q, len); 162 s[len] = '\0'; 163 164 return (s); 165 } 166 167 /* 168 * A 'normal' name. 169 * 170 * Skip back over trailing blanks from the end of the field. 171 * In the SVR4 format, a '/' is used as a terminator for 172 * non-special names. 173 */ 174 for (q = buf + bufsize - 1; q >= buf && *q == ' '; --q) 175 ; 176 177 if (q >= buf) { 178 if (*q == '/') { 179 /* 180 * SVR4 style names: ignore the trailing 181 * character '/', but only if the name is not 182 * one of the special names "/" and "//". 183 */ 184 if (q > buf + 1 || 185 (q == (buf + 1) && *buf != '/')) 186 q--; 187 } 188 189 len = (size_t) (q - buf + 2); /* Space for a trailing NUL. */ 190 } else { 191 /* The buffer only had blanks. */ 192 buf = (const unsigned char *) ""; 193 len = 1; 194 } 195 196 if ((s = malloc(len)) == NULL) { 197 LIBELF_SET_ERROR(RESOURCE, 0); 198 return (NULL); 199 } 200 201 (void) strncpy(s, (const char *) buf, len - 1); 202 s[len - 1] = '\0'; 203 204 return (s); 205} 206 207/* 208 * Return the raw name for an archive member, inclusive of any 209 * formatting characters. 210 */ 211char * 212_libelf_ar_get_raw_name(const struct ar_hdr *arh) 213{ 214 char *rawname; 215 const size_t namesz = sizeof(arh->ar_name); 216 217 if ((rawname = malloc(namesz + 1)) == NULL) { 218 LIBELF_SET_ERROR(RESOURCE, 0); 219 return (NULL); 220 } 221 222 (void) strncpy(rawname, arh->ar_name, namesz); 223 rawname[namesz] = '\0'; 224 return (rawname); 225} 226 227/* 228 * Open an 'ar' archive. 229 */ 230Elf * 231_libelf_ar_open(Elf *e, int reporterror) 232{ 233 size_t sz; 234 int scanahead; 235 struct ar_hdr arh; 236 unsigned char *s, *end; 237 238 _libelf_init_elf(e, ELF_K_AR); 239 240 e->e_u.e_ar.e_nchildren = 0; 241 e->e_u.e_ar.e_next = (off_t) -1; 242 243 /* 244 * Look for special members. 245 */ 246 247 s = e->e_rawfile + SARMAG; 248 end = e->e_rawfile + e->e_rawsize; 249 250 assert(e->e_rawsize > 0); 251 252 /* 253 * We use heuristics to determine the flavor of the archive we 254 * are examining. 255 * 256 * SVR4 flavor archives use the name "/ " and "// " for 257 * special members. 258 * 259 * In BSD flavor archives the symbol table, if present, is the 260 * first archive with name "__.SYMDEF". 261 */ 262 263#define READ_AR_HEADER(S, ARH, SZ, END) \ 264 do { \ 265 if ((S) + sizeof((ARH)) > (END)) \ 266 goto error; \ 267 (void) memcpy(&(ARH), (S), sizeof((ARH))); \ 268 if ((ARH).ar_fmag[0] != '`' || (ARH).ar_fmag[1] != '\n') \ 269 goto error; \ 270 if (_libelf_ar_get_number((char *) (ARH).ar_size, \ 271 sizeof((ARH).ar_size), 10, &(SZ)) == 0) \ 272 goto error; \ 273 } while (0) 274 275 READ_AR_HEADER(s, arh, sz, end); 276 277 /* 278 * Handle special archive members for the SVR4 format. 279 */ 280 if (arh.ar_name[0] == '/') { 281 282 if (sz == 0) 283 goto error; 284 285 e->e_flags |= LIBELF_F_AR_VARIANT_SVR4; 286 287 scanahead = 0; 288 289 /* 290 * The symbol table (file name "/ ") always comes before the 291 * string table (file name "// "). 292 */ 293 if (arh.ar_name[1] == ' ') { 294 /* "/ " => symbol table. */ 295 scanahead = 1; /* The string table to follow. */ 296 297 s += sizeof(arh); 298 e->e_u.e_ar.e_rawsymtab = s; 299 e->e_u.e_ar.e_rawsymtabsz = sz; 300 301 sz = LIBELF_ADJUST_AR_SIZE(sz); 302 s += sz; 303 304 } else if (arh.ar_name[1] == '/' && arh.ar_name[2] == ' ') { 305 /* "// " => string table for long file names. */ 306 s += sizeof(arh); 307 e->e_u.e_ar.e_rawstrtab = s; 308 e->e_u.e_ar.e_rawstrtabsz = sz; 309 310 sz = LIBELF_ADJUST_AR_SIZE(sz); 311 s += sz; 312 } 313 314 /* 315 * If the string table hasn't been seen yet, look for 316 * it in the next member. 317 */ 318 if (scanahead) { 319 READ_AR_HEADER(s, arh, sz, end); 320 321 /* "// " => string table for long file names. */ 322 if (arh.ar_name[0] == '/' && arh.ar_name[1] == '/' && 323 arh.ar_name[2] == ' ') { 324 325 s += sizeof(arh); 326 327 e->e_u.e_ar.e_rawstrtab = s; 328 e->e_u.e_ar.e_rawstrtabsz = sz; 329 330 sz = LIBELF_ADJUST_AR_SIZE(sz); 331 s += sz; 332 } 333 } 334 } else if (strncmp(arh.ar_name, LIBELF_AR_BSD_SYMTAB_NAME, 335 sizeof(LIBELF_AR_BSD_SYMTAB_NAME) - 1) == 0) { 336 /* 337 * BSD style archive symbol table. 338 */ 339 s += sizeof(arh); 340 e->e_u.e_ar.e_rawsymtab = s; 341 e->e_u.e_ar.e_rawsymtabsz = sz; 342 343 sz = LIBELF_ADJUST_AR_SIZE(sz); 344 s += sz; 345 } 346 347 /* 348 * Update the 'next' offset, so that a subsequent elf_begin() 349 * works as expected. 350 */ 351 e->e_u.e_ar.e_next = (off_t) (s - e->e_rawfile); 352 353 return (e); 354 355error: 356 if (!reporterror) { 357 e->e_kind = ELF_K_NONE; 358 return (e); 359 } 360 361 LIBELF_SET_ERROR(ARCHIVE, 0); 362 return (NULL); 363} 364