diff options
Diffstat (limited to 'tools/perf/util/symbol.c')
-rw-r--r-- | tools/perf/util/symbol.c | 268 |
1 files changed, 202 insertions, 66 deletions
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 4683b67b5ee4..5c0f42e6b33b 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -9,6 +9,16 @@ | |||
9 | 9 | ||
10 | const char *sym_hist_filter; | 10 | const char *sym_hist_filter; |
11 | 11 | ||
12 | enum dso_origin { | ||
13 | DSO__ORIG_KERNEL = 0, | ||
14 | DSO__ORIG_JAVA_JIT, | ||
15 | DSO__ORIG_FEDORA, | ||
16 | DSO__ORIG_UBUNTU, | ||
17 | DSO__ORIG_BUILDID, | ||
18 | DSO__ORIG_DSO, | ||
19 | DSO__ORIG_NOT_FOUND, | ||
20 | }; | ||
21 | |||
12 | static struct symbol *symbol__new(u64 start, u64 len, | 22 | static struct symbol *symbol__new(u64 start, u64 len, |
13 | const char *name, unsigned int priv_size, | 23 | const char *name, unsigned int priv_size, |
14 | u64 obj_start, int verbose) | 24 | u64 obj_start, int verbose) |
@@ -65,6 +75,8 @@ struct dso *dso__new(const char *name, unsigned int sym_priv_size) | |||
65 | self->syms = RB_ROOT; | 75 | self->syms = RB_ROOT; |
66 | self->sym_priv_size = sym_priv_size; | 76 | self->sym_priv_size = sym_priv_size; |
67 | self->find_symbol = dso__find_symbol; | 77 | self->find_symbol = dso__find_symbol; |
78 | self->slen_calculated = 0; | ||
79 | self->origin = DSO__ORIG_NOT_FOUND; | ||
68 | } | 80 | } |
69 | 81 | ||
70 | return self; | 82 | return self; |
@@ -373,36 +385,61 @@ static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, | |||
373 | idx < nr_entries; \ | 385 | idx < nr_entries; \ |
374 | ++idx, pos = gelf_getrela(reldata, idx, &pos_mem)) | 386 | ++idx, pos = gelf_getrela(reldata, idx, &pos_mem)) |
375 | 387 | ||
376 | static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf, | 388 | /* |
377 | GElf_Ehdr *ehdr, Elf_Scn *scn_dynsym, | 389 | * We need to check if we have a .dynsym, so that we can handle the |
378 | GElf_Shdr *shdr_dynsym, | 390 | * .plt, synthesizing its symbols, that aren't on the symtabs (be it |
379 | size_t dynsym_idx, int verbose) | 391 | * .dynsym or .symtab). |
392 | * And always look at the original dso, not at debuginfo packages, that | ||
393 | * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS). | ||
394 | */ | ||
395 | static int dso__synthesize_plt_symbols(struct dso *self, int verbose) | ||
380 | { | 396 | { |
381 | uint32_t nr_rel_entries, idx; | 397 | uint32_t nr_rel_entries, idx; |
382 | GElf_Sym sym; | 398 | GElf_Sym sym; |
383 | u64 plt_offset; | 399 | u64 plt_offset; |
384 | GElf_Shdr shdr_plt; | 400 | GElf_Shdr shdr_plt; |
385 | struct symbol *f; | 401 | struct symbol *f; |
386 | GElf_Shdr shdr_rel_plt; | 402 | GElf_Shdr shdr_rel_plt, shdr_dynsym; |
387 | Elf_Data *reldata, *syms, *symstrs; | 403 | Elf_Data *reldata, *syms, *symstrs; |
388 | Elf_Scn *scn_plt_rel, *scn_symstrs; | 404 | Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym; |
405 | size_t dynsym_idx; | ||
406 | GElf_Ehdr ehdr; | ||
389 | char sympltname[1024]; | 407 | char sympltname[1024]; |
390 | int nr = 0, symidx; | 408 | Elf *elf; |
409 | int nr = 0, symidx, fd, err = 0; | ||
410 | |||
411 | fd = open(self->name, O_RDONLY); | ||
412 | if (fd < 0) | ||
413 | goto out; | ||
391 | 414 | ||
392 | scn_plt_rel = elf_section_by_name(elf, ehdr, &shdr_rel_plt, | 415 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); |
416 | if (elf == NULL) | ||
417 | goto out_close; | ||
418 | |||
419 | if (gelf_getehdr(elf, &ehdr) == NULL) | ||
420 | goto out_elf_end; | ||
421 | |||
422 | scn_dynsym = elf_section_by_name(elf, &ehdr, &shdr_dynsym, | ||
423 | ".dynsym", &dynsym_idx); | ||
424 | if (scn_dynsym == NULL) | ||
425 | goto out_elf_end; | ||
426 | |||
427 | scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt, | ||
393 | ".rela.plt", NULL); | 428 | ".rela.plt", NULL); |
394 | if (scn_plt_rel == NULL) { | 429 | if (scn_plt_rel == NULL) { |
395 | scn_plt_rel = elf_section_by_name(elf, ehdr, &shdr_rel_plt, | 430 | scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt, |
396 | ".rel.plt", NULL); | 431 | ".rel.plt", NULL); |
397 | if (scn_plt_rel == NULL) | 432 | if (scn_plt_rel == NULL) |
398 | return 0; | 433 | goto out_elf_end; |
399 | } | 434 | } |
400 | 435 | ||
436 | err = -1; | ||
437 | |||
401 | if (shdr_rel_plt.sh_link != dynsym_idx) | 438 | if (shdr_rel_plt.sh_link != dynsym_idx) |
402 | return 0; | 439 | goto out_elf_end; |
403 | 440 | ||
404 | if (elf_section_by_name(elf, ehdr, &shdr_plt, ".plt", NULL) == NULL) | 441 | if (elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL) == NULL) |
405 | return 0; | 442 | goto out_elf_end; |
406 | 443 | ||
407 | /* | 444 | /* |
408 | * Fetch the relocation section to find the indexes to the GOT | 445 | * Fetch the relocation section to find the indexes to the GOT |
@@ -410,19 +447,19 @@ static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf, | |||
410 | */ | 447 | */ |
411 | reldata = elf_getdata(scn_plt_rel, NULL); | 448 | reldata = elf_getdata(scn_plt_rel, NULL); |
412 | if (reldata == NULL) | 449 | if (reldata == NULL) |
413 | return -1; | 450 | goto out_elf_end; |
414 | 451 | ||
415 | syms = elf_getdata(scn_dynsym, NULL); | 452 | syms = elf_getdata(scn_dynsym, NULL); |
416 | if (syms == NULL) | 453 | if (syms == NULL) |
417 | return -1; | 454 | goto out_elf_end; |
418 | 455 | ||
419 | scn_symstrs = elf_getscn(elf, shdr_dynsym->sh_link); | 456 | scn_symstrs = elf_getscn(elf, shdr_dynsym.sh_link); |
420 | if (scn_symstrs == NULL) | 457 | if (scn_symstrs == NULL) |
421 | return -1; | 458 | goto out_elf_end; |
422 | 459 | ||
423 | symstrs = elf_getdata(scn_symstrs, NULL); | 460 | symstrs = elf_getdata(scn_symstrs, NULL); |
424 | if (symstrs == NULL) | 461 | if (symstrs == NULL) |
425 | return -1; | 462 | goto out_elf_end; |
426 | 463 | ||
427 | nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize; | 464 | nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize; |
428 | plt_offset = shdr_plt.sh_offset; | 465 | plt_offset = shdr_plt.sh_offset; |
@@ -441,7 +478,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf, | |||
441 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | 478 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, |
442 | sympltname, self->sym_priv_size, 0, verbose); | 479 | sympltname, self->sym_priv_size, 0, verbose); |
443 | if (!f) | 480 | if (!f) |
444 | return -1; | 481 | goto out_elf_end; |
445 | 482 | ||
446 | dso__insert_symbol(self, f); | 483 | dso__insert_symbol(self, f); |
447 | ++nr; | 484 | ++nr; |
@@ -459,19 +496,25 @@ static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf, | |||
459 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | 496 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, |
460 | sympltname, self->sym_priv_size, 0, verbose); | 497 | sympltname, self->sym_priv_size, 0, verbose); |
461 | if (!f) | 498 | if (!f) |
462 | return -1; | 499 | goto out_elf_end; |
463 | 500 | ||
464 | dso__insert_symbol(self, f); | 501 | dso__insert_symbol(self, f); |
465 | ++nr; | 502 | ++nr; |
466 | } | 503 | } |
467 | } else { | ||
468 | /* | ||
469 | * TODO: There are still one more shdr_rel_plt.sh_type | ||
470 | * I have to investigate, but probably should be ignored. | ||
471 | */ | ||
472 | } | 504 | } |
473 | 505 | ||
474 | return nr; | 506 | err = 0; |
507 | out_elf_end: | ||
508 | elf_end(elf); | ||
509 | out_close: | ||
510 | close(fd); | ||
511 | |||
512 | if (err == 0) | ||
513 | return nr; | ||
514 | out: | ||
515 | fprintf(stderr, "%s: problems reading %s PLT info.\n", | ||
516 | __func__, self->name); | ||
517 | return 0; | ||
475 | } | 518 | } |
476 | 519 | ||
477 | static int dso__load_sym(struct dso *self, int fd, const char *name, | 520 | static int dso__load_sym(struct dso *self, int fd, const char *name, |
@@ -485,10 +528,9 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
485 | GElf_Shdr shdr; | 528 | GElf_Shdr shdr; |
486 | Elf_Data *syms; | 529 | Elf_Data *syms; |
487 | GElf_Sym sym; | 530 | GElf_Sym sym; |
488 | Elf_Scn *sec, *sec_dynsym, *sec_strndx; | 531 | Elf_Scn *sec, *sec_strndx; |
489 | Elf *elf; | 532 | Elf *elf; |
490 | size_t dynsym_idx; | 533 | int nr = 0, kernel = !strcmp("[kernel]", self->name); |
491 | int nr = 0; | ||
492 | 534 | ||
493 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); | 535 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); |
494 | if (elf == NULL) { | 536 | if (elf == NULL) { |
@@ -504,32 +546,11 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
504 | goto out_elf_end; | 546 | goto out_elf_end; |
505 | } | 547 | } |
506 | 548 | ||
507 | /* | ||
508 | * We need to check if we have a .dynsym, so that we can handle the | ||
509 | * .plt, synthesizing its symbols, that aren't on the symtabs (be it | ||
510 | * .dynsym or .symtab) | ||
511 | */ | ||
512 | sec_dynsym = elf_section_by_name(elf, &ehdr, &shdr, | ||
513 | ".dynsym", &dynsym_idx); | ||
514 | if (sec_dynsym != NULL) { | ||
515 | nr = dso__synthesize_plt_symbols(self, elf, &ehdr, | ||
516 | sec_dynsym, &shdr, | ||
517 | dynsym_idx, verbose); | ||
518 | if (nr < 0) | ||
519 | goto out_elf_end; | ||
520 | } | ||
521 | |||
522 | /* | ||
523 | * But if we have a full .symtab (that is a superset of .dynsym) we | ||
524 | * should add the symbols not in the .dynsyn | ||
525 | */ | ||
526 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); | 549 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); |
527 | if (sec == NULL) { | 550 | if (sec == NULL) { |
528 | if (sec_dynsym == NULL) | 551 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL); |
552 | if (sec == NULL) | ||
529 | goto out_elf_end; | 553 | goto out_elf_end; |
530 | |||
531 | sec = sec_dynsym; | ||
532 | gelf_getshdr(sec, &shdr); | ||
533 | } | 554 | } |
534 | 555 | ||
535 | syms = elf_getdata(sec, NULL); | 556 | syms = elf_getdata(sec, NULL); |
@@ -549,18 +570,23 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
549 | goto out_elf_end; | 570 | goto out_elf_end; |
550 | 571 | ||
551 | secstrs = elf_getdata(sec_strndx, NULL); | 572 | secstrs = elf_getdata(sec_strndx, NULL); |
552 | if (symstrs == NULL) | 573 | if (secstrs == NULL) |
553 | goto out_elf_end; | 574 | goto out_elf_end; |
554 | 575 | ||
555 | nr_syms = shdr.sh_size / shdr.sh_entsize; | 576 | nr_syms = shdr.sh_size / shdr.sh_entsize; |
556 | 577 | ||
557 | memset(&sym, 0, sizeof(sym)); | 578 | memset(&sym, 0, sizeof(sym)); |
558 | self->adjust_symbols = (ehdr.e_type == ET_EXEC || | 579 | if (!kernel) { |
580 | self->adjust_symbols = (ehdr.e_type == ET_EXEC || | ||
559 | elf_section_by_name(elf, &ehdr, &shdr, | 581 | elf_section_by_name(elf, &ehdr, &shdr, |
560 | ".gnu.prelink_undo", | 582 | ".gnu.prelink_undo", |
561 | NULL) != NULL); | 583 | NULL) != NULL); |
584 | } else self->adjust_symbols = 0; | ||
585 | |||
562 | elf_symtab__for_each_symbol(syms, nr_syms, index, sym) { | 586 | elf_symtab__for_each_symbol(syms, nr_syms, index, sym) { |
563 | struct symbol *f; | 587 | struct symbol *f; |
588 | const char *name; | ||
589 | char *demangled; | ||
564 | u64 obj_start; | 590 | u64 obj_start; |
565 | struct section *section = NULL; | 591 | struct section *section = NULL; |
566 | int is_label = elf_sym__is_label(&sym); | 592 | int is_label = elf_sym__is_label(&sym); |
@@ -599,10 +625,19 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
599 | goto out_elf_end; | 625 | goto out_elf_end; |
600 | } | 626 | } |
601 | } | 627 | } |
628 | /* | ||
629 | * We need to figure out if the object was created from C++ sources | ||
630 | * DWARF DW_compile_unit has this, but we don't always have access | ||
631 | * to it... | ||
632 | */ | ||
633 | name = elf_sym__name(&sym, symstrs); | ||
634 | demangled = bfd_demangle(NULL, name, DMGL_PARAMS | DMGL_ANSI); | ||
635 | if (demangled != NULL) | ||
636 | name = demangled; | ||
602 | 637 | ||
603 | f = symbol__new(sym.st_value, sym.st_size, | 638 | f = symbol__new(sym.st_value, sym.st_size, name, |
604 | elf_sym__name(&sym, symstrs), | ||
605 | self->sym_priv_size, obj_start, verbose); | 639 | self->sym_priv_size, obj_start, verbose); |
640 | free(demangled); | ||
606 | if (!f) | 641 | if (!f) |
607 | goto out_elf_end; | 642 | goto out_elf_end; |
608 | 643 | ||
@@ -622,11 +657,85 @@ out_close: | |||
622 | return err; | 657 | return err; |
623 | } | 658 | } |
624 | 659 | ||
660 | #define BUILD_ID_SIZE 128 | ||
661 | |||
662 | static char *dso__read_build_id(struct dso *self, int verbose) | ||
663 | { | ||
664 | int i; | ||
665 | GElf_Ehdr ehdr; | ||
666 | GElf_Shdr shdr; | ||
667 | Elf_Data *build_id_data; | ||
668 | Elf_Scn *sec; | ||
669 | char *build_id = NULL, *bid; | ||
670 | unsigned char *raw; | ||
671 | Elf *elf; | ||
672 | int fd = open(self->name, O_RDONLY); | ||
673 | |||
674 | if (fd < 0) | ||
675 | goto out; | ||
676 | |||
677 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); | ||
678 | if (elf == NULL) { | ||
679 | if (verbose) | ||
680 | fprintf(stderr, "%s: cannot read %s ELF file.\n", | ||
681 | __func__, self->name); | ||
682 | goto out_close; | ||
683 | } | ||
684 | |||
685 | if (gelf_getehdr(elf, &ehdr) == NULL) { | ||
686 | if (verbose) | ||
687 | fprintf(stderr, "%s: cannot get elf header.\n", __func__); | ||
688 | goto out_elf_end; | ||
689 | } | ||
690 | |||
691 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".note.gnu.build-id", NULL); | ||
692 | if (sec == NULL) | ||
693 | goto out_elf_end; | ||
694 | |||
695 | build_id_data = elf_getdata(sec, NULL); | ||
696 | if (build_id_data == NULL) | ||
697 | goto out_elf_end; | ||
698 | build_id = malloc(BUILD_ID_SIZE); | ||
699 | if (build_id == NULL) | ||
700 | goto out_elf_end; | ||
701 | raw = build_id_data->d_buf + 16; | ||
702 | bid = build_id; | ||
703 | |||
704 | for (i = 0; i < 20; ++i) { | ||
705 | sprintf(bid, "%02x", *raw); | ||
706 | ++raw; | ||
707 | bid += 2; | ||
708 | } | ||
709 | if (verbose >= 2) | ||
710 | printf("%s(%s): %s\n", __func__, self->name, build_id); | ||
711 | out_elf_end: | ||
712 | elf_end(elf); | ||
713 | out_close: | ||
714 | close(fd); | ||
715 | out: | ||
716 | return build_id; | ||
717 | } | ||
718 | |||
719 | char dso__symtab_origin(const struct dso *self) | ||
720 | { | ||
721 | static const char origin[] = { | ||
722 | [DSO__ORIG_KERNEL] = 'k', | ||
723 | [DSO__ORIG_JAVA_JIT] = 'j', | ||
724 | [DSO__ORIG_FEDORA] = 'f', | ||
725 | [DSO__ORIG_UBUNTU] = 'u', | ||
726 | [DSO__ORIG_BUILDID] = 'b', | ||
727 | [DSO__ORIG_DSO] = 'd', | ||
728 | }; | ||
729 | |||
730 | if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND) | ||
731 | return '!'; | ||
732 | return origin[self->origin]; | ||
733 | } | ||
734 | |||
625 | int dso__load(struct dso *self, symbol_filter_t filter, int verbose) | 735 | int dso__load(struct dso *self, symbol_filter_t filter, int verbose) |
626 | { | 736 | { |
627 | int size = strlen(self->name) + sizeof("/usr/lib/debug%s.debug"); | 737 | int size = PATH_MAX; |
628 | char *name = malloc(size); | 738 | char *name = malloc(size), *build_id = NULL; |
629 | int variant = 0; | ||
630 | int ret = -1; | 739 | int ret = -1; |
631 | int fd; | 740 | int fd; |
632 | 741 | ||
@@ -635,26 +744,43 @@ int dso__load(struct dso *self, symbol_filter_t filter, int verbose) | |||
635 | 744 | ||
636 | self->adjust_symbols = 0; | 745 | self->adjust_symbols = 0; |
637 | 746 | ||
638 | if (strncmp(self->name, "/tmp/perf-", 10) == 0) | 747 | if (strncmp(self->name, "/tmp/perf-", 10) == 0) { |
639 | return dso__load_perf_map(self, filter, verbose); | 748 | ret = dso__load_perf_map(self, filter, verbose); |
749 | self->origin = ret > 0 ? DSO__ORIG_JAVA_JIT : | ||
750 | DSO__ORIG_NOT_FOUND; | ||
751 | return ret; | ||
752 | } | ||
753 | |||
754 | self->origin = DSO__ORIG_FEDORA - 1; | ||
640 | 755 | ||
641 | more: | 756 | more: |
642 | do { | 757 | do { |
643 | switch (variant) { | 758 | self->origin++; |
644 | case 0: /* Fedora */ | 759 | switch (self->origin) { |
760 | case DSO__ORIG_FEDORA: | ||
645 | snprintf(name, size, "/usr/lib/debug%s.debug", self->name); | 761 | snprintf(name, size, "/usr/lib/debug%s.debug", self->name); |
646 | break; | 762 | break; |
647 | case 1: /* Ubuntu */ | 763 | case DSO__ORIG_UBUNTU: |
648 | snprintf(name, size, "/usr/lib/debug%s", self->name); | 764 | snprintf(name, size, "/usr/lib/debug%s", self->name); |
649 | break; | 765 | break; |
650 | case 2: /* Sane people */ | 766 | case DSO__ORIG_BUILDID: |
767 | build_id = dso__read_build_id(self, verbose); | ||
768 | if (build_id != NULL) { | ||
769 | snprintf(name, size, | ||
770 | "/usr/lib/debug/.build-id/%.2s/%s.debug", | ||
771 | build_id, build_id + 2); | ||
772 | free(build_id); | ||
773 | break; | ||
774 | } | ||
775 | self->origin++; | ||
776 | /* Fall thru */ | ||
777 | case DSO__ORIG_DSO: | ||
651 | snprintf(name, size, "%s", self->name); | 778 | snprintf(name, size, "%s", self->name); |
652 | break; | 779 | break; |
653 | 780 | ||
654 | default: | 781 | default: |
655 | goto out; | 782 | goto out; |
656 | } | 783 | } |
657 | variant++; | ||
658 | 784 | ||
659 | fd = open(name, O_RDONLY); | 785 | fd = open(name, O_RDONLY); |
660 | } while (fd < 0); | 786 | } while (fd < 0); |
@@ -668,8 +794,15 @@ more: | |||
668 | if (!ret) | 794 | if (!ret) |
669 | goto more; | 795 | goto more; |
670 | 796 | ||
797 | if (ret > 0) { | ||
798 | int nr_plt = dso__synthesize_plt_symbols(self, verbose); | ||
799 | if (nr_plt > 0) | ||
800 | ret += nr_plt; | ||
801 | } | ||
671 | out: | 802 | out: |
672 | free(name); | 803 | free(name); |
804 | if (ret < 0 && strstr(self->name, " (deleted)") != NULL) | ||
805 | return 0; | ||
673 | return ret; | 806 | return ret; |
674 | } | 807 | } |
675 | 808 | ||
@@ -785,6 +918,9 @@ int dso__load_kernel(struct dso *self, const char *vmlinux, | |||
785 | if (err <= 0) | 918 | if (err <= 0) |
786 | err = dso__load_kallsyms(self, filter, verbose); | 919 | err = dso__load_kallsyms(self, filter, verbose); |
787 | 920 | ||
921 | if (err > 0) | ||
922 | self->origin = DSO__ORIG_KERNEL; | ||
923 | |||
788 | return err; | 924 | return err; |
789 | } | 925 | } |
790 | 926 | ||