diff options
author | Arnaldo Carvalho de Melo <acme@redhat.com> | 2009-06-02 23:54:33 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-06-03 03:58:52 -0400 |
commit | 8ce998d6693bd02ab3b74ee1cc303ecb1fa9b514 (patch) | |
tree | 425ed52012c706d6b7465f5c319e9f9891edc701 /Documentation/perf_counter/util/symbol.c | |
parent | a32881066e58346f2901afe0ebdfbf0c562877e5 (diff) |
perf_counter tools: Cover PLT symbols too
PLT, the Program Linking Table, is used with the dynamic linker to
allow PIC code in executables and shared objects to figure out
where functions are in other shared objects.
It is one of the sources of unknown/unresolved symbols - this patch
does what binutils figures out when you ask it to disassembly.
(objdump -S)
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: John Kacur <jkacur@redhat.com>
Cc: Stephane Eranian <eranian@googlemail.com>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'Documentation/perf_counter/util/symbol.c')
-rw-r--r-- | Documentation/perf_counter/util/symbol.c | 143 |
1 files changed, 138 insertions, 5 deletions
diff --git a/Documentation/perf_counter/util/symbol.c b/Documentation/perf_counter/util/symbol.c index 039931fcb1b5..d52a1ae5342a 100644 --- a/Documentation/perf_counter/util/symbol.c +++ b/Documentation/perf_counter/util/symbol.c | |||
@@ -258,6 +258,117 @@ static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, | |||
258 | return sec; | 258 | return sec; |
259 | } | 259 | } |
260 | 260 | ||
261 | #define elf_section__for_each_rel(reldata, pos, pos_mem, idx, nr_entries) \ | ||
262 | for (idx = 0, pos = gelf_getrel(reldata, 0, &pos_mem); \ | ||
263 | idx < nr_entries; \ | ||
264 | ++idx, pos = gelf_getrel(reldata, idx, &pos_mem)) | ||
265 | |||
266 | #define elf_section__for_each_rela(reldata, pos, pos_mem, idx, nr_entries) \ | ||
267 | for (idx = 0, pos = gelf_getrela(reldata, 0, &pos_mem); \ | ||
268 | idx < nr_entries; \ | ||
269 | ++idx, pos = gelf_getrela(reldata, idx, &pos_mem)) | ||
270 | |||
271 | static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf, | ||
272 | GElf_Ehdr *ehdr, Elf_Scn *scn_dynsym, | ||
273 | GElf_Shdr *shdr_dynsym, | ||
274 | size_t dynsym_idx) | ||
275 | { | ||
276 | uint32_t nr_rel_entries, idx; | ||
277 | GElf_Sym sym; | ||
278 | __u64 plt_offset; | ||
279 | GElf_Shdr shdr_plt; | ||
280 | struct symbol *f; | ||
281 | GElf_Shdr shdr_rel_plt; | ||
282 | Elf_Data *reldata, *syms, *symstrs; | ||
283 | Elf_Scn *scn_plt_rel, *scn_symstrs; | ||
284 | char sympltname[1024]; | ||
285 | int nr = 0, symidx; | ||
286 | |||
287 | scn_plt_rel = elf_section_by_name(elf, ehdr, &shdr_rel_plt, | ||
288 | ".rela.plt", NULL); | ||
289 | if (scn_plt_rel == NULL) { | ||
290 | scn_plt_rel = elf_section_by_name(elf, ehdr, &shdr_rel_plt, | ||
291 | ".rel.plt", NULL); | ||
292 | if (scn_plt_rel == NULL) | ||
293 | return 0; | ||
294 | } | ||
295 | |||
296 | if (shdr_rel_plt.sh_link != dynsym_idx) | ||
297 | return 0; | ||
298 | |||
299 | if (elf_section_by_name(elf, ehdr, &shdr_plt, ".plt", NULL) == NULL) | ||
300 | return 0; | ||
301 | |||
302 | /* | ||
303 | * Fetch the relocation section to find the indexes to the GOT | ||
304 | * and the symbols in the .dynsym they refer to. | ||
305 | */ | ||
306 | reldata = elf_getdata(scn_plt_rel, NULL); | ||
307 | if (reldata == NULL) | ||
308 | return -1; | ||
309 | |||
310 | syms = elf_getdata(scn_dynsym, NULL); | ||
311 | if (syms == NULL) | ||
312 | return -1; | ||
313 | |||
314 | scn_symstrs = elf_getscn(elf, shdr_dynsym->sh_link); | ||
315 | if (scn_symstrs == NULL) | ||
316 | return -1; | ||
317 | |||
318 | symstrs = elf_getdata(scn_symstrs, NULL); | ||
319 | if (symstrs == NULL) | ||
320 | return -1; | ||
321 | |||
322 | nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize; | ||
323 | plt_offset = shdr_plt.sh_offset; | ||
324 | |||
325 | if (shdr_rel_plt.sh_type == SHT_RELA) { | ||
326 | GElf_Rela pos_mem, *pos; | ||
327 | |||
328 | elf_section__for_each_rela(reldata, pos, pos_mem, idx, | ||
329 | nr_rel_entries) { | ||
330 | symidx = GELF_R_SYM(pos->r_info); | ||
331 | plt_offset += shdr_plt.sh_entsize; | ||
332 | gelf_getsym(syms, symidx, &sym); | ||
333 | snprintf(sympltname, sizeof(sympltname), | ||
334 | "%s@plt", elf_sym__name(&sym, symstrs)); | ||
335 | |||
336 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | ||
337 | sympltname, self->sym_priv_size); | ||
338 | if (!f) | ||
339 | return -1; | ||
340 | |||
341 | dso__insert_symbol(self, f); | ||
342 | ++nr; | ||
343 | } | ||
344 | } else if (shdr_rel_plt.sh_type == SHT_REL) { | ||
345 | GElf_Rel pos_mem, *pos; | ||
346 | elf_section__for_each_rel(reldata, pos, pos_mem, idx, | ||
347 | nr_rel_entries) { | ||
348 | symidx = GELF_R_SYM(pos->r_info); | ||
349 | plt_offset += shdr_plt.sh_entsize; | ||
350 | gelf_getsym(syms, symidx, &sym); | ||
351 | snprintf(sympltname, sizeof(sympltname), | ||
352 | "%s@plt", elf_sym__name(&sym, symstrs)); | ||
353 | |||
354 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | ||
355 | sympltname, self->sym_priv_size); | ||
356 | if (!f) | ||
357 | return -1; | ||
358 | |||
359 | dso__insert_symbol(self, f); | ||
360 | ++nr; | ||
361 | } | ||
362 | } else { | ||
363 | /* | ||
364 | * TODO: There are still one more shdr_rel_plt.sh_type | ||
365 | * I have to investigate, but probably should be ignored. | ||
366 | */ | ||
367 | } | ||
368 | |||
369 | return nr; | ||
370 | } | ||
371 | |||
261 | static int dso__load_sym(struct dso *self, int fd, const char *name, | 372 | static int dso__load_sym(struct dso *self, int fd, const char *name, |
262 | symbol_filter_t filter) | 373 | symbol_filter_t filter) |
263 | { | 374 | { |
@@ -269,8 +380,9 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
269 | GElf_Shdr shdr; | 380 | GElf_Shdr shdr; |
270 | Elf_Data *syms; | 381 | Elf_Data *syms; |
271 | GElf_Sym sym; | 382 | GElf_Sym sym; |
272 | Elf_Scn *sec; | 383 | Elf_Scn *sec, *sec_dynsym; |
273 | Elf *elf; | 384 | Elf *elf; |
385 | size_t dynsym_idx; | ||
274 | int nr = 0; | 386 | int nr = 0; |
275 | 387 | ||
276 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); | 388 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); |
@@ -285,12 +397,33 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
285 | goto out_elf_end; | 397 | goto out_elf_end; |
286 | } | 398 | } |
287 | 399 | ||
400 | /* | ||
401 | * We need to check if we have a .dynsym, so that we can handle the | ||
402 | * .plt, synthesizing its symbols, that aren't on the symtabs (be it | ||
403 | * .dynsym or .symtab) | ||
404 | */ | ||
405 | sec_dynsym = elf_section_by_name(elf, &ehdr, &shdr, | ||
406 | ".dynsym", &dynsym_idx); | ||
407 | if (sec_dynsym != NULL) { | ||
408 | nr = dso__synthesize_plt_symbols(self, elf, &ehdr, | ||
409 | sec_dynsym, &shdr, | ||
410 | dynsym_idx); | ||
411 | if (nr < 0) | ||
412 | goto out_elf_end; | ||
413 | } | ||
414 | |||
415 | /* | ||
416 | * But if we have a full .symtab (that is a superset of .dynsym) we | ||
417 | * should add the symbols not in the .dynsyn | ||
418 | */ | ||
288 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); | 419 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); |
289 | if (sec == NULL) | 420 | if (sec == NULL) { |
290 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL); | 421 | if (sec_dynsym == NULL) |
422 | goto out_elf_end; | ||
291 | 423 | ||
292 | if (sec == NULL) | 424 | sec = sec_dynsym; |
293 | goto out_elf_end; | 425 | gelf_getshdr(sec, &shdr); |
426 | } | ||
294 | 427 | ||
295 | syms = elf_getdata(sec, NULL); | 428 | syms = elf_getdata(sec, NULL); |
296 | if (syms == NULL) | 429 | if (syms == NULL) |