1/*- 2 * Copyright (c) 2006 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__FBSDID("$FreeBSD$"); 29 30#include <ar.h> 31#include <assert.h> 32#include <ctype.h> 33#include <libelf.h> 34#include <stdlib.h> 35#include <string.h> 36 37#include "_libelf.h" 38 39#define LIBELF_NALLOC_SIZE 16 40 41/* 42 * `ar' archive handling. 43 * 44 * `ar' archives start with signature `ARMAG'. Each archive member is 45 * preceded by a header containing meta-data for the member. This 46 * header is described in <ar.h> (struct ar_hdr). The header always 47 * starts on an even address. File data is padded with "\n" 48 * characters to keep this invariant. 49 * 50 * Special considerations for `ar' archives: 51 * 52 * The `ar' header only has space for a 16 character file name. File 53 * names are terminated with a '/', so this effectively leaves 15 54 * characters for the actual file name. In order to accomodate longer 55 * file names, names may be stored in a separate 'string table' and 56 * referenced indirectly by a member header. The string table itself 57 * appears as an archive member with name "// ". An indirect file name 58 * in an `ar' header matches the pattern "/[0-9]*". The digits form a 59 * decimal number that corresponds to a byte offset into the string 60 * table where the actual file name of the object starts. Strings in 61 * the string table are padded to start on even addresses. 62 * 63 * Archives may also have a symbol table (see ranlib(1)), mapping 64 * program symbols to object files inside the archive. A symbol table 65 * uses a file name of "/ " in its archive header. The symbol table 66 * is structured as: 67 * - a 4-byte count of entries stored as a binary value, MSB first 68 * - 'n' 4-byte offsets, stored as binary values, MSB first 69 * - 'n' NUL-terminated strings, for ELF symbol names, stored unpadded. 70 * 71 * If the symbol table and string table are is present in an archive 72 * they must be the very first objects and in that order. 73 */ 74 75 76 77Elf_Arhdr * 78_libelf_ar_gethdr(Elf *e) 79{ 80 Elf *parent; 81 struct ar_hdr *arh; 82 Elf_Arhdr *eh; 83 size_t n; 84 85 if ((parent = e->e_parent) == NULL) { 86 LIBELF_SET_ERROR(ARGUMENT, 0); 87 return (NULL); 88 } 89 90 arh = (struct ar_hdr *) ((uintptr_t) e->e_rawfile - sizeof(struct ar_hdr)); 91 92 assert((uintptr_t) arh >= (uintptr_t) parent->e_rawfile + SARMAG); 93 assert((uintptr_t) arh <= (uintptr_t) parent->e_rawfile + parent->e_rawsize - 94 sizeof(struct ar_hdr)); 95 96 if ((eh = malloc(sizeof(Elf_Arhdr))) == NULL) { 97 LIBELF_SET_ERROR(RESOURCE, 0); 98 return (NULL); 99 } 100 101 e->e_arhdr = eh; 102 eh->ar_name = eh->ar_rawname = NULL; 103 104 if ((eh->ar_name = _libelf_ar_get_name(arh->ar_name, sizeof(arh->ar_name), 105 parent)) == NULL) 106 goto error; 107 108 if (_libelf_ar_get_number(arh->ar_uid, sizeof(arh->ar_uid), 10, &n) == 0) 109 goto error; 110 eh->ar_uid = (uid_t) n; 111 112 if (_libelf_ar_get_number(arh->ar_gid, sizeof(arh->ar_gid), 10, &n) == 0) 113 goto error; 114 eh->ar_gid = (gid_t) n; 115 116 if (_libelf_ar_get_number(arh->ar_mode, sizeof(arh->ar_mode), 8, &n) == 0) 117 goto error; 118 eh->ar_mode = (mode_t) n; 119 120 if (_libelf_ar_get_number(arh->ar_size, sizeof(arh->ar_size), 10, &n) == 0) 121 goto error; 122 eh->ar_size = n; 123 124 if ((eh->ar_rawname = _libelf_ar_get_string(arh->ar_name, 125 sizeof(arh->ar_name), 1)) == NULL) 126 goto error; 127 128 return (eh); 129 130 error: 131 if (eh) { 132 if (eh->ar_name) 133 free(eh->ar_name); 134 if (eh->ar_rawname) 135 free(eh->ar_rawname); 136 free(eh); 137 } 138 e->e_arhdr = NULL; 139 140 return (NULL); 141} 142 143Elf * 144_libelf_ar_open_member(int fd, Elf_Cmd c, Elf *elf) 145{ 146 Elf *e; 147 off_t next; 148 struct ar_hdr *arh; 149 size_t sz; 150 151 assert(elf->e_kind == ELF_K_AR); 152 153 next = elf->e_u.e_ar.e_next; 154 155 /* 156 * `next' is only set to zero by elf_next() when the last 157 * member of an archive is processed. 158 */ 159 if (next == (off_t) 0) 160 return (NULL); 161 162 assert((next & 1) == 0); 163 164 arh = (struct ar_hdr *) (elf->e_rawfile + next); 165 166 if (_libelf_ar_get_number(arh->ar_size, sizeof(arh->ar_size), 10, &sz) == 0) { 167 LIBELF_SET_ERROR(ARCHIVE, 0); 168 return (NULL); 169 } 170 171 assert(sz > 0); 172 173 arh++; /* skip over archive member header */ 174 175 if ((e = elf_memory((char *) arh, sz)) == NULL) 176 return (NULL); 177 178 e->e_fd = fd; 179 e->e_cmd = c; 180 181 elf->e_u.e_ar.e_nchildren++; 182 e->e_parent = elf; 183 184 return (e); 185} 186 187/* 188 * An ar(1) symbol table has the following layout: 189 * 190 * The first 4 bytes are a binary count of the number of entries in the 191 * symbol table, stored MSB-first. 192 * 193 * Then there are 'n' 4-byte binary offsets, also stored MSB first. 194 * 195 * Following this, there are 'n' null-terminated strings. 196 */ 197 198#define GET_WORD(P, V) do { \ 199 (V) = 0; \ 200 (V) = (P)[0]; (V) <<= 8; \ 201 (V) += (P)[1]; (V) <<= 8; \ 202 (V) += (P)[2]; (V) <<= 8; \ 203 (V) += (P)[3]; \ 204 } while (0) 205 206#define INTSZ 4 207 208Elf_Arsym * 209_libelf_ar_process_symtab(Elf *e, size_t *count) 210{ 211 size_t n, nentries, off; 212 Elf_Arsym *symtab, *sym; 213 unsigned char *p, *s, *end; 214 215 assert(e != NULL); 216 assert(count != NULL); 217 218 if (e->e_u.e_ar.e_rawsymtabsz < INTSZ) { 219 LIBELF_SET_ERROR(ARCHIVE, 0); 220 return (NULL); 221 } 222 223 p = (unsigned char *) e->e_u.e_ar.e_rawsymtab; 224 end = p + e->e_u.e_ar.e_rawsymtabsz; 225 226 GET_WORD(p, nentries); 227 p += INTSZ; 228 229 if (nentries == 0 || p + nentries * INTSZ >= end) { 230 LIBELF_SET_ERROR(ARCHIVE, 0); 231 return (NULL); 232 } 233 234 /* Allocate space for a nentries + a sentinel. */ 235 if ((symtab = malloc(sizeof(Elf_Arsym) * (nentries+1))) == NULL) { 236 LIBELF_SET_ERROR(RESOURCE, 0); 237 return (NULL); 238 } 239 240 s = p + (nentries * INTSZ); /* start of the string table. */ 241 242 for (n = nentries, sym = symtab; n > 0; n--) { 243 off = 0; 244 245 GET_WORD(p, off); 246 247 sym->as_off = off; 248 sym->as_hash = elf_hash(s); 249 sym->as_name = s; 250 251 p += INTSZ; 252 sym++; 253 254 for (; s < end && *s++ != '\0';) /* skip to next string */ 255 ; 256 if (s > end) { 257 LIBELF_SET_ERROR(ARCHIVE, 0); 258 free(symtab); 259 return (NULL); 260 } 261 } 262 263 /* Fill up the sentinel entry. */ 264 sym->as_name = NULL; 265 sym->as_hash = ~0UL; 266 sym->as_off = (off_t) 0; 267 268 *count = e->e_u.e_ar.e_symtabsz = nentries + 1; 269 e->e_u.e_ar.e_symtab = symtab; 270 271 return (symtab); 272} 273