Commit 3c0421c9 authored by Lorenz Bauer's avatar Lorenz Bauer Committed by Andrii Nakryiko
Browse files

libbpf: Use mmap to parse vmlinux BTF from sysfs



Teach libbpf to use mmap when parsing vmlinux BTF from /sys. We don't
apply this to fall-back paths on the regular file system because there
is no way to ensure that modifications underlying the MAP_PRIVATE
mapping are not visible to the process.

Signed-off-by: default avatarLorenz Bauer <lmb@isovalent.com>
Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
Tested-by: default avatarAlan Maguire <alan.maguire@oracle.com>
Acked-by: default avatarAndrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20250520-vmlinux-mmap-v5-3-e8c941acc414@isovalent.com
parent 828226b6
Loading
Loading
Loading
Loading
+71 −18
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@
#include <sys/utsname.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/btf.h>
@@ -120,6 +121,9 @@ struct btf {
	/* whether base_btf should be freed in btf_free for this instance */
	bool owns_base;

	/* whether raw_data is a (read-only) mmap */
	bool raw_data_is_mmap;

	/* BTF object FD, if loaded into kernel */
	int fd;

@@ -951,6 +955,17 @@ static bool btf_is_modifiable(const struct btf *btf)
	return (void *)btf->hdr != btf->raw_data;
}

static void btf_free_raw_data(struct btf *btf)
{
	if (btf->raw_data_is_mmap) {
		munmap(btf->raw_data, btf->raw_size);
		btf->raw_data_is_mmap = false;
	} else {
		free(btf->raw_data);
	}
	btf->raw_data = NULL;
}

void btf__free(struct btf *btf)
{
	if (IS_ERR_OR_NULL(btf))
@@ -970,7 +985,7 @@ void btf__free(struct btf *btf)
		free(btf->types_data);
		strset__free(btf->strs_set);
	}
	free(btf->raw_data);
	btf_free_raw_data(btf);
	free(btf->raw_data_swapped);
	free(btf->type_offs);
	if (btf->owns_base)
@@ -1030,7 +1045,7 @@ struct btf *btf__new_empty_split(struct btf *base_btf)
	return libbpf_ptr(btf_new_empty(base_btf));
}

static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf)
static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf, bool is_mmap)
{
	struct btf *btf;
	int err;
@@ -1050,12 +1065,18 @@ static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf)
		btf->start_str_off = base_btf->hdr->str_len;
	}

	if (is_mmap) {
		btf->raw_data = (void *)data;
		btf->raw_data_is_mmap = true;
	} else {
		btf->raw_data = malloc(size);
		if (!btf->raw_data) {
			err = -ENOMEM;
			goto done;
		}
		memcpy(btf->raw_data, data, size);
	}

	btf->raw_size = size;

	btf->hdr = btf->raw_data;
@@ -1083,12 +1104,12 @@ static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf)

struct btf *btf__new(const void *data, __u32 size)
{
	return libbpf_ptr(btf_new(data, size, NULL));
	return libbpf_ptr(btf_new(data, size, NULL, false));
}

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, false));
}

struct btf_elf_secs {
@@ -1209,7 +1230,7 @@ static struct btf *btf_parse_elf(const char *path, struct btf *base_btf,

	if (secs.btf_base_data) {
		dist_base_btf = btf_new(secs.btf_base_data->d_buf, secs.btf_base_data->d_size,
					NULL);
					NULL, false);
		if (IS_ERR(dist_base_btf)) {
			err = PTR_ERR(dist_base_btf);
			dist_base_btf = NULL;
@@ -1218,7 +1239,7 @@ static struct btf *btf_parse_elf(const char *path, struct btf *base_btf,
	}

	btf = btf_new(secs.btf_data->d_buf, secs.btf_data->d_size,
		      dist_base_btf ?: base_btf);
		      dist_base_btf ?: base_btf, false);
	if (IS_ERR(btf)) {
		err = PTR_ERR(btf);
		goto done;
@@ -1335,7 +1356,7 @@ static struct btf *btf_parse_raw(const char *path, struct btf *base_btf)
	}

	/* finally parse BTF data */
	btf = btf_new(data, sz, base_btf);
	btf = btf_new(data, sz, base_btf, false);

err_out:
	free(data);
@@ -1354,6 +1375,37 @@ struct btf *btf__parse_raw_split(const char *path, struct btf *base_btf)
	return libbpf_ptr(btf_parse_raw(path, base_btf));
}

static struct btf *btf_parse_raw_mmap(const char *path, struct btf *base_btf)
{
	struct stat st;
	void *data;
	struct btf *btf;
	int fd, err;

	fd = open(path, O_RDONLY);
	if (fd < 0)
		return libbpf_err_ptr(-errno);

	if (fstat(fd, &st) < 0) {
		err = -errno;
		close(fd);
		return libbpf_err_ptr(err);
	}

	data = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
	err = -errno;
	close(fd);

	if (data == MAP_FAILED)
		return libbpf_err_ptr(err);

	btf = btf_new(data, st.st_size, base_btf, true);
	if (IS_ERR(btf))
		munmap(data, st.st_size);

	return btf;
}

static struct btf *btf_parse(const char *path, struct btf *base_btf, struct btf_ext **btf_ext)
{
	struct btf *btf;
@@ -1618,7 +1670,7 @@ struct btf *btf_get_from_fd(int btf_fd, struct btf *base_btf)
		goto exit_free;
	}

	btf = btf_new(ptr, btf_info.btf_size, base_btf);
	btf = btf_new(ptr, btf_info.btf_size, base_btf, false);

exit_free:
	free(ptr);
@@ -1658,10 +1710,8 @@ struct btf *btf__load_from_kernel_by_id(__u32 id)

static void btf_invalidate_raw_data(struct btf *btf)
{
	if (btf->raw_data) {
		free(btf->raw_data);
		btf->raw_data = NULL;
	}
	if (btf->raw_data)
		btf_free_raw_data(btf);
	if (btf->raw_data_swapped) {
		free(btf->raw_data_swapped);
		btf->raw_data_swapped = NULL;
@@ -5331,7 +5381,10 @@ struct btf *btf__load_vmlinux_btf(void)
		pr_warn("kernel BTF is missing at '%s', was CONFIG_DEBUG_INFO_BTF enabled?\n",
			sysfs_btf_path);
	} else {
		btf = btf_parse_raw_mmap(sysfs_btf_path, NULL);
		if (IS_ERR(btf))
			btf = btf__parse(sysfs_btf_path, NULL);

		if (!btf) {
			err = -errno;
			pr_warn("failed to read kernel BTF from '%s': %s\n",