1 /*
   2  * Copyright (c) 2003, 2005, 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,
  20  * CA 94065 USA or visit www.oracle.com if you need additional information or
  21  * have any questions.
  22  *
  23  */
  24 
  25 #include <unistd.h>
  26 #include <sys/procfs.h>
  27 #include <search.h>
  28 #include <stdlib.h>
  29 #include <string.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   char *names = NULL;
  60   struct symtab* symtab = NULL;
  61 
  62   // Reading of elf header
  63   struct elf_section *scn_cache = NULL;
  64   int cnt = 0;
  65   ELF_SHDR* shbuf = NULL;
  66   ELF_SHDR* cursct = NULL;
  67   ELF_PHDR* phbuf = NULL;
  68   ELF_PHDR* phdr = NULL;
  69 
  70   uintptr_t baseaddr = (uintptr_t)-1;
  71 
  72   lseek(fd, (off_t)0L, SEEK_SET);
  73   if (! read_elf_header(fd, &ehdr)) {
  74     // not an elf
  75     return NULL;
  76   }
  77 
  78   // read ELF header
  79   if ((shbuf = read_section_header_table(fd, &ehdr)) == NULL) {
  80     goto quit;
  81   }
  82 
  83   baseaddr = find_base_address(fd, &ehdr);
  84 
  85   scn_cache = (struct elf_section *)
  86               calloc(ehdr.e_shnum * sizeof(struct elf_section), 1);
  87   if (scn_cache == NULL) {
  88     goto quit;
  89   }
  90 
  91   for (cursct = shbuf, cnt = 0; cnt < ehdr.e_shnum; cnt++) {
  92     scn_cache[cnt].c_shdr = cursct;
  93     if (cursct->sh_type == SHT_SYMTAB || cursct->sh_type == SHT_STRTAB) {
  94       if ( (scn_cache[cnt].c_data = read_section_data(fd, &ehdr, cursct)) == NULL) {
  95          goto quit;
  96       }
  97     }
  98     cursct++;
  99   }
 100 
 101   for (cnt = 1; cnt < ehdr.e_shnum; cnt++) {
 102     ELF_SHDR *shdr = scn_cache[cnt].c_shdr;
 103 
 104     if (shdr->sh_type == SHT_SYMTAB) {
 105       ELF_SYM  *syms;
 106       int j, n, rslt;
 107       size_t size;
 108 
 109       // FIXME: there could be multiple data buffers associated with the
 110       // same ELF section. Here we can handle only one buffer. See man page
 111       // for elf_getdata on Solaris.
 112 
 113       // guarantee(symtab == NULL, "multiple symtab");
 114       symtab = (struct symtab*)calloc(1, sizeof(struct symtab));
 115       if (symtab == NULL) {
 116          goto quit;
 117       }
 118       // the symbol table
 119       syms = (ELF_SYM *)scn_cache[cnt].c_data;
 120 
 121       // number of symbols
 122       n = shdr->sh_size / shdr->sh_entsize;
 123 
 124       // create hash table, we use hcreate_r, hsearch_r and hdestroy_r to
 125       // manipulate the hash table.
 126       symtab->hash_table = (struct hsearch_data*) calloc(1, sizeof(struct hsearch_data));
 127       rslt = hcreate_r(n, symtab->hash_table);
 128       // guarantee(rslt, "unexpected failure: hcreate_r");
 129 
 130       // shdr->sh_link points to the section that contains the actual strings
 131       // for symbol names. the st_name field in ELF_SYM is just the
 132       // string table index. we make a copy of the string table so the
 133       // strings will not be destroyed by elf_end.
 134       size = scn_cache[shdr->sh_link].c_shdr->sh_size;
 135       symtab->strs = (char *)malloc(size);
 136       memcpy(symtab->strs, scn_cache[shdr->sh_link].c_data, size);
 137 
 138       // allocate memory for storing symbol offset and size;
 139       symtab->num_symbols = n;
 140       symtab->symbols = (struct elf_symbol *)calloc(n , sizeof(struct elf_symbol));
 141 
 142       // copy symbols info our symtab and enter them info the hash table
 143       for (j = 0; j < n; j++, syms++) {
 144         ENTRY item, *ret;
 145         char *sym_name = symtab->strs + syms->st_name;
 146 
 147         // skip non-object and non-function symbols
 148         int st_type = ELF_ST_TYPE(syms->st_info);
 149         if ( st_type != STT_FUNC && st_type != STT_OBJECT)
 150            continue;
 151         // skip empty strings and undefined symbols
 152         if (*sym_name == '\0' || syms->st_shndx == SHN_UNDEF) continue;
 153 
 154         symtab->symbols[j].name   = sym_name;
 155         symtab->symbols[j].offset = syms->st_value - baseaddr;
 156         symtab->symbols[j].size   = syms->st_size;
 157 
 158         item.key = sym_name;
 159         item.data = (void *)&(symtab->symbols[j]);
 160 
 161         hsearch_r(item, ENTER, &ret, symtab->hash_table);
 162       }
 163     }
 164   }
 165 
 166 quit:
 167   if (shbuf) free(shbuf);
 168   if (phbuf) free(phbuf);
 169   if (scn_cache) {
 170     for (cnt = 0; cnt < ehdr.e_shnum; cnt++) {
 171       if (scn_cache[cnt].c_data != NULL) {
 172         free(scn_cache[cnt].c_data);
 173       }
 174     }
 175     free(scn_cache);
 176   }
 177   return symtab;
 178 }
 179 
 180 void destroy_symtab(struct symtab* symtab) {
 181   if (!symtab) return;
 182   if (symtab->strs) free(symtab->strs);
 183   if (symtab->symbols) free(symtab->symbols);
 184   if (symtab->hash_table) {
 185      hdestroy_r(symtab->hash_table);
 186      free(symtab->hash_table);
 187   }
 188   free(symtab);
 189 }
 190 
 191 uintptr_t search_symbol(struct symtab* symtab, uintptr_t base,
 192                       const char *sym_name, int *sym_size) {
 193   ENTRY item;
 194   ENTRY* ret = NULL;
 195 
 196   // library does not have symbol table
 197   if (!symtab || !symtab->hash_table)
 198      return (uintptr_t)NULL;
 199 
 200   item.key = (char*) strdup(sym_name);
 201   hsearch_r(item, FIND, &ret, symtab->hash_table);
 202   if (ret) {
 203     struct elf_symbol * sym = (struct elf_symbol *)(ret->data);
 204     uintptr_t rslt = (uintptr_t) ((char*)base + sym->offset);
 205     if (sym_size) *sym_size = sym->size;
 206     free(item.key);
 207     return rslt;
 208   }
 209 
 210 quit:
 211   free(item.key);
 212   return (uintptr_t) NULL;
 213 }
 214 
 215 const char* nearest_symbol(struct symtab* symtab, uintptr_t offset,
 216                            uintptr_t* poffset) {
 217   int n = 0;
 218   if (!symtab) return NULL;
 219   for (; n < symtab->num_symbols; n++) {
 220      struct elf_symbol* sym = &(symtab->symbols[n]);
 221      if (sym->name != NULL &&
 222          offset >= sym->offset && offset < sym->offset + sym->size) {
 223         if (poffset) *poffset = (offset - sym->offset);
 224         return sym->name;
 225      }
 226   }
 227   return NULL;
 228 }