1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #define LOG_TAG "Corkscrew" 18 //#define LOG_NDEBUG 0 19 20 #include <corkscrew/symbol_table.h> 21 22 #include <stdlib.h> 23 #include <fcntl.h> 24 #include <string.h> 25 #include <sys/stat.h> 26 #include <sys/mman.h> 27 #include <sys/exec_elf.h> 28 #include <cutils/log.h> 29 30 // Compare function for qsort 31 static int qcompar(const void *a, const void *b) { 32 const symbol_t* asym = (const symbol_t*)a; 33 const symbol_t* bsym = (const symbol_t*)b; 34 if (asym->start > bsym->start) return 1; 35 if (asym->start < bsym->start) return -1; 36 return 0; 37 } 38 39 // Compare function for bsearch 40 static int bcompar(const void *key, const void *element) { 41 uintptr_t addr = *(const uintptr_t*)key; 42 const symbol_t* symbol = (const symbol_t*)element; 43 if (addr < symbol->start) return -1; 44 if (addr >= symbol->end) return 1; 45 return 0; 46 } 47 48 symbol_table_t* load_symbol_table(const char *filename) { 49 symbol_table_t* table = NULL; 50 ALOGV("Loading symbol table from '%s'.", filename); 51 52 int fd = open(filename, O_RDONLY); 53 if (fd < 0) { 54 goto out; 55 } 56 57 struct stat sb; 58 if (fstat(fd, &sb)) { 59 goto out_close; 60 } 61 62 size_t length = sb.st_size; 63 char* base = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0); 64 if (base == MAP_FAILED) { 65 goto out_close; 66 } 67 68 // Parse the file header 69 Elf32_Ehdr *hdr = (Elf32_Ehdr*)base; 70 if (!IS_ELF(*hdr)) { 71 goto out_close; 72 } 73 Elf32_Shdr *shdr = (Elf32_Shdr*)(base + hdr->e_shoff); 74 75 // Search for the dynamic symbols section 76 int sym_idx = -1; 77 int dynsym_idx = -1; 78 for (Elf32_Half i = 0; i < hdr->e_shnum; i++) { 79 if (shdr[i].sh_type == SHT_SYMTAB) { 80 sym_idx = i; 81 } 82 if (shdr[i].sh_type == SHT_DYNSYM) { 83 dynsym_idx = i; 84 } 85 } 86 if (dynsym_idx == -1 && sym_idx == -1) { 87 goto out_unmap; 88 } 89 90 table = malloc(sizeof(symbol_table_t)); 91 if(!table) { 92 goto out_unmap; 93 } 94 table->num_symbols = 0; 95 96 Elf32_Sym *dynsyms = NULL; 97 int dynnumsyms = 0; 98 char *dynstr = NULL; 99 if (dynsym_idx != -1) { 100 dynsyms = (Elf32_Sym*)(base + shdr[dynsym_idx].sh_offset); 101 dynnumsyms = shdr[dynsym_idx].sh_size / shdr[dynsym_idx].sh_entsize; 102 int dynstr_idx = shdr[dynsym_idx].sh_link; 103 dynstr = base + shdr[dynstr_idx].sh_offset; 104 } 105 106 Elf32_Sym *syms = NULL; 107 int numsyms = 0; 108 char *str = NULL; 109 if (sym_idx != -1) { 110 syms = (Elf32_Sym*)(base + shdr[sym_idx].sh_offset); 111 numsyms = shdr[sym_idx].sh_size / shdr[sym_idx].sh_entsize; 112 int str_idx = shdr[sym_idx].sh_link; 113 str = base + shdr[str_idx].sh_offset; 114 } 115 116 int dynsymbol_count = 0; 117 if (dynsym_idx != -1) { 118 // Iterate through the dynamic symbol table, and count how many symbols 119 // are actually defined 120 for (int i = 0; i < dynnumsyms; i++) { 121 if (dynsyms[i].st_shndx != SHN_UNDEF) { 122 dynsymbol_count++; 123 } 124 } 125 } 126 127 size_t symbol_count = 0; 128 if (sym_idx != -1) { 129 // Iterate through the symbol table, and count how many symbols 130 // are actually defined 131 for (int i = 0; i < numsyms; i++) { 132 if (syms[i].st_shndx != SHN_UNDEF 133 && str[syms[i].st_name] 134 && syms[i].st_value 135 && syms[i].st_size) { 136 symbol_count++; 137 } 138 } 139 } 140 141 // Now, create an entry in our symbol table structure for each symbol... 142 table->num_symbols += symbol_count + dynsymbol_count; 143 table->symbols = malloc(table->num_symbols * sizeof(symbol_t)); 144 if (!table->symbols) { 145 free(table); 146 table = NULL; 147 goto out_unmap; 148 } 149 150 size_t symbol_index = 0; 151 if (dynsym_idx != -1) { 152 // ...and populate them 153 for (int i = 0; i < dynnumsyms; i++) { 154 if (dynsyms[i].st_shndx != SHN_UNDEF) { 155 table->symbols[symbol_index].name = strdup(dynstr + dynsyms[i].st_name); 156 table->symbols[symbol_index].start = dynsyms[i].st_value; 157 table->symbols[symbol_index].end = dynsyms[i].st_value + dynsyms[i].st_size; 158 ALOGV(" [%d] '%s' 0x%08x-0x%08x (DYNAMIC)", 159 symbol_index, table->symbols[symbol_index].name, 160 table->symbols[symbol_index].start, table->symbols[symbol_index].end); 161 symbol_index += 1; 162 } 163 } 164 } 165 166 if (sym_idx != -1) { 167 // ...and populate them 168 for (int i = 0; i < numsyms; i++) { 169 if (syms[i].st_shndx != SHN_UNDEF 170 && str[syms[i].st_name] 171 && syms[i].st_value 172 && syms[i].st_size) { 173 table->symbols[symbol_index].name = strdup(str + syms[i].st_name); 174 table->symbols[symbol_index].start = syms[i].st_value; 175 table->symbols[symbol_index].end = syms[i].st_value + syms[i].st_size; 176 ALOGV(" [%d] '%s' 0x%08x-0x%08x", 177 symbol_index, table->symbols[symbol_index].name, 178 table->symbols[symbol_index].start, table->symbols[symbol_index].end); 179 symbol_index += 1; 180 } 181 } 182 } 183 184 // Sort the symbol table entries, so they can be bsearched later 185 qsort(table->symbols, table->num_symbols, sizeof(symbol_t), qcompar); 186 187 out_unmap: 188 munmap(base, length); 189 190 out_close: 191 close(fd); 192 193 out: 194 return table; 195 } 196 197 void free_symbol_table(symbol_table_t* table) { 198 if (table) { 199 for (size_t i = 0; i < table->num_symbols; i++) { 200 free(table->symbols[i].name); 201 } 202 free(table->symbols); 203 free(table); 204 } 205 } 206 207 const symbol_t* find_symbol(const symbol_table_t* table, uintptr_t addr) { 208 if (!table) return NULL; 209 return (const symbol_t*)bsearch(&addr, table->symbols, table->num_symbols, 210 sizeof(symbol_t), bcompar); 211 } 212