1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
  24  */
  25 
  26 /*      Copyright (c) 1988 AT&T     */
  27 /*        All Rights Reserved   */
  28 
  29 #include <stdlib.h>
  30 #include <errno.h>
  31 #include <libelf.h>
  32 #include "decl.h"
  33 #include "msg.h"
  34 
  35 
  36 /*
  37  * Convert archive symbol table to memory format
  38  *
  39  * This takes a pointer to file's archive symbol table, alignment
  40  * unconstrained.  Returns null terminated vector of Elf_Arsym
  41  * structures. Elf_Arsym uses size_t to represent offsets, which
  42  * will be 32-bit in 32-bit versions, and 64-bits otherwise.
  43  *
  44  * There are two forms of archive symbol table, the original 32-bit
  45  * form, and a 64-bit form originally found in IRIX64. The two formats
  46  * differ only in the width of the integer word:
  47  *
  48  *              # offsets       4/8-byte word
  49  *              offset[0...]    4/8-byte word each
  50  *              strings         null-terminated, for offset[x]
  51  *
  52  * By default, the 64-bit form is only used when the archive exceeds
  53  * the limits of 32-bits (4GB) in size. However, this is not required,
  54  * and the ar -S option can be used to create a 64-bit symbol table in
  55  * an archive that is under 4GB.
  56  *
  57  * Both 32 and 64-bit versions of libelf can read the 32-bit format
  58  * without loss of information. Similarly, a 64-bit version of libelf
  59  * will have no problem reading a 64-bit symbol table. This leaves the
  60  * case where a 32-bit libelf reads a 64-bit symbol table, which requires
  61  * some explanation. The offsets in a 64-bit symbol table will have zeros
  62  * in the upper half of the words until the size of the archive exceeds 4GB.
  63  * However, 32-bit libelf is unable to read any files larger than 2GB
  64  * (see comments in update.c). As such, any archive that the 32-bit version
  65  * of this code will encounter will be under 4GB in size. The upper 4
  66  * bytes of each word will be zero, and can be safely ignored.
  67  */
  68 
  69 
  70 /*
  71  * Offsets in archive headers are written in MSB (large endian) order
  72  * on all platforms, regardless of native byte order. These macros read
  73  * 4 and 8 byte values from unaligned memory.
  74  *
  75  * note:
  76  * -    The get8() macro for 32-bit code can ignore the first 4 bytes of
  77  *      of the word, because they are known to be 0.
  78  *
  79  * -    The inner most value in these macros is cast to an unsigned integer
  80  *      of the final width in order to prevent the C comilier from doing
  81  *      unwanted sign extension when the topmost bit of a byte is set.
  82  */
  83 #define get4(p) (((((((uint32_t)p[0]<<8)+p[1])<<8)+p[2])<<8)+p[3])
  84 
  85 #ifdef _LP64
  86 #define get8(p) (((((((((((((((uint64_t)p[0]<<8)+p[1])<<8)+p[2])<<8)+ \
  87     p[3])<<8)+p[4])<<8)+p[5])<<8)+p[6])<<8)+p[7])
  88 #else
  89 #define get8(p) (((((((uint64_t)p[4]<<8)+p[5])<<8)+p[6])<<8)+p[7])
  90 #endif
  91 
  92 
  93 static Elf_Void *
  94 arsym(Byte *off, size_t sz, size_t *e, int is64)
  95 {
  96         char            *endstr = (char *)off + sz;
  97         register char   *str;
  98         Byte            *endoff;
  99         Elf_Void        *oas;
 100         size_t          eltsize = is64 ? 8 : 4;
 101 
 102         {
 103                 register size_t n;
 104 
 105                 if (is64) {
 106                         if (sz < 8 || (sz - 8) / 8 < (n = get8(off))) {
 107                                 _elf_seterr(EFMT_ARSYMSZ, 0);
 108                                 return (0);
 109                         }
 110                 } else {
 111                         if (sz < 4 || (sz - 4) / 4 < (n = get4(off))) {
 112                                 _elf_seterr(EFMT_ARSYMSZ, 0);
 113                                 return (0);
 114                         }
 115                 }
 116                 off += eltsize;
 117                 endoff = off + n * eltsize;
 118 
 119                 /*
 120                  * string table must be present, null terminated
 121                  */
 122 
 123                 if (((str = (char *)endoff) >= endstr) ||
 124                     (*(endstr - 1) != '\0')) {
 125                         _elf_seterr(EFMT_ARSYM, 0);
 126                         return (0);
 127                 }
 128 
 129                 /*
 130                  * overflow can occur here, but not likely
 131                  */
 132 
 133                 *e = n + 1;
 134                 n = sizeof (Elf_Arsym) * (n + 1);
 135                 if ((oas = malloc(n)) == 0) {
 136                         _elf_seterr(EMEM_ARSYM, errno);
 137                         return (0);
 138                 }
 139         }
 140         {
 141                 register Elf_Arsym      *as = (Elf_Arsym *)oas;
 142 
 143                 while (off < endoff) {
 144                         if (str >= endstr) {
 145                                 _elf_seterr(EFMT_ARSYMSTR, 0);
 146                                 free(oas);
 147                                 return (0);
 148                         }
 149                         if (is64)
 150                                 as->as_off = get8(off);
 151                         else
 152                                 as->as_off = get4(off);
 153                         as->as_name = str;
 154                         as->as_hash = elf_hash(str);
 155                         ++as;
 156                         off += eltsize;
 157                         while (*str++ != '\0')
 158                                 /* LINTED */
 159                                 ;
 160                 }
 161                 as->as_name = 0;
 162                 as->as_off = 0;
 163                 as->as_hash = ~(unsigned long)0L;
 164         }
 165         return (oas);
 166 }
 167 
 168 
 169 Elf_Arsym *
 170 elf_getarsym(Elf *elf, size_t *ptr)
 171 {
 172         Byte            *as;
 173         size_t          sz;
 174         Elf_Arsym       *rc;
 175         int             is64;
 176 
 177         if (ptr != 0)
 178                 *ptr = 0;
 179         if (elf == NULL)
 180                 return (0);
 181         ELFRLOCK(elf);
 182         if (elf->ed_kind != ELF_K_AR) {
 183                 ELFUNLOCK(elf);
 184                 _elf_seterr(EREQ_AR, 0);
 185                 return (0);
 186         }
 187         if ((as = (Byte *)elf->ed_arsym) == 0) {
 188                 ELFUNLOCK(elf);
 189                 return (0);
 190         }
 191         if (elf->ed_myflags & EDF_ASALLOC) {
 192                 if (ptr != 0)
 193                         *ptr = elf->ed_arsymsz;
 194                 ELFUNLOCK(elf);
 195                 /* LINTED */
 196                 return ((Elf_Arsym *)as);
 197         }
 198         is64 = (elf->ed_myflags & EDF_ARSYM64) != 0;
 199 
 200         /*
 201          * We're gonna need a write lock.
 202          */
 203         ELFUNLOCK(elf)
 204         ELFWLOCK(elf)
 205         sz = elf->ed_arsymsz;
 206         if (_elf_vm(elf, (size_t)(as - (Byte *)elf->ed_ident), sz) !=
 207             OK_YES) {
 208                 ELFUNLOCK(elf);
 209                 return (0);
 210         }
 211         if ((elf->ed_arsym = arsym(as, sz, &elf->ed_arsymsz, is64)) == 0) {
 212                 ELFUNLOCK(elf);
 213                 return (0);
 214         }
 215         elf->ed_myflags |= EDF_ASALLOC;
 216         if (ptr != 0)
 217                 *ptr = elf->ed_arsymsz;
 218         rc = (Elf_Arsym *)elf->ed_arsym;
 219         ELFUNLOCK(elf);
 220         return (rc);
 221 }
 222 
 223 /*
 224  * Private function to obtain the value sizeof() would return
 225  * for a word from the symbol table from the given archive. Normally,
 226  * this is an unimportant implementation detail hidden within
 227  * elf_getarsym(). However, it is useful to elfdump for formatting the
 228  * output correctly, and for the file command.
 229  *
 230  * exit:
 231  *      Returns 4 (32-bit) or 8 (64-bit) if a symbol table is present.
 232  *      Returns 0 in all other cases.
 233  */
 234 size_t
 235 _elf_getarsymwordsize(Elf *elf)
 236 {
 237         size_t  size;
 238 
 239         if (elf == NULL)
 240                 return (0);
 241 
 242         ELFRLOCK(elf);
 243         if ((elf->ed_kind == ELF_K_AR) && (elf->ed_arsym != 0))
 244                 size = (elf->ed_myflags & EDF_ARSYM64) ? 8 : 4;
 245         else
 246                 size = 0;
 247         ELFUNLOCK(elf);
 248 
 249         return (size);
 250 }