Commit | Line | Data |
---|---|---|
a2928c42 ACM |
1 | #include "util.h" |
2 | #include "../perf.h" | |
a0055ae2 | 3 | #include "string.h" |
a2928c42 ACM |
4 | #include "symbol.h" |
5 | ||
6 | #include <libelf.h> | |
7 | #include <gelf.h> | |
8 | #include <elf.h> | |
2cdbc46d PZ |
9 | |
10 | #ifndef NO_DEMANGLE | |
28ac909b | 11 | #include <bfd.h> |
2cdbc46d PZ |
12 | #else |
13 | static inline | |
14 | char *bfd_demangle(void __used *v, const char __used *c, int __used i) | |
15 | { | |
16 | return NULL; | |
17 | } | |
18 | #endif | |
a2928c42 | 19 | |
0b73da3f IM |
20 | const char *sym_hist_filter; |
21 | ||
28ac909b ACM |
22 | #ifndef DMGL_PARAMS |
23 | #define DMGL_PARAMS (1 << 0) /* Include function args */ | |
24 | #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ | |
25 | #endif | |
26 | ||
94cb9e38 ACM |
27 | enum dso_origin { |
28 | DSO__ORIG_KERNEL = 0, | |
29 | DSO__ORIG_JAVA_JIT, | |
30 | DSO__ORIG_FEDORA, | |
31 | DSO__ORIG_UBUNTU, | |
32 | DSO__ORIG_BUILDID, | |
33 | DSO__ORIG_DSO, | |
34 | DSO__ORIG_NOT_FOUND, | |
35 | }; | |
36 | ||
9cffa8d5 | 37 | static struct symbol *symbol__new(u64 start, u64 len, |
0b73da3f | 38 | const char *name, unsigned int priv_size, |
9cffa8d5 | 39 | u64 obj_start, int verbose) |
a2928c42 | 40 | { |
0085c954 | 41 | size_t namelen = strlen(name) + 1; |
0b73da3f | 42 | struct symbol *self = calloc(1, priv_size + sizeof(*self) + namelen); |
a2928c42 | 43 | |
0b73da3f IM |
44 | if (!self) |
45 | return NULL; | |
46 | ||
47 | if (verbose >= 2) | |
48 | printf("new symbol: %016Lx [%08lx]: %s, hist: %p, obj_start: %p\n", | |
9cffa8d5 | 49 | (u64)start, (unsigned long)len, name, self->hist, (void *)(unsigned long)obj_start); |
0b73da3f IM |
50 | |
51 | self->obj_start= obj_start; | |
52 | self->hist = NULL; | |
53 | self->hist_sum = 0; | |
54 | ||
55 | if (sym_hist_filter && !strcmp(name, sym_hist_filter)) | |
9cffa8d5 | 56 | self->hist = calloc(sizeof(u64), len); |
0b73da3f IM |
57 | |
58 | if (priv_size) { | |
59 | memset(self, 0, priv_size); | |
60 | self = ((void *)self) + priv_size; | |
a2928c42 | 61 | } |
0b73da3f | 62 | self->start = start; |
6cfcc53e | 63 | self->end = len ? start + len - 1 : start; |
0b73da3f | 64 | memcpy(self->name, name, namelen); |
a2928c42 ACM |
65 | |
66 | return self; | |
67 | } | |
68 | ||
0085c954 | 69 | static void symbol__delete(struct symbol *self, unsigned int priv_size) |
a2928c42 | 70 | { |
0085c954 | 71 | free(((void *)self) - priv_size); |
a2928c42 ACM |
72 | } |
73 | ||
74 | static size_t symbol__fprintf(struct symbol *self, FILE *fp) | |
75 | { | |
6cfcc53e MG |
76 | if (!self->module) |
77 | return fprintf(fp, " %llx-%llx %s\n", | |
a2928c42 | 78 | self->start, self->end, self->name); |
6cfcc53e MG |
79 | else |
80 | return fprintf(fp, " %llx-%llx %s \t[%s]\n", | |
81 | self->start, self->end, self->name, self->module->name); | |
a2928c42 ACM |
82 | } |
83 | ||
0085c954 | 84 | struct dso *dso__new(const char *name, unsigned int sym_priv_size) |
a2928c42 ACM |
85 | { |
86 | struct dso *self = malloc(sizeof(*self) + strlen(name) + 1); | |
87 | ||
88 | if (self != NULL) { | |
89 | strcpy(self->name, name); | |
90 | self->syms = RB_ROOT; | |
0085c954 | 91 | self->sym_priv_size = sym_priv_size; |
fc54db51 | 92 | self->find_symbol = dso__find_symbol; |
52d422de | 93 | self->slen_calculated = 0; |
94cb9e38 | 94 | self->origin = DSO__ORIG_NOT_FOUND; |
a2928c42 ACM |
95 | } |
96 | ||
97 | return self; | |
98 | } | |
99 | ||
100 | static void dso__delete_symbols(struct dso *self) | |
101 | { | |
102 | struct symbol *pos; | |
103 | struct rb_node *next = rb_first(&self->syms); | |
104 | ||
105 | while (next) { | |
106 | pos = rb_entry(next, struct symbol, rb_node); | |
107 | next = rb_next(&pos->rb_node); | |
c8c96525 | 108 | rb_erase(&pos->rb_node, &self->syms); |
0085c954 | 109 | symbol__delete(pos, self->sym_priv_size); |
a2928c42 ACM |
110 | } |
111 | } | |
112 | ||
113 | void dso__delete(struct dso *self) | |
114 | { | |
115 | dso__delete_symbols(self); | |
116 | free(self); | |
117 | } | |
118 | ||
119 | static void dso__insert_symbol(struct dso *self, struct symbol *sym) | |
120 | { | |
121 | struct rb_node **p = &self->syms.rb_node; | |
122 | struct rb_node *parent = NULL; | |
9cffa8d5 | 123 | const u64 ip = sym->start; |
a2928c42 ACM |
124 | struct symbol *s; |
125 | ||
126 | while (*p != NULL) { | |
127 | parent = *p; | |
128 | s = rb_entry(parent, struct symbol, rb_node); | |
129 | if (ip < s->start) | |
130 | p = &(*p)->rb_left; | |
131 | else | |
132 | p = &(*p)->rb_right; | |
133 | } | |
134 | rb_link_node(&sym->rb_node, parent, p); | |
135 | rb_insert_color(&sym->rb_node, &self->syms); | |
136 | } | |
137 | ||
9cffa8d5 | 138 | struct symbol *dso__find_symbol(struct dso *self, u64 ip) |
a2928c42 ACM |
139 | { |
140 | struct rb_node *n; | |
141 | ||
142 | if (self == NULL) | |
143 | return NULL; | |
144 | ||
145 | n = self->syms.rb_node; | |
146 | ||
147 | while (n) { | |
148 | struct symbol *s = rb_entry(n, struct symbol, rb_node); | |
149 | ||
150 | if (ip < s->start) | |
151 | n = n->rb_left; | |
152 | else if (ip > s->end) | |
153 | n = n->rb_right; | |
154 | else | |
155 | return s; | |
156 | } | |
157 | ||
158 | return NULL; | |
159 | } | |
160 | ||
161 | size_t dso__fprintf(struct dso *self, FILE *fp) | |
162 | { | |
163 | size_t ret = fprintf(fp, "dso: %s\n", self->name); | |
164 | ||
165 | struct rb_node *nd; | |
166 | for (nd = rb_first(&self->syms); nd; nd = rb_next(nd)) { | |
167 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | |
168 | ret += symbol__fprintf(pos, fp); | |
169 | } | |
170 | ||
171 | return ret; | |
172 | } | |
173 | ||
bd74137e | 174 | static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int verbose) |
a2928c42 ACM |
175 | { |
176 | struct rb_node *nd, *prevnd; | |
177 | char *line = NULL; | |
178 | size_t n; | |
179 | FILE *file = fopen("/proc/kallsyms", "r"); | |
9974f496 | 180 | int count = 0; |
a2928c42 ACM |
181 | |
182 | if (file == NULL) | |
183 | goto out_failure; | |
184 | ||
185 | while (!feof(file)) { | |
9cffa8d5 | 186 | u64 start; |
a2928c42 ACM |
187 | struct symbol *sym; |
188 | int line_len, len; | |
189 | char symbol_type; | |
190 | ||
191 | line_len = getline(&line, &n, file); | |
192 | if (line_len < 0) | |
193 | break; | |
194 | ||
195 | if (!line) | |
196 | goto out_failure; | |
197 | ||
198 | line[--line_len] = '\0'; /* \n */ | |
199 | ||
a0055ae2 | 200 | len = hex2u64(line, &start); |
a2928c42 ACM |
201 | |
202 | len++; | |
203 | if (len + 2 >= line_len) | |
204 | continue; | |
205 | ||
206 | symbol_type = toupper(line[len]); | |
207 | /* | |
208 | * We're interested only in code ('T'ext) | |
209 | */ | |
210 | if (symbol_type != 'T' && symbol_type != 'W') | |
211 | continue; | |
212 | /* | |
213 | * Well fix up the end later, when we have all sorted. | |
214 | */ | |
0085c954 | 215 | sym = symbol__new(start, 0xdead, line + len + 2, |
0b73da3f | 216 | self->sym_priv_size, 0, verbose); |
a2928c42 ACM |
217 | |
218 | if (sym == NULL) | |
219 | goto out_delete_line; | |
220 | ||
69ee69f6 ACM |
221 | if (filter && filter(self, sym)) |
222 | symbol__delete(sym, self->sym_priv_size); | |
9974f496 | 223 | else { |
69ee69f6 | 224 | dso__insert_symbol(self, sym); |
9974f496 MG |
225 | count++; |
226 | } | |
a2928c42 ACM |
227 | } |
228 | ||
229 | /* | |
230 | * Now that we have all sorted out, just set the ->end of all | |
231 | * symbols | |
232 | */ | |
233 | prevnd = rb_first(&self->syms); | |
234 | ||
235 | if (prevnd == NULL) | |
236 | goto out_delete_line; | |
237 | ||
238 | for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { | |
239 | struct symbol *prev = rb_entry(prevnd, struct symbol, rb_node), | |
240 | *curr = rb_entry(nd, struct symbol, rb_node); | |
241 | ||
242 | prev->end = curr->start - 1; | |
243 | prevnd = nd; | |
244 | } | |
245 | ||
246 | free(line); | |
247 | fclose(file); | |
248 | ||
9974f496 | 249 | return count; |
a2928c42 ACM |
250 | |
251 | out_delete_line: | |
252 | free(line); | |
253 | out_failure: | |
254 | return -1; | |
255 | } | |
256 | ||
80d496be PE |
257 | static int dso__load_perf_map(struct dso *self, symbol_filter_t filter, int verbose) |
258 | { | |
259 | char *line = NULL; | |
260 | size_t n; | |
261 | FILE *file; | |
262 | int nr_syms = 0; | |
263 | ||
264 | file = fopen(self->name, "r"); | |
265 | if (file == NULL) | |
266 | goto out_failure; | |
267 | ||
268 | while (!feof(file)) { | |
9cffa8d5 | 269 | u64 start, size; |
80d496be PE |
270 | struct symbol *sym; |
271 | int line_len, len; | |
272 | ||
273 | line_len = getline(&line, &n, file); | |
274 | if (line_len < 0) | |
275 | break; | |
276 | ||
277 | if (!line) | |
278 | goto out_failure; | |
279 | ||
280 | line[--line_len] = '\0'; /* \n */ | |
281 | ||
282 | len = hex2u64(line, &start); | |
283 | ||
284 | len++; | |
285 | if (len + 2 >= line_len) | |
286 | continue; | |
287 | ||
288 | len += hex2u64(line + len, &size); | |
289 | ||
290 | len++; | |
291 | if (len + 2 >= line_len) | |
292 | continue; | |
293 | ||
294 | sym = symbol__new(start, size, line + len, | |
295 | self->sym_priv_size, start, verbose); | |
296 | ||
297 | if (sym == NULL) | |
298 | goto out_delete_line; | |
299 | ||
300 | if (filter && filter(self, sym)) | |
301 | symbol__delete(sym, self->sym_priv_size); | |
302 | else { | |
303 | dso__insert_symbol(self, sym); | |
304 | nr_syms++; | |
305 | } | |
306 | } | |
307 | ||
308 | free(line); | |
309 | fclose(file); | |
310 | ||
311 | return nr_syms; | |
312 | ||
313 | out_delete_line: | |
314 | free(line); | |
315 | out_failure: | |
316 | return -1; | |
317 | } | |
318 | ||
a2928c42 ACM |
319 | /** |
320 | * elf_symtab__for_each_symbol - iterate thru all the symbols | |
321 | * | |
322 | * @self: struct elf_symtab instance to iterate | |
323 | * @index: uint32_t index | |
324 | * @sym: GElf_Sym iterator | |
325 | */ | |
326 | #define elf_symtab__for_each_symbol(syms, nr_syms, index, sym) \ | |
327 | for (index = 0, gelf_getsym(syms, index, &sym);\ | |
328 | index < nr_syms; \ | |
329 | index++, gelf_getsym(syms, index, &sym)) | |
330 | ||
331 | static inline uint8_t elf_sym__type(const GElf_Sym *sym) | |
332 | { | |
333 | return GELF_ST_TYPE(sym->st_info); | |
334 | } | |
335 | ||
336 | static inline int elf_sym__is_function(const GElf_Sym *sym) | |
337 | { | |
338 | return elf_sym__type(sym) == STT_FUNC && | |
339 | sym->st_name != 0 && | |
340 | sym->st_shndx != SHN_UNDEF && | |
341 | sym->st_size != 0; | |
342 | } | |
343 | ||
6cfcc53e MG |
344 | static inline int elf_sym__is_label(const GElf_Sym *sym) |
345 | { | |
346 | return elf_sym__type(sym) == STT_NOTYPE && | |
347 | sym->st_name != 0 && | |
348 | sym->st_shndx != SHN_UNDEF && | |
349 | sym->st_shndx != SHN_ABS; | |
350 | } | |
351 | ||
352 | static inline const char *elf_sec__name(const GElf_Shdr *shdr, | |
353 | const Elf_Data *secstrs) | |
354 | { | |
355 | return secstrs->d_buf + shdr->sh_name; | |
356 | } | |
357 | ||
358 | static inline int elf_sec__is_text(const GElf_Shdr *shdr, | |
359 | const Elf_Data *secstrs) | |
360 | { | |
361 | return strstr(elf_sec__name(shdr, secstrs), "text") != NULL; | |
362 | } | |
363 | ||
a2928c42 ACM |
364 | static inline const char *elf_sym__name(const GElf_Sym *sym, |
365 | const Elf_Data *symstrs) | |
366 | { | |
367 | return symstrs->d_buf + sym->st_name; | |
368 | } | |
369 | ||
370 | static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, | |
371 | GElf_Shdr *shp, const char *name, | |
372 | size_t *index) | |
373 | { | |
374 | Elf_Scn *sec = NULL; | |
375 | size_t cnt = 1; | |
376 | ||
377 | while ((sec = elf_nextscn(elf, sec)) != NULL) { | |
378 | char *str; | |
379 | ||
380 | gelf_getshdr(sec, shp); | |
381 | str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); | |
382 | if (!strcmp(name, str)) { | |
383 | if (index) | |
384 | *index = cnt; | |
385 | break; | |
386 | } | |
387 | ++cnt; | |
388 | } | |
389 | ||
390 | return sec; | |
391 | } | |
392 | ||
8ce998d6 ACM |
393 | #define elf_section__for_each_rel(reldata, pos, pos_mem, idx, nr_entries) \ |
394 | for (idx = 0, pos = gelf_getrel(reldata, 0, &pos_mem); \ | |
395 | idx < nr_entries; \ | |
396 | ++idx, pos = gelf_getrel(reldata, idx, &pos_mem)) | |
397 | ||
398 | #define elf_section__for_each_rela(reldata, pos, pos_mem, idx, nr_entries) \ | |
399 | for (idx = 0, pos = gelf_getrela(reldata, 0, &pos_mem); \ | |
400 | idx < nr_entries; \ | |
401 | ++idx, pos = gelf_getrela(reldata, idx, &pos_mem)) | |
402 | ||
a25e46c4 ACM |
403 | /* |
404 | * We need to check if we have a .dynsym, so that we can handle the | |
405 | * .plt, synthesizing its symbols, that aren't on the symtabs (be it | |
406 | * .dynsym or .symtab). | |
407 | * And always look at the original dso, not at debuginfo packages, that | |
408 | * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS). | |
409 | */ | |
410 | static int dso__synthesize_plt_symbols(struct dso *self, int verbose) | |
8ce998d6 ACM |
411 | { |
412 | uint32_t nr_rel_entries, idx; | |
413 | GElf_Sym sym; | |
9cffa8d5 | 414 | u64 plt_offset; |
8ce998d6 ACM |
415 | GElf_Shdr shdr_plt; |
416 | struct symbol *f; | |
a25e46c4 | 417 | GElf_Shdr shdr_rel_plt, shdr_dynsym; |
8ce998d6 | 418 | Elf_Data *reldata, *syms, *symstrs; |
a25e46c4 ACM |
419 | Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym; |
420 | size_t dynsym_idx; | |
421 | GElf_Ehdr ehdr; | |
8ce998d6 | 422 | char sympltname[1024]; |
a25e46c4 ACM |
423 | Elf *elf; |
424 | int nr = 0, symidx, fd, err = 0; | |
425 | ||
426 | fd = open(self->name, O_RDONLY); | |
427 | if (fd < 0) | |
428 | goto out; | |
429 | ||
430 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); | |
431 | if (elf == NULL) | |
432 | goto out_close; | |
433 | ||
434 | if (gelf_getehdr(elf, &ehdr) == NULL) | |
435 | goto out_elf_end; | |
436 | ||
437 | scn_dynsym = elf_section_by_name(elf, &ehdr, &shdr_dynsym, | |
438 | ".dynsym", &dynsym_idx); | |
439 | if (scn_dynsym == NULL) | |
440 | goto out_elf_end; | |
8ce998d6 | 441 | |
a25e46c4 | 442 | scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt, |
8ce998d6 ACM |
443 | ".rela.plt", NULL); |
444 | if (scn_plt_rel == NULL) { | |
a25e46c4 | 445 | scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt, |
8ce998d6 ACM |
446 | ".rel.plt", NULL); |
447 | if (scn_plt_rel == NULL) | |
a25e46c4 | 448 | goto out_elf_end; |
8ce998d6 ACM |
449 | } |
450 | ||
a25e46c4 ACM |
451 | err = -1; |
452 | ||
8ce998d6 | 453 | if (shdr_rel_plt.sh_link != dynsym_idx) |
a25e46c4 | 454 | goto out_elf_end; |
8ce998d6 | 455 | |
a25e46c4 ACM |
456 | if (elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL) == NULL) |
457 | goto out_elf_end; | |
8ce998d6 ACM |
458 | |
459 | /* | |
460 | * Fetch the relocation section to find the indexes to the GOT | |
461 | * and the symbols in the .dynsym they refer to. | |
462 | */ | |
463 | reldata = elf_getdata(scn_plt_rel, NULL); | |
464 | if (reldata == NULL) | |
a25e46c4 | 465 | goto out_elf_end; |
8ce998d6 ACM |
466 | |
467 | syms = elf_getdata(scn_dynsym, NULL); | |
468 | if (syms == NULL) | |
a25e46c4 | 469 | goto out_elf_end; |
8ce998d6 | 470 | |
a25e46c4 | 471 | scn_symstrs = elf_getscn(elf, shdr_dynsym.sh_link); |
8ce998d6 | 472 | if (scn_symstrs == NULL) |
a25e46c4 | 473 | goto out_elf_end; |
8ce998d6 ACM |
474 | |
475 | symstrs = elf_getdata(scn_symstrs, NULL); | |
476 | if (symstrs == NULL) | |
a25e46c4 | 477 | goto out_elf_end; |
8ce998d6 ACM |
478 | |
479 | nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize; | |
480 | plt_offset = shdr_plt.sh_offset; | |
481 | ||
482 | if (shdr_rel_plt.sh_type == SHT_RELA) { | |
483 | GElf_Rela pos_mem, *pos; | |
484 | ||
485 | elf_section__for_each_rela(reldata, pos, pos_mem, idx, | |
486 | nr_rel_entries) { | |
487 | symidx = GELF_R_SYM(pos->r_info); | |
488 | plt_offset += shdr_plt.sh_entsize; | |
489 | gelf_getsym(syms, symidx, &sym); | |
490 | snprintf(sympltname, sizeof(sympltname), | |
491 | "%s@plt", elf_sym__name(&sym, symstrs)); | |
492 | ||
493 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | |
0b73da3f | 494 | sympltname, self->sym_priv_size, 0, verbose); |
8ce998d6 | 495 | if (!f) |
a25e46c4 | 496 | goto out_elf_end; |
8ce998d6 ACM |
497 | |
498 | dso__insert_symbol(self, f); | |
499 | ++nr; | |
500 | } | |
501 | } else if (shdr_rel_plt.sh_type == SHT_REL) { | |
502 | GElf_Rel pos_mem, *pos; | |
503 | elf_section__for_each_rel(reldata, pos, pos_mem, idx, | |
504 | nr_rel_entries) { | |
505 | symidx = GELF_R_SYM(pos->r_info); | |
506 | plt_offset += shdr_plt.sh_entsize; | |
507 | gelf_getsym(syms, symidx, &sym); | |
508 | snprintf(sympltname, sizeof(sympltname), | |
509 | "%s@plt", elf_sym__name(&sym, symstrs)); | |
510 | ||
511 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | |
0b73da3f | 512 | sympltname, self->sym_priv_size, 0, verbose); |
8ce998d6 | 513 | if (!f) |
a25e46c4 | 514 | goto out_elf_end; |
8ce998d6 ACM |
515 | |
516 | dso__insert_symbol(self, f); | |
517 | ++nr; | |
518 | } | |
8ce998d6 ACM |
519 | } |
520 | ||
a25e46c4 ACM |
521 | err = 0; |
522 | out_elf_end: | |
523 | elf_end(elf); | |
524 | out_close: | |
525 | close(fd); | |
526 | ||
527 | if (err == 0) | |
528 | return nr; | |
529 | out: | |
530 | fprintf(stderr, "%s: problems reading %s PLT info.\n", | |
531 | __func__, self->name); | |
532 | return 0; | |
8ce998d6 ACM |
533 | } |
534 | ||
69ee69f6 | 535 | static int dso__load_sym(struct dso *self, int fd, const char *name, |
6cfcc53e | 536 | symbol_filter_t filter, int verbose, struct module *mod) |
a2928c42 | 537 | { |
6cfcc53e | 538 | Elf_Data *symstrs, *secstrs; |
a2928c42 ACM |
539 | uint32_t nr_syms; |
540 | int err = -1; | |
541 | uint32_t index; | |
542 | GElf_Ehdr ehdr; | |
543 | GElf_Shdr shdr; | |
544 | Elf_Data *syms; | |
545 | GElf_Sym sym; | |
a25e46c4 | 546 | Elf_Scn *sec, *sec_strndx; |
a2928c42 | 547 | Elf *elf; |
d20ff6bd | 548 | int nr = 0, kernel = !strcmp("[kernel]", self->name); |
a2928c42 ACM |
549 | |
550 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); | |
551 | if (elf == NULL) { | |
bd74137e IM |
552 | if (verbose) |
553 | fprintf(stderr, "%s: cannot read %s ELF file.\n", | |
554 | __func__, name); | |
a2928c42 ACM |
555 | goto out_close; |
556 | } | |
557 | ||
558 | if (gelf_getehdr(elf, &ehdr) == NULL) { | |
bd74137e IM |
559 | if (verbose) |
560 | fprintf(stderr, "%s: cannot get elf header.\n", __func__); | |
a2928c42 ACM |
561 | goto out_elf_end; |
562 | } | |
563 | ||
564 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); | |
8ce998d6 | 565 | if (sec == NULL) { |
a25e46c4 ACM |
566 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL); |
567 | if (sec == NULL) | |
8ce998d6 | 568 | goto out_elf_end; |
8ce998d6 | 569 | } |
a2928c42 ACM |
570 | |
571 | syms = elf_getdata(sec, NULL); | |
572 | if (syms == NULL) | |
573 | goto out_elf_end; | |
574 | ||
575 | sec = elf_getscn(elf, shdr.sh_link); | |
576 | if (sec == NULL) | |
577 | goto out_elf_end; | |
578 | ||
579 | symstrs = elf_getdata(sec, NULL); | |
580 | if (symstrs == NULL) | |
581 | goto out_elf_end; | |
582 | ||
6cfcc53e MG |
583 | sec_strndx = elf_getscn(elf, ehdr.e_shstrndx); |
584 | if (sec_strndx == NULL) | |
585 | goto out_elf_end; | |
586 | ||
587 | secstrs = elf_getdata(sec_strndx, NULL); | |
9b30a26b | 588 | if (secstrs == NULL) |
6cfcc53e MG |
589 | goto out_elf_end; |
590 | ||
a2928c42 ACM |
591 | nr_syms = shdr.sh_size / shdr.sh_entsize; |
592 | ||
e9fbc9dc | 593 | memset(&sym, 0, sizeof(sym)); |
d20ff6bd MG |
594 | if (!kernel) { |
595 | self->adjust_symbols = (ehdr.e_type == ET_EXEC || | |
30d7a77d ACM |
596 | elf_section_by_name(elf, &ehdr, &shdr, |
597 | ".gnu.prelink_undo", | |
598 | NULL) != NULL); | |
d20ff6bd MG |
599 | } else self->adjust_symbols = 0; |
600 | ||
a2928c42 ACM |
601 | elf_symtab__for_each_symbol(syms, nr_syms, index, sym) { |
602 | struct symbol *f; | |
28ac909b ACM |
603 | const char *name; |
604 | char *demangled; | |
9cffa8d5 | 605 | u64 obj_start; |
6cfcc53e MG |
606 | struct section *section = NULL; |
607 | int is_label = elf_sym__is_label(&sym); | |
608 | const char *section_name; | |
a2928c42 | 609 | |
6cfcc53e | 610 | if (!is_label && !elf_sym__is_function(&sym)) |
a2928c42 ACM |
611 | continue; |
612 | ||
613 | sec = elf_getscn(elf, sym.st_shndx); | |
614 | if (!sec) | |
615 | goto out_elf_end; | |
616 | ||
617 | gelf_getshdr(sec, &shdr); | |
6cfcc53e MG |
618 | |
619 | if (is_label && !elf_sec__is_text(&shdr, secstrs)) | |
620 | continue; | |
621 | ||
622 | section_name = elf_sec__name(&shdr, secstrs); | |
0b73da3f IM |
623 | obj_start = sym.st_value; |
624 | ||
30d7a77d | 625 | if (self->adjust_symbols) { |
f5812a7a ACM |
626 | if (verbose >= 2) |
627 | printf("adjusting symbol: st_value: %Lx sh_addr: %Lx sh_offset: %Lx\n", | |
628 | (u64)sym.st_value, (u64)shdr.sh_addr, (u64)shdr.sh_offset); | |
520f2c34 | 629 | |
f5812a7a ACM |
630 | sym.st_value -= shdr.sh_addr - shdr.sh_offset; |
631 | } | |
a2928c42 | 632 | |
6cfcc53e MG |
633 | if (mod) { |
634 | section = mod->sections->find_section(mod->sections, section_name); | |
635 | if (section) | |
636 | sym.st_value += section->vma; | |
637 | else { | |
638 | fprintf(stderr, "dso__load_sym() module %s lookup of %s failed\n", | |
639 | mod->name, section_name); | |
640 | goto out_elf_end; | |
641 | } | |
642 | } | |
28ac909b ACM |
643 | /* |
644 | * We need to figure out if the object was created from C++ sources | |
645 | * DWARF DW_compile_unit has this, but we don't always have access | |
646 | * to it... | |
647 | */ | |
648 | name = elf_sym__name(&sym, symstrs); | |
649 | demangled = bfd_demangle(NULL, name, DMGL_PARAMS | DMGL_ANSI); | |
650 | if (demangled != NULL) | |
651 | name = demangled; | |
6cfcc53e | 652 | |
28ac909b | 653 | f = symbol__new(sym.st_value, sym.st_size, name, |
0b73da3f | 654 | self->sym_priv_size, obj_start, verbose); |
28ac909b | 655 | free(demangled); |
a2928c42 ACM |
656 | if (!f) |
657 | goto out_elf_end; | |
658 | ||
69ee69f6 ACM |
659 | if (filter && filter(self, f)) |
660 | symbol__delete(f, self->sym_priv_size); | |
661 | else { | |
6cfcc53e | 662 | f->module = mod; |
69ee69f6 ACM |
663 | dso__insert_symbol(self, f); |
664 | nr++; | |
665 | } | |
a2928c42 ACM |
666 | } |
667 | ||
668 | err = nr; | |
669 | out_elf_end: | |
670 | elf_end(elf); | |
671 | out_close: | |
672 | return err; | |
673 | } | |
674 | ||
4d1e00a8 ACM |
675 | #define BUILD_ID_SIZE 128 |
676 | ||
677 | static char *dso__read_build_id(struct dso *self, int verbose) | |
678 | { | |
679 | int i; | |
680 | GElf_Ehdr ehdr; | |
681 | GElf_Shdr shdr; | |
682 | Elf_Data *build_id_data; | |
683 | Elf_Scn *sec; | |
684 | char *build_id = NULL, *bid; | |
685 | unsigned char *raw; | |
686 | Elf *elf; | |
687 | int fd = open(self->name, O_RDONLY); | |
688 | ||
689 | if (fd < 0) | |
690 | goto out; | |
691 | ||
692 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); | |
693 | if (elf == NULL) { | |
694 | if (verbose) | |
695 | fprintf(stderr, "%s: cannot read %s ELF file.\n", | |
696 | __func__, self->name); | |
697 | goto out_close; | |
698 | } | |
699 | ||
700 | if (gelf_getehdr(elf, &ehdr) == NULL) { | |
701 | if (verbose) | |
702 | fprintf(stderr, "%s: cannot get elf header.\n", __func__); | |
703 | goto out_elf_end; | |
704 | } | |
705 | ||
706 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".note.gnu.build-id", NULL); | |
707 | if (sec == NULL) | |
708 | goto out_elf_end; | |
709 | ||
710 | build_id_data = elf_getdata(sec, NULL); | |
711 | if (build_id_data == NULL) | |
712 | goto out_elf_end; | |
713 | build_id = malloc(BUILD_ID_SIZE); | |
714 | if (build_id == NULL) | |
715 | goto out_elf_end; | |
716 | raw = build_id_data->d_buf + 16; | |
717 | bid = build_id; | |
718 | ||
719 | for (i = 0; i < 20; ++i) { | |
720 | sprintf(bid, "%02x", *raw); | |
721 | ++raw; | |
722 | bid += 2; | |
723 | } | |
94cb9e38 | 724 | if (verbose >= 2) |
4d1e00a8 ACM |
725 | printf("%s(%s): %s\n", __func__, self->name, build_id); |
726 | out_elf_end: | |
727 | elf_end(elf); | |
728 | out_close: | |
729 | close(fd); | |
730 | out: | |
731 | return build_id; | |
732 | } | |
733 | ||
94cb9e38 ACM |
734 | char dso__symtab_origin(const struct dso *self) |
735 | { | |
736 | static const char origin[] = { | |
737 | [DSO__ORIG_KERNEL] = 'k', | |
738 | [DSO__ORIG_JAVA_JIT] = 'j', | |
739 | [DSO__ORIG_FEDORA] = 'f', | |
740 | [DSO__ORIG_UBUNTU] = 'u', | |
741 | [DSO__ORIG_BUILDID] = 'b', | |
742 | [DSO__ORIG_DSO] = 'd', | |
743 | }; | |
744 | ||
745 | if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND) | |
746 | return '!'; | |
747 | return origin[self->origin]; | |
748 | } | |
749 | ||
bd74137e | 750 | int dso__load(struct dso *self, symbol_filter_t filter, int verbose) |
a2928c42 | 751 | { |
4d1e00a8 ACM |
752 | int size = PATH_MAX; |
753 | char *name = malloc(size), *build_id = NULL; | |
a2928c42 ACM |
754 | int ret = -1; |
755 | int fd; | |
756 | ||
757 | if (!name) | |
758 | return -1; | |
759 | ||
30d7a77d | 760 | self->adjust_symbols = 0; |
f5812a7a | 761 | |
94cb9e38 ACM |
762 | if (strncmp(self->name, "/tmp/perf-", 10) == 0) { |
763 | ret = dso__load_perf_map(self, filter, verbose); | |
764 | self->origin = ret > 0 ? DSO__ORIG_JAVA_JIT : | |
765 | DSO__ORIG_NOT_FOUND; | |
766 | return ret; | |
767 | } | |
768 | ||
769 | self->origin = DSO__ORIG_FEDORA - 1; | |
80d496be | 770 | |
a2928c42 ACM |
771 | more: |
772 | do { | |
94cb9e38 ACM |
773 | self->origin++; |
774 | switch (self->origin) { | |
775 | case DSO__ORIG_FEDORA: | |
a2928c42 ACM |
776 | snprintf(name, size, "/usr/lib/debug%s.debug", self->name); |
777 | break; | |
94cb9e38 | 778 | case DSO__ORIG_UBUNTU: |
a2928c42 ACM |
779 | snprintf(name, size, "/usr/lib/debug%s", self->name); |
780 | break; | |
94cb9e38 | 781 | case DSO__ORIG_BUILDID: |
4d1e00a8 ACM |
782 | build_id = dso__read_build_id(self, verbose); |
783 | if (build_id != NULL) { | |
784 | snprintf(name, size, | |
785 | "/usr/lib/debug/.build-id/%.2s/%s.debug", | |
786 | build_id, build_id + 2); | |
787 | free(build_id); | |
788 | break; | |
789 | } | |
94cb9e38 | 790 | self->origin++; |
4d1e00a8 | 791 | /* Fall thru */ |
94cb9e38 | 792 | case DSO__ORIG_DSO: |
a2928c42 ACM |
793 | snprintf(name, size, "%s", self->name); |
794 | break; | |
795 | ||
796 | default: | |
797 | goto out; | |
798 | } | |
a2928c42 ACM |
799 | |
800 | fd = open(name, O_RDONLY); | |
801 | } while (fd < 0); | |
802 | ||
6cfcc53e | 803 | ret = dso__load_sym(self, fd, name, filter, verbose, NULL); |
a2928c42 ACM |
804 | close(fd); |
805 | ||
806 | /* | |
807 | * Some people seem to have debuginfo files _WITHOUT_ debug info!?!? | |
808 | */ | |
809 | if (!ret) | |
810 | goto more; | |
811 | ||
a25e46c4 ACM |
812 | if (ret > 0) { |
813 | int nr_plt = dso__synthesize_plt_symbols(self, verbose); | |
814 | if (nr_plt > 0) | |
815 | ret += nr_plt; | |
816 | } | |
a2928c42 ACM |
817 | out: |
818 | free(name); | |
819 | return ret; | |
820 | } | |
821 | ||
6cfcc53e MG |
822 | static int dso__load_module(struct dso *self, struct mod_dso *mods, const char *name, |
823 | symbol_filter_t filter, int verbose) | |
824 | { | |
825 | struct module *mod = mod_dso__find_module(mods, name); | |
826 | int err = 0, fd; | |
827 | ||
828 | if (mod == NULL || !mod->active) | |
829 | return err; | |
830 | ||
831 | fd = open(mod->path, O_RDONLY); | |
832 | ||
833 | if (fd < 0) | |
834 | return err; | |
835 | ||
836 | err = dso__load_sym(self, fd, name, filter, verbose, mod); | |
837 | close(fd); | |
838 | ||
839 | return err; | |
840 | } | |
841 | ||
842 | int dso__load_modules(struct dso *self, symbol_filter_t filter, int verbose) | |
843 | { | |
844 | struct mod_dso *mods = mod_dso__new_dso("modules"); | |
845 | struct module *pos; | |
846 | struct rb_node *next; | |
847 | int err; | |
848 | ||
849 | err = mod_dso__load_modules(mods); | |
850 | ||
851 | if (err <= 0) | |
852 | return err; | |
853 | ||
854 | /* | |
855 | * Iterate over modules, and load active symbols. | |
856 | */ | |
857 | next = rb_first(&mods->mods); | |
858 | while (next) { | |
859 | pos = rb_entry(next, struct module, rb_node); | |
860 | err = dso__load_module(self, mods, pos->name, filter, verbose); | |
861 | ||
862 | if (err < 0) | |
863 | break; | |
864 | ||
865 | next = rb_next(&pos->rb_node); | |
866 | } | |
867 | ||
868 | if (err < 0) { | |
869 | mod_dso__delete_modules(mods); | |
870 | mod_dso__delete_self(mods); | |
871 | } | |
872 | ||
873 | return err; | |
874 | } | |
875 | ||
876 | static inline void dso__fill_symbol_holes(struct dso *self) | |
877 | { | |
878 | struct symbol *prev = NULL; | |
879 | struct rb_node *nd; | |
880 | ||
881 | for (nd = rb_last(&self->syms); nd; nd = rb_prev(nd)) { | |
882 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | |
883 | ||
884 | if (prev) { | |
885 | u64 hole = 0; | |
886 | int alias = pos->start == prev->start; | |
887 | ||
888 | if (!alias) | |
889 | hole = prev->start - pos->end - 1; | |
890 | ||
891 | if (hole || alias) { | |
892 | if (alias) | |
893 | pos->end = prev->end; | |
894 | else if (hole) | |
895 | pos->end = prev->start - 1; | |
896 | } | |
897 | } | |
898 | prev = pos; | |
899 | } | |
900 | } | |
901 | ||
69ee69f6 | 902 | static int dso__load_vmlinux(struct dso *self, const char *vmlinux, |
bd74137e | 903 | symbol_filter_t filter, int verbose) |
a2928c42 ACM |
904 | { |
905 | int err, fd = open(vmlinux, O_RDONLY); | |
906 | ||
907 | if (fd < 0) | |
908 | return -1; | |
909 | ||
6cfcc53e MG |
910 | err = dso__load_sym(self, fd, vmlinux, filter, verbose, NULL); |
911 | ||
912 | if (err > 0) | |
913 | dso__fill_symbol_holes(self); | |
914 | ||
a2928c42 ACM |
915 | close(fd); |
916 | ||
917 | return err; | |
918 | } | |
919 | ||
bd74137e | 920 | int dso__load_kernel(struct dso *self, const char *vmlinux, |
6cfcc53e | 921 | symbol_filter_t filter, int verbose, int modules) |
a827c875 ACM |
922 | { |
923 | int err = -1; | |
924 | ||
6cfcc53e | 925 | if (vmlinux) { |
bd74137e | 926 | err = dso__load_vmlinux(self, vmlinux, filter, verbose); |
6cfcc53e MG |
927 | if (err > 0 && modules) |
928 | err = dso__load_modules(self, filter, verbose); | |
929 | } | |
a827c875 | 930 | |
9974f496 | 931 | if (err <= 0) |
bd74137e | 932 | err = dso__load_kallsyms(self, filter, verbose); |
a827c875 | 933 | |
94cb9e38 ACM |
934 | if (err > 0) |
935 | self->origin = DSO__ORIG_KERNEL; | |
936 | ||
a827c875 ACM |
937 | return err; |
938 | } | |
939 | ||
a2928c42 ACM |
940 | void symbol__init(void) |
941 | { | |
942 | elf_version(EV_CURRENT); | |
943 | } |