/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved. */ /* Copyright (c) 1988 AT&T */ /* All Rights Reserved */ #include #include #include #include "decl.h" #include "msg.h" /* * Convert archive symbol table to memory format * * This takes a pointer to file's archive symbol table, alignment * unconstrained. Returns null terminated vector of Elf_Arsym * structures. Elf_Arsym uses size_t to represent offsets, which * will be 32-bit in 32-bit versions, and 64-bits otherwise. * * There are two forms of archive symbol table, the original 32-bit * form, and a 64-bit form originally found in IRIX64. The two formats * differ only in the width of the integer word: * * # offsets 4/8-byte word * offset[0...] 4/8-byte word each * strings null-terminated, for offset[x] * * By default, the 64-bit form is only used when the archive exceeds * the limits of 32-bits (4GB) in size. However, this is not required, * and the ar -S option can be used to create a 64-bit symbol table in * an archive that is under 4GB. * * Both 32 and 64-bit versions of libelf can read the 32-bit format * without loss of information. Similarly, a 64-bit version of libelf * will have no problem reading a 64-bit symbol table. This leaves the * case where a 32-bit libelf reads a 64-bit symbol table, which requires * some explanation. The offsets in a 64-bit symbol table will have zeros * in the upper half of the words until the size of the archive exceeds 4GB. * However, 32-bit libelf is unable to read any files larger than 2GB * (see comments in update.c). As such, any archive that the 32-bit version * of this code will encounter will be under 4GB in size. The upper 4 * bytes of each word will be zero, and can be safely ignored. */ /* * Offsets in archive headers are written in MSB (large endian) order * on all platforms, regardless of native byte order. These macros read * 4 and 8 byte values from unaligned memory. * * note: * - The get8() macro for 32-bit code can ignore the first 4 bytes of * of the word, because they are known to be 0. * * - The inner most value in these macros is cast to an unsigned integer * of the final width in order to prevent the C comilier from doing * unwanted sign extension when the topmost bit of a byte is set. */ #define get4(p) (((((((uint32_t)p[0]<<8)+p[1])<<8)+p[2])<<8)+p[3]) #ifdef _LP64 #define get8(p) (((((((((((((((uint64_t)p[0]<<8)+p[1])<<8)+p[2])<<8)+ \ p[3])<<8)+p[4])<<8)+p[5])<<8)+p[6])<<8)+p[7]) #else #define get8(p) (((((((uint64_t)p[4]<<8)+p[5])<<8)+p[6])<<8)+p[7]) #endif static Elf_Void * arsym(Byte *off, size_t sz, size_t *e, int is64) { char *endstr = (char *)off + sz; register char *str; Byte *endoff; Elf_Void *oas; size_t eltsize = is64 ? 8 : 4; { register size_t n; if (is64) { if (sz < 8 || (sz - 8) / 8 < (n = get8(off))) { _elf_seterr(EFMT_ARSYMSZ, 0); return (NULL); } } else { if (sz < 4 || (sz - 4) / 4 < (n = get4(off))) { _elf_seterr(EFMT_ARSYMSZ, 0); return (NULL); } } off += eltsize; endoff = off + n * eltsize; /* * If there are symbols in the symbol table, a * string table must be present and NULL terminated. * * The format dictates that the string table must always be * present, however in the case of an archive containing no * symbols GNU ar will not create one. We are permissive for * the sake of compatibility. */ if ((n > 0) && (((str = (char *)endoff) >= endstr) || (*(endstr - 1) != '\0'))) { _elf_seterr(EFMT_ARSYM, 0); return (NULL); } /* * There is always at least one entry returned if a symtab * exists since the table's last entry is an artificial one * with a NULL as_name, but is included in the count. * * overflow can occur here, but not likely */ *e = n + 1; if ((oas = calloc(n + 1, sizeof (Elf_Arsym))) == NULL) { _elf_seterr(EMEM_ARSYM, errno); return (NULL); } } { register Elf_Arsym *as = (Elf_Arsym *)oas; while (off < endoff) { if (str >= endstr) { _elf_seterr(EFMT_ARSYMSTR, 0); free(oas); return (NULL); } if (is64) as->as_off = get8(off); else as->as_off = get4(off); as->as_name = str; as->as_hash = elf_hash(str); ++as; off += eltsize; while (*str++ != '\0') /* LINTED */ ; } as->as_name = NULL; as->as_off = 0; as->as_hash = ~(unsigned long)0L; } return (oas); } Elf_Arsym * elf_getarsym(Elf *elf, size_t *ptr) { Byte *as; size_t sz; Elf_Arsym *rc; int is64; if (ptr != 0) *ptr = 0; if (elf == NULL) return (0); ELFRLOCK(elf); if (elf->ed_kind != ELF_K_AR) { ELFUNLOCK(elf); _elf_seterr(EREQ_AR, 0); return (0); } if ((as = (Byte *)elf->ed_arsym) == 0) { ELFUNLOCK(elf); return (0); } if (elf->ed_myflags & EDF_ASALLOC) { if (ptr != 0) *ptr = elf->ed_arsymsz; ELFUNLOCK(elf); /* LINTED */ return ((Elf_Arsym *)as); } is64 = (elf->ed_myflags & EDF_ARSYM64) != 0; /* * We're gonna need a write lock. */ ELFUNLOCK(elf) ELFWLOCK(elf) sz = elf->ed_arsymsz; if (_elf_vm(elf, (size_t)(as - (Byte *)elf->ed_ident), sz) != OK_YES) { ELFUNLOCK(elf); return (0); } if ((elf->ed_arsym = arsym(as, sz, &elf->ed_arsymsz, is64)) == 0) { ELFUNLOCK(elf); return (0); } elf->ed_myflags |= EDF_ASALLOC; if (ptr != 0) *ptr = elf->ed_arsymsz; rc = (Elf_Arsym *)elf->ed_arsym; ELFUNLOCK(elf); return (rc); } /* * Private function to obtain the value sizeof() would return * for a word from the symbol table from the given archive. Normally, * this is an unimportant implementation detail hidden within * elf_getarsym(). However, it is useful to elfdump for formatting the * output correctly, and for the file command. * * exit: * Returns 4 (32-bit) or 8 (64-bit) if a symbol table is present. * Returns 0 in all other cases. */ size_t _elf_getarsymwordsize(Elf *elf) { size_t size; if (elf == NULL) return (0); ELFRLOCK(elf); if ((elf->ed_kind == ELF_K_AR) && (elf->ed_arsym != 0)) size = (elf->ed_myflags & EDF_ARSYM64) ? 8 : 4; else size = 0; ELFUNLOCK(elf); return (size); }