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

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
Loading
Loading
Loading
Loading
+110 −54
Original line number Diff line number Diff line
@@ -116,6 +116,9 @@ struct btf {
	/* whether strings are already deduplicated */
	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 */
	int fd;

@@ -969,6 +972,8 @@ void btf__free(struct btf *btf)
	free(btf->raw_data);
	free(btf->raw_data_swapped);
	free(btf->type_offs);
	if (btf->owns_base)
		btf__free(btf->base_btf);
	free(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));
}

static struct btf *btf_parse_elf(const char *path, struct btf *base_btf,
				 struct btf_ext **btf_ext)
struct btf_elf_secs {
	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 *elf = NULL;
	Elf_Data *data;
	GElf_Ehdr ehdr;
	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)) {
		pr_warn("failed to get EHDR from %s\n", path);
		goto done;
		goto err;
	}

	if (elf_getshdrstrndx(elf, &shstrndx)) {
		pr_warn("failed to get section names section index for %s\n",
			path);
		goto done;
		goto err;
	}

	if (!elf_rawdata(elf_getscn(elf, shstrndx), NULL)) {
		pr_warn("failed to get e_shstrndx from %s\n", path);
		goto done;
		goto err;
	}

	while ((scn = elf_nextscn(elf, scn)) != NULL) {
		Elf_Data **field;
		GElf_Shdr sh;
		char *name;

@@ -1138,42 +1128,102 @@ static struct btf *btf_parse_elf(const char *path, struct btf *base_btf,
		if (gelf_getshdr(scn, &sh) != &sh) {
			pr_warn("failed to get section(%d) header from %s\n",
				idx, path);
			goto done;
			goto err;
		}
		name = elf_strptr(elf, shstrndx, sh.sh_name);
		if (!name) {
			pr_warn("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_warn("failed to get section(%d, %s) data from %s\n",
					idx, name, path);
				goto done;
			goto err;
		}

		if (strcmp(name, BTF_ELF_SEC) == 0)
			field = &secs->btf_data;
		else if (strcmp(name, BTF_EXT_ELF_SEC) == 0)
			field = &secs->btf_ext_data;
		else if (strcmp(name, BTF_BASE_ELF_SEC) == 0)
			field = &secs->btf_base_data;
		else
			continue;
		} else if (btf_ext && strcmp(name, BTF_EXT_ELF_SEC) == 0) {
			btf_ext_data = elf_getdata(scn, 0);
			if (!btf_ext_data) {

		data = elf_getdata(scn, 0);
		if (!data) {
			pr_warn("failed to get section(%d, %s) data from %s\n",
				idx, name, path);
				goto done;
			goto err;
		}
			continue;
		*field = data;
	}

	return 0;

err:
	return -LIBBPF_ERRNO__FORMAT;
}

	if (!btf_data) {
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);
	}

	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);
		err = -ENODATA;
		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) {
		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;
	}
	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)) {
	case ELFCLASS32:
@@ -1187,11 +1237,12 @@ static struct btf *btf_parse_elf(const char *path, struct btf *base_btf,
		break;
	}

	if (btf_ext && btf_ext_data) {
		*btf_ext = btf_ext__new(btf_ext_data->d_buf, btf_ext_data->d_size);
		err = libbpf_get_error(*btf_ext);
		if (err)
	if (btf_ext && secs.btf_ext_data) {
		*btf_ext = btf_ext__new(secs.btf_ext_data->d_buf, secs.btf_ext_data->d_size);
		if (IS_ERR(*btf_ext)) {
			err = PTR_ERR(*btf_ext);
			goto done;
		}
	} else if (btf_ext) {
		*btf_ext = NULL;
	}
@@ -1205,6 +1256,7 @@ static struct btf *btf_parse_elf(const char *path, struct btf *base_btf,

	if (btf_ext)
		btf_ext__free(*btf_ext);
	btf__free(dist_base_btf);
	btf__free(btf);

	return ERR_PTR(err);
@@ -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)
{
	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);
}
+1 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ extern "C" {

#define BTF_ELF_SEC ".BTF"
#define BTF_EXT_ELF_SEC ".BTF.ext"
#define BTF_BASE_ELF_SEC ".BTF.base"
#define MAPS_ELF_SEC ".maps"

struct btf;