perf symbols: Filename__read_build_id should look at .notes section too
Arnaldo Carvalho de Melo [Fri, 20 Nov 2009 22:51:25 +0000 (20:51 -0200)]
In the kernel we have more than one notes section, so the linker
script combines all and puts them into a ".notes" combined
section. So we need to look at both sections and also traverse
them looking at multiple GElf_Nhdr entries till we find the one
we want, with the build_id.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frédéric Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
LKML-Reference: <1258757489-5978-2-git-send-email-acme@infradead.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>

tools/perf/util/symbol.c

index 3b23c18..d220308 100644 (file)
@@ -899,13 +899,19 @@ bool dsos__read_build_ids(void)
        return have_build_id;
 }
 
+/*
+ * Align offset to 4 bytes as needed for note name and descriptor data.
+ */
+#define NOTE_ALIGN(n) (((n) + 3) & -4U)
+
 int filename__read_build_id(const char *filename, void *bf, size_t size)
 {
        int fd, err = -1;
        GElf_Ehdr ehdr;
        GElf_Shdr shdr;
-       Elf_Data *build_id_data;
+       Elf_Data *data;
        Elf_Scn *sec;
+       void *ptr;
        Elf *elf;
 
        if (size < BUILD_ID_SIZE)
@@ -928,14 +934,37 @@ int filename__read_build_id(const char *filename, void *bf, size_t size)
 
        sec = elf_section_by_name(elf, &ehdr, &shdr,
                                  ".note.gnu.build-id", NULL);
-       if (sec == NULL)
-               goto out_elf_end;
+       if (sec == NULL) {
+               sec = elf_section_by_name(elf, &ehdr, &shdr,
+                                         ".notes", NULL);
+               if (sec == NULL)
+                       goto out_elf_end;
+       }
 
-       build_id_data = elf_getdata(sec, NULL);
-       if (build_id_data == NULL)
+       data = elf_getdata(sec, NULL);
+       if (data == NULL)
                goto out_elf_end;
-       memcpy(bf, build_id_data->d_buf + 16, BUILD_ID_SIZE);
-       err = BUILD_ID_SIZE;
+
+       ptr = data->d_buf;
+       while (ptr < (data->d_buf + data->d_size)) {
+               GElf_Nhdr *nhdr = ptr;
+               int namesz = NOTE_ALIGN(nhdr->n_namesz),
+                   descsz = NOTE_ALIGN(nhdr->n_descsz);
+               const char *name;
+
+               ptr += sizeof(*nhdr);
+               name = ptr;
+               ptr += namesz;
+               if (nhdr->n_type == NT_GNU_BUILD_ID &&
+                   nhdr->n_namesz == sizeof("GNU")) {
+                       if (memcmp(name, "GNU", sizeof("GNU")) == 0) {
+                               memcpy(bf, ptr, BUILD_ID_SIZE);
+                               err = BUILD_ID_SIZE;
+                               break;
+                       }
+               }
+               ptr += descsz;
+       }
 out_elf_end:
        elf_end(elf);
 out_close:
@@ -963,8 +992,8 @@ int sysfs__read_build_id(const char *filename, void *build_id, size_t size)
                if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr))
                        break;
 
-               namesz = (nhdr.n_namesz + 3) & -4U;
-               descsz = (nhdr.n_descsz + 3) & -4U;
+               namesz = NOTE_ALIGN(nhdr.n_namesz);
+               descsz = NOTE_ALIGN(nhdr.n_descsz);
                if (nhdr.n_type == NT_GNU_BUILD_ID &&
                    nhdr.n_namesz == sizeof("GNU")) {
                        if (read(fd, bf, namesz) != namesz)