1164190Sjkoshy/*- 2164190Sjkoshy * Copyright (c) 2006 Joseph Koshy 3164190Sjkoshy * All rights reserved. 4164190Sjkoshy * 5164190Sjkoshy * Redistribution and use in source and binary forms, with or without 6164190Sjkoshy * modification, are permitted provided that the following conditions 7164190Sjkoshy * are met: 8164190Sjkoshy * 1. Redistributions of source code must retain the above copyright 9164190Sjkoshy * notice, this list of conditions and the following disclaimer. 10164190Sjkoshy * 2. Redistributions in binary form must reproduce the above copyright 11164190Sjkoshy * notice, this list of conditions and the following disclaimer in the 12164190Sjkoshy * documentation and/or other materials provided with the distribution. 13164190Sjkoshy * 14164190Sjkoshy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS `AS IS' AND 15164190Sjkoshy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16164190Sjkoshy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17164190Sjkoshy * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18164190Sjkoshy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19164190Sjkoshy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20164190Sjkoshy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21164190Sjkoshy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22164190Sjkoshy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23164190Sjkoshy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24164190Sjkoshy * SUCH DAMAGE. 25164190Sjkoshy */ 26164190Sjkoshy 27164190Sjkoshy#include <sys/cdefs.h> 28164190Sjkoshy__FBSDID("$FreeBSD$"); 29164190Sjkoshy 30164190Sjkoshy#include <ar.h> 31164190Sjkoshy#include <assert.h> 32164190Sjkoshy#include <ctype.h> 33164190Sjkoshy#include <libelf.h> 34164190Sjkoshy#include <stdlib.h> 35164190Sjkoshy#include <string.h> 36164190Sjkoshy 37164190Sjkoshy#include "_libelf.h" 38164190Sjkoshy 39164190Sjkoshy#define LIBELF_NALLOC_SIZE 16 40164190Sjkoshy 41164190Sjkoshy/* 42164190Sjkoshy * `ar' archive handling. 43164190Sjkoshy * 44164190Sjkoshy * `ar' archives start with signature `ARMAG'. Each archive member is 45164190Sjkoshy * preceded by a header containing meta-data for the member. This 46164190Sjkoshy * header is described in <ar.h> (struct ar_hdr). The header always 47164190Sjkoshy * starts on an even address. File data is padded with "\n" 48164190Sjkoshy * characters to keep this invariant. 49164190Sjkoshy * 50164190Sjkoshy * Special considerations for `ar' archives: 51164190Sjkoshy * 52164190Sjkoshy * The `ar' header only has space for a 16 character file name. File 53164190Sjkoshy * names are terminated with a '/', so this effectively leaves 15 54164190Sjkoshy * characters for the actual file name. In order to accomodate longer 55164190Sjkoshy * file names, names may be stored in a separate 'string table' and 56164190Sjkoshy * referenced indirectly by a member header. The string table itself 57164190Sjkoshy * appears as an archive member with name "// ". An indirect file name 58164190Sjkoshy * in an `ar' header matches the pattern "/[0-9]*". The digits form a 59164190Sjkoshy * decimal number that corresponds to a byte offset into the string 60164190Sjkoshy * table where the actual file name of the object starts. Strings in 61164190Sjkoshy * the string table are padded to start on even addresses. 62164190Sjkoshy * 63164190Sjkoshy * Archives may also have a symbol table (see ranlib(1)), mapping 64164190Sjkoshy * program symbols to object files inside the archive. A symbol table 65164190Sjkoshy * uses a file name of "/ " in its archive header. The symbol table 66164190Sjkoshy * is structured as: 67164190Sjkoshy * - a 4-byte count of entries stored as a binary value, MSB first 68164190Sjkoshy * - 'n' 4-byte offsets, stored as binary values, MSB first 69164190Sjkoshy * - 'n' NUL-terminated strings, for ELF symbol names, stored unpadded. 70164190Sjkoshy * 71164190Sjkoshy * If the symbol table and string table are is present in an archive 72164190Sjkoshy * they must be the very first objects and in that order. 73164190Sjkoshy */ 74164190Sjkoshy 75164190Sjkoshy 76164190Sjkoshy 77164190SjkoshyElf_Arhdr * 78164190Sjkoshy_libelf_ar_gethdr(Elf *e) 79164190Sjkoshy{ 80164190Sjkoshy Elf *parent; 81164190Sjkoshy struct ar_hdr *arh; 82164190Sjkoshy Elf_Arhdr *eh; 83164190Sjkoshy size_t n; 84164190Sjkoshy 85164190Sjkoshy if ((parent = e->e_parent) == NULL) { 86164190Sjkoshy LIBELF_SET_ERROR(ARGUMENT, 0); 87164190Sjkoshy return (NULL); 88164190Sjkoshy } 89164190Sjkoshy 90164190Sjkoshy arh = (struct ar_hdr *) ((uintptr_t) e->e_rawfile - sizeof(struct ar_hdr)); 91164190Sjkoshy 92164190Sjkoshy assert((uintptr_t) arh >= (uintptr_t) parent->e_rawfile + SARMAG); 93164190Sjkoshy assert((uintptr_t) arh <= (uintptr_t) parent->e_rawfile + parent->e_rawsize - 94164190Sjkoshy sizeof(struct ar_hdr)); 95164190Sjkoshy 96164190Sjkoshy if ((eh = malloc(sizeof(Elf_Arhdr))) == NULL) { 97164190Sjkoshy LIBELF_SET_ERROR(RESOURCE, 0); 98164190Sjkoshy return (NULL); 99164190Sjkoshy } 100164190Sjkoshy 101164190Sjkoshy e->e_arhdr = eh; 102164190Sjkoshy eh->ar_name = eh->ar_rawname = NULL; 103164190Sjkoshy 104164190Sjkoshy if ((eh->ar_name = _libelf_ar_get_name(arh->ar_name, sizeof(arh->ar_name), 105164190Sjkoshy parent)) == NULL) 106164190Sjkoshy goto error; 107164190Sjkoshy 108164190Sjkoshy if (_libelf_ar_get_number(arh->ar_uid, sizeof(arh->ar_uid), 10, &n) == 0) 109164190Sjkoshy goto error; 110164190Sjkoshy eh->ar_uid = (uid_t) n; 111164190Sjkoshy 112164190Sjkoshy if (_libelf_ar_get_number(arh->ar_gid, sizeof(arh->ar_gid), 10, &n) == 0) 113164190Sjkoshy goto error; 114164190Sjkoshy eh->ar_gid = (gid_t) n; 115164190Sjkoshy 116164190Sjkoshy if (_libelf_ar_get_number(arh->ar_mode, sizeof(arh->ar_mode), 8, &n) == 0) 117164190Sjkoshy goto error; 118164190Sjkoshy eh->ar_mode = (mode_t) n; 119164190Sjkoshy 120164190Sjkoshy if (_libelf_ar_get_number(arh->ar_size, sizeof(arh->ar_size), 10, &n) == 0) 121164190Sjkoshy goto error; 122164190Sjkoshy eh->ar_size = n; 123164190Sjkoshy 124164190Sjkoshy if ((eh->ar_rawname = _libelf_ar_get_string(arh->ar_name, 125164190Sjkoshy sizeof(arh->ar_name), 1)) == NULL) 126164190Sjkoshy goto error; 127164190Sjkoshy 128164190Sjkoshy return (eh); 129164190Sjkoshy 130164190Sjkoshy error: 131164190Sjkoshy if (eh) { 132164190Sjkoshy if (eh->ar_name) 133164190Sjkoshy free(eh->ar_name); 134164190Sjkoshy if (eh->ar_rawname) 135164190Sjkoshy free(eh->ar_rawname); 136164190Sjkoshy free(eh); 137164190Sjkoshy } 138164190Sjkoshy e->e_arhdr = NULL; 139164190Sjkoshy 140164190Sjkoshy return (NULL); 141164190Sjkoshy} 142164190Sjkoshy 143164190SjkoshyElf * 144164190Sjkoshy_libelf_ar_open_member(int fd, Elf_Cmd c, Elf *elf) 145164190Sjkoshy{ 146164190Sjkoshy Elf *e; 147164190Sjkoshy off_t next; 148164190Sjkoshy struct ar_hdr *arh; 149164190Sjkoshy size_t sz; 150164190Sjkoshy 151164190Sjkoshy assert(elf->e_kind == ELF_K_AR); 152164190Sjkoshy 153164190Sjkoshy next = elf->e_u.e_ar.e_next; 154164190Sjkoshy 155164190Sjkoshy /* 156164190Sjkoshy * `next' is only set to zero by elf_next() when the last 157164190Sjkoshy * member of an archive is processed. 158164190Sjkoshy */ 159164190Sjkoshy if (next == (off_t) 0) 160164190Sjkoshy return (NULL); 161164190Sjkoshy 162164190Sjkoshy assert((next & 1) == 0); 163164190Sjkoshy 164164190Sjkoshy arh = (struct ar_hdr *) (elf->e_rawfile + next); 165164190Sjkoshy 166164190Sjkoshy if (_libelf_ar_get_number(arh->ar_size, sizeof(arh->ar_size), 10, &sz) == 0) { 167164190Sjkoshy LIBELF_SET_ERROR(ARCHIVE, 0); 168164190Sjkoshy return (NULL); 169164190Sjkoshy } 170164190Sjkoshy 171164190Sjkoshy assert(sz > 0); 172164190Sjkoshy 173164190Sjkoshy arh++; /* skip over archive member header */ 174164190Sjkoshy 175164190Sjkoshy if ((e = elf_memory((char *) arh, sz)) == NULL) 176164190Sjkoshy return (NULL); 177164190Sjkoshy 178164190Sjkoshy e->e_fd = fd; 179164190Sjkoshy e->e_cmd = c; 180164190Sjkoshy 181164190Sjkoshy elf->e_u.e_ar.e_nchildren++; 182164190Sjkoshy e->e_parent = elf; 183164190Sjkoshy 184164190Sjkoshy return (e); 185164190Sjkoshy} 186164190Sjkoshy 187164190Sjkoshy/* 188164190Sjkoshy * An ar(1) symbol table has the following layout: 189164190Sjkoshy * 190164190Sjkoshy * The first 4 bytes are a binary count of the number of entries in the 191164190Sjkoshy * symbol table, stored MSB-first. 192164190Sjkoshy * 193164190Sjkoshy * Then there are 'n' 4-byte binary offsets, also stored MSB first. 194164190Sjkoshy * 195164190Sjkoshy * Following this, there are 'n' null-terminated strings. 196164190Sjkoshy */ 197164190Sjkoshy 198164190Sjkoshy#define GET_WORD(P, V) do { \ 199164190Sjkoshy (V) = 0; \ 200164190Sjkoshy (V) = (P)[0]; (V) <<= 8; \ 201164190Sjkoshy (V) += (P)[1]; (V) <<= 8; \ 202164190Sjkoshy (V) += (P)[2]; (V) <<= 8; \ 203164190Sjkoshy (V) += (P)[3]; \ 204164190Sjkoshy } while (0) 205164190Sjkoshy 206164190Sjkoshy#define INTSZ 4 207164190Sjkoshy 208164190SjkoshyElf_Arsym * 209164190Sjkoshy_libelf_ar_process_symtab(Elf *e, size_t *count) 210164190Sjkoshy{ 211164190Sjkoshy size_t n, nentries, off; 212164190Sjkoshy Elf_Arsym *symtab, *sym; 213164190Sjkoshy unsigned char *p, *s, *end; 214164190Sjkoshy 215164190Sjkoshy assert(e != NULL); 216164190Sjkoshy assert(count != NULL); 217164190Sjkoshy 218164190Sjkoshy if (e->e_u.e_ar.e_rawsymtabsz < INTSZ) { 219164190Sjkoshy LIBELF_SET_ERROR(ARCHIVE, 0); 220164190Sjkoshy return (NULL); 221164190Sjkoshy } 222164190Sjkoshy 223164190Sjkoshy p = (unsigned char *) e->e_u.e_ar.e_rawsymtab; 224164190Sjkoshy end = p + e->e_u.e_ar.e_rawsymtabsz; 225164190Sjkoshy 226164190Sjkoshy GET_WORD(p, nentries); 227164190Sjkoshy p += INTSZ; 228164190Sjkoshy 229164190Sjkoshy if (nentries == 0 || p + nentries * INTSZ >= end) { 230164190Sjkoshy LIBELF_SET_ERROR(ARCHIVE, 0); 231164190Sjkoshy return (NULL); 232164190Sjkoshy } 233164190Sjkoshy 234164190Sjkoshy /* Allocate space for a nentries + a sentinel. */ 235164190Sjkoshy if ((symtab = malloc(sizeof(Elf_Arsym) * (nentries+1))) == NULL) { 236164190Sjkoshy LIBELF_SET_ERROR(RESOURCE, 0); 237164190Sjkoshy return (NULL); 238164190Sjkoshy } 239164190Sjkoshy 240164190Sjkoshy s = p + (nentries * INTSZ); /* start of the string table. */ 241164190Sjkoshy 242164190Sjkoshy for (n = nentries, sym = symtab; n > 0; n--) { 243164190Sjkoshy off = 0; 244164190Sjkoshy 245164190Sjkoshy GET_WORD(p, off); 246164190Sjkoshy 247164190Sjkoshy sym->as_off = off; 248164190Sjkoshy sym->as_hash = elf_hash(s); 249164190Sjkoshy sym->as_name = s; 250164190Sjkoshy 251164190Sjkoshy p += INTSZ; 252164190Sjkoshy sym++; 253164190Sjkoshy 254164190Sjkoshy for (; s < end && *s++ != '\0';) /* skip to next string */ 255164190Sjkoshy ; 256164190Sjkoshy if (s > end) { 257164190Sjkoshy LIBELF_SET_ERROR(ARCHIVE, 0); 258164190Sjkoshy free(symtab); 259164190Sjkoshy return (NULL); 260164190Sjkoshy } 261164190Sjkoshy } 262164190Sjkoshy 263164190Sjkoshy /* Fill up the sentinel entry. */ 264164190Sjkoshy sym->as_name = NULL; 265164190Sjkoshy sym->as_hash = ~0UL; 266164190Sjkoshy sym->as_off = (off_t) 0; 267164190Sjkoshy 268164190Sjkoshy *count = e->e_u.e_ar.e_symtabsz = nentries + 1; 269164190Sjkoshy e->e_u.e_ar.e_symtab = symtab; 270164190Sjkoshy 271164190Sjkoshy return (symtab); 272164190Sjkoshy} 273