-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathsymbols.c
324 lines (269 loc) · 7.06 KB
/
symbols.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
/*
* Copyright (c) 2007-2012 Message Systems, Inc. All rights reserved
* For licensing information, see:
* https://bitbucket.org/wez/gimli/src/tip/LICENSE
*/
#include "impl.h"
struct gimli_symbol *gimli_add_symbol(gimli_mapped_object_t f,
const char *name, gimli_addr_t addr, uint32_t size)
{
struct gimli_symbol *s;
char buf[1024];
if (f->symcount + 1 >= f->symallocd) {
f->symallocd = f->symallocd ? f->symallocd * 2 : 1024;
f->symtab = realloc(f->symtab, f->symallocd * sizeof(*s));
}
f->symchanged = 1;
s = &f->symtab[f->symcount++];
memset(s, 0, sizeof(*s));
s->rawname = name;//strdup(name);
s->name = s->rawname;
if (gimli_demangle(s->rawname, buf, sizeof(buf))) {
s->name = strdup(buf);
}
s->addr = addr;
s->size = size;
if (debug && 0) {
printf("add symbol: %s`%s = " PTRFMT " (%d) %s\n",
f->objname, s->name, s->addr, s->size,
s->rawname != s->name ? s->rawname : "");
}
return s;
}
static int sort_syms_by_addr_asc(const void *A, const void *B)
{
struct gimli_symbol *a = (struct gimli_symbol*)A;
struct gimli_symbol *b = (struct gimli_symbol*)B;
if (a->addr < b->addr) {
return -1;
}
if (a->addr > b->addr) {
return 1;
}
#ifndef __MACH__
/* no size information available on darwin */
if (a->size && b->size) {
return a->size - b->size;
}
#endif
/* use their relative ordering as a last resort */
return a - b;
}
static void bake_symtab(gimli_mapped_object_t f)
{
int i, j;
struct gimli_symbol *s;
if (!f->symchanged) return;
f->symchanged = 0;
if (debug) {
printf("baking %" PRId64 " symbols for %s base=" PTRFMT "\n",
f->symcount, f->objname, f->base_addr);
}
if (f->symhash) {
gimli_hash_delete_all(f->symhash, 0);
} else {
f->symhash = gimli_hash_new_size(NULL, 0, f->symcount);
}
/* sort for bsearch */
qsort(f->symtab, f->symcount, sizeof(struct gimli_symbol),
sort_syms_by_addr_asc);
//printf("sorting %d symbols in %s\n", f->symcount, f->objname);
for (i = 0; i < f->symcount; i++) {
s = &f->symtab[i];
#ifdef __MACH__
/* the nlist symbols on this system have no size information;
* synthesize it now by looking at the next highest item. */
s->size = 8; /* start with something lame */
for (j = i + 1; j < f->symcount; j++) {
if (f->symtab[j].addr > s->addr) {
s->size = f->symtab[j].addr - s->addr;
break;
}
}
#endif
/* this may fail due to duplicate names */
gimli_hash_insert(f->symhash, s->rawname, s);
}
}
/* lower is better.
* We weight underscores at the start heavier than
* those later on.
*/
static int calc_readability(const char *name)
{
int start = 1;
int value = 0;
while (*name) {
if (*name == '_') {
if (start) {
value += 2;
} else {
value++;
}
} else {
start = 0;
}
name++;
}
return value;
}
static int search_compare_symaddr(const void *addrp, const void *S)
{
gimli_addr_t addr = *(gimli_addr_t*)addrp;
struct gimli_symbol *s = (struct gimli_symbol*)S;
if (addr < s->addr) {
return -1;
}
if (addr < s->addr + s->size) {
return 0;
}
return 1;
}
struct gimli_symbol *find_symbol_for_addr(gimli_mapped_object_t f,
gimli_addr_t addr)
{
struct gimli_symbol *csym, *best, *last;
int i, n, bu, cu;
bake_symtab(f);
if (!f->symcount) return NULL;
csym = bsearch(&addr, f->symtab, f->symcount,
sizeof(struct gimli_symbol), search_compare_symaddr);
if (!csym) {
return NULL;
}
/* we have a candidate symbol. However, it may be an arbitrary
* symbol in a run of multiple candidates; we need to find the
* best candidate */
i = csym - f->symtab;
/* look backwards until we find the first symbol in the range */
n = i;
while (n > 0) {
if (f->symtab[n-1].addr < addr) {
csym = &f->symtab[n];
break;
}
n--;
}
/* look forwards for the last in the range */
last = csym;
n = i;
while (n < f->symcount - 1) {
if (f->symtab[n+1].addr > addr) {
last = &f->symtab[n];
break;
}
n++;
}
/* best available */
best = csym;
bu = calc_readability(best->name);
csym++;
while (csym <= last) {
cu = calc_readability(csym->name);
if (cu < bu) {
/* this one is better */
best = csym;
bu = cu;
}
csym++;
}
return best;
}
static struct gimli_symbol *sym_lookup(gimli_mapped_object_t file,
const char *name)
{
struct gimli_symbol *sym = NULL;
bake_symtab(file);
if (!file->symcount) return NULL;
if (gimli_hash_find(file->symhash, name, (void**)&sym)) {
return sym;
}
return NULL;
}
struct find_sym {
const char *name;
gimli_mapped_object_t file;
struct gimli_symbol *sym;
};
static gimli_iter_status_t search_for_sym(const char *k, int klen,
void *item, void *arg)
{
gimli_mapped_object_t file = item;
struct find_sym *find = arg;
find->sym = sym_lookup(file, find->name);
if (find->sym) return GIMLI_ITER_STOP;
return GIMLI_ITER_CONT;
}
static gimli_iter_status_t search_for_basename(const char *k, int klen,
void *item, void *arg)
{
gimli_mapped_object_t file = item;
struct find_sym *find = arg;
char buf[1024];
strcpy(buf, file->objname);
if (!strcmp(basename(buf), find->name)) {
find->file = file;
return GIMLI_ITER_STOP;
}
return GIMLI_ITER_CONT;
}
static gimli_iter_status_t search_for_symlink(const char *k, int klen,
void *item, void *arg)
{
gimli_mapped_object_t file = item;
struct find_sym *find = arg;
char dir[1024];
char buf[1024];
int len;
strcpy(dir, file->objname);
snprintf(buf, sizeof(buf)-1, "%s/%s", dirname(dir), find->name);
if (realpath(buf, dir)) {
if (!strcmp(dir, file->objname)) {
find->file = file;
return GIMLI_ITER_STOP;
}
}
return GIMLI_ITER_CONT;
}
struct gimli_symbol *gimli_sym_lookup(gimli_proc_t proc, const char *obj, const char *name)
{
gimli_mapped_object_t f;
struct gimli_symbol *sym = NULL;
struct find_sym find;
find.name = name;
/* if obj is NULL, we're looking for it anywhere we can find it */
if (obj == NULL) {
gimli_hash_iter(proc->files, search_for_sym, &find);
if (debug) {
printf("sym_lookup: %s => " PTRFMT "\n", name, sym ? sym->addr : 0);
}
return find.sym;
}
f = gimli_find_object(proc, obj);
if (!f) {
/* we may have just been given the basename of the object, in which
* case, we need to run through the list and match on basenames */
find.file = NULL;
find.name = obj;
gimli_hash_iter(proc->files, search_for_basename, &find);
if (!find.file) {
/* so maybe we were given the basename it refers to a symlink
* that we need to resolve... */
gimli_hash_iter(proc->files, search_for_symlink, &find);
}
if (!find.file) {
return NULL;
}
/* save the mapping */
gimli_hash_insert(proc->files, obj, find.file);
gimli_mapped_object_addref(find.file);
f = find.file;
}
sym = sym_lookup(f, name);
if (debug) {
printf("sym_lookup: %s`%s => " PTRFMT "\n", obj, name, sym ? sym->addr : 0);
}
return sym;
}
/* vim:ts=2:sw=2:et:
*/