diff options
author | Namhyung Kim <namhyung.kim@lge.com> | 2012-08-06 00:41:20 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2012-08-09 15:26:33 -0400 |
commit | e5a1845fc0aeca85c98115980c3531129f87e18d (patch) | |
tree | f67ec3bfc5dc9ae6e2d89ad9b6493a873d4c8652 /tools/perf/util/symbol.c | |
parent | 166ccc9c244828da9214a0e7ba4d5dde6a26dcc1 (diff) |
perf symbols: Split out util/symbol-elf.c
Factor out the dependency of ELF handling into separate symbol-elf.c
file. It is a preparation of building a minimalistic version perf tools
which doesn't depend on the elfutils.
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1344228082-15569-3-git-send-email-namhyung@kernel.org
[ committer note: removed blank line at symbol-elf.c EOF ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/util/symbol.c')
-rw-r--r-- | tools/perf/util/symbol.c | 804 |
1 files changed, 7 insertions, 797 deletions
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index a61fec4518da..f02de8ac842d 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -15,8 +15,6 @@ | |||
15 | #include "symbol.h" | 15 | #include "symbol.h" |
16 | #include "strlist.h" | 16 | #include "strlist.h" |
17 | 17 | ||
18 | #include <libelf.h> | ||
19 | #include <gelf.h> | ||
20 | #include <elf.h> | 18 | #include <elf.h> |
21 | #include <limits.h> | 19 | #include <limits.h> |
22 | #include <sys/utsname.h> | 20 | #include <sys/utsname.h> |
@@ -25,15 +23,7 @@ | |||
25 | #define KSYM_NAME_LEN 256 | 23 | #define KSYM_NAME_LEN 256 |
26 | #endif | 24 | #endif |
27 | 25 | ||
28 | #ifndef NT_GNU_BUILD_ID | ||
29 | #define NT_GNU_BUILD_ID 3 | ||
30 | #endif | ||
31 | |||
32 | static void dso_cache__free(struct rb_root *root); | 26 | static void dso_cache__free(struct rb_root *root); |
33 | static bool dso__build_id_equal(const struct dso *dso, u8 *build_id); | ||
34 | static int elf_read_build_id(Elf *elf, void *bf, size_t size); | ||
35 | static void dsos__add(struct list_head *head, struct dso *dso); | ||
36 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); | ||
37 | static int dso__load_kernel_sym(struct dso *dso, struct map *map, | 27 | static int dso__load_kernel_sym(struct dso *dso, struct map *map, |
38 | symbol_filter_t filter); | 28 | symbol_filter_t filter); |
39 | static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, | 29 | static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, |
@@ -170,7 +160,7 @@ static int choose_best_symbol(struct symbol *syma, struct symbol *symb) | |||
170 | return SYMBOL_B; | 160 | return SYMBOL_B; |
171 | } | 161 | } |
172 | 162 | ||
173 | static void symbols__fixup_duplicate(struct rb_root *symbols) | 163 | void symbols__fixup_duplicate(struct rb_root *symbols) |
174 | { | 164 | { |
175 | struct rb_node *nd; | 165 | struct rb_node *nd; |
176 | struct symbol *curr, *next; | 166 | struct symbol *curr, *next; |
@@ -199,7 +189,7 @@ again: | |||
199 | } | 189 | } |
200 | } | 190 | } |
201 | 191 | ||
202 | static void symbols__fixup_end(struct rb_root *symbols) | 192 | void symbols__fixup_end(struct rb_root *symbols) |
203 | { | 193 | { |
204 | struct rb_node *nd, *prevnd = rb_first(symbols); | 194 | struct rb_node *nd, *prevnd = rb_first(symbols); |
205 | struct symbol *curr, *prev; | 195 | struct symbol *curr, *prev; |
@@ -222,7 +212,7 @@ static void symbols__fixup_end(struct rb_root *symbols) | |||
222 | curr->end = roundup(curr->start, 4096); | 212 | curr->end = roundup(curr->start, 4096); |
223 | } | 213 | } |
224 | 214 | ||
225 | static void __map_groups__fixup_end(struct map_groups *mg, enum map_type type) | 215 | void __map_groups__fixup_end(struct map_groups *mg, enum map_type type) |
226 | { | 216 | { |
227 | struct map *prev, *curr; | 217 | struct map *prev, *curr; |
228 | struct rb_node *nd, *prevnd = rb_first(&mg->maps[type]); | 218 | struct rb_node *nd, *prevnd = rb_first(&mg->maps[type]); |
@@ -252,8 +242,7 @@ static void map_groups__fixup_end(struct map_groups *mg) | |||
252 | __map_groups__fixup_end(mg, i); | 242 | __map_groups__fixup_end(mg, i); |
253 | } | 243 | } |
254 | 244 | ||
255 | static struct symbol *symbol__new(u64 start, u64 len, u8 binding, | 245 | struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name) |
256 | const char *name) | ||
257 | { | 246 | { |
258 | size_t namelen = strlen(name) + 1; | 247 | size_t namelen = strlen(name) + 1; |
259 | struct symbol *sym = calloc(1, (symbol_conf.priv_size + | 248 | struct symbol *sym = calloc(1, (symbol_conf.priv_size + |
@@ -390,7 +379,7 @@ void dso__set_build_id(struct dso *dso, void *build_id) | |||
390 | dso->has_build_id = 1; | 379 | dso->has_build_id = 1; |
391 | } | 380 | } |
392 | 381 | ||
393 | static void symbols__insert(struct rb_root *symbols, struct symbol *sym) | 382 | void symbols__insert(struct rb_root *symbols, struct symbol *sym) |
394 | { | 383 | { |
395 | struct rb_node **p = &symbols->rb_node; | 384 | struct rb_node **p = &symbols->rb_node; |
396 | struct rb_node *parent = NULL; | 385 | struct rb_node *parent = NULL; |
@@ -904,561 +893,7 @@ out_failure: | |||
904 | return -1; | 893 | return -1; |
905 | } | 894 | } |
906 | 895 | ||
907 | /** | 896 | bool dso__build_id_equal(const struct dso *dso, u8 *build_id) |
908 | * elf_symtab__for_each_symbol - iterate thru all the symbols | ||
909 | * | ||
910 | * @syms: struct elf_symtab instance to iterate | ||
911 | * @idx: uint32_t idx | ||
912 | * @sym: GElf_Sym iterator | ||
913 | */ | ||
914 | #define elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) \ | ||
915 | for (idx = 0, gelf_getsym(syms, idx, &sym);\ | ||
916 | idx < nr_syms; \ | ||
917 | idx++, gelf_getsym(syms, idx, &sym)) | ||
918 | |||
919 | static inline uint8_t elf_sym__type(const GElf_Sym *sym) | ||
920 | { | ||
921 | return GELF_ST_TYPE(sym->st_info); | ||
922 | } | ||
923 | |||
924 | static inline int elf_sym__is_function(const GElf_Sym *sym) | ||
925 | { | ||
926 | return elf_sym__type(sym) == STT_FUNC && | ||
927 | sym->st_name != 0 && | ||
928 | sym->st_shndx != SHN_UNDEF; | ||
929 | } | ||
930 | |||
931 | static inline bool elf_sym__is_object(const GElf_Sym *sym) | ||
932 | { | ||
933 | return elf_sym__type(sym) == STT_OBJECT && | ||
934 | sym->st_name != 0 && | ||
935 | sym->st_shndx != SHN_UNDEF; | ||
936 | } | ||
937 | |||
938 | static inline int elf_sym__is_label(const GElf_Sym *sym) | ||
939 | { | ||
940 | return elf_sym__type(sym) == STT_NOTYPE && | ||
941 | sym->st_name != 0 && | ||
942 | sym->st_shndx != SHN_UNDEF && | ||
943 | sym->st_shndx != SHN_ABS; | ||
944 | } | ||
945 | |||
946 | static inline const char *elf_sec__name(const GElf_Shdr *shdr, | ||
947 | const Elf_Data *secstrs) | ||
948 | { | ||
949 | return secstrs->d_buf + shdr->sh_name; | ||
950 | } | ||
951 | |||
952 | static inline int elf_sec__is_text(const GElf_Shdr *shdr, | ||
953 | const Elf_Data *secstrs) | ||
954 | { | ||
955 | return strstr(elf_sec__name(shdr, secstrs), "text") != NULL; | ||
956 | } | ||
957 | |||
958 | static inline bool elf_sec__is_data(const GElf_Shdr *shdr, | ||
959 | const Elf_Data *secstrs) | ||
960 | { | ||
961 | return strstr(elf_sec__name(shdr, secstrs), "data") != NULL; | ||
962 | } | ||
963 | |||
964 | static inline const char *elf_sym__name(const GElf_Sym *sym, | ||
965 | const Elf_Data *symstrs) | ||
966 | { | ||
967 | return symstrs->d_buf + sym->st_name; | ||
968 | } | ||
969 | |||
970 | static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, | ||
971 | GElf_Shdr *shp, const char *name, | ||
972 | size_t *idx) | ||
973 | { | ||
974 | Elf_Scn *sec = NULL; | ||
975 | size_t cnt = 1; | ||
976 | |||
977 | while ((sec = elf_nextscn(elf, sec)) != NULL) { | ||
978 | char *str; | ||
979 | |||
980 | gelf_getshdr(sec, shp); | ||
981 | str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); | ||
982 | if (!strcmp(name, str)) { | ||
983 | if (idx) | ||
984 | *idx = cnt; | ||
985 | break; | ||
986 | } | ||
987 | ++cnt; | ||
988 | } | ||
989 | |||
990 | return sec; | ||
991 | } | ||
992 | |||
993 | #define elf_section__for_each_rel(reldata, pos, pos_mem, idx, nr_entries) \ | ||
994 | for (idx = 0, pos = gelf_getrel(reldata, 0, &pos_mem); \ | ||
995 | idx < nr_entries; \ | ||
996 | ++idx, pos = gelf_getrel(reldata, idx, &pos_mem)) | ||
997 | |||
998 | #define elf_section__for_each_rela(reldata, pos, pos_mem, idx, nr_entries) \ | ||
999 | for (idx = 0, pos = gelf_getrela(reldata, 0, &pos_mem); \ | ||
1000 | idx < nr_entries; \ | ||
1001 | ++idx, pos = gelf_getrela(reldata, idx, &pos_mem)) | ||
1002 | |||
1003 | /* | ||
1004 | * We need to check if we have a .dynsym, so that we can handle the | ||
1005 | * .plt, synthesizing its symbols, that aren't on the symtabs (be it | ||
1006 | * .dynsym or .symtab). | ||
1007 | * And always look at the original dso, not at debuginfo packages, that | ||
1008 | * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS). | ||
1009 | */ | ||
1010 | static int | ||
1011 | dso__synthesize_plt_symbols(struct dso *dso, char *name, struct map *map, | ||
1012 | symbol_filter_t filter) | ||
1013 | { | ||
1014 | uint32_t nr_rel_entries, idx; | ||
1015 | GElf_Sym sym; | ||
1016 | u64 plt_offset; | ||
1017 | GElf_Shdr shdr_plt; | ||
1018 | struct symbol *f; | ||
1019 | GElf_Shdr shdr_rel_plt, shdr_dynsym; | ||
1020 | Elf_Data *reldata, *syms, *symstrs; | ||
1021 | Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym; | ||
1022 | size_t dynsym_idx; | ||
1023 | GElf_Ehdr ehdr; | ||
1024 | char sympltname[1024]; | ||
1025 | Elf *elf; | ||
1026 | int nr = 0, symidx, fd, err = 0; | ||
1027 | |||
1028 | fd = open(name, O_RDONLY); | ||
1029 | if (fd < 0) | ||
1030 | goto out; | ||
1031 | |||
1032 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | ||
1033 | if (elf == NULL) | ||
1034 | goto out_close; | ||
1035 | |||
1036 | if (gelf_getehdr(elf, &ehdr) == NULL) | ||
1037 | goto out_elf_end; | ||
1038 | |||
1039 | scn_dynsym = elf_section_by_name(elf, &ehdr, &shdr_dynsym, | ||
1040 | ".dynsym", &dynsym_idx); | ||
1041 | if (scn_dynsym == NULL) | ||
1042 | goto out_elf_end; | ||
1043 | |||
1044 | scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt, | ||
1045 | ".rela.plt", NULL); | ||
1046 | if (scn_plt_rel == NULL) { | ||
1047 | scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt, | ||
1048 | ".rel.plt", NULL); | ||
1049 | if (scn_plt_rel == NULL) | ||
1050 | goto out_elf_end; | ||
1051 | } | ||
1052 | |||
1053 | err = -1; | ||
1054 | |||
1055 | if (shdr_rel_plt.sh_link != dynsym_idx) | ||
1056 | goto out_elf_end; | ||
1057 | |||
1058 | if (elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL) == NULL) | ||
1059 | goto out_elf_end; | ||
1060 | |||
1061 | /* | ||
1062 | * Fetch the relocation section to find the idxes to the GOT | ||
1063 | * and the symbols in the .dynsym they refer to. | ||
1064 | */ | ||
1065 | reldata = elf_getdata(scn_plt_rel, NULL); | ||
1066 | if (reldata == NULL) | ||
1067 | goto out_elf_end; | ||
1068 | |||
1069 | syms = elf_getdata(scn_dynsym, NULL); | ||
1070 | if (syms == NULL) | ||
1071 | goto out_elf_end; | ||
1072 | |||
1073 | scn_symstrs = elf_getscn(elf, shdr_dynsym.sh_link); | ||
1074 | if (scn_symstrs == NULL) | ||
1075 | goto out_elf_end; | ||
1076 | |||
1077 | symstrs = elf_getdata(scn_symstrs, NULL); | ||
1078 | if (symstrs == NULL) | ||
1079 | goto out_elf_end; | ||
1080 | |||
1081 | nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize; | ||
1082 | plt_offset = shdr_plt.sh_offset; | ||
1083 | |||
1084 | if (shdr_rel_plt.sh_type == SHT_RELA) { | ||
1085 | GElf_Rela pos_mem, *pos; | ||
1086 | |||
1087 | elf_section__for_each_rela(reldata, pos, pos_mem, idx, | ||
1088 | nr_rel_entries) { | ||
1089 | symidx = GELF_R_SYM(pos->r_info); | ||
1090 | plt_offset += shdr_plt.sh_entsize; | ||
1091 | gelf_getsym(syms, symidx, &sym); | ||
1092 | snprintf(sympltname, sizeof(sympltname), | ||
1093 | "%s@plt", elf_sym__name(&sym, symstrs)); | ||
1094 | |||
1095 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | ||
1096 | STB_GLOBAL, sympltname); | ||
1097 | if (!f) | ||
1098 | goto out_elf_end; | ||
1099 | |||
1100 | if (filter && filter(map, f)) | ||
1101 | symbol__delete(f); | ||
1102 | else { | ||
1103 | symbols__insert(&dso->symbols[map->type], f); | ||
1104 | ++nr; | ||
1105 | } | ||
1106 | } | ||
1107 | } else if (shdr_rel_plt.sh_type == SHT_REL) { | ||
1108 | GElf_Rel pos_mem, *pos; | ||
1109 | elf_section__for_each_rel(reldata, pos, pos_mem, idx, | ||
1110 | nr_rel_entries) { | ||
1111 | symidx = GELF_R_SYM(pos->r_info); | ||
1112 | plt_offset += shdr_plt.sh_entsize; | ||
1113 | gelf_getsym(syms, symidx, &sym); | ||
1114 | snprintf(sympltname, sizeof(sympltname), | ||
1115 | "%s@plt", elf_sym__name(&sym, symstrs)); | ||
1116 | |||
1117 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | ||
1118 | STB_GLOBAL, sympltname); | ||
1119 | if (!f) | ||
1120 | goto out_elf_end; | ||
1121 | |||
1122 | if (filter && filter(map, f)) | ||
1123 | symbol__delete(f); | ||
1124 | else { | ||
1125 | symbols__insert(&dso->symbols[map->type], f); | ||
1126 | ++nr; | ||
1127 | } | ||
1128 | } | ||
1129 | } | ||
1130 | |||
1131 | err = 0; | ||
1132 | out_elf_end: | ||
1133 | elf_end(elf); | ||
1134 | out_close: | ||
1135 | close(fd); | ||
1136 | |||
1137 | if (err == 0) | ||
1138 | return nr; | ||
1139 | out: | ||
1140 | pr_debug("%s: problems reading %s PLT info.\n", | ||
1141 | __func__, dso->long_name); | ||
1142 | return 0; | ||
1143 | } | ||
1144 | |||
1145 | static bool elf_sym__is_a(GElf_Sym *sym, enum map_type type) | ||
1146 | { | ||
1147 | switch (type) { | ||
1148 | case MAP__FUNCTION: | ||
1149 | return elf_sym__is_function(sym); | ||
1150 | case MAP__VARIABLE: | ||
1151 | return elf_sym__is_object(sym); | ||
1152 | default: | ||
1153 | return false; | ||
1154 | } | ||
1155 | } | ||
1156 | |||
1157 | static bool elf_sec__is_a(GElf_Shdr *shdr, Elf_Data *secstrs, | ||
1158 | enum map_type type) | ||
1159 | { | ||
1160 | switch (type) { | ||
1161 | case MAP__FUNCTION: | ||
1162 | return elf_sec__is_text(shdr, secstrs); | ||
1163 | case MAP__VARIABLE: | ||
1164 | return elf_sec__is_data(shdr, secstrs); | ||
1165 | default: | ||
1166 | return false; | ||
1167 | } | ||
1168 | } | ||
1169 | |||
1170 | static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr) | ||
1171 | { | ||
1172 | Elf_Scn *sec = NULL; | ||
1173 | GElf_Shdr shdr; | ||
1174 | size_t cnt = 1; | ||
1175 | |||
1176 | while ((sec = elf_nextscn(elf, sec)) != NULL) { | ||
1177 | gelf_getshdr(sec, &shdr); | ||
1178 | |||
1179 | if ((addr >= shdr.sh_addr) && | ||
1180 | (addr < (shdr.sh_addr + shdr.sh_size))) | ||
1181 | return cnt; | ||
1182 | |||
1183 | ++cnt; | ||
1184 | } | ||
1185 | |||
1186 | return -1; | ||
1187 | } | ||
1188 | |||
1189 | static int dso__swap_init(struct dso *dso, unsigned char eidata) | ||
1190 | { | ||
1191 | static unsigned int const endian = 1; | ||
1192 | |||
1193 | dso->needs_swap = DSO_SWAP__NO; | ||
1194 | |||
1195 | switch (eidata) { | ||
1196 | case ELFDATA2LSB: | ||
1197 | /* We are big endian, DSO is little endian. */ | ||
1198 | if (*(unsigned char const *)&endian != 1) | ||
1199 | dso->needs_swap = DSO_SWAP__YES; | ||
1200 | break; | ||
1201 | |||
1202 | case ELFDATA2MSB: | ||
1203 | /* We are little endian, DSO is big endian. */ | ||
1204 | if (*(unsigned char const *)&endian != 0) | ||
1205 | dso->needs_swap = DSO_SWAP__YES; | ||
1206 | break; | ||
1207 | |||
1208 | default: | ||
1209 | pr_err("unrecognized DSO data encoding %d\n", eidata); | ||
1210 | return -EINVAL; | ||
1211 | } | ||
1212 | |||
1213 | return 0; | ||
1214 | } | ||
1215 | |||
1216 | static int dso__load_sym(struct dso *dso, struct map *map, const char *name, | ||
1217 | int fd, symbol_filter_t filter, int kmodule, | ||
1218 | int want_symtab) | ||
1219 | { | ||
1220 | struct kmap *kmap = dso->kernel ? map__kmap(map) : NULL; | ||
1221 | struct map *curr_map = map; | ||
1222 | struct dso *curr_dso = dso; | ||
1223 | Elf_Data *symstrs, *secstrs; | ||
1224 | uint32_t nr_syms; | ||
1225 | int err = -1; | ||
1226 | uint32_t idx; | ||
1227 | GElf_Ehdr ehdr; | ||
1228 | GElf_Shdr shdr, opdshdr; | ||
1229 | Elf_Data *syms, *opddata = NULL; | ||
1230 | GElf_Sym sym; | ||
1231 | Elf_Scn *sec, *sec_strndx, *opdsec; | ||
1232 | Elf *elf; | ||
1233 | int nr = 0; | ||
1234 | size_t opdidx = 0; | ||
1235 | |||
1236 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | ||
1237 | if (elf == NULL) { | ||
1238 | pr_debug("%s: cannot read %s ELF file.\n", __func__, name); | ||
1239 | goto out_close; | ||
1240 | } | ||
1241 | |||
1242 | if (gelf_getehdr(elf, &ehdr) == NULL) { | ||
1243 | pr_debug("%s: cannot get elf header.\n", __func__); | ||
1244 | goto out_elf_end; | ||
1245 | } | ||
1246 | |||
1247 | if (dso__swap_init(dso, ehdr.e_ident[EI_DATA])) | ||
1248 | goto out_elf_end; | ||
1249 | |||
1250 | /* Always reject images with a mismatched build-id: */ | ||
1251 | if (dso->has_build_id) { | ||
1252 | u8 build_id[BUILD_ID_SIZE]; | ||
1253 | |||
1254 | if (elf_read_build_id(elf, build_id, BUILD_ID_SIZE) < 0) | ||
1255 | goto out_elf_end; | ||
1256 | |||
1257 | if (!dso__build_id_equal(dso, build_id)) | ||
1258 | goto out_elf_end; | ||
1259 | } | ||
1260 | |||
1261 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); | ||
1262 | if (sec == NULL) { | ||
1263 | if (want_symtab) | ||
1264 | goto out_elf_end; | ||
1265 | |||
1266 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL); | ||
1267 | if (sec == NULL) | ||
1268 | goto out_elf_end; | ||
1269 | } | ||
1270 | |||
1271 | opdsec = elf_section_by_name(elf, &ehdr, &opdshdr, ".opd", &opdidx); | ||
1272 | if (opdshdr.sh_type != SHT_PROGBITS) | ||
1273 | opdsec = NULL; | ||
1274 | if (opdsec) | ||
1275 | opddata = elf_rawdata(opdsec, NULL); | ||
1276 | |||
1277 | syms = elf_getdata(sec, NULL); | ||
1278 | if (syms == NULL) | ||
1279 | goto out_elf_end; | ||
1280 | |||
1281 | sec = elf_getscn(elf, shdr.sh_link); | ||
1282 | if (sec == NULL) | ||
1283 | goto out_elf_end; | ||
1284 | |||
1285 | symstrs = elf_getdata(sec, NULL); | ||
1286 | if (symstrs == NULL) | ||
1287 | goto out_elf_end; | ||
1288 | |||
1289 | sec_strndx = elf_getscn(elf, ehdr.e_shstrndx); | ||
1290 | if (sec_strndx == NULL) | ||
1291 | goto out_elf_end; | ||
1292 | |||
1293 | secstrs = elf_getdata(sec_strndx, NULL); | ||
1294 | if (secstrs == NULL) | ||
1295 | goto out_elf_end; | ||
1296 | |||
1297 | nr_syms = shdr.sh_size / shdr.sh_entsize; | ||
1298 | |||
1299 | memset(&sym, 0, sizeof(sym)); | ||
1300 | if (dso->kernel == DSO_TYPE_USER) { | ||
1301 | dso->adjust_symbols = (ehdr.e_type == ET_EXEC || | ||
1302 | elf_section_by_name(elf, &ehdr, &shdr, | ||
1303 | ".gnu.prelink_undo", | ||
1304 | NULL) != NULL); | ||
1305 | } else { | ||
1306 | dso->adjust_symbols = 0; | ||
1307 | } | ||
1308 | elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) { | ||
1309 | struct symbol *f; | ||
1310 | const char *elf_name = elf_sym__name(&sym, symstrs); | ||
1311 | char *demangled = NULL; | ||
1312 | int is_label = elf_sym__is_label(&sym); | ||
1313 | const char *section_name; | ||
1314 | |||
1315 | if (kmap && kmap->ref_reloc_sym && kmap->ref_reloc_sym->name && | ||
1316 | strcmp(elf_name, kmap->ref_reloc_sym->name) == 0) | ||
1317 | kmap->ref_reloc_sym->unrelocated_addr = sym.st_value; | ||
1318 | |||
1319 | if (!is_label && !elf_sym__is_a(&sym, map->type)) | ||
1320 | continue; | ||
1321 | |||
1322 | /* Reject ARM ELF "mapping symbols": these aren't unique and | ||
1323 | * don't identify functions, so will confuse the profile | ||
1324 | * output: */ | ||
1325 | if (ehdr.e_machine == EM_ARM) { | ||
1326 | if (!strcmp(elf_name, "$a") || | ||
1327 | !strcmp(elf_name, "$d") || | ||
1328 | !strcmp(elf_name, "$t")) | ||
1329 | continue; | ||
1330 | } | ||
1331 | |||
1332 | if (opdsec && sym.st_shndx == opdidx) { | ||
1333 | u32 offset = sym.st_value - opdshdr.sh_addr; | ||
1334 | u64 *opd = opddata->d_buf + offset; | ||
1335 | sym.st_value = DSO__SWAP(dso, u64, *opd); | ||
1336 | sym.st_shndx = elf_addr_to_index(elf, sym.st_value); | ||
1337 | } | ||
1338 | |||
1339 | sec = elf_getscn(elf, sym.st_shndx); | ||
1340 | if (!sec) | ||
1341 | goto out_elf_end; | ||
1342 | |||
1343 | gelf_getshdr(sec, &shdr); | ||
1344 | |||
1345 | if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type)) | ||
1346 | continue; | ||
1347 | |||
1348 | section_name = elf_sec__name(&shdr, secstrs); | ||
1349 | |||
1350 | /* On ARM, symbols for thumb functions have 1 added to | ||
1351 | * the symbol address as a flag - remove it */ | ||
1352 | if ((ehdr.e_machine == EM_ARM) && | ||
1353 | (map->type == MAP__FUNCTION) && | ||
1354 | (sym.st_value & 1)) | ||
1355 | --sym.st_value; | ||
1356 | |||
1357 | if (dso->kernel != DSO_TYPE_USER || kmodule) { | ||
1358 | char dso_name[PATH_MAX]; | ||
1359 | |||
1360 | if (strcmp(section_name, | ||
1361 | (curr_dso->short_name + | ||
1362 | dso->short_name_len)) == 0) | ||
1363 | goto new_symbol; | ||
1364 | |||
1365 | if (strcmp(section_name, ".text") == 0) { | ||
1366 | curr_map = map; | ||
1367 | curr_dso = dso; | ||
1368 | goto new_symbol; | ||
1369 | } | ||
1370 | |||
1371 | snprintf(dso_name, sizeof(dso_name), | ||
1372 | "%s%s", dso->short_name, section_name); | ||
1373 | |||
1374 | curr_map = map_groups__find_by_name(kmap->kmaps, map->type, dso_name); | ||
1375 | if (curr_map == NULL) { | ||
1376 | u64 start = sym.st_value; | ||
1377 | |||
1378 | if (kmodule) | ||
1379 | start += map->start + shdr.sh_offset; | ||
1380 | |||
1381 | curr_dso = dso__new(dso_name); | ||
1382 | if (curr_dso == NULL) | ||
1383 | goto out_elf_end; | ||
1384 | curr_dso->kernel = dso->kernel; | ||
1385 | curr_dso->long_name = dso->long_name; | ||
1386 | curr_dso->long_name_len = dso->long_name_len; | ||
1387 | curr_map = map__new2(start, curr_dso, | ||
1388 | map->type); | ||
1389 | if (curr_map == NULL) { | ||
1390 | dso__delete(curr_dso); | ||
1391 | goto out_elf_end; | ||
1392 | } | ||
1393 | curr_map->map_ip = identity__map_ip; | ||
1394 | curr_map->unmap_ip = identity__map_ip; | ||
1395 | curr_dso->symtab_type = dso->symtab_type; | ||
1396 | map_groups__insert(kmap->kmaps, curr_map); | ||
1397 | dsos__add(&dso->node, curr_dso); | ||
1398 | dso__set_loaded(curr_dso, map->type); | ||
1399 | } else | ||
1400 | curr_dso = curr_map->dso; | ||
1401 | |||
1402 | goto new_symbol; | ||
1403 | } | ||
1404 | |||
1405 | if (curr_dso->adjust_symbols) { | ||
1406 | pr_debug4("%s: adjusting symbol: st_value: %#" PRIx64 " " | ||
1407 | "sh_addr: %#" PRIx64 " sh_offset: %#" PRIx64 "\n", __func__, | ||
1408 | (u64)sym.st_value, (u64)shdr.sh_addr, | ||
1409 | (u64)shdr.sh_offset); | ||
1410 | sym.st_value -= shdr.sh_addr - shdr.sh_offset; | ||
1411 | } | ||
1412 | /* | ||
1413 | * We need to figure out if the object was created from C++ sources | ||
1414 | * DWARF DW_compile_unit has this, but we don't always have access | ||
1415 | * to it... | ||
1416 | */ | ||
1417 | demangled = bfd_demangle(NULL, elf_name, DMGL_PARAMS | DMGL_ANSI); | ||
1418 | if (demangled != NULL) | ||
1419 | elf_name = demangled; | ||
1420 | new_symbol: | ||
1421 | f = symbol__new(sym.st_value, sym.st_size, | ||
1422 | GELF_ST_BIND(sym.st_info), elf_name); | ||
1423 | free(demangled); | ||
1424 | if (!f) | ||
1425 | goto out_elf_end; | ||
1426 | |||
1427 | if (filter && filter(curr_map, f)) | ||
1428 | symbol__delete(f); | ||
1429 | else { | ||
1430 | symbols__insert(&curr_dso->symbols[curr_map->type], f); | ||
1431 | nr++; | ||
1432 | } | ||
1433 | } | ||
1434 | |||
1435 | /* | ||
1436 | * For misannotated, zeroed, ASM function sizes. | ||
1437 | */ | ||
1438 | if (nr > 0) { | ||
1439 | symbols__fixup_duplicate(&dso->symbols[map->type]); | ||
1440 | symbols__fixup_end(&dso->symbols[map->type]); | ||
1441 | if (kmap) { | ||
1442 | /* | ||
1443 | * We need to fixup this here too because we create new | ||
1444 | * maps here, for things like vsyscall sections. | ||
1445 | */ | ||
1446 | __map_groups__fixup_end(kmap->kmaps, map->type); | ||
1447 | } | ||
1448 | } | ||
1449 | err = nr; | ||
1450 | out_elf_end: | ||
1451 | elf_end(elf); | ||
1452 | out_close: | ||
1453 | return err; | ||
1454 | } | ||
1455 | |||
1456 | void symbol__elf_init(void) | ||
1457 | { | ||
1458 | elf_version(EV_CURRENT); | ||
1459 | } | ||
1460 | |||
1461 | static bool dso__build_id_equal(const struct dso *dso, u8 *build_id) | ||
1462 | { | 897 | { |
1463 | return memcmp(dso->build_id, build_id, sizeof(dso->build_id)) == 0; | 898 | return memcmp(dso->build_id, build_id, sizeof(dso->build_id)) == 0; |
1464 | } | 899 | } |
@@ -1485,212 +920,6 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits) | |||
1485 | return have_build_id; | 920 | return have_build_id; |
1486 | } | 921 | } |
1487 | 922 | ||
1488 | /* | ||
1489 | * Align offset to 4 bytes as needed for note name and descriptor data. | ||
1490 | */ | ||
1491 | #define NOTE_ALIGN(n) (((n) + 3) & -4U) | ||
1492 | |||
1493 | static int elf_read_build_id(Elf *elf, void *bf, size_t size) | ||
1494 | { | ||
1495 | int err = -1; | ||
1496 | GElf_Ehdr ehdr; | ||
1497 | GElf_Shdr shdr; | ||
1498 | Elf_Data *data; | ||
1499 | Elf_Scn *sec; | ||
1500 | Elf_Kind ek; | ||
1501 | void *ptr; | ||
1502 | |||
1503 | if (size < BUILD_ID_SIZE) | ||
1504 | goto out; | ||
1505 | |||
1506 | ek = elf_kind(elf); | ||
1507 | if (ek != ELF_K_ELF) | ||
1508 | goto out; | ||
1509 | |||
1510 | if (gelf_getehdr(elf, &ehdr) == NULL) { | ||
1511 | pr_err("%s: cannot get elf header.\n", __func__); | ||
1512 | goto out; | ||
1513 | } | ||
1514 | |||
1515 | /* | ||
1516 | * Check following sections for notes: | ||
1517 | * '.note.gnu.build-id' | ||
1518 | * '.notes' | ||
1519 | * '.note' (VDSO specific) | ||
1520 | */ | ||
1521 | do { | ||
1522 | sec = elf_section_by_name(elf, &ehdr, &shdr, | ||
1523 | ".note.gnu.build-id", NULL); | ||
1524 | if (sec) | ||
1525 | break; | ||
1526 | |||
1527 | sec = elf_section_by_name(elf, &ehdr, &shdr, | ||
1528 | ".notes", NULL); | ||
1529 | if (sec) | ||
1530 | break; | ||
1531 | |||
1532 | sec = elf_section_by_name(elf, &ehdr, &shdr, | ||
1533 | ".note", NULL); | ||
1534 | if (sec) | ||
1535 | break; | ||
1536 | |||
1537 | return err; | ||
1538 | |||
1539 | } while (0); | ||
1540 | |||
1541 | data = elf_getdata(sec, NULL); | ||
1542 | if (data == NULL) | ||
1543 | goto out; | ||
1544 | |||
1545 | ptr = data->d_buf; | ||
1546 | while (ptr < (data->d_buf + data->d_size)) { | ||
1547 | GElf_Nhdr *nhdr = ptr; | ||
1548 | size_t namesz = NOTE_ALIGN(nhdr->n_namesz), | ||
1549 | descsz = NOTE_ALIGN(nhdr->n_descsz); | ||
1550 | const char *name; | ||
1551 | |||
1552 | ptr += sizeof(*nhdr); | ||
1553 | name = ptr; | ||
1554 | ptr += namesz; | ||
1555 | if (nhdr->n_type == NT_GNU_BUILD_ID && | ||
1556 | nhdr->n_namesz == sizeof("GNU")) { | ||
1557 | if (memcmp(name, "GNU", sizeof("GNU")) == 0) { | ||
1558 | size_t sz = min(size, descsz); | ||
1559 | memcpy(bf, ptr, sz); | ||
1560 | memset(bf + sz, 0, size - sz); | ||
1561 | err = descsz; | ||
1562 | break; | ||
1563 | } | ||
1564 | } | ||
1565 | ptr += descsz; | ||
1566 | } | ||
1567 | |||
1568 | out: | ||
1569 | return err; | ||
1570 | } | ||
1571 | |||
1572 | int filename__read_build_id(const char *filename, void *bf, size_t size) | ||
1573 | { | ||
1574 | int fd, err = -1; | ||
1575 | Elf *elf; | ||
1576 | |||
1577 | if (size < BUILD_ID_SIZE) | ||
1578 | goto out; | ||
1579 | |||
1580 | fd = open(filename, O_RDONLY); | ||
1581 | if (fd < 0) | ||
1582 | goto out; | ||
1583 | |||
1584 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | ||
1585 | if (elf == NULL) { | ||
1586 | pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename); | ||
1587 | goto out_close; | ||
1588 | } | ||
1589 | |||
1590 | err = elf_read_build_id(elf, bf, size); | ||
1591 | |||
1592 | elf_end(elf); | ||
1593 | out_close: | ||
1594 | close(fd); | ||
1595 | out: | ||
1596 | return err; | ||
1597 | } | ||
1598 | |||
1599 | int sysfs__read_build_id(const char *filename, void *build_id, size_t size) | ||
1600 | { | ||
1601 | int fd, err = -1; | ||
1602 | |||
1603 | if (size < BUILD_ID_SIZE) | ||
1604 | goto out; | ||
1605 | |||
1606 | fd = open(filename, O_RDONLY); | ||
1607 | if (fd < 0) | ||
1608 | goto out; | ||
1609 | |||
1610 | while (1) { | ||
1611 | char bf[BUFSIZ]; | ||
1612 | GElf_Nhdr nhdr; | ||
1613 | size_t namesz, descsz; | ||
1614 | |||
1615 | if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr)) | ||
1616 | break; | ||
1617 | |||
1618 | namesz = NOTE_ALIGN(nhdr.n_namesz); | ||
1619 | descsz = NOTE_ALIGN(nhdr.n_descsz); | ||
1620 | if (nhdr.n_type == NT_GNU_BUILD_ID && | ||
1621 | nhdr.n_namesz == sizeof("GNU")) { | ||
1622 | if (read(fd, bf, namesz) != (ssize_t)namesz) | ||
1623 | break; | ||
1624 | if (memcmp(bf, "GNU", sizeof("GNU")) == 0) { | ||
1625 | size_t sz = min(descsz, size); | ||
1626 | if (read(fd, build_id, sz) == (ssize_t)sz) { | ||
1627 | memset(build_id + sz, 0, size - sz); | ||
1628 | err = 0; | ||
1629 | break; | ||
1630 | } | ||
1631 | } else if (read(fd, bf, descsz) != (ssize_t)descsz) | ||
1632 | break; | ||
1633 | } else { | ||
1634 | int n = namesz + descsz; | ||
1635 | if (read(fd, bf, n) != n) | ||
1636 | break; | ||
1637 | } | ||
1638 | } | ||
1639 | close(fd); | ||
1640 | out: | ||
1641 | return err; | ||
1642 | } | ||
1643 | |||
1644 | static int filename__read_debuglink(const char *filename, | ||
1645 | char *debuglink, size_t size) | ||
1646 | { | ||
1647 | int fd, err = -1; | ||
1648 | Elf *elf; | ||
1649 | GElf_Ehdr ehdr; | ||
1650 | GElf_Shdr shdr; | ||
1651 | Elf_Data *data; | ||
1652 | Elf_Scn *sec; | ||
1653 | Elf_Kind ek; | ||
1654 | |||
1655 | fd = open(filename, O_RDONLY); | ||
1656 | if (fd < 0) | ||
1657 | goto out; | ||
1658 | |||
1659 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | ||
1660 | if (elf == NULL) { | ||
1661 | pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename); | ||
1662 | goto out_close; | ||
1663 | } | ||
1664 | |||
1665 | ek = elf_kind(elf); | ||
1666 | if (ek != ELF_K_ELF) | ||
1667 | goto out_close; | ||
1668 | |||
1669 | if (gelf_getehdr(elf, &ehdr) == NULL) { | ||
1670 | pr_err("%s: cannot get elf header.\n", __func__); | ||
1671 | goto out_close; | ||
1672 | } | ||
1673 | |||
1674 | sec = elf_section_by_name(elf, &ehdr, &shdr, | ||
1675 | ".gnu_debuglink", NULL); | ||
1676 | if (sec == NULL) | ||
1677 | goto out_close; | ||
1678 | |||
1679 | data = elf_getdata(sec, NULL); | ||
1680 | if (data == NULL) | ||
1681 | goto out_close; | ||
1682 | |||
1683 | /* the start of this section is a zero-terminated string */ | ||
1684 | strncpy(debuglink, data->d_buf, size); | ||
1685 | |||
1686 | elf_end(elf); | ||
1687 | |||
1688 | out_close: | ||
1689 | close(fd); | ||
1690 | out: | ||
1691 | return err; | ||
1692 | } | ||
1693 | |||
1694 | char dso__symtab_origin(const struct dso *dso) | 923 | char dso__symtab_origin(const struct dso *dso) |
1695 | { | 924 | { |
1696 | static const char origin[] = { | 925 | static const char origin[] = { |
@@ -2035,25 +1264,6 @@ static int machine__set_modules_path(struct machine *machine) | |||
2035 | return map_groups__set_modules_path_dir(&machine->kmaps, modules_path); | 1264 | return map_groups__set_modules_path_dir(&machine->kmaps, modules_path); |
2036 | } | 1265 | } |
2037 | 1266 | ||
2038 | /* | ||
2039 | * Constructor variant for modules (where we know from /proc/modules where | ||
2040 | * they are loaded) and for vmlinux, where only after we load all the | ||
2041 | * symbols we'll know where it starts and ends. | ||
2042 | */ | ||
2043 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type) | ||
2044 | { | ||
2045 | struct map *map = calloc(1, (sizeof(*map) + | ||
2046 | (dso->kernel ? sizeof(struct kmap) : 0))); | ||
2047 | if (map != NULL) { | ||
2048 | /* | ||
2049 | * ->end will be filled after we load all the symbols | ||
2050 | */ | ||
2051 | map__init(map, type, start, 0, 0, dso); | ||
2052 | } | ||
2053 | |||
2054 | return map; | ||
2055 | } | ||
2056 | |||
2057 | struct map *machine__new_module(struct machine *machine, u64 start, | 1267 | struct map *machine__new_module(struct machine *machine, u64 start, |
2058 | const char *filename) | 1268 | const char *filename) |
2059 | { | 1269 | { |
@@ -2357,7 +1567,7 @@ out_try_fixup: | |||
2357 | return err; | 1567 | return err; |
2358 | } | 1568 | } |
2359 | 1569 | ||
2360 | static void dsos__add(struct list_head *head, struct dso *dso) | 1570 | void dsos__add(struct list_head *head, struct dso *dso) |
2361 | { | 1571 | { |
2362 | list_add_tail(&dso->node, head); | 1572 | list_add_tail(&dso->node, head); |
2363 | } | 1573 | } |