mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	bpf: Prepare relo_core.c for kernel duty.
Make relo_core.c to be compiled for the kernel and for user space libbpf.
Note the patch is reducing BPF_CORE_SPEC_MAX_LEN from 64 to 32.
This is the maximum number of nested structs and arrays.
For example:
 struct sample {
     int a;
     struct {
         int b[10];
     };
 };
 struct sample *s = ...;
 int *y = &s->b[5];
This field access is encoded as "0:1:0:5" and spec len is 4.
The follow up patch might bump it back to 64.
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Acked-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20211201181040.23337-4-alexei.starovoitov@gmail.com
			
			
This commit is contained in:
		
							parent
							
								
									8293eb995f
								
							
						
					
					
						commit
						29db4bea1d
					
				
					 4 changed files with 176 additions and 11 deletions
				
			
		|  | @ -144,6 +144,53 @@ static inline bool btf_type_is_enum(const struct btf_type *t) | ||||||
| 	return BTF_INFO_KIND(t->info) == BTF_KIND_ENUM; | 	return BTF_INFO_KIND(t->info) == BTF_KIND_ENUM; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static inline bool str_is_empty(const char *s) | ||||||
|  | { | ||||||
|  | 	return !s || !s[0]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline u16 btf_kind(const struct btf_type *t) | ||||||
|  | { | ||||||
|  | 	return BTF_INFO_KIND(t->info); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline bool btf_is_enum(const struct btf_type *t) | ||||||
|  | { | ||||||
|  | 	return btf_kind(t) == BTF_KIND_ENUM; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline bool btf_is_composite(const struct btf_type *t) | ||||||
|  | { | ||||||
|  | 	u16 kind = btf_kind(t); | ||||||
|  | 
 | ||||||
|  | 	return kind == BTF_KIND_STRUCT || kind == BTF_KIND_UNION; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline bool btf_is_array(const struct btf_type *t) | ||||||
|  | { | ||||||
|  | 	return btf_kind(t) == BTF_KIND_ARRAY; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline bool btf_is_int(const struct btf_type *t) | ||||||
|  | { | ||||||
|  | 	return btf_kind(t) == BTF_KIND_INT; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline bool btf_is_ptr(const struct btf_type *t) | ||||||
|  | { | ||||||
|  | 	return btf_kind(t) == BTF_KIND_PTR; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline u8 btf_int_offset(const struct btf_type *t) | ||||||
|  | { | ||||||
|  | 	return BTF_INT_OFFSET(*(u32 *)(t + 1)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline u8 btf_int_encoding(const struct btf_type *t) | ||||||
|  | { | ||||||
|  | 	return BTF_INT_ENCODING(*(u32 *)(t + 1)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static inline bool btf_type_is_scalar(const struct btf_type *t) | static inline bool btf_type_is_scalar(const struct btf_type *t) | ||||||
| { | { | ||||||
| 	return btf_type_is_int(t) || btf_type_is_enum(t); | 	return btf_type_is_int(t) || btf_type_is_enum(t); | ||||||
|  | @ -184,6 +231,11 @@ static inline u16 btf_type_vlen(const struct btf_type *t) | ||||||
| 	return BTF_INFO_VLEN(t->info); | 	return BTF_INFO_VLEN(t->info); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static inline u16 btf_vlen(const struct btf_type *t) | ||||||
|  | { | ||||||
|  | 	return btf_type_vlen(t); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static inline u16 btf_func_linkage(const struct btf_type *t) | static inline u16 btf_func_linkage(const struct btf_type *t) | ||||||
| { | { | ||||||
| 	return BTF_INFO_VLEN(t->info); | 	return BTF_INFO_VLEN(t->info); | ||||||
|  | @ -208,11 +260,40 @@ static inline u32 __btf_member_bitfield_size(const struct btf_type *struct_type, | ||||||
| 					   : 0; | 					   : 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static inline struct btf_member *btf_members(const struct btf_type *t) | ||||||
|  | { | ||||||
|  | 	return (struct btf_member *)(t + 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline u32 btf_member_bit_offset(const struct btf_type *t, u32 member_idx) | ||||||
|  | { | ||||||
|  | 	const struct btf_member *m = btf_members(t) + member_idx; | ||||||
|  | 
 | ||||||
|  | 	return __btf_member_bit_offset(t, m); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline u32 btf_member_bitfield_size(const struct btf_type *t, u32 member_idx) | ||||||
|  | { | ||||||
|  | 	const struct btf_member *m = btf_members(t) + member_idx; | ||||||
|  | 
 | ||||||
|  | 	return __btf_member_bitfield_size(t, m); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static inline const struct btf_member *btf_type_member(const struct btf_type *t) | static inline const struct btf_member *btf_type_member(const struct btf_type *t) | ||||||
| { | { | ||||||
| 	return (const struct btf_member *)(t + 1); | 	return (const struct btf_member *)(t + 1); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static inline struct btf_array *btf_array(const struct btf_type *t) | ||||||
|  | { | ||||||
|  | 	return (struct btf_array *)(t + 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline struct btf_enum *btf_enum(const struct btf_type *t) | ||||||
|  | { | ||||||
|  | 	return (struct btf_enum *)(t + 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static inline const struct btf_var_secinfo *btf_type_var_secinfo( | static inline const struct btf_var_secinfo *btf_type_var_secinfo( | ||||||
| 		const struct btf_type *t) | 		const struct btf_type *t) | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -36,3 +36,7 @@ obj-$(CONFIG_BPF_SYSCALL) += bpf_struct_ops.o | ||||||
| obj-${CONFIG_BPF_LSM} += bpf_lsm.o | obj-${CONFIG_BPF_LSM} += bpf_lsm.o | ||||||
| endif | endif | ||||||
| obj-$(CONFIG_BPF_PRELOAD) += preload/ | obj-$(CONFIG_BPF_PRELOAD) += preload/ | ||||||
|  | 
 | ||||||
|  | obj-$(CONFIG_BPF_SYSCALL) += relo_core.o | ||||||
|  | $(obj)/relo_core.o: $(srctree)/tools/lib/bpf/relo_core.c FORCE | ||||||
|  | 	$(call if_changed_rule,cc_o_c) | ||||||
|  |  | ||||||
|  | @ -6413,3 +6413,29 @@ bool bpf_check_mod_kfunc_call(struct kfunc_btf_id_list *klist, u32 kfunc_id, | ||||||
| 
 | 
 | ||||||
| DEFINE_KFUNC_BTF_ID_LIST(bpf_tcp_ca_kfunc_list); | DEFINE_KFUNC_BTF_ID_LIST(bpf_tcp_ca_kfunc_list); | ||||||
| DEFINE_KFUNC_BTF_ID_LIST(prog_test_kfunc_list); | DEFINE_KFUNC_BTF_ID_LIST(prog_test_kfunc_list); | ||||||
|  | 
 | ||||||
|  | int bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id, | ||||||
|  | 			      const struct btf *targ_btf, __u32 targ_id) | ||||||
|  | { | ||||||
|  | 	return -EOPNOTSUPP; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool bpf_core_is_flavor_sep(const char *s) | ||||||
|  | { | ||||||
|  | 	/* check X___Y name pattern, where X and Y are not underscores */ | ||||||
|  | 	return s[0] != '_' &&				      /* X */ | ||||||
|  | 	       s[1] == '_' && s[2] == '_' && s[3] == '_' &&   /* ___ */ | ||||||
|  | 	       s[4] != '_';				      /* Y */ | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | size_t bpf_core_essential_name_len(const char *name) | ||||||
|  | { | ||||||
|  | 	size_t n = strlen(name); | ||||||
|  | 	int i; | ||||||
|  | 
 | ||||||
|  | 	for (i = n - 5; i >= 0; i--) { | ||||||
|  | 		if (bpf_core_is_flavor_sep(name + i)) | ||||||
|  | 			return i + 1; | ||||||
|  | 	} | ||||||
|  | 	return n; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -1,6 +1,60 @@ | ||||||
| // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
 | ||||||
| /* Copyright (c) 2019 Facebook */ | /* Copyright (c) 2019 Facebook */ | ||||||
| 
 | 
 | ||||||
|  | #ifdef __KERNEL__ | ||||||
|  | #include <linux/bpf.h> | ||||||
|  | #include <linux/btf.h> | ||||||
|  | #include <linux/string.h> | ||||||
|  | #include <linux/bpf_verifier.h> | ||||||
|  | #include "relo_core.h" | ||||||
|  | 
 | ||||||
|  | static const char *btf_kind_str(const struct btf_type *t) | ||||||
|  | { | ||||||
|  | 	return btf_type_str(t); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool is_ldimm64_insn(struct bpf_insn *insn) | ||||||
|  | { | ||||||
|  | 	return insn->code == (BPF_LD | BPF_IMM | BPF_DW); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct btf_type * | ||||||
|  | skip_mods_and_typedefs(const struct btf *btf, u32 id, u32 *res_id) | ||||||
|  | { | ||||||
|  | 	return btf_type_skip_modifiers(btf, id, res_id); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const char *btf__name_by_offset(const struct btf *btf, u32 offset) | ||||||
|  | { | ||||||
|  | 	return btf_name_by_offset(btf, offset); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static s64 btf__resolve_size(const struct btf *btf, u32 type_id) | ||||||
|  | { | ||||||
|  | 	const struct btf_type *t; | ||||||
|  | 	int size; | ||||||
|  | 
 | ||||||
|  | 	t = btf_type_by_id(btf, type_id); | ||||||
|  | 	t = btf_resolve_size(btf, t, &size); | ||||||
|  | 	if (IS_ERR(t)) | ||||||
|  | 		return PTR_ERR(t); | ||||||
|  | 	return size; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | enum libbpf_print_level { | ||||||
|  | 	LIBBPF_WARN, | ||||||
|  | 	LIBBPF_INFO, | ||||||
|  | 	LIBBPF_DEBUG, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #undef pr_warn | ||||||
|  | #undef pr_info | ||||||
|  | #undef pr_debug | ||||||
|  | #define pr_warn(fmt, log, ...)	bpf_log((void *)log, fmt, "", ##__VA_ARGS__) | ||||||
|  | #define pr_info(fmt, log, ...)	bpf_log((void *)log, fmt, "", ##__VA_ARGS__) | ||||||
|  | #define pr_debug(fmt, log, ...)	bpf_log((void *)log, fmt, "", ##__VA_ARGS__) | ||||||
|  | #define libbpf_print(level, fmt, ...)	bpf_log((void *)prog_name, fmt, ##__VA_ARGS__) | ||||||
|  | #else | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <errno.h> | #include <errno.h> | ||||||
|  | @ -12,8 +66,9 @@ | ||||||
| #include "btf.h" | #include "btf.h" | ||||||
| #include "str_error.h" | #include "str_error.h" | ||||||
| #include "libbpf_internal.h" | #include "libbpf_internal.h" | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| #define BPF_CORE_SPEC_MAX_LEN 64 | #define BPF_CORE_SPEC_MAX_LEN 32 | ||||||
| 
 | 
 | ||||||
| /* represents BPF CO-RE field or array element accessor */ | /* represents BPF CO-RE field or array element accessor */ | ||||||
| struct bpf_core_accessor { | struct bpf_core_accessor { | ||||||
|  | @ -150,7 +205,7 @@ static bool core_relo_is_enumval_based(enum bpf_core_relo_kind kind) | ||||||
|  * Enum value-based relocations (ENUMVAL_EXISTS/ENUMVAL_VALUE) use access |  * Enum value-based relocations (ENUMVAL_EXISTS/ENUMVAL_VALUE) use access | ||||||
|  * string to specify enumerator's value index that need to be relocated. |  * string to specify enumerator's value index that need to be relocated. | ||||||
|  */ |  */ | ||||||
| static int bpf_core_parse_spec(const struct btf *btf, | static int bpf_core_parse_spec(const char *prog_name, const struct btf *btf, | ||||||
| 			       __u32 type_id, | 			       __u32 type_id, | ||||||
| 			       const char *spec_str, | 			       const char *spec_str, | ||||||
| 			       enum bpf_core_relo_kind relo_kind, | 			       enum bpf_core_relo_kind relo_kind, | ||||||
|  | @ -272,8 +327,8 @@ static int bpf_core_parse_spec(const struct btf *btf, | ||||||
| 				return sz; | 				return sz; | ||||||
| 			spec->bit_offset += access_idx * sz * 8; | 			spec->bit_offset += access_idx * sz * 8; | ||||||
| 		} else { | 		} else { | ||||||
| 			pr_warn("relo for [%u] %s (at idx %d) captures type [%d] of unexpected kind %s\n", | 			pr_warn("prog '%s': relo for [%u] %s (at idx %d) captures type [%d] of unexpected kind %s\n", | ||||||
| 				type_id, spec_str, i, id, btf_kind_str(t)); | 				prog_name, type_id, spec_str, i, id, btf_kind_str(t)); | ||||||
| 			return -EINVAL; | 			return -EINVAL; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | @ -346,8 +401,6 @@ static int bpf_core_fields_are_compat(const struct btf *local_btf, | ||||||
| 		targ_id = btf_array(targ_type)->type; | 		targ_id = btf_array(targ_type)->type; | ||||||
| 		goto recur; | 		goto recur; | ||||||
| 	default: | 	default: | ||||||
| 		pr_warn("unexpected kind %d relocated, local [%d], target [%d]\n", |  | ||||||
| 			btf_kind(local_type), local_id, targ_id); |  | ||||||
| 		return 0; | 		return 0; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | @ -1045,7 +1098,7 @@ static int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn, | ||||||
|  * [<type-id>] (<type-name>) + <raw-spec> => <offset>@<spec>, |  * [<type-id>] (<type-name>) + <raw-spec> => <offset>@<spec>, | ||||||
|  * where <spec> is a C-syntax view of recorded field access, e.g.: x.a[3].b |  * where <spec> is a C-syntax view of recorded field access, e.g.: x.a[3].b | ||||||
|  */ |  */ | ||||||
| static void bpf_core_dump_spec(int level, const struct bpf_core_spec *spec) | static void bpf_core_dump_spec(const char *prog_name, int level, const struct bpf_core_spec *spec) | ||||||
| { | { | ||||||
| 	const struct btf_type *t; | 	const struct btf_type *t; | ||||||
| 	const struct btf_enum *e; | 	const struct btf_enum *e; | ||||||
|  | @ -1167,7 +1220,8 @@ int bpf_core_apply_relo_insn(const char *prog_name, struct bpf_insn *insn, | ||||||
| 	if (str_is_empty(spec_str)) | 	if (str_is_empty(spec_str)) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 
 | 
 | ||||||
| 	err = bpf_core_parse_spec(local_btf, local_id, spec_str, relo->kind, &local_spec); | 	err = bpf_core_parse_spec(prog_name, local_btf, local_id, spec_str, | ||||||
|  | 				  relo->kind, &local_spec); | ||||||
| 	if (err) { | 	if (err) { | ||||||
| 		pr_warn("prog '%s': relo #%d: parsing [%d] %s %s + %s failed: %d\n", | 		pr_warn("prog '%s': relo #%d: parsing [%d] %s %s + %s failed: %d\n", | ||||||
| 			prog_name, relo_idx, local_id, btf_kind_str(local_type), | 			prog_name, relo_idx, local_id, btf_kind_str(local_type), | ||||||
|  | @ -1178,7 +1232,7 @@ int bpf_core_apply_relo_insn(const char *prog_name, struct bpf_insn *insn, | ||||||
| 
 | 
 | ||||||
| 	pr_debug("prog '%s': relo #%d: kind <%s> (%d), spec is ", prog_name, | 	pr_debug("prog '%s': relo #%d: kind <%s> (%d), spec is ", prog_name, | ||||||
| 		 relo_idx, core_relo_kind_str(relo->kind), relo->kind); | 		 relo_idx, core_relo_kind_str(relo->kind), relo->kind); | ||||||
| 	bpf_core_dump_spec(LIBBPF_DEBUG, &local_spec); | 	bpf_core_dump_spec(prog_name, LIBBPF_DEBUG, &local_spec); | ||||||
| 	libbpf_print(LIBBPF_DEBUG, "\n"); | 	libbpf_print(LIBBPF_DEBUG, "\n"); | ||||||
| 
 | 
 | ||||||
| 	/* TYPE_ID_LOCAL relo is special and doesn't need candidate search */ | 	/* TYPE_ID_LOCAL relo is special and doesn't need candidate search */ | ||||||
|  | @ -1204,14 +1258,14 @@ int bpf_core_apply_relo_insn(const char *prog_name, struct bpf_insn *insn, | ||||||
| 		if (err < 0) { | 		if (err < 0) { | ||||||
| 			pr_warn("prog '%s': relo #%d: error matching candidate #%d ", | 			pr_warn("prog '%s': relo #%d: error matching candidate #%d ", | ||||||
| 				prog_name, relo_idx, i); | 				prog_name, relo_idx, i); | ||||||
| 			bpf_core_dump_spec(LIBBPF_WARN, &cand_spec); | 			bpf_core_dump_spec(prog_name, LIBBPF_WARN, &cand_spec); | ||||||
| 			libbpf_print(LIBBPF_WARN, ": %d\n", err); | 			libbpf_print(LIBBPF_WARN, ": %d\n", err); | ||||||
| 			return err; | 			return err; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		pr_debug("prog '%s': relo #%d: %s candidate #%d ", prog_name, | 		pr_debug("prog '%s': relo #%d: %s candidate #%d ", prog_name, | ||||||
| 			 relo_idx, err == 0 ? "non-matching" : "matching", i); | 			 relo_idx, err == 0 ? "non-matching" : "matching", i); | ||||||
| 		bpf_core_dump_spec(LIBBPF_DEBUG, &cand_spec); | 		bpf_core_dump_spec(prog_name, LIBBPF_DEBUG, &cand_spec); | ||||||
| 		libbpf_print(LIBBPF_DEBUG, "\n"); | 		libbpf_print(LIBBPF_DEBUG, "\n"); | ||||||
| 
 | 
 | ||||||
| 		if (err == 0) | 		if (err == 0) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Alexei Starovoitov
						Alexei Starovoitov