diff options
Diffstat (limited to 'tools/lib/bpf/btf.c')
| -rw-r--r-- | tools/lib/bpf/btf.c | 226 | 
1 files changed, 171 insertions, 55 deletions
| diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c index 38bc6b14b066..37682908cb0f 100644 --- a/tools/lib/bpf/btf.c +++ b/tools/lib/bpf/btf.c @@ -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) @@ -996,7 +1011,7 @@ static struct btf *btf_new_empty(struct btf *base_btf)  	if (base_btf) {  		btf->base_btf = base_btf;  		btf->start_id = btf__type_cnt(base_btf); -		btf->start_str_off = base_btf->hdr->str_len; +		btf->start_str_off = base_btf->hdr->str_len + base_btf->start_str_off;  		btf->swapped_endian = base_btf->swapped_endian;  	} @@ -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;  	} -	btf->raw_data = malloc(size); -	if (!btf->raw_data) { -		err = -ENOMEM; -		goto done; +	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);  	} -	memcpy(btf->raw_data, data, size); +  	btf->raw_size = size;  	btf->hdr = btf->raw_data; @@ -1083,12 +1104,12 @@ done:  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 { @@ -1148,6 +1169,12 @@ static int btf_find_elf_sections(Elf *elf, const char *path, struct btf_elf_secs  		else  			continue; +		if (sh.sh_type != SHT_PROGBITS) { +			pr_warn("unexpected section type (%d) of section(%d, %s) from %s\n", +				sh.sh_type, idx, name, path); +			goto err; +		} +  		data = elf_getdata(scn, 0);  		if (!data) {  			pr_warn("failed to get section(%d, %s) data from %s\n", @@ -1203,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; @@ -1212,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; @@ -1329,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); @@ -1348,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 ERR_PTR(-errno); + +	if (fstat(fd, &st) < 0) { +		err = -errno; +		close(fd); +		return ERR_PTR(err); +	} + +	data = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); +	err = -errno; +	close(fd); + +	if (data == MAP_FAILED) +		return 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; @@ -1612,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); @@ -1652,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; @@ -4350,46 +4406,109 @@ static inline __u16 btf_fwd_kind(struct btf_type *t)  	return btf_kflag(t) ? BTF_KIND_UNION : BTF_KIND_STRUCT;  } -/* Check if given two types are identical ARRAY definitions */ -static bool btf_dedup_identical_arrays(struct btf_dedup *d, __u32 id1, __u32 id2) +static bool btf_dedup_identical_types(struct btf_dedup *d, __u32 id1, __u32 id2, int depth)  {  	struct btf_type *t1, *t2; +	int k1, k2; +recur: +	if (depth <= 0) +		return false;  	t1 = btf_type_by_id(d->btf, id1);  	t2 = btf_type_by_id(d->btf, id2); -	if (!btf_is_array(t1) || !btf_is_array(t2)) + +	k1 = btf_kind(t1); +	k2 = btf_kind(t2); +	if (k1 != k2)  		return false; -	return btf_equal_array(t1, t2); -} +	switch (k1) { +	case BTF_KIND_UNKN: /* VOID */ +		return true; +	case BTF_KIND_INT: +		return btf_equal_int_tag(t1, t2); +	case BTF_KIND_ENUM: +	case BTF_KIND_ENUM64: +		return btf_compat_enum(t1, t2); +	case BTF_KIND_FWD: +	case BTF_KIND_FLOAT: +		return btf_equal_common(t1, t2); +	case BTF_KIND_CONST: +	case BTF_KIND_VOLATILE: +	case BTF_KIND_RESTRICT: +	case BTF_KIND_PTR: +	case BTF_KIND_TYPEDEF: +	case BTF_KIND_FUNC: +	case BTF_KIND_TYPE_TAG: +		if (t1->info != t2->info || t1->name_off != t2->name_off) +			return false; +		id1 = t1->type; +		id2 = t2->type; +		goto recur; +	case BTF_KIND_ARRAY: { +		struct btf_array *a1, *a2; -/* Check if given two types are identical STRUCT/UNION definitions */ -static bool btf_dedup_identical_structs(struct btf_dedup *d, __u32 id1, __u32 id2) -{ -	const struct btf_member *m1, *m2; -	struct btf_type *t1, *t2; -	int n, i; +		if (!btf_compat_array(t1, t2)) +			return false; -	t1 = btf_type_by_id(d->btf, id1); -	t2 = btf_type_by_id(d->btf, id2); +		a1 = btf_array(t1); +		a2 = btf_array(t1); -	if (!btf_is_composite(t1) || btf_kind(t1) != btf_kind(t2)) -		return false; +		if (a1->index_type != a2->index_type && +		    !btf_dedup_identical_types(d, a1->index_type, a2->index_type, depth - 1)) +			return false; -	if (!btf_shallow_equal_struct(t1, t2)) -		return false; +		if (a1->type != a2->type && +		    !btf_dedup_identical_types(d, a1->type, a2->type, depth - 1)) +			return false; -	m1 = btf_members(t1); -	m2 = btf_members(t2); -	for (i = 0, n = btf_vlen(t1); i < n; i++, m1++, m2++) { -		if (m1->type != m2->type && -		    !btf_dedup_identical_arrays(d, m1->type, m2->type) && -		    !btf_dedup_identical_structs(d, m1->type, m2->type)) +		return true; +	} +	case BTF_KIND_STRUCT: +	case BTF_KIND_UNION: { +		const struct btf_member *m1, *m2; +		int i, n; + +		if (!btf_shallow_equal_struct(t1, t2))  			return false; + +		m1 = btf_members(t1); +		m2 = btf_members(t2); +		for (i = 0, n = btf_vlen(t1); i < n; i++, m1++, m2++) { +			if (m1->type == m2->type) +				continue; +			if (!btf_dedup_identical_types(d, m1->type, m2->type, depth - 1)) +				return false; +		} +		return true; +	} +	case BTF_KIND_FUNC_PROTO: { +		const struct btf_param *p1, *p2; +		int i, n; + +		if (!btf_compat_fnproto(t1, t2)) +			return false; + +		if (t1->type != t2->type && +		    !btf_dedup_identical_types(d, t1->type, t2->type, depth - 1)) +			return false; + +		p1 = btf_params(t1); +		p2 = btf_params(t2); +		for (i = 0, n = btf_vlen(t1); i < n; i++, p1++, p2++) { +			if (p1->type == p2->type) +				continue; +			if (!btf_dedup_identical_types(d, p1->type, p2->type, depth - 1)) +				return false; +		} +		return true; +	} +	default: +		return false;  	} -	return true;  } +  /*   * Check equivalence of BTF type graph formed by candidate struct/union (we'll   * call it "candidate graph" in this description for brevity) to a type graph @@ -4508,19 +4627,13 @@ static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id,  		 * different fields within the *same* struct. This breaks type  		 * equivalence check, which makes an assumption that candidate  		 * types sub-graph has a consistent and deduped-by-compiler -		 * types within a single CU. So work around that by explicitly -		 * allowing identical array types here. +		 * types within a single CU. And similar situation can happen +		 * with struct/union sometimes, and event with pointers. +		 * So accommodate cases like this doing a structural +		 * comparison recursively, but avoiding being stuck in endless +		 * loops by limiting the depth up to which we check.  		 */ -		if (btf_dedup_identical_arrays(d, hypot_type_id, cand_id)) -			return 1; -		/* It turns out that similar situation can happen with -		 * struct/union sometimes, sigh... Handle the case where -		 * structs/unions are exactly the same, down to the referenced -		 * type IDs. Anything more complicated (e.g., if referenced -		 * types are different, but equivalent) is *way more* -		 * complicated and requires a many-to-many equivalence mapping. -		 */ -		if (btf_dedup_identical_structs(d, hypot_type_id, cand_id)) +		if (btf_dedup_identical_types(d, hypot_type_id, cand_id, 16))  			return 1;  		return 0;  	} @@ -5268,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(sysfs_btf_path, NULL); +		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", | 
