blob: 982ccc8e24d1f37d662fa9a2eef017acb3f0971c [file] [log] [blame]
Jeff Brown501edd22011-10-19 20:35:35 -07001/*
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
Elliott Hughes71363a82012-05-18 11:56:17 -070022#include <stdbool.h>
Jeff Brown501edd22011-10-19 20:35:35 -070023#include <stdlib.h>
24#include <fcntl.h>
25#include <string.h>
26#include <sys/stat.h>
27#include <sys/mman.h>
Jeff Brown501edd22011-10-19 20:35:35 -070028#include <cutils/log.h>
29
Elliott Hughesbfec3a32012-05-24 19:03:07 -070030#if defined(__APPLE__)
31#else
32
33#include <elf.h>
34
Elliott Hughes71363a82012-05-18 11:56:17 -070035static bool is_elf(Elf32_Ehdr* e) {
36 return (e->e_ident[EI_MAG0] == ELFMAG0 &&
37 e->e_ident[EI_MAG1] == ELFMAG1 &&
38 e->e_ident[EI_MAG2] == ELFMAG2 &&
39 e->e_ident[EI_MAG3] == ELFMAG3);
40}
41
Elliott Hughesbfec3a32012-05-24 19:03:07 -070042#endif
43
Jeff Brown501edd22011-10-19 20:35:35 -070044// Compare function for qsort
45static int qcompar(const void *a, const void *b) {
46 const symbol_t* asym = (const symbol_t*)a;
47 const symbol_t* bsym = (const symbol_t*)b;
48 if (asym->start > bsym->start) return 1;
49 if (asym->start < bsym->start) return -1;
50 return 0;
51}
52
53// Compare function for bsearch
54static int bcompar(const void *key, const void *element) {
55 uintptr_t addr = *(const uintptr_t*)key;
56 const symbol_t* symbol = (const symbol_t*)element;
57 if (addr < symbol->start) return -1;
58 if (addr >= symbol->end) return 1;
59 return 0;
60}
61
62symbol_table_t* load_symbol_table(const char *filename) {
63 symbol_table_t* table = NULL;
Elliott Hughesbfec3a32012-05-24 19:03:07 -070064#if !defined(__APPLE__)
Jeff Brown19b39f32011-11-21 21:10:00 -080065 ALOGV("Loading symbol table from '%s'.", filename);
Jeff Brown501edd22011-10-19 20:35:35 -070066
67 int fd = open(filename, O_RDONLY);
68 if (fd < 0) {
69 goto out;
70 }
71
72 struct stat sb;
73 if (fstat(fd, &sb)) {
74 goto out_close;
75 }
76
77 size_t length = sb.st_size;
78 char* base = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0);
79 if (base == MAP_FAILED) {
80 goto out_close;
81 }
82
83 // Parse the file header
84 Elf32_Ehdr *hdr = (Elf32_Ehdr*)base;
Elliott Hughes71363a82012-05-18 11:56:17 -070085 if (!is_elf(hdr)) {
Jeff Brown501edd22011-10-19 20:35:35 -070086 goto out_close;
87 }
88 Elf32_Shdr *shdr = (Elf32_Shdr*)(base + hdr->e_shoff);
89
90 // Search for the dynamic symbols section
91 int sym_idx = -1;
92 int dynsym_idx = -1;
93 for (Elf32_Half i = 0; i < hdr->e_shnum; i++) {
94 if (shdr[i].sh_type == SHT_SYMTAB) {
95 sym_idx = i;
96 }
97 if (shdr[i].sh_type == SHT_DYNSYM) {
98 dynsym_idx = i;
99 }
100 }
101 if (dynsym_idx == -1 && sym_idx == -1) {
102 goto out_unmap;
103 }
104
105 table = malloc(sizeof(symbol_table_t));
106 if(!table) {
107 goto out_unmap;
108 }
109 table->num_symbols = 0;
110
111 Elf32_Sym *dynsyms = NULL;
112 int dynnumsyms = 0;
113 char *dynstr = NULL;
114 if (dynsym_idx != -1) {
115 dynsyms = (Elf32_Sym*)(base + shdr[dynsym_idx].sh_offset);
116 dynnumsyms = shdr[dynsym_idx].sh_size / shdr[dynsym_idx].sh_entsize;
117 int dynstr_idx = shdr[dynsym_idx].sh_link;
118 dynstr = base + shdr[dynstr_idx].sh_offset;
119 }
120
121 Elf32_Sym *syms = NULL;
122 int numsyms = 0;
123 char *str = NULL;
124 if (sym_idx != -1) {
125 syms = (Elf32_Sym*)(base + shdr[sym_idx].sh_offset);
126 numsyms = shdr[sym_idx].sh_size / shdr[sym_idx].sh_entsize;
127 int str_idx = shdr[sym_idx].sh_link;
128 str = base + shdr[str_idx].sh_offset;
129 }
130
131 int dynsymbol_count = 0;
132 if (dynsym_idx != -1) {
133 // Iterate through the dynamic symbol table, and count how many symbols
134 // are actually defined
135 for (int i = 0; i < dynnumsyms; i++) {
136 if (dynsyms[i].st_shndx != SHN_UNDEF) {
137 dynsymbol_count++;
138 }
139 }
140 }
141
142 size_t symbol_count = 0;
143 if (sym_idx != -1) {
144 // Iterate through the symbol table, and count how many symbols
145 // are actually defined
146 for (int i = 0; i < numsyms; i++) {
147 if (syms[i].st_shndx != SHN_UNDEF
148 && str[syms[i].st_name]
149 && syms[i].st_value
150 && syms[i].st_size) {
151 symbol_count++;
152 }
153 }
154 }
155
156 // Now, create an entry in our symbol table structure for each symbol...
157 table->num_symbols += symbol_count + dynsymbol_count;
158 table->symbols = malloc(table->num_symbols * sizeof(symbol_t));
159 if (!table->symbols) {
160 free(table);
161 table = NULL;
162 goto out_unmap;
163 }
164
165 size_t symbol_index = 0;
166 if (dynsym_idx != -1) {
167 // ...and populate them
168 for (int i = 0; i < dynnumsyms; i++) {
169 if (dynsyms[i].st_shndx != SHN_UNDEF) {
170 table->symbols[symbol_index].name = strdup(dynstr + dynsyms[i].st_name);
171 table->symbols[symbol_index].start = dynsyms[i].st_value;
172 table->symbols[symbol_index].end = dynsyms[i].st_value + dynsyms[i].st_size;
Jeff Brown19b39f32011-11-21 21:10:00 -0800173 ALOGV(" [%d] '%s' 0x%08x-0x%08x (DYNAMIC)",
174 symbol_index, table->symbols[symbol_index].name,
175 table->symbols[symbol_index].start, table->symbols[symbol_index].end);
Jeff Brown501edd22011-10-19 20:35:35 -0700176 symbol_index += 1;
177 }
178 }
179 }
180
181 if (sym_idx != -1) {
182 // ...and populate them
183 for (int i = 0; i < numsyms; i++) {
184 if (syms[i].st_shndx != SHN_UNDEF
185 && str[syms[i].st_name]
186 && syms[i].st_value
187 && syms[i].st_size) {
188 table->symbols[symbol_index].name = strdup(str + syms[i].st_name);
189 table->symbols[symbol_index].start = syms[i].st_value;
190 table->symbols[symbol_index].end = syms[i].st_value + syms[i].st_size;
Jeff Brown19b39f32011-11-21 21:10:00 -0800191 ALOGV(" [%d] '%s' 0x%08x-0x%08x",
192 symbol_index, table->symbols[symbol_index].name,
193 table->symbols[symbol_index].start, table->symbols[symbol_index].end);
Jeff Brown501edd22011-10-19 20:35:35 -0700194 symbol_index += 1;
195 }
196 }
197 }
198
199 // Sort the symbol table entries, so they can be bsearched later
200 qsort(table->symbols, table->num_symbols, sizeof(symbol_t), qcompar);
201
202out_unmap:
203 munmap(base, length);
204
205out_close:
206 close(fd);
Elliott Hughesbfec3a32012-05-24 19:03:07 -0700207#endif
Jeff Brown501edd22011-10-19 20:35:35 -0700208
209out:
210 return table;
211}
212
213void free_symbol_table(symbol_table_t* table) {
214 if (table) {
215 for (size_t i = 0; i < table->num_symbols; i++) {
216 free(table->symbols[i].name);
217 }
218 free(table->symbols);
219 free(table);
220 }
221}
222
223const symbol_t* find_symbol(const symbol_table_t* table, uintptr_t addr) {
224 if (!table) return NULL;
225 return (const symbol_t*)bsearch(&addr, table->symbols, table->num_symbols,
226 sizeof(symbol_t), bcompar);
227}