selftests/bpf: Add read_build_id function
authorJiri Olsa <jolsa@kernel.org>
Fri, 31 Mar 2023 09:31:56 +0000 (11:31 +0200)
committerAlexei Starovoitov <ast@kernel.org>
Fri, 31 Mar 2023 16:40:16 +0000 (09:40 -0700)
Adding read_build_id function that parses out build id from
specified binary.

It will replace extract_build_id and also be used in following
changes.

Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Link: https://lore.kernel.org/r/20230331093157.1749137-3-jolsa@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
tools/testing/selftests/bpf/trace_helpers.c
tools/testing/selftests/bpf/trace_helpers.h

index 934bf28..9b070cd 100644 (file)
@@ -11,6 +11,9 @@
 #include <linux/perf_event.h>
 #include <sys/mman.h>
 #include "trace_helpers.h"
+#include <linux/limits.h>
+#include <libelf.h>
+#include <gelf.h>
 
 #define TRACEFS_PIPE   "/sys/kernel/tracing/trace_pipe"
 #define DEBUGFS_PIPE   "/sys/kernel/debug/tracing/trace_pipe"
@@ -234,3 +237,82 @@ ssize_t get_rel_offset(uintptr_t addr)
        fclose(f);
        return -EINVAL;
 }
+
+static int
+parse_build_id_buf(const void *note_start, Elf32_Word note_size, char *build_id)
+{
+       Elf32_Word note_offs = 0;
+
+       while (note_offs + sizeof(Elf32_Nhdr) < note_size) {
+               Elf32_Nhdr *nhdr = (Elf32_Nhdr *)(note_start + note_offs);
+
+               if (nhdr->n_type == 3 && nhdr->n_namesz == sizeof("GNU") &&
+                   !strcmp((char *)(nhdr + 1), "GNU") && nhdr->n_descsz > 0 &&
+                   nhdr->n_descsz <= BPF_BUILD_ID_SIZE) {
+                       memcpy(build_id, note_start + note_offs +
+                              ALIGN(sizeof("GNU"), 4) + sizeof(Elf32_Nhdr), nhdr->n_descsz);
+                       memset(build_id + nhdr->n_descsz, 0, BPF_BUILD_ID_SIZE - nhdr->n_descsz);
+                       return (int) nhdr->n_descsz;
+               }
+
+               note_offs = note_offs + sizeof(Elf32_Nhdr) +
+                          ALIGN(nhdr->n_namesz, 4) + ALIGN(nhdr->n_descsz, 4);
+       }
+
+       return -ENOENT;
+}
+
+/* Reads binary from *path* file and returns it in the *build_id* buffer
+ * with *size* which is expected to be at least BPF_BUILD_ID_SIZE bytes.
+ * Returns size of build id on success. On error the error value is
+ * returned.
+ */
+int read_build_id(const char *path, char *build_id, size_t size)
+{
+       int fd, err = -EINVAL;
+       Elf *elf = NULL;
+       GElf_Ehdr ehdr;
+       size_t max, i;
+
+       if (size < BPF_BUILD_ID_SIZE)
+               return -EINVAL;
+
+       fd = open(path, O_RDONLY | O_CLOEXEC);
+       if (fd < 0)
+               return -errno;
+
+       (void)elf_version(EV_CURRENT);
+
+       elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
+       if (!elf)
+               goto out;
+       if (elf_kind(elf) != ELF_K_ELF)
+               goto out;
+       if (!gelf_getehdr(elf, &ehdr))
+               goto out;
+
+       for (i = 0; i < ehdr.e_phnum; i++) {
+               GElf_Phdr mem, *phdr;
+               char *data;
+
+               phdr = gelf_getphdr(elf, i, &mem);
+               if (!phdr)
+                       goto out;
+               if (phdr->p_type != PT_NOTE)
+                       continue;
+               data = elf_rawfile(elf, &max);
+               if (!data)
+                       goto out;
+               if (phdr->p_offset + phdr->p_memsz > max)
+                       goto out;
+               err = parse_build_id_buf(data + phdr->p_offset, phdr->p_memsz, build_id);
+               if (err > 0)
+                       break;
+       }
+
+out:
+       if (elf)
+               elf_end(elf);
+       close(fd);
+       return err;
+}
index 53efde0..876f3e7 100644 (file)
@@ -4,6 +4,9 @@
 
 #include <bpf/libbpf.h>
 
+#define __ALIGN_MASK(x, mask)  (((x)+(mask))&~(mask))
+#define ALIGN(x, a)            __ALIGN_MASK(x, (typeof(x))(a)-1)
+
 struct ksym {
        long addr;
        char *name;
@@ -23,4 +26,6 @@ void read_trace_pipe(void);
 ssize_t get_uprobe_offset(const void *addr);
 ssize_t get_rel_offset(uintptr_t addr);
 
+int read_build_id(const char *path, char *build_id, size_t size);
+
 #endif