perf_counter tools: Fix off-by-one bug in symbol__new
[safe/jmp/linux-2.6] / Documentation / perf_counter / util / symbol.c
1 #include "util.h"
2 #include "../perf.h"
3 #include "string.h"
4 #include "symbol.h"
5
6 #include <libelf.h>
7 #include <gelf.h>
8 #include <elf.h>
9
10 static struct symbol *symbol__new(uint64_t start, uint64_t len,
11                                   const char *name, unsigned int priv_size)
12 {
13         size_t namelen = strlen(name) + 1;
14         struct symbol *self = malloc(priv_size + sizeof(*self) + namelen);
15
16         if (self != NULL) {
17                 if (priv_size) {
18                         memset(self, 0, priv_size);
19                         self = ((void *)self) + priv_size;
20                 }
21                 self->start = start;
22                 self->end   = start + len - 1;
23                 memcpy(self->name, name, namelen);
24         }
25
26         return self;
27 }
28
29 static void symbol__delete(struct symbol *self, unsigned int priv_size)
30 {
31         free(((void *)self) - priv_size);
32 }
33
34 static size_t symbol__fprintf(struct symbol *self, FILE *fp)
35 {
36         return fprintf(fp, " %llx-%llx %s\n",
37                        self->start, self->end, self->name);
38 }
39
40 struct dso *dso__new(const char *name, unsigned int sym_priv_size)
41 {
42         struct dso *self = malloc(sizeof(*self) + strlen(name) + 1);
43
44         if (self != NULL) {
45                 strcpy(self->name, name);
46                 self->syms = RB_ROOT;
47                 self->sym_priv_size = sym_priv_size;
48         }
49
50         return self;
51 }
52
53 static void dso__delete_symbols(struct dso *self)
54 {
55         struct symbol *pos;
56         struct rb_node *next = rb_first(&self->syms);
57
58         while (next) {
59                 pos = rb_entry(next, struct symbol, rb_node);
60                 next = rb_next(&pos->rb_node);
61                 rb_erase(&pos->rb_node, &self->syms);
62                 symbol__delete(pos, self->sym_priv_size);
63         }
64 }
65
66 void dso__delete(struct dso *self)
67 {
68         dso__delete_symbols(self);
69         free(self);
70 }
71
72 static void dso__insert_symbol(struct dso *self, struct symbol *sym)
73 {
74         struct rb_node **p = &self->syms.rb_node;
75         struct rb_node *parent = NULL;
76         const uint64_t ip = sym->start;
77         struct symbol *s;
78
79         while (*p != NULL) {
80                 parent = *p;
81                 s = rb_entry(parent, struct symbol, rb_node);
82                 if (ip < s->start)
83                         p = &(*p)->rb_left;
84                 else
85                         p = &(*p)->rb_right;
86         }
87         rb_link_node(&sym->rb_node, parent, p);
88         rb_insert_color(&sym->rb_node, &self->syms);
89 }
90
91 struct symbol *dso__find_symbol(struct dso *self, uint64_t ip)
92 {
93         struct rb_node *n;
94
95         if (self == NULL)
96                 return NULL;
97
98         n = self->syms.rb_node;
99
100         while (n) {
101                 struct symbol *s = rb_entry(n, struct symbol, rb_node);
102
103                 if (ip < s->start)
104                         n = n->rb_left;
105                 else if (ip > s->end)
106                         n = n->rb_right;
107                 else
108                         return s;
109         }
110
111         return NULL;
112 }
113
114 size_t dso__fprintf(struct dso *self, FILE *fp)
115 {
116         size_t ret = fprintf(fp, "dso: %s\n", self->name);
117
118         struct rb_node *nd;
119         for (nd = rb_first(&self->syms); nd; nd = rb_next(nd)) {
120                 struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
121                 ret += symbol__fprintf(pos, fp);
122         }
123
124         return ret;
125 }
126
127 static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter)
128 {
129         struct rb_node *nd, *prevnd;
130         char *line = NULL;
131         size_t n;
132         FILE *file = fopen("/proc/kallsyms", "r");
133
134         if (file == NULL)
135                 goto out_failure;
136
137         while (!feof(file)) {
138                 __u64 start;
139                 struct symbol *sym;
140                 int line_len, len;
141                 char symbol_type;
142
143                 line_len = getline(&line, &n, file);
144                 if (line_len < 0)
145                         break;
146
147                 if (!line)
148                         goto out_failure;
149
150                 line[--line_len] = '\0'; /* \n */
151
152                 len = hex2u64(line, &start);
153
154                 len++;
155                 if (len + 2 >= line_len)
156                         continue;
157
158                 symbol_type = toupper(line[len]);
159                 /*
160                  * We're interested only in code ('T'ext)
161                  */
162                 if (symbol_type != 'T' && symbol_type != 'W')
163                         continue;
164                 /*
165                  * Well fix up the end later, when we have all sorted.
166                  */
167                 sym = symbol__new(start, 0xdead, line + len + 2,
168                                   self->sym_priv_size);
169
170                 if (sym == NULL)
171                         goto out_delete_line;
172
173                 if (filter && filter(self, sym))
174                         symbol__delete(sym, self->sym_priv_size);
175                 else
176                         dso__insert_symbol(self, sym);
177         }
178
179         /*
180          * Now that we have all sorted out, just set the ->end of all
181          * symbols
182          */
183         prevnd = rb_first(&self->syms);
184
185         if (prevnd == NULL)
186                 goto out_delete_line;
187
188         for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) {
189                 struct symbol *prev = rb_entry(prevnd, struct symbol, rb_node),
190                               *curr = rb_entry(nd, struct symbol, rb_node);
191
192                 prev->end = curr->start - 1;
193                 prevnd = nd;
194         }
195
196         free(line);
197         fclose(file);
198
199         return 0;
200
201 out_delete_line:
202         free(line);
203 out_failure:
204         return -1;
205 }
206
207 /**
208  * elf_symtab__for_each_symbol - iterate thru all the symbols
209  *
210  * @self: struct elf_symtab instance to iterate
211  * @index: uint32_t index
212  * @sym: GElf_Sym iterator
213  */
214 #define elf_symtab__for_each_symbol(syms, nr_syms, index, sym) \
215         for (index = 0, gelf_getsym(syms, index, &sym);\
216              index < nr_syms; \
217              index++, gelf_getsym(syms, index, &sym))
218
219 static inline uint8_t elf_sym__type(const GElf_Sym *sym)
220 {
221         return GELF_ST_TYPE(sym->st_info);
222 }
223
224 static inline int elf_sym__is_function(const GElf_Sym *sym)
225 {
226         return elf_sym__type(sym) == STT_FUNC &&
227                sym->st_name != 0 &&
228                sym->st_shndx != SHN_UNDEF &&
229                sym->st_size != 0;
230 }
231
232 static inline const char *elf_sym__name(const GElf_Sym *sym,
233                                         const Elf_Data *symstrs)
234 {
235         return symstrs->d_buf + sym->st_name;
236 }
237
238 static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
239                                     GElf_Shdr *shp, const char *name,
240                                     size_t *index)
241 {
242         Elf_Scn *sec = NULL;
243         size_t cnt = 1;
244
245         while ((sec = elf_nextscn(elf, sec)) != NULL) {
246                 char *str;
247
248                 gelf_getshdr(sec, shp);
249                 str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name);
250                 if (!strcmp(name, str)) {
251                         if (index)
252                                 *index = cnt;
253                         break;
254                 }
255                 ++cnt;
256         }
257
258         return sec;
259 }
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
372 static int dso__load_sym(struct dso *self, int fd, const char *name,
373                          symbol_filter_t filter)
374 {
375         Elf_Data *symstrs;
376         uint32_t nr_syms;
377         int err = -1;
378         uint32_t index;
379         GElf_Ehdr ehdr;
380         GElf_Shdr shdr;
381         Elf_Data *syms;
382         GElf_Sym sym;
383         Elf_Scn *sec, *sec_dynsym;
384         Elf *elf;
385         size_t dynsym_idx;
386         int nr = 0;
387
388         elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
389         if (elf == NULL) {
390                 fprintf(stderr, "%s: cannot read %s ELF file.\n",
391                         __func__, name);
392                 goto out_close;
393         }
394
395         if (gelf_getehdr(elf, &ehdr) == NULL) {
396                 fprintf(stderr, "%s: cannot get elf header.\n", __func__);
397                 goto out_elf_end;
398         }
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          */
419         sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL);
420         if (sec == NULL) {
421                 if (sec_dynsym == NULL)
422                         goto out_elf_end;
423
424                 sec = sec_dynsym;
425                 gelf_getshdr(sec, &shdr);
426         }
427
428         syms = elf_getdata(sec, NULL);
429         if (syms == NULL)
430                 goto out_elf_end;
431
432         sec = elf_getscn(elf, shdr.sh_link);
433         if (sec == NULL)
434                 goto out_elf_end;
435
436         symstrs = elf_getdata(sec, NULL);
437         if (symstrs == NULL)
438                 goto out_elf_end;
439
440         nr_syms = shdr.sh_size / shdr.sh_entsize;
441
442         elf_symtab__for_each_symbol(syms, nr_syms, index, sym) {
443                 struct symbol *f;
444
445                 if (!elf_sym__is_function(&sym))
446                         continue;
447
448                 sec = elf_getscn(elf, sym.st_shndx);
449                 if (!sec)
450                         goto out_elf_end;
451
452                 gelf_getshdr(sec, &shdr);
453                 sym.st_value -= shdr.sh_addr - shdr.sh_offset;
454
455                 f = symbol__new(sym.st_value, sym.st_size,
456                                 elf_sym__name(&sym, symstrs),
457                                 self->sym_priv_size);
458                 if (!f)
459                         goto out_elf_end;
460
461                 if (filter && filter(self, f))
462                         symbol__delete(f, self->sym_priv_size);
463                 else {
464                         dso__insert_symbol(self, f);
465                         nr++;
466                 }
467         }
468
469         err = nr;
470 out_elf_end:
471         elf_end(elf);
472 out_close:
473         return err;
474 }
475
476 int dso__load(struct dso *self, symbol_filter_t filter)
477 {
478         int size = strlen(self->name) + sizeof("/usr/lib/debug%s.debug");
479         char *name = malloc(size);
480         int variant = 0;
481         int ret = -1;
482         int fd;
483
484         if (!name)
485                 return -1;
486
487 more:
488         do {
489                 switch (variant) {
490                 case 0: /* Fedora */
491                         snprintf(name, size, "/usr/lib/debug%s.debug", self->name);
492                         break;
493                 case 1: /* Ubuntu */
494                         snprintf(name, size, "/usr/lib/debug%s", self->name);
495                         break;
496                 case 2: /* Sane people */
497                         snprintf(name, size, "%s", self->name);
498                         break;
499
500                 default:
501                         goto out;
502                 }
503                 variant++;
504
505                 fd = open(name, O_RDONLY);
506         } while (fd < 0);
507
508         ret = dso__load_sym(self, fd, name, filter);
509         close(fd);
510
511         /*
512          * Some people seem to have debuginfo files _WITHOUT_ debug info!?!?
513          */
514         if (!ret)
515                 goto more;
516
517 out:
518         free(name);
519         return ret;
520 }
521
522 static int dso__load_vmlinux(struct dso *self, const char *vmlinux,
523                              symbol_filter_t filter)
524 {
525         int err, fd = open(vmlinux, O_RDONLY);
526
527         if (fd < 0)
528                 return -1;
529
530         err = dso__load_sym(self, fd, vmlinux, filter);
531         close(fd);
532
533         return err;
534 }
535
536 int dso__load_kernel(struct dso *self, const char *vmlinux, symbol_filter_t filter)
537 {
538         int err = -1;
539
540         if (vmlinux)
541                 err = dso__load_vmlinux(self, vmlinux, filter);
542
543         if (err)
544                 err = dso__load_kallsyms(self, filter);
545
546         return err;
547 }
548
549 void symbol__init(void)
550 {
551         elf_version(EV_CURRENT);
552 }