libbpf: add btf__parse_elf API to load .BTF and .BTF.ext
authorAndrii Nakryiko <andriin@fb.com>
Fri, 24 May 2019 18:58:57 +0000 (11:58 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Fri, 24 May 2019 21:05:57 +0000 (14:05 -0700)
Loading BTF and BTF.ext from ELF file is a common need. Instead of
requiring every user to re-implement it, let's provide this API from
libbpf itself. It's mostly copy/paste from `bpftool btf dump`
implementation, which will be switched to libbpf's version in next
patch. btf__parse_elf allows to load BTF and optionally BTF.ext.
This is also useful for tests that need to load/work with BTF, loaded
from test ELF files.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
tools/lib/bpf/btf.c
tools/lib/bpf/btf.h
tools/lib/bpf/libbpf.map

index 03348c4d6bd480f480d137bd45fc2c2302a489f3..6139550810a18a8c7a4d90559bb8f607835a6bb9 100644 (file)
@@ -4,10 +4,12 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
 #include <linux/err.h>
 #include <linux/btf.h>
+#include <gelf.h>
 #include "btf.h"
 #include "bpf.h"
 #include "libbpf.h"
@@ -417,6 +419,132 @@ done:
        return btf;
 }
 
+static bool btf_check_endianness(const GElf_Ehdr *ehdr)
+{
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+       return ehdr->e_ident[EI_DATA] == ELFDATA2LSB;
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+       return ehdr->e_ident[EI_DATA] == ELFDATA2MSB;
+#else
+# error "Unrecognized __BYTE_ORDER__"
+#endif
+}
+
+struct btf *btf__parse_elf(const char *path, struct btf_ext **btf_ext)
+{
+       Elf_Data *btf_data = NULL, *btf_ext_data = NULL;
+       int err = 0, fd = -1, idx = 0;
+       struct btf *btf = NULL;
+       Elf_Scn *scn = NULL;
+       Elf *elf = NULL;
+       GElf_Ehdr ehdr;
+
+       if (elf_version(EV_CURRENT) == EV_NONE) {
+               pr_warning("failed to init libelf for %s\n", path);
+               return ERR_PTR(-LIBBPF_ERRNO__LIBELF);
+       }
+
+       fd = open(path, O_RDONLY);
+       if (fd < 0) {
+               err = -errno;
+               pr_warning("failed to open %s: %s\n", path, strerror(errno));
+               return ERR_PTR(err);
+       }
+
+       err = -LIBBPF_ERRNO__FORMAT;
+
+       elf = elf_begin(fd, ELF_C_READ, NULL);
+       if (!elf) {
+               pr_warning("failed to open %s as ELF file\n", path);
+               goto done;
+       }
+       if (!gelf_getehdr(elf, &ehdr)) {
+               pr_warning("failed to get EHDR from %s\n", path);
+               goto done;
+       }
+       if (!btf_check_endianness(&ehdr)) {
+               pr_warning("non-native ELF endianness is not supported\n");
+               goto done;
+       }
+       if (!elf_rawdata(elf_getscn(elf, ehdr.e_shstrndx), NULL)) {
+               pr_warning("failed to get e_shstrndx from %s\n", path);
+               goto done;
+       }
+
+       while ((scn = elf_nextscn(elf, scn)) != NULL) {
+               GElf_Shdr sh;
+               char *name;
+
+               idx++;
+               if (gelf_getshdr(scn, &sh) != &sh) {
+                       pr_warning("failed to get section(%d) header from %s\n",
+                                  idx, path);
+                       goto done;
+               }
+               name = elf_strptr(elf, ehdr.e_shstrndx, sh.sh_name);
+               if (!name) {
+                       pr_warning("failed to get section(%d) name from %s\n",
+                                  idx, path);
+                       goto done;
+               }
+               if (strcmp(name, BTF_ELF_SEC) == 0) {
+                       btf_data = elf_getdata(scn, 0);
+                       if (!btf_data) {
+                               pr_warning("failed to get section(%d, %s) data from %s\n",
+                                          idx, name, path);
+                               goto done;
+                       }
+                       continue;
+               } else if (btf_ext && strcmp(name, BTF_EXT_ELF_SEC) == 0) {
+                       btf_ext_data = elf_getdata(scn, 0);
+                       if (!btf_ext_data) {
+                               pr_warning("failed to get section(%d, %s) data from %s\n",
+                                          idx, name, path);
+                               goto done;
+                       }
+                       continue;
+               }
+       }
+
+       err = 0;
+
+       if (!btf_data) {
+               err = -ENOENT;
+               goto done;
+       }
+       btf = btf__new(btf_data->d_buf, btf_data->d_size);
+       if (IS_ERR(btf))
+               goto done;
+
+       if (btf_ext && btf_ext_data) {
+               *btf_ext = btf_ext__new(btf_ext_data->d_buf,
+                                       btf_ext_data->d_size);
+               if (IS_ERR(*btf_ext))
+                       goto done;
+       } else if (btf_ext) {
+               *btf_ext = NULL;
+       }
+done:
+       if (elf)
+               elf_end(elf);
+       close(fd);
+
+       if (err)
+               return ERR_PTR(err);
+       /*
+        * btf is always parsed before btf_ext, so no need to clean up
+        * btf_ext, if btf loading failed
+        */
+       if (IS_ERR(btf))
+               return btf;
+       if (btf_ext && IS_ERR(*btf_ext)) {
+               btf__free(btf);
+               err = PTR_ERR(*btf_ext);
+               return ERR_PTR(err);
+       }
+       return btf;
+}
+
 static int compare_vsi_off(const void *_a, const void *_b)
 {
        const struct btf_var_secinfo *a = _a;
index c7b399e81fcefae69ac07e48dd103811924d1e29..bded210df9e8061c4a903f686eda4966a5b85326 100644 (file)
@@ -59,6 +59,8 @@ struct btf_ext_header {
 
 LIBBPF_API void btf__free(struct btf *btf);
 LIBBPF_API struct btf *btf__new(__u8 *data, __u32 size);
+LIBBPF_API struct btf *btf__parse_elf(const char *path,
+                                     struct btf_ext **btf_ext);
 LIBBPF_API int btf__finalize_data(struct bpf_object *obj, struct btf *btf);
 LIBBPF_API int btf__load(struct btf *btf);
 LIBBPF_API __s32 btf__find_by_name(const struct btf *btf,
index 673001787cba26f32572ae2e8dc2dc0a8bc9663f..6ea5ce19b9e0b9f8088e81ceb913fa1055b04a03 100644 (file)
@@ -164,3 +164,8 @@ LIBBPF_0.0.3 {
                bpf_map_freeze;
                btf__finalize_data;
 } LIBBPF_0.0.2;
+
+LIBBPF_0.0.4 {
+       global:
+               btf__parse_elf;
+} LIBBPF_0.0.3;