perf symbol: Fix symbol parsing in certain cases: use the build-id as a symlink
[deliverable/linux.git] / tools / perf / util / symbol.c
index 0580b94785e73280501a2c0131ece0d5b3ed1473..16ddca202948cb0fc0ad23efddb6cbc1fcfbfdd3 100644 (file)
@@ -661,10 +661,69 @@ out_close:
        return err;
 }
 
+#define BUILD_ID_SIZE 128
+
+static char *dso__read_build_id(struct dso *self, int verbose)
+{
+       int i;
+       GElf_Ehdr ehdr;
+       GElf_Shdr shdr;
+       Elf_Data *build_id_data;
+       Elf_Scn *sec;
+       char *build_id = NULL, *bid;
+       unsigned char *raw;
+       Elf *elf;
+       int fd = open(self->name, O_RDONLY);
+
+       if (fd < 0)
+               goto out;
+
+       elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
+       if (elf == NULL) {
+               if (verbose)
+                       fprintf(stderr, "%s: cannot read %s ELF file.\n",
+                               __func__, self->name);
+               goto out_close;
+       }
+
+       if (gelf_getehdr(elf, &ehdr) == NULL) {
+               if (verbose)
+                       fprintf(stderr, "%s: cannot get elf header.\n", __func__);
+               goto out_elf_end;
+       }
+
+       sec = elf_section_by_name(elf, &ehdr, &shdr, ".note.gnu.build-id", NULL);
+       if (sec == NULL)
+               goto out_elf_end;
+
+       build_id_data = elf_getdata(sec, NULL);
+       if (build_id_data == NULL)
+               goto out_elf_end;
+       build_id = malloc(BUILD_ID_SIZE);
+       if (build_id == NULL)
+               goto out_elf_end;
+       raw = build_id_data->d_buf + 16;
+       bid = build_id;
+
+       for (i = 0; i < 20; ++i) {
+               sprintf(bid, "%02x", *raw);
+               ++raw;
+               bid += 2;
+       }
+       if (verbose)
+               printf("%s(%s): %s\n", __func__, self->name, build_id);
+out_elf_end:
+       elf_end(elf);
+out_close:
+       close(fd);
+out:
+       return build_id;
+}
+
 int dso__load(struct dso *self, symbol_filter_t filter, int verbose)
 {
-       int size = strlen(self->name) + sizeof("/usr/lib/debug%s.debug");
-       char *name = malloc(size);
+       int size = PATH_MAX;
+       char *name = malloc(size), *build_id = NULL;
        int variant = 0;
        int ret = -1;
        int fd;
@@ -686,7 +745,18 @@ more:
                case 1: /* Ubuntu */
                        snprintf(name, size, "/usr/lib/debug%s", self->name);
                        break;
-               case 2: /* Sane people */
+               case 2:
+                       build_id = dso__read_build_id(self, verbose);
+                       if (build_id != NULL) {
+                               snprintf(name, size,
+                                        "/usr/lib/debug/.build-id/%.2s/%s.debug",
+                                       build_id, build_id + 2);
+                               free(build_id);
+                               break;
+                       }
+                       variant++;
+                       /* Fall thru */
+               case 3: /* Sane people */
                        snprintf(name, size, "%s", self->name);
                        break;
 
This page took 0.047466 seconds and 5 git commands to generate.