Skip to content

Commit e6c6485

Browse files
anakryikoAlexei Starovoitov
authored andcommitted
libbpf: add btf__parse_elf API to load .BTF and .BTF.ext
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>
1 parent 1d7a08b commit e6c6485

File tree

3 files changed

+135
-0
lines changed

3 files changed

+135
-0
lines changed

tools/lib/bpf/btf.c

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
#include <stdio.h>
55
#include <stdlib.h>
66
#include <string.h>
7+
#include <fcntl.h>
78
#include <unistd.h>
89
#include <errno.h>
910
#include <linux/err.h>
1011
#include <linux/btf.h>
12+
#include <gelf.h>
1113
#include "btf.h"
1214
#include "bpf.h"
1315
#include "libbpf.h"
@@ -417,6 +419,132 @@ struct btf *btf__new(__u8 *data, __u32 size)
417419
return btf;
418420
}
419421

422+
static bool btf_check_endianness(const GElf_Ehdr *ehdr)
423+
{
424+
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
425+
return ehdr->e_ident[EI_DATA] == ELFDATA2LSB;
426+
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
427+
return ehdr->e_ident[EI_DATA] == ELFDATA2MSB;
428+
#else
429+
# error "Unrecognized __BYTE_ORDER__"
430+
#endif
431+
}
432+
433+
struct btf *btf__parse_elf(const char *path, struct btf_ext **btf_ext)
434+
{
435+
Elf_Data *btf_data = NULL, *btf_ext_data = NULL;
436+
int err = 0, fd = -1, idx = 0;
437+
struct btf *btf = NULL;
438+
Elf_Scn *scn = NULL;
439+
Elf *elf = NULL;
440+
GElf_Ehdr ehdr;
441+
442+
if (elf_version(EV_CURRENT) == EV_NONE) {
443+
pr_warning("failed to init libelf for %s\n", path);
444+
return ERR_PTR(-LIBBPF_ERRNO__LIBELF);
445+
}
446+
447+
fd = open(path, O_RDONLY);
448+
if (fd < 0) {
449+
err = -errno;
450+
pr_warning("failed to open %s: %s\n", path, strerror(errno));
451+
return ERR_PTR(err);
452+
}
453+
454+
err = -LIBBPF_ERRNO__FORMAT;
455+
456+
elf = elf_begin(fd, ELF_C_READ, NULL);
457+
if (!elf) {
458+
pr_warning("failed to open %s as ELF file\n", path);
459+
goto done;
460+
}
461+
if (!gelf_getehdr(elf, &ehdr)) {
462+
pr_warning("failed to get EHDR from %s\n", path);
463+
goto done;
464+
}
465+
if (!btf_check_endianness(&ehdr)) {
466+
pr_warning("non-native ELF endianness is not supported\n");
467+
goto done;
468+
}
469+
if (!elf_rawdata(elf_getscn(elf, ehdr.e_shstrndx), NULL)) {
470+
pr_warning("failed to get e_shstrndx from %s\n", path);
471+
goto done;
472+
}
473+
474+
while ((scn = elf_nextscn(elf, scn)) != NULL) {
475+
GElf_Shdr sh;
476+
char *name;
477+
478+
idx++;
479+
if (gelf_getshdr(scn, &sh) != &sh) {
480+
pr_warning("failed to get section(%d) header from %s\n",
481+
idx, path);
482+
goto done;
483+
}
484+
name = elf_strptr(elf, ehdr.e_shstrndx, sh.sh_name);
485+
if (!name) {
486+
pr_warning("failed to get section(%d) name from %s\n",
487+
idx, path);
488+
goto done;
489+
}
490+
if (strcmp(name, BTF_ELF_SEC) == 0) {
491+
btf_data = elf_getdata(scn, 0);
492+
if (!btf_data) {
493+
pr_warning("failed to get section(%d, %s) data from %s\n",
494+
idx, name, path);
495+
goto done;
496+
}
497+
continue;
498+
} else if (btf_ext && strcmp(name, BTF_EXT_ELF_SEC) == 0) {
499+
btf_ext_data = elf_getdata(scn, 0);
500+
if (!btf_ext_data) {
501+
pr_warning("failed to get section(%d, %s) data from %s\n",
502+
idx, name, path);
503+
goto done;
504+
}
505+
continue;
506+
}
507+
}
508+
509+
err = 0;
510+
511+
if (!btf_data) {
512+
err = -ENOENT;
513+
goto done;
514+
}
515+
btf = btf__new(btf_data->d_buf, btf_data->d_size);
516+
if (IS_ERR(btf))
517+
goto done;
518+
519+
if (btf_ext && btf_ext_data) {
520+
*btf_ext = btf_ext__new(btf_ext_data->d_buf,
521+
btf_ext_data->d_size);
522+
if (IS_ERR(*btf_ext))
523+
goto done;
524+
} else if (btf_ext) {
525+
*btf_ext = NULL;
526+
}
527+
done:
528+
if (elf)
529+
elf_end(elf);
530+
close(fd);
531+
532+
if (err)
533+
return ERR_PTR(err);
534+
/*
535+
* btf is always parsed before btf_ext, so no need to clean up
536+
* btf_ext, if btf loading failed
537+
*/
538+
if (IS_ERR(btf))
539+
return btf;
540+
if (btf_ext && IS_ERR(*btf_ext)) {
541+
btf__free(btf);
542+
err = PTR_ERR(*btf_ext);
543+
return ERR_PTR(err);
544+
}
545+
return btf;
546+
}
547+
420548
static int compare_vsi_off(const void *_a, const void *_b)
421549
{
422550
const struct btf_var_secinfo *a = _a;

tools/lib/bpf/btf.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ struct btf_ext_header {
5959

6060
LIBBPF_API void btf__free(struct btf *btf);
6161
LIBBPF_API struct btf *btf__new(__u8 *data, __u32 size);
62+
LIBBPF_API struct btf *btf__parse_elf(const char *path,
63+
struct btf_ext **btf_ext);
6264
LIBBPF_API int btf__finalize_data(struct bpf_object *obj, struct btf *btf);
6365
LIBBPF_API int btf__load(struct btf *btf);
6466
LIBBPF_API __s32 btf__find_by_name(const struct btf *btf,

tools/lib/bpf/libbpf.map

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,8 @@ LIBBPF_0.0.3 {
164164
bpf_map_freeze;
165165
btf__finalize_data;
166166
} LIBBPF_0.0.2;
167+
168+
LIBBPF_0.0.4 {
169+
global:
170+
btf__parse_elf;
171+
} LIBBPF_0.0.3;

0 commit comments

Comments
 (0)