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