diff options
Diffstat (limited to 'tools/perf/util/symbol-elf.c')
-rw-r--r-- | tools/perf/util/symbol-elf.c | 190 |
1 files changed, 181 insertions, 9 deletions
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 4b12bf850325..a9c829be5216 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c | |||
@@ -8,6 +8,22 @@ | |||
8 | #include "symbol.h" | 8 | #include "symbol.h" |
9 | #include "debug.h" | 9 | #include "debug.h" |
10 | 10 | ||
11 | #ifndef HAVE_ELF_GETPHDRNUM | ||
12 | static int elf_getphdrnum(Elf *elf, size_t *dst) | ||
13 | { | ||
14 | GElf_Ehdr gehdr; | ||
15 | GElf_Ehdr *ehdr; | ||
16 | |||
17 | ehdr = gelf_getehdr(elf, &gehdr); | ||
18 | if (!ehdr) | ||
19 | return -1; | ||
20 | |||
21 | *dst = ehdr->e_phnum; | ||
22 | |||
23 | return 0; | ||
24 | } | ||
25 | #endif | ||
26 | |||
11 | #ifndef NT_GNU_BUILD_ID | 27 | #ifndef NT_GNU_BUILD_ID |
12 | #define NT_GNU_BUILD_ID 3 | 28 | #define NT_GNU_BUILD_ID 3 |
13 | #endif | 29 | #endif |
@@ -599,11 +615,13 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, | |||
599 | if (dso->kernel == DSO_TYPE_USER) { | 615 | if (dso->kernel == DSO_TYPE_USER) { |
600 | GElf_Shdr shdr; | 616 | GElf_Shdr shdr; |
601 | ss->adjust_symbols = (ehdr.e_type == ET_EXEC || | 617 | ss->adjust_symbols = (ehdr.e_type == ET_EXEC || |
618 | ehdr.e_type == ET_REL || | ||
602 | elf_section_by_name(elf, &ehdr, &shdr, | 619 | elf_section_by_name(elf, &ehdr, &shdr, |
603 | ".gnu.prelink_undo", | 620 | ".gnu.prelink_undo", |
604 | NULL) != NULL); | 621 | NULL) != NULL); |
605 | } else { | 622 | } else { |
606 | ss->adjust_symbols = 0; | 623 | ss->adjust_symbols = ehdr.e_type == ET_EXEC || |
624 | ehdr.e_type == ET_REL; | ||
607 | } | 625 | } |
608 | 626 | ||
609 | ss->name = strdup(name); | 627 | ss->name = strdup(name); |
@@ -624,6 +642,37 @@ out_close: | |||
624 | return err; | 642 | return err; |
625 | } | 643 | } |
626 | 644 | ||
645 | /** | ||
646 | * ref_reloc_sym_not_found - has kernel relocation symbol been found. | ||
647 | * @kmap: kernel maps and relocation reference symbol | ||
648 | * | ||
649 | * This function returns %true if we are dealing with the kernel maps and the | ||
650 | * relocation reference symbol has not yet been found. Otherwise %false is | ||
651 | * returned. | ||
652 | */ | ||
653 | static bool ref_reloc_sym_not_found(struct kmap *kmap) | ||
654 | { | ||
655 | return kmap && kmap->ref_reloc_sym && kmap->ref_reloc_sym->name && | ||
656 | !kmap->ref_reloc_sym->unrelocated_addr; | ||
657 | } | ||
658 | |||
659 | /** | ||
660 | * ref_reloc - kernel relocation offset. | ||
661 | * @kmap: kernel maps and relocation reference symbol | ||
662 | * | ||
663 | * This function returns the offset of kernel addresses as determined by using | ||
664 | * the relocation reference symbol i.e. if the kernel has not been relocated | ||
665 | * then the return value is zero. | ||
666 | */ | ||
667 | static u64 ref_reloc(struct kmap *kmap) | ||
668 | { | ||
669 | if (kmap && kmap->ref_reloc_sym && | ||
670 | kmap->ref_reloc_sym->unrelocated_addr) | ||
671 | return kmap->ref_reloc_sym->addr - | ||
672 | kmap->ref_reloc_sym->unrelocated_addr; | ||
673 | return 0; | ||
674 | } | ||
675 | |||
627 | int dso__load_sym(struct dso *dso, struct map *map, | 676 | int dso__load_sym(struct dso *dso, struct map *map, |
628 | struct symsrc *syms_ss, struct symsrc *runtime_ss, | 677 | struct symsrc *syms_ss, struct symsrc *runtime_ss, |
629 | symbol_filter_t filter, int kmodule) | 678 | symbol_filter_t filter, int kmodule) |
@@ -642,8 +691,17 @@ int dso__load_sym(struct dso *dso, struct map *map, | |||
642 | Elf_Scn *sec, *sec_strndx; | 691 | Elf_Scn *sec, *sec_strndx; |
643 | Elf *elf; | 692 | Elf *elf; |
644 | int nr = 0; | 693 | int nr = 0; |
694 | bool remap_kernel = false, adjust_kernel_syms = false; | ||
645 | 695 | ||
646 | dso->symtab_type = syms_ss->type; | 696 | dso->symtab_type = syms_ss->type; |
697 | dso->rel = syms_ss->ehdr.e_type == ET_REL; | ||
698 | |||
699 | /* | ||
700 | * Modules may already have symbols from kallsyms, but those symbols | ||
701 | * have the wrong values for the dso maps, so remove them. | ||
702 | */ | ||
703 | if (kmodule && syms_ss->symtab) | ||
704 | symbols__delete(&dso->symbols[map->type]); | ||
647 | 705 | ||
648 | if (!syms_ss->symtab) { | 706 | if (!syms_ss->symtab) { |
649 | syms_ss->symtab = syms_ss->dynsym; | 707 | syms_ss->symtab = syms_ss->dynsym; |
@@ -681,7 +739,31 @@ int dso__load_sym(struct dso *dso, struct map *map, | |||
681 | nr_syms = shdr.sh_size / shdr.sh_entsize; | 739 | nr_syms = shdr.sh_size / shdr.sh_entsize; |
682 | 740 | ||
683 | memset(&sym, 0, sizeof(sym)); | 741 | memset(&sym, 0, sizeof(sym)); |
684 | dso->adjust_symbols = runtime_ss->adjust_symbols; | 742 | |
743 | /* | ||
744 | * The kernel relocation symbol is needed in advance in order to adjust | ||
745 | * kernel maps correctly. | ||
746 | */ | ||
747 | if (ref_reloc_sym_not_found(kmap)) { | ||
748 | elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) { | ||
749 | const char *elf_name = elf_sym__name(&sym, symstrs); | ||
750 | |||
751 | if (strcmp(elf_name, kmap->ref_reloc_sym->name)) | ||
752 | continue; | ||
753 | kmap->ref_reloc_sym->unrelocated_addr = sym.st_value; | ||
754 | break; | ||
755 | } | ||
756 | } | ||
757 | |||
758 | dso->adjust_symbols = runtime_ss->adjust_symbols || ref_reloc(kmap); | ||
759 | /* | ||
760 | * Initial kernel and module mappings do not map to the dso. For | ||
761 | * function mappings, flag the fixups. | ||
762 | */ | ||
763 | if (map->type == MAP__FUNCTION && (dso->kernel || kmodule)) { | ||
764 | remap_kernel = true; | ||
765 | adjust_kernel_syms = dso->adjust_symbols; | ||
766 | } | ||
685 | elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) { | 767 | elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) { |
686 | struct symbol *f; | 768 | struct symbol *f; |
687 | const char *elf_name = elf_sym__name(&sym, symstrs); | 769 | const char *elf_name = elf_sym__name(&sym, symstrs); |
@@ -690,10 +772,6 @@ int dso__load_sym(struct dso *dso, struct map *map, | |||
690 | const char *section_name; | 772 | const char *section_name; |
691 | bool used_opd = false; | 773 | bool used_opd = false; |
692 | 774 | ||
693 | if (kmap && kmap->ref_reloc_sym && kmap->ref_reloc_sym->name && | ||
694 | strcmp(elf_name, kmap->ref_reloc_sym->name) == 0) | ||
695 | kmap->ref_reloc_sym->unrelocated_addr = sym.st_value; | ||
696 | |||
697 | if (!is_label && !elf_sym__is_a(&sym, map->type)) | 775 | if (!is_label && !elf_sym__is_a(&sym, map->type)) |
698 | continue; | 776 | continue; |
699 | 777 | ||
@@ -745,20 +823,55 @@ int dso__load_sym(struct dso *dso, struct map *map, | |||
745 | (sym.st_value & 1)) | 823 | (sym.st_value & 1)) |
746 | --sym.st_value; | 824 | --sym.st_value; |
747 | 825 | ||
748 | if (dso->kernel != DSO_TYPE_USER || kmodule) { | 826 | if (dso->kernel || kmodule) { |
749 | char dso_name[PATH_MAX]; | 827 | char dso_name[PATH_MAX]; |
750 | 828 | ||
829 | /* Adjust symbol to map to file offset */ | ||
830 | if (adjust_kernel_syms) | ||
831 | sym.st_value -= shdr.sh_addr - shdr.sh_offset; | ||
832 | |||
751 | if (strcmp(section_name, | 833 | if (strcmp(section_name, |
752 | (curr_dso->short_name + | 834 | (curr_dso->short_name + |
753 | dso->short_name_len)) == 0) | 835 | dso->short_name_len)) == 0) |
754 | goto new_symbol; | 836 | goto new_symbol; |
755 | 837 | ||
756 | if (strcmp(section_name, ".text") == 0) { | 838 | if (strcmp(section_name, ".text") == 0) { |
839 | /* | ||
840 | * The initial kernel mapping is based on | ||
841 | * kallsyms and identity maps. Overwrite it to | ||
842 | * map to the kernel dso. | ||
843 | */ | ||
844 | if (remap_kernel && dso->kernel) { | ||
845 | remap_kernel = false; | ||
846 | map->start = shdr.sh_addr + | ||
847 | ref_reloc(kmap); | ||
848 | map->end = map->start + shdr.sh_size; | ||
849 | map->pgoff = shdr.sh_offset; | ||
850 | map->map_ip = map__map_ip; | ||
851 | map->unmap_ip = map__unmap_ip; | ||
852 | /* Ensure maps are correctly ordered */ | ||
853 | map_groups__remove(kmap->kmaps, map); | ||
854 | map_groups__insert(kmap->kmaps, map); | ||
855 | } | ||
856 | |||
857 | /* | ||
858 | * The initial module mapping is based on | ||
859 | * /proc/modules mapped to offset zero. | ||
860 | * Overwrite it to map to the module dso. | ||
861 | */ | ||
862 | if (remap_kernel && kmodule) { | ||
863 | remap_kernel = false; | ||
864 | map->pgoff = shdr.sh_offset; | ||
865 | } | ||
866 | |||
757 | curr_map = map; | 867 | curr_map = map; |
758 | curr_dso = dso; | 868 | curr_dso = dso; |
759 | goto new_symbol; | 869 | goto new_symbol; |
760 | } | 870 | } |
761 | 871 | ||
872 | if (!kmap) | ||
873 | goto new_symbol; | ||
874 | |||
762 | snprintf(dso_name, sizeof(dso_name), | 875 | snprintf(dso_name, sizeof(dso_name), |
763 | "%s%s", dso->short_name, section_name); | 876 | "%s%s", dso->short_name, section_name); |
764 | 877 | ||
@@ -781,8 +894,16 @@ int dso__load_sym(struct dso *dso, struct map *map, | |||
781 | dso__delete(curr_dso); | 894 | dso__delete(curr_dso); |
782 | goto out_elf_end; | 895 | goto out_elf_end; |
783 | } | 896 | } |
784 | curr_map->map_ip = identity__map_ip; | 897 | if (adjust_kernel_syms) { |
785 | curr_map->unmap_ip = identity__map_ip; | 898 | curr_map->start = shdr.sh_addr + |
899 | ref_reloc(kmap); | ||
900 | curr_map->end = curr_map->start + | ||
901 | shdr.sh_size; | ||
902 | curr_map->pgoff = shdr.sh_offset; | ||
903 | } else { | ||
904 | curr_map->map_ip = identity__map_ip; | ||
905 | curr_map->unmap_ip = identity__map_ip; | ||
906 | } | ||
786 | curr_dso->symtab_type = dso->symtab_type; | 907 | curr_dso->symtab_type = dso->symtab_type; |
787 | map_groups__insert(kmap->kmaps, curr_map); | 908 | map_groups__insert(kmap->kmaps, curr_map); |
788 | dsos__add(&dso->node, curr_dso); | 909 | dsos__add(&dso->node, curr_dso); |
@@ -846,6 +967,57 @@ out_elf_end: | |||
846 | return err; | 967 | return err; |
847 | } | 968 | } |
848 | 969 | ||
970 | static int elf_read_maps(Elf *elf, bool exe, mapfn_t mapfn, void *data) | ||
971 | { | ||
972 | GElf_Phdr phdr; | ||
973 | size_t i, phdrnum; | ||
974 | int err; | ||
975 | u64 sz; | ||
976 | |||
977 | if (elf_getphdrnum(elf, &phdrnum)) | ||
978 | return -1; | ||
979 | |||
980 | for (i = 0; i < phdrnum; i++) { | ||
981 | if (gelf_getphdr(elf, i, &phdr) == NULL) | ||
982 | return -1; | ||
983 | if (phdr.p_type != PT_LOAD) | ||
984 | continue; | ||
985 | if (exe) { | ||
986 | if (!(phdr.p_flags & PF_X)) | ||
987 | continue; | ||
988 | } else { | ||
989 | if (!(phdr.p_flags & PF_R)) | ||
990 | continue; | ||
991 | } | ||
992 | sz = min(phdr.p_memsz, phdr.p_filesz); | ||
993 | if (!sz) | ||
994 | continue; | ||
995 | err = mapfn(phdr.p_vaddr, sz, phdr.p_offset, data); | ||
996 | if (err) | ||
997 | return err; | ||
998 | } | ||
999 | return 0; | ||
1000 | } | ||
1001 | |||
1002 | int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data, | ||
1003 | bool *is_64_bit) | ||
1004 | { | ||
1005 | int err; | ||
1006 | Elf *elf; | ||
1007 | |||
1008 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | ||
1009 | if (elf == NULL) | ||
1010 | return -1; | ||
1011 | |||
1012 | if (is_64_bit) | ||
1013 | *is_64_bit = (gelf_getclass(elf) == ELFCLASS64); | ||
1014 | |||
1015 | err = elf_read_maps(elf, exe, mapfn, data); | ||
1016 | |||
1017 | elf_end(elf); | ||
1018 | return err; | ||
1019 | } | ||
1020 | |||
849 | void symbol__elf_init(void) | 1021 | void symbol__elf_init(void) |
850 | { | 1022 | { |
851 | elf_version(EV_CURRENT); | 1023 | elf_version(EV_CURRENT); |