1 /*
   2  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 #include <unistd.h>
  26 #include <search.h>
  27 #include <stdlib.h>
  28 #include <string.h>
  29 #include "hsearch_r.h"
  30 #include "symtab.h"
  31 #include "salibelf.h"
  32 
  33 
  34 // ----------------------------------------------------
  35 // functions for symbol lookups
  36 // ----------------------------------------------------
  37 
  38 struct elf_section {
  39   ELF_SHDR   *c_shdr;
  40   void       *c_data;
  41 };
  42 
  43 struct elf_symbol {
  44   char *name;
  45   uintptr_t offset;
  46   uintptr_t size;
  47 };
  48 
  49 typedef struct symtab {
  50   char *strs;
  51   size_t num_symbols;
  52   struct elf_symbol *symbols;
  53   struct hsearch_data *hash_table;
  54 } symtab_t;
  55 
  56 // read symbol table from given fd.
  57 struct symtab* build_symtab(int fd) {
  58   ELF_EHDR ehdr;
  59   struct symtab* symtab = NULL;
  60 
  61   // Reading of elf header
  62   struct elf_section *scn_cache = NULL;
  63   int cnt = 0;
  64   ELF_SHDR* shbuf = NULL;
  65   ELF_SHDR* cursct = NULL;
  66   ELF_PHDR* phbuf = NULL;
  67   int symtab_found = 0;
  68   int dynsym_found = 0;
  69   uint32_t symsection = SHT_SYMTAB;
  70 
  71   uintptr_t baseaddr = (uintptr_t)-1;
  72 
  73   lseek(fd, (off_t)0L, SEEK_SET);
  74   if (! read_elf_header(fd, &ehdr)) {
  75     // not an elf
  76     return NULL;
  77   }
  78 
  79   // read ELF header
  80   if ((shbuf = read_section_header_table(fd, &ehdr)) == NULL) {
  81     goto quit;
  82   }
  83 
  84   baseaddr = find_base_address(fd, &ehdr);
  85 
  86   scn_cache = (struct elf_section *)
  87               calloc(ehdr.e_shnum * sizeof(struct elf_section), 1);
  88   if (scn_cache == NULL) {
  89     goto quit;
  90   }
  91 
  92   for (cursct = shbuf, cnt = 0; cnt < ehdr.e_shnum; cnt++) {
  93     scn_cache[cnt].c_shdr = cursct;
  94     if (cursct->sh_type == SHT_SYMTAB ||
  95         cursct->sh_type == SHT_STRTAB ||
  96         cursct->sh_type == SHT_DYNSYM) {
  97       if ( (scn_cache[cnt].c_data = read_section_data(fd, &ehdr, cursct)) == NULL) {
  98          goto quit;
  99       }
 100     }
 101 
 102     if (cursct->sh_type == SHT_SYMTAB)
 103        symtab_found++;
 104 
 105     if (cursct->sh_type == SHT_DYNSYM)
 106        dynsym_found++;
 107 
 108     cursct++;
 109   }
 110 
 111   if (!symtab_found && dynsym_found)
 112      symsection = SHT_DYNSYM;
 113 
 114   for (cnt = 1; cnt < ehdr.e_shnum; cnt++) {
 115     ELF_SHDR *shdr = scn_cache[cnt].c_shdr;
 116 
 117     if (shdr->sh_type == symsection) {
 118       ELF_SYM  *syms;
 119       int j, n, rslt;
 120       size_t size;
 121 
 122       // FIXME: there could be multiple data buffers associated with the
 123       // same ELF section. Here we can handle only one buffer. See man page
 124       // for elf_getdata on Solaris.
 125 
 126       // guarantee(symtab == NULL, "multiple symtab");
 127       symtab = (struct symtab*)calloc(1, sizeof(struct symtab));
 128       if (symtab == NULL) {
 129          goto quit;
 130       }
 131       // the symbol table
 132       syms = (ELF_SYM *)scn_cache[cnt].c_data;
 133 
 134       // number of symbols
 135       n = shdr->sh_size / shdr->sh_entsize;
 136 
 137       // create hash table, we use hcreate_r, hsearch_r and hdestroy_r to
 138       // manipulate the hash table.
 139       symtab->hash_table = (struct hsearch_data*) calloc(1, sizeof(struct hsearch_data));
 140       rslt = hcreate_r(n, symtab->hash_table);
 141       // guarantee(rslt, "unexpected failure: hcreate_r");
 142 
 143       // shdr->sh_link points to the section that contains the actual strings
 144       // for symbol names. the st_name field in ELF_SYM is just the
 145       // string table index. we make a copy of the string table so the
 146       // strings will not be destroyed by elf_end.
 147       size = scn_cache[shdr->sh_link].c_shdr->sh_size;
 148       symtab->strs = (char *)malloc(size);
 149       memcpy(symtab->strs, scn_cache[shdr->sh_link].c_data, size);
 150 
 151       // allocate memory for storing symbol offset and size;
 152       symtab->num_symbols = n;
 153       symtab->symbols = (struct elf_symbol *)calloc(n , sizeof(struct elf_symbol));
 154 
 155       // copy symbols info our symtab and enter them info the hash table
 156       for (j = 0; j < n; j++, syms++) {
 157         ENTRY item, *ret;
 158         char *sym_name = symtab->strs + syms->st_name;
 159 
 160         // skip non-object and non-function symbols
 161         int st_type = ELF_ST_TYPE(syms->st_info);
 162         if ( st_type != STT_FUNC && st_type != STT_OBJECT)
 163            continue;
 164         // skip empty strings and undefined symbols
 165         if (*sym_name == '\0' || syms->st_shndx == SHN_UNDEF) continue;
 166 
 167         symtab->symbols[j].name   = sym_name;
 168         symtab->symbols[j].offset = syms->st_value - baseaddr;
 169         symtab->symbols[j].size   = syms->st_size;
 170 
 171         item.key = sym_name;
 172         item.data = (void *)&(symtab->symbols[j]);
 173 
 174         hsearch_r(item, ENTER, &ret, symtab->hash_table);
 175       }
 176     }
 177   }
 178 
 179 quit:
 180   if (shbuf) free(shbuf);
 181   if (phbuf) free(phbuf);
 182   if (scn_cache) {
 183     for (cnt = 0; cnt < ehdr.e_shnum; cnt++) {
 184       if (scn_cache[cnt].c_data != NULL) {
 185         free(scn_cache[cnt].c_data);
 186       }
 187     }
 188     free(scn_cache);
 189   }
 190   return symtab;
 191 }
 192 
 193 void destroy_symtab(struct symtab* symtab) {
 194   if (!symtab) return;
 195   if (symtab->strs) free(symtab->strs);
 196   if (symtab->symbols) free(symtab->symbols);
 197   if (symtab->hash_table) {
 198      hdestroy_r(symtab->hash_table);
 199      free(symtab->hash_table);
 200   }
 201   free(symtab);
 202 }
 203 
 204 uintptr_t search_symbol(struct symtab* symtab, uintptr_t base,
 205                       const char *sym_name, int *sym_size) {
 206   ENTRY item;
 207   ENTRY* ret = NULL;
 208 
 209   // library does not have symbol table
 210   if (!symtab || !symtab->hash_table)
 211      return (uintptr_t)NULL;
 212 
 213   item.key = (char*) strdup(sym_name);
 214   hsearch_r(item, FIND, &ret, symtab->hash_table);
 215   if (ret) {
 216     struct elf_symbol * sym = (struct elf_symbol *)(ret->data);
 217     uintptr_t rslt = (uintptr_t) ((char*)base + sym->offset);
 218     if (sym_size) *sym_size = sym->size;
 219     free(item.key);
 220     return rslt;
 221   }
 222 
 223 quit:
 224   free(item.key);
 225   return (uintptr_t) NULL;
 226 }
 227 
 228 const char* nearest_symbol(struct symtab* symtab, uintptr_t offset,
 229                            uintptr_t* poffset) {
 230   int n = 0;
 231   if (!symtab) return NULL;
 232   for (; n < symtab->num_symbols; n++) {
 233      struct elf_symbol* sym = &(symtab->symbols[n]);
 234      if (sym->name != NULL &&
 235          offset >= sym->offset && offset < sym->offset + sym->size) {
 236         if (poffset) *poffset = (offset - sym->offset);
 237         return sym->name;
 238      }
 239   }
 240   return NULL;
 241 }