Commit c86f180f authored by Eduard Zingerman's avatar Eduard Zingerman Committed by Andrii Nakryiko

libbpf: Make btf_parse_elf process .BTF.base transparently

Update btf_parse_elf() to check if .BTF.base section is present.
The logic is as follows:

  if .BTF.base section exists:
     distilled_base := btf_new(.BTF.base)
  if distilled_base:
     btf := btf_new(.BTF, .base_btf=distilled_base)
     if base_btf:
        btf_relocate(btf, base_btf)
  else:
     btf := btf_new(.BTF)
  return btf

In other words:
- if .BTF.base section exists, load BTF from it and use it as a base
  for .BTF load;
- if base_btf is specified and .BTF.base section exist, relocate newly
  loaded .BTF against base_btf.
Signed-off-by: default avatarEduard Zingerman <eddyz87@gmail.com>
Signed-off-by: default avatarAlan Maguire <alan.maguire@oracle.com>
Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20240613095014.357981-6-alan.maguire@oracle.com
parent affdeb50
...@@ -116,6 +116,9 @@ struct btf { ...@@ -116,6 +116,9 @@ struct btf {
/* whether strings are already deduplicated */ /* whether strings are already deduplicated */
bool strs_deduped; bool strs_deduped;
/* whether base_btf should be freed in btf_free for this instance */
bool owns_base;
/* BTF object FD, if loaded into kernel */ /* BTF object FD, if loaded into kernel */
int fd; int fd;
...@@ -969,6 +972,8 @@ void btf__free(struct btf *btf) ...@@ -969,6 +972,8 @@ void btf__free(struct btf *btf)
free(btf->raw_data); free(btf->raw_data);
free(btf->raw_data_swapped); free(btf->raw_data_swapped);
free(btf->type_offs); free(btf->type_offs);
if (btf->owns_base)
btf__free(btf->base_btf);
free(btf); free(btf);
} }
...@@ -1084,53 +1089,38 @@ struct btf *btf__new_split(const void *data, __u32 size, struct btf *base_btf) ...@@ -1084,53 +1089,38 @@ struct btf *btf__new_split(const void *data, __u32 size, struct btf *base_btf)
return libbpf_ptr(btf_new(data, size, base_btf)); return libbpf_ptr(btf_new(data, size, base_btf));
} }
static struct btf *btf_parse_elf(const char *path, struct btf *base_btf, struct btf_elf_secs {
struct btf_ext **btf_ext) Elf_Data *btf_data;
Elf_Data *btf_ext_data;
Elf_Data *btf_base_data;
};
static int btf_find_elf_sections(Elf *elf, const char *path, struct btf_elf_secs *secs)
{ {
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_Scn *scn = NULL;
Elf *elf = NULL; Elf_Data *data;
GElf_Ehdr ehdr; GElf_Ehdr ehdr;
size_t shstrndx; size_t shstrndx;
int idx = 0;
if (elf_version(EV_CURRENT) == EV_NONE) {
pr_warn("failed to init libelf for %s\n", path);
return ERR_PTR(-LIBBPF_ERRNO__LIBELF);
}
fd = open(path, O_RDONLY | O_CLOEXEC);
if (fd < 0) {
err = -errno;
pr_warn("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_warn("failed to open %s as ELF file\n", path);
goto done;
}
if (!gelf_getehdr(elf, &ehdr)) { if (!gelf_getehdr(elf, &ehdr)) {
pr_warn("failed to get EHDR from %s\n", path); pr_warn("failed to get EHDR from %s\n", path);
goto done; goto err;
} }
if (elf_getshdrstrndx(elf, &shstrndx)) { if (elf_getshdrstrndx(elf, &shstrndx)) {
pr_warn("failed to get section names section index for %s\n", pr_warn("failed to get section names section index for %s\n",
path); path);
goto done; goto err;
} }
if (!elf_rawdata(elf_getscn(elf, shstrndx), NULL)) { if (!elf_rawdata(elf_getscn(elf, shstrndx), NULL)) {
pr_warn("failed to get e_shstrndx from %s\n", path); pr_warn("failed to get e_shstrndx from %s\n", path);
goto done; goto err;
} }
while ((scn = elf_nextscn(elf, scn)) != NULL) { while ((scn = elf_nextscn(elf, scn)) != NULL) {
Elf_Data **field;
GElf_Shdr sh; GElf_Shdr sh;
char *name; char *name;
...@@ -1138,42 +1128,102 @@ static struct btf *btf_parse_elf(const char *path, struct btf *base_btf, ...@@ -1138,42 +1128,102 @@ static struct btf *btf_parse_elf(const char *path, struct btf *base_btf,
if (gelf_getshdr(scn, &sh) != &sh) { if (gelf_getshdr(scn, &sh) != &sh) {
pr_warn("failed to get section(%d) header from %s\n", pr_warn("failed to get section(%d) header from %s\n",
idx, path); idx, path);
goto done; goto err;
} }
name = elf_strptr(elf, shstrndx, sh.sh_name); name = elf_strptr(elf, shstrndx, sh.sh_name);
if (!name) { if (!name) {
pr_warn("failed to get section(%d) name from %s\n", pr_warn("failed to get section(%d) name from %s\n",
idx, path); idx, path);
goto done; goto err;
} }
if (strcmp(name, BTF_ELF_SEC) == 0) {
btf_data = elf_getdata(scn, 0); if (strcmp(name, BTF_ELF_SEC) == 0)
if (!btf_data) { field = &secs->btf_data;
pr_warn("failed to get section(%d, %s) data from %s\n", else if (strcmp(name, BTF_EXT_ELF_SEC) == 0)
idx, name, path); field = &secs->btf_ext_data;
goto done; else if (strcmp(name, BTF_BASE_ELF_SEC) == 0)
} field = &secs->btf_base_data;
continue; else
} else if (btf_ext && strcmp(name, BTF_EXT_ELF_SEC) == 0) {
btf_ext_data = elf_getdata(scn, 0);
if (!btf_ext_data) {
pr_warn("failed to get section(%d, %s) data from %s\n",
idx, name, path);
goto done;
}
continue; continue;
data = elf_getdata(scn, 0);
if (!data) {
pr_warn("failed to get section(%d, %s) data from %s\n",
idx, name, path);
goto err;
} }
*field = data;
}
return 0;
err:
return -LIBBPF_ERRNO__FORMAT;
}
static struct btf *btf_parse_elf(const char *path, struct btf *base_btf,
struct btf_ext **btf_ext)
{
struct btf_elf_secs secs = {};
struct btf *dist_base_btf = NULL;
struct btf *btf = NULL;
int err = 0, fd = -1;
Elf *elf = NULL;
if (elf_version(EV_CURRENT) == EV_NONE) {
pr_warn("failed to init libelf for %s\n", path);
return ERR_PTR(-LIBBPF_ERRNO__LIBELF);
}
fd = open(path, O_RDONLY | O_CLOEXEC);
if (fd < 0) {
err = -errno;
pr_warn("failed to open %s: %s\n", path, strerror(errno));
return ERR_PTR(err);
} }
if (!btf_data) { elf = elf_begin(fd, ELF_C_READ, NULL);
if (!elf) {
pr_warn("failed to open %s as ELF file\n", path);
goto done;
}
err = btf_find_elf_sections(elf, path, &secs);
if (err)
goto done;
if (!secs.btf_data) {
pr_warn("failed to find '%s' ELF section in %s\n", BTF_ELF_SEC, path); pr_warn("failed to find '%s' ELF section in %s\n", BTF_ELF_SEC, path);
err = -ENODATA; err = -ENODATA;
goto done; goto done;
} }
btf = btf_new(btf_data->d_buf, btf_data->d_size, base_btf);
err = libbpf_get_error(btf); if (secs.btf_base_data) {
if (err) dist_base_btf = btf_new(secs.btf_base_data->d_buf, secs.btf_base_data->d_size,
NULL);
if (IS_ERR(dist_base_btf)) {
err = PTR_ERR(dist_base_btf);
dist_base_btf = NULL;
goto done;
}
}
btf = btf_new(secs.btf_data->d_buf, secs.btf_data->d_size,
dist_base_btf ?: base_btf);
if (IS_ERR(btf)) {
err = PTR_ERR(btf);
goto done; goto done;
}
if (dist_base_btf && base_btf) {
err = btf__relocate(btf, base_btf);
if (err)
goto done;
btf__free(dist_base_btf);
dist_base_btf = NULL;
}
if (dist_base_btf)
btf->owns_base = true;
switch (gelf_getclass(elf)) { switch (gelf_getclass(elf)) {
case ELFCLASS32: case ELFCLASS32:
...@@ -1187,11 +1237,12 @@ static struct btf *btf_parse_elf(const char *path, struct btf *base_btf, ...@@ -1187,11 +1237,12 @@ static struct btf *btf_parse_elf(const char *path, struct btf *base_btf,
break; break;
} }
if (btf_ext && btf_ext_data) { if (btf_ext && secs.btf_ext_data) {
*btf_ext = btf_ext__new(btf_ext_data->d_buf, btf_ext_data->d_size); *btf_ext = btf_ext__new(secs.btf_ext_data->d_buf, secs.btf_ext_data->d_size);
err = libbpf_get_error(*btf_ext); if (IS_ERR(*btf_ext)) {
if (err) err = PTR_ERR(*btf_ext);
goto done; goto done;
}
} else if (btf_ext) { } else if (btf_ext) {
*btf_ext = NULL; *btf_ext = NULL;
} }
...@@ -1205,6 +1256,7 @@ static struct btf *btf_parse_elf(const char *path, struct btf *base_btf, ...@@ -1205,6 +1256,7 @@ static struct btf *btf_parse_elf(const char *path, struct btf *base_btf,
if (btf_ext) if (btf_ext)
btf_ext__free(*btf_ext); btf_ext__free(*btf_ext);
btf__free(dist_base_btf);
btf__free(btf); btf__free(btf);
return ERR_PTR(err); return ERR_PTR(err);
...@@ -5598,5 +5650,9 @@ void btf_set_base_btf(struct btf *btf, const struct btf *base_btf) ...@@ -5598,5 +5650,9 @@ void btf_set_base_btf(struct btf *btf, const struct btf *base_btf)
int btf__relocate(struct btf *btf, const struct btf *base_btf) int btf__relocate(struct btf *btf, const struct btf *base_btf)
{ {
return libbpf_err(btf_relocate(btf, base_btf, NULL)); int err = btf_relocate(btf, base_btf, NULL);
if (!err)
btf->owns_base = false;
return libbpf_err(err);
} }
...@@ -18,6 +18,7 @@ extern "C" { ...@@ -18,6 +18,7 @@ extern "C" {
#define BTF_ELF_SEC ".BTF" #define BTF_ELF_SEC ".BTF"
#define BTF_EXT_ELF_SEC ".BTF.ext" #define BTF_EXT_ELF_SEC ".BTF.ext"
#define BTF_BASE_ELF_SEC ".BTF.base"
#define MAPS_ELF_SEC ".maps" #define MAPS_ELF_SEC ".maps"
struct btf; struct btf;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment