perf: Port symbol-elf.c
[akaros.git] / tools / profile / perf / symbol-elf.c
1 /* Copyright (c) 2011-2015 Linux Perf Authors
2  *
3  * This source code is licensed under the GNU General Public License Version 2.
4  * See the file LICENSE-gpl-2.0.txt for more details. */
5
6 #include <fcntl.h>
7 #include <stdio.h>
8 #include <errno.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <inttypes.h>
12 #include <stdbool.h>
13
14 #include <gelf.h>
15 #include <libelf.h>
16 #include "xlib.h"
17 #include "elf.h"
18
19 #define pr_err(args...) fprintf(stderr, args)
20 #define pr_debug2(args...) pr_err(args)
21
22 #define PERF_ELF_C_READ_MMAP ELF_C_READ_MMAP
23
24 enum map_type {
25         MAP__FUNCTION = 0,
26         MAP__VARIABLE,
27 };
28
29 static inline char *bfd_demangle(void *v, const char *c, int i)
30 {
31         return NULL;
32 }
33
34 #ifndef NT_GNU_BUILD_ID
35 #define NT_GNU_BUILD_ID 3
36 #endif
37
38 /**
39  * elf_symtab__for_each_symbol - iterate thru all the symbols
40  *
41  * @syms: struct elf_symtab instance to iterate
42  * @idx: uint32_t idx
43  * @sym: GElf_Sym iterator
44  */
45 #define elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) \
46         for (idx = 0, gelf_getsym(syms, idx, &sym);\
47              idx < nr_syms; \
48              idx++, gelf_getsym(syms, idx, &sym))
49
50 static inline uint8_t elf_sym__type(const GElf_Sym *sym)
51 {
52         return GELF_ST_TYPE(sym->st_info);
53 }
54
55 #ifndef STT_GNU_IFUNC
56 #define STT_GNU_IFUNC 10
57 #endif
58
59 static inline int elf_sym__is_function(const GElf_Sym *sym)
60 {
61         return (elf_sym__type(sym) == STT_FUNC ||
62                 elf_sym__type(sym) == STT_GNU_IFUNC) &&
63                sym->st_name != 0 &&
64                sym->st_shndx != SHN_UNDEF;
65 }
66
67 static inline bool elf_sym__is_object(const GElf_Sym *sym)
68 {
69         return elf_sym__type(sym) == STT_OBJECT &&
70                 sym->st_name != 0 &&
71                 sym->st_shndx != SHN_UNDEF;
72 }
73
74 static inline int elf_sym__is_label(const GElf_Sym *sym)
75 {
76         return elf_sym__type(sym) == STT_NOTYPE &&
77                 sym->st_name != 0 &&
78                 sym->st_shndx != SHN_UNDEF &&
79                 sym->st_shndx != SHN_ABS;
80 }
81
82 static bool elf_sym__is_a(GElf_Sym *sym, enum map_type type)
83 {
84         switch (type) {
85         case MAP__FUNCTION:
86                 return elf_sym__is_function(sym);
87         case MAP__VARIABLE:
88                 return elf_sym__is_object(sym);
89         default:
90                 return false;
91         }
92 }
93
94 static inline const char *elf_sym__name(const GElf_Sym *sym,
95                                         const Elf_Data *symstrs)
96 {
97         return symstrs->d_buf + sym->st_name;
98 }
99
100 static inline const char *elf_sec__name(const GElf_Shdr *shdr,
101                                         const Elf_Data *secstrs)
102 {
103         return secstrs->d_buf + shdr->sh_name;
104 }
105
106 static inline int elf_sec__is_text(const GElf_Shdr *shdr,
107                                         const Elf_Data *secstrs)
108 {
109         return strstr(elf_sec__name(shdr, secstrs), "text") != NULL;
110 }
111
112 static inline bool elf_sec__is_data(const GElf_Shdr *shdr,
113                                     const Elf_Data *secstrs)
114 {
115         return strstr(elf_sec__name(shdr, secstrs), "data") != NULL;
116 }
117
118 static bool elf_sec__is_a(GElf_Shdr *shdr, Elf_Data *secstrs,
119                           enum map_type type)
120 {
121         switch (type) {
122         case MAP__FUNCTION:
123                 return elf_sec__is_text(shdr, secstrs);
124         case MAP__VARIABLE:
125                 return elf_sec__is_data(shdr, secstrs);
126         default:
127                 return false;
128         }
129 }
130
131 static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr)
132 {
133         Elf_Scn *sec = NULL;
134         GElf_Shdr shdr;
135         size_t cnt = 1;
136
137         while ((sec = elf_nextscn(elf, sec)) != NULL) {
138                 gelf_getshdr(sec, &shdr);
139
140                 if ((addr >= shdr.sh_addr) &&
141                     (addr < (shdr.sh_addr + shdr.sh_size)))
142                         return cnt;
143
144                 ++cnt;
145         }
146
147         return -1;
148 }
149
150 Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
151                              GElf_Shdr *shp, const char *name, size_t *idx)
152 {
153         Elf_Scn *sec = NULL;
154         size_t cnt = 1;
155
156         /* Elf is corrupted/truncated, avoid calling elf_strptr. */
157         if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL))
158                 return NULL;
159
160         while ((sec = elf_nextscn(elf, sec)) != NULL) {
161                 char *str;
162
163                 gelf_getshdr(sec, shp);
164                 str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name);
165                 if (str && !strcmp(name, str)) {
166                         if (idx)
167                                 *idx = cnt;
168                         return sec;
169                 }
170                 ++cnt;
171         }
172
173         return NULL;
174 }
175
176 #define elf_section__for_each_rel(reldata, pos, pos_mem, idx, nr_entries) \
177         for (idx = 0, pos = gelf_getrel(reldata, 0, &pos_mem); \
178              idx < nr_entries; \
179              ++idx, pos = gelf_getrel(reldata, idx, &pos_mem))
180
181 #define elf_section__for_each_rela(reldata, pos, pos_mem, idx, nr_entries) \
182         for (idx = 0, pos = gelf_getrela(reldata, 0, &pos_mem); \
183              idx < nr_entries; \
184              ++idx, pos = gelf_getrela(reldata, idx, &pos_mem))
185
186
187 /*
188  * Align offset to 4 bytes as needed for note name and descriptor data.
189  */
190 #define NOTE_ALIGN(n) (((n) + 3) & -4U)
191
192 static int elf_read_build_id(Elf *elf, void *bf, size_t size)
193 {
194         int err = -1;
195         GElf_Ehdr ehdr;
196         GElf_Shdr shdr;
197         Elf_Data *data;
198         Elf_Scn *sec;
199         Elf_Kind ek;
200         void *ptr;
201
202         if (size < BUILD_ID_SIZE)
203                 goto out;
204
205         ek = elf_kind(elf);
206         if (ek != ELF_K_ELF)
207                 goto out;
208
209         if (gelf_getehdr(elf, &ehdr) == NULL) {
210                 pr_err("%s: cannot get elf header.\n", __func__);
211                 goto out;
212         }
213
214         /*
215          * Check following sections for notes:
216          *   '.note.gnu.build-id'
217          *   '.notes'
218          *   '.note' (VDSO specific)
219          */
220         do {
221                 sec = elf_section_by_name(elf, &ehdr, &shdr,
222                                           ".note.gnu.build-id", NULL);
223                 if (sec)
224                         break;
225
226                 sec = elf_section_by_name(elf, &ehdr, &shdr,
227                                           ".notes", NULL);
228                 if (sec)
229                         break;
230
231                 sec = elf_section_by_name(elf, &ehdr, &shdr,
232                                           ".note", NULL);
233                 if (sec)
234                         break;
235
236                 return err;
237
238         } while (0);
239
240         data = elf_getdata(sec, NULL);
241         if (data == NULL)
242                 goto out;
243
244         ptr = data->d_buf;
245         while (ptr < (data->d_buf + data->d_size)) {
246                 GElf_Nhdr *nhdr = ptr;
247                 size_t namesz = NOTE_ALIGN(nhdr->n_namesz),
248                        descsz = NOTE_ALIGN(nhdr->n_descsz);
249                 const char *name;
250
251                 ptr += sizeof(*nhdr);
252                 name = ptr;
253                 ptr += namesz;
254                 if (nhdr->n_type == NT_GNU_BUILD_ID &&
255                     nhdr->n_namesz == sizeof("GNU")) {
256                         if (memcmp(name, "GNU", sizeof("GNU")) == 0) {
257                                 size_t sz = min(size, descsz);
258                                 memcpy(bf, ptr, sz);
259                                 memset(bf + sz, 0, size - sz);
260                                 err = descsz;
261                                 break;
262                         }
263                 }
264                 ptr += descsz;
265         }
266
267 out:
268         return err;
269 }
270
271 int filename__read_build_id(const char *filename, void *bf, size_t size)
272 {
273         int fd, err = -1;
274         Elf *elf;
275
276         if (size < BUILD_ID_SIZE)
277                 goto out;
278
279         fd = open(filename, O_RDONLY);
280         if (fd < 0)
281                 goto out;
282
283         elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
284         if (elf == NULL) {
285                 pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename);
286                 goto out_close;
287         }
288
289         err = elf_read_build_id(elf, bf, size);
290
291         elf_end(elf);
292 out_close:
293         close(fd);
294 out:
295         return err;
296 }
297
298 void symbol__elf_init(void)
299 {
300         elf_version(EV_CURRENT);
301 }