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