Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

libbpf: Implement enum value-based CO-RE relocations

Implement two relocations of a new enumerator value-based CO-RE relocation
kind: ENUMVAL_EXISTS and ENUMVAL_VALUE.

First, ENUMVAL_EXISTS, allows to detect the presence of a named enumerator
value in the target (kernel) BTF. This is useful to do BPF helper/map/program
type support detection from BPF program side. bpf_core_enum_value_exists()
macro helper is provided to simplify built-in usage.

Second, ENUMVAL_VALUE, allows to capture enumerator integer value and relocate
it according to the target BTF, if it changes. This is useful to have
a guarantee against intentional or accidental re-ordering/re-numbering of some
of the internal (non-UAPI) enumerations, where kernel developers don't care
about UAPI backwards compatiblity concerns. bpf_core_enum_value() allows to
capture this succinctly and use correct enum values in code.

LLVM uses ldimm64 instruction to capture enumerator value-based relocations,
so add support for ldimm64 instruction patching as well.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Yonghong Song <yhs@fb.com>
Link: https://lore.kernel.org/bpf/20200819194519.3375898-5-andriin@fb.com

authored by

Andrii Nakryiko and committed by
Alexei Starovoitov
eacaaed7 4836bf5e

+170 -5
+28
tools/lib/bpf/bpf_core_read.h
··· 31 31 BPF_TYPE_SIZE = 1, /* type size in target kernel */ 32 32 }; 33 33 34 + /* second argument to __builtin_preserve_enum_value() built-in */ 35 + enum bpf_enum_value_kind { 36 + BPF_ENUMVAL_EXISTS = 0, /* enum value existence in kernel */ 37 + BPF_ENUMVAL_VALUE = 1, /* enum value value relocation */ 38 + }; 39 + 34 40 #define __CORE_RELO(src, field, info) \ 35 41 __builtin_preserve_field_info((src)->field, BPF_FIELD_##info) 36 42 ··· 155 149 */ 156 150 #define bpf_core_type_size(type) \ 157 151 __builtin_preserve_type_info(*(typeof(type) *)0, BPF_TYPE_SIZE) 152 + 153 + /* 154 + * Convenience macro to check that provided enumerator value is defined in 155 + * a target kernel. 156 + * Returns: 157 + * 1, if specified enum type and its enumerator value are present in target 158 + * kernel's BTF; 159 + * 0, if no matching enum and/or enum value within that enum is found. 160 + */ 161 + #define bpf_core_enum_value_exists(enum_type, enum_value) \ 162 + __builtin_preserve_enum_value(*(typeof(enum_type) *)enum_value, BPF_ENUMVAL_EXISTS) 163 + 164 + /* 165 + * Convenience macro to get the integer value of an enumerator value in 166 + * a target kernel. 167 + * Returns: 168 + * 64-bit value, if specified enum type and its enumerator value are 169 + * present in target kernel's BTF; 170 + * 0, if no matching enum and/or enum value within that enum is found. 171 + */ 172 + #define bpf_core_enum_value(enum_type, enum_value) \ 173 + __builtin_preserve_enum_value(*(typeof(enum_type) *)enum_value, BPF_ENUMVAL_VALUE) 158 174 159 175 /* 160 176 * bpf_core_read() abstracts away bpf_probe_read_kernel() call and captures
+140 -5
tools/lib/bpf/libbpf.c
··· 4115 4115 case BPF_TYPE_ID_TARGET: return "target_type_id"; 4116 4116 case BPF_TYPE_EXISTS: return "type_exists"; 4117 4117 case BPF_TYPE_SIZE: return "type_size"; 4118 + case BPF_ENUMVAL_EXISTS: return "enumval_exists"; 4119 + case BPF_ENUMVAL_VALUE: return "enumval_value"; 4118 4120 default: return "unknown"; 4119 4121 } 4120 4122 } ··· 4143 4141 case BPF_TYPE_ID_TARGET: 4144 4142 case BPF_TYPE_EXISTS: 4145 4143 case BPF_TYPE_SIZE: 4144 + return true; 4145 + default: 4146 + return false; 4147 + } 4148 + } 4149 + 4150 + static bool core_relo_is_enumval_based(enum bpf_core_relo_kind kind) 4151 + { 4152 + switch (kind) { 4153 + case BPF_ENUMVAL_EXISTS: 4154 + case BPF_ENUMVAL_VALUE: 4146 4155 return true; 4147 4156 default: 4148 4157 return false; ··· 4193 4180 * Type-based relocations (TYPE_EXISTS/TYPE_SIZE, 4194 4181 * TYPE_ID_LOCAL/TYPE_ID_TARGET) don't capture any field information. Their 4195 4182 * spec and raw_spec are kept empty. 4183 + * 4184 + * Enum value-based relocations (ENUMVAL_EXISTS/ENUMVAL_VALUE) use access 4185 + * string to specify enumerator's value index that need to be relocated. 4196 4186 */ 4197 4187 static int bpf_core_parse_spec(const struct btf *btf, 4198 4188 __u32 type_id, ··· 4240 4224 if (spec->raw_len == 0) 4241 4225 return -EINVAL; 4242 4226 4243 - /* first spec value is always reloc type array index */ 4244 4227 t = skip_mods_and_typedefs(btf, type_id, &id); 4245 4228 if (!t) 4246 4229 return -EINVAL; 4247 4230 4248 4231 access_idx = spec->raw_spec[0]; 4249 - spec->spec[0].type_id = id; 4250 - spec->spec[0].idx = access_idx; 4232 + acc = &spec->spec[0]; 4233 + acc->type_id = id; 4234 + acc->idx = access_idx; 4251 4235 spec->len++; 4236 + 4237 + if (core_relo_is_enumval_based(relo_kind)) { 4238 + if (!btf_is_enum(t) || spec->raw_len > 1 || access_idx >= btf_vlen(t)) 4239 + return -EINVAL; 4240 + 4241 + /* record enumerator name in a first accessor */ 4242 + acc->name = btf__name_by_offset(btf, btf_enum(t)[access_idx].name_off); 4243 + return 0; 4244 + } 4252 4245 4253 4246 if (!core_relo_is_field_based(relo_kind)) 4254 4247 return -EINVAL; ··· 4701 4676 local_acc = &local_spec->spec[0]; 4702 4677 targ_acc = &targ_spec->spec[0]; 4703 4678 4679 + if (core_relo_is_enumval_based(local_spec->relo_kind)) { 4680 + size_t local_essent_len, targ_essent_len; 4681 + const struct btf_enum *e; 4682 + const char *targ_name; 4683 + 4684 + /* has to resolve to an enum */ 4685 + targ_type = skip_mods_and_typedefs(targ_spec->btf, targ_id, &targ_id); 4686 + if (!btf_is_enum(targ_type)) 4687 + return 0; 4688 + 4689 + local_essent_len = bpf_core_essential_name_len(local_acc->name); 4690 + 4691 + for (i = 0, e = btf_enum(targ_type); i < btf_vlen(targ_type); i++, e++) { 4692 + targ_name = btf__name_by_offset(targ_spec->btf, e->name_off); 4693 + targ_essent_len = bpf_core_essential_name_len(targ_name); 4694 + if (targ_essent_len != local_essent_len) 4695 + continue; 4696 + if (strncmp(local_acc->name, targ_name, local_essent_len) == 0) { 4697 + targ_acc->type_id = targ_id; 4698 + targ_acc->idx = i; 4699 + targ_acc->name = targ_name; 4700 + targ_spec->len++; 4701 + targ_spec->raw_spec[targ_spec->raw_len] = targ_acc->idx; 4702 + targ_spec->raw_len++; 4703 + return 1; 4704 + } 4705 + } 4706 + return 0; 4707 + } 4708 + 4709 + if (!core_relo_is_field_based(local_spec->relo_kind)) 4710 + return -EINVAL; 4711 + 4704 4712 for (i = 0; i < local_spec->len; i++, local_acc++, targ_acc++) { 4705 4713 targ_type = skip_mods_and_typedefs(targ_spec->btf, targ_id, 4706 4714 &targ_id); ··· 4938 4880 return 0; 4939 4881 } 4940 4882 4883 + static int bpf_core_calc_enumval_relo(const struct bpf_core_relo *relo, 4884 + const struct bpf_core_spec *spec, 4885 + __u32 *val) 4886 + { 4887 + const struct btf_type *t; 4888 + const struct btf_enum *e; 4889 + 4890 + switch (relo->kind) { 4891 + case BPF_ENUMVAL_EXISTS: 4892 + *val = spec ? 1 : 0; 4893 + break; 4894 + case BPF_ENUMVAL_VALUE: 4895 + if (!spec) 4896 + return -EUCLEAN; /* request instruction poisoning */ 4897 + t = btf__type_by_id(spec->btf, spec->spec[0].type_id); 4898 + e = btf_enum(t) + spec->spec[0].idx; 4899 + *val = e->val; 4900 + break; 4901 + default: 4902 + return -EOPNOTSUPP; 4903 + } 4904 + 4905 + return 0; 4906 + } 4907 + 4941 4908 struct bpf_core_relo_res 4942 4909 { 4943 4910 /* expected value in the instruction, unless validate == false */ ··· 5001 4918 } else if (core_relo_is_type_based(relo->kind)) { 5002 4919 err = bpf_core_calc_type_relo(relo, local_spec, &res->orig_val); 5003 4920 err = err ?: bpf_core_calc_type_relo(relo, targ_spec, &res->new_val); 4921 + } else if (core_relo_is_enumval_based(relo->kind)) { 4922 + err = bpf_core_calc_enumval_relo(relo, local_spec, &res->orig_val); 4923 + err = err ?: bpf_core_calc_enumval_relo(relo, targ_spec, &res->new_val); 5004 4924 } 5005 4925 5006 4926 if (err == -EUCLEAN) { ··· 5040 4954 insn->imm = 195896080; /* => 0xbad2310 => "bad relo" */ 5041 4955 } 5042 4956 4957 + static bool is_ldimm64(struct bpf_insn *insn) 4958 + { 4959 + return insn->code == (BPF_LD | BPF_IMM | BPF_DW); 4960 + } 4961 + 5043 4962 /* 5044 4963 * Patch relocatable BPF instruction. 5045 4964 * ··· 5057 4966 * Currently three kinds of BPF instructions are supported: 5058 4967 * 1. rX = <imm> (assignment with immediate operand); 5059 4968 * 2. rX += <imm> (arithmetic operations with immediate operand); 4969 + * 3. rX = <imm64> (load with 64-bit immediate value). 5060 4970 */ 5061 4971 static int bpf_core_patch_insn(struct bpf_program *prog, 5062 4972 const struct bpf_core_relo *relo, ··· 5076 4984 class = BPF_CLASS(insn->code); 5077 4985 5078 4986 if (res->poison) { 4987 + /* poison second part of ldimm64 to avoid confusing error from 4988 + * verifier about "unknown opcode 00" 4989 + */ 4990 + if (is_ldimm64(insn)) 4991 + bpf_core_poison_insn(prog, relo_idx, insn_idx + 1, insn + 1); 5079 4992 bpf_core_poison_insn(prog, relo_idx, insn_idx, insn); 5080 4993 return 0; 5081 4994 } ··· 5109 5012 case BPF_ST: 5110 5013 case BPF_STX: 5111 5014 if (res->validate && insn->off != orig_val) { 5112 - pr_warn("prog '%s': relo #%d: unexpected insn #%d (LD/LDX/ST/STX) value: got %u, exp %u -> %u\n", 5015 + pr_warn("prog '%s': relo #%d: unexpected insn #%d (LDX/ST/STX) value: got %u, exp %u -> %u\n", 5113 5016 bpf_program__title(prog, false), relo_idx, 5114 5017 insn_idx, insn->off, orig_val, new_val); 5115 5018 return -EINVAL; ··· 5126 5029 bpf_program__title(prog, false), relo_idx, insn_idx, 5127 5030 orig_val, new_val); 5128 5031 break; 5032 + case BPF_LD: { 5033 + __u64 imm; 5034 + 5035 + if (!is_ldimm64(insn) || 5036 + insn[0].src_reg != 0 || insn[0].off != 0 || 5037 + insn_idx + 1 >= prog->insns_cnt || 5038 + insn[1].code != 0 || insn[1].dst_reg != 0 || 5039 + insn[1].src_reg != 0 || insn[1].off != 0) { 5040 + pr_warn("prog '%s': relo #%d: insn #%d (LDIMM64) has unexpected form\n", 5041 + bpf_program__title(prog, false), relo_idx, insn_idx); 5042 + return -EINVAL; 5043 + } 5044 + 5045 + imm = insn[0].imm + ((__u64)insn[1].imm << 32); 5046 + if (res->validate && imm != orig_val) { 5047 + pr_warn("prog '%s': relo #%d: unexpected insn #%d (LDIMM64) value: got %llu, exp %u -> %u\n", 5048 + bpf_program__title(prog, false), relo_idx, 5049 + insn_idx, imm, orig_val, new_val); 5050 + return -EINVAL; 5051 + } 5052 + 5053 + insn[0].imm = new_val; 5054 + insn[1].imm = 0; /* currently only 32-bit values are supported */ 5055 + pr_debug("prog '%s': relo #%d: patched insn #%d (LDIMM64) imm64 %llu -> %u\n", 5056 + bpf_program__title(prog, false), relo_idx, insn_idx, 5057 + imm, new_val); 5058 + break; 5059 + } 5129 5060 default: 5130 - pr_warn("prog '%s': relo #%d: trying to relocate unrecognized insn #%d, code:%x, src:%x, dst:%x, off:%x, imm:%x\n", 5061 + pr_warn("prog '%s': relo #%d: trying to relocate unrecognized insn #%d, code:0x%x, src:0x%x, dst:0x%x, off:0x%x, imm:0x%x\n", 5131 5062 bpf_program__title(prog, false), relo_idx, 5132 5063 insn_idx, insn->code, insn->src_reg, insn->dst_reg, 5133 5064 insn->off, insn->imm); ··· 5172 5047 static void bpf_core_dump_spec(int level, const struct bpf_core_spec *spec) 5173 5048 { 5174 5049 const struct btf_type *t; 5050 + const struct btf_enum *e; 5175 5051 const char *s; 5176 5052 __u32 type_id; 5177 5053 int i; ··· 5185 5059 5186 5060 if (core_relo_is_type_based(spec->relo_kind)) 5187 5061 return; 5062 + 5063 + if (core_relo_is_enumval_based(spec->relo_kind)) { 5064 + t = skip_mods_and_typedefs(spec->btf, type_id, NULL); 5065 + e = btf_enum(t) + spec->raw_spec[0]; 5066 + s = btf__name_by_offset(spec->btf, e->name_off); 5067 + 5068 + libbpf_print(level, "::%s = %u", s, e->val); 5069 + return; 5070 + } 5188 5071 5189 5072 if (core_relo_is_field_based(spec->relo_kind)) { 5190 5073 for (i = 0; i < spec->len; i++) {
+2
tools/lib/bpf/libbpf_internal.h
··· 242 242 BPF_TYPE_ID_TARGET = 7, /* type ID in target kernel */ 243 243 BPF_TYPE_EXISTS = 8, /* type existence in target kernel */ 244 244 BPF_TYPE_SIZE = 9, /* type size in bytes */ 245 + BPF_ENUMVAL_EXISTS = 10, /* enum value existence in target kernel */ 246 + BPF_ENUMVAL_VALUE = 11, /* enum value integer value */ 245 247 }; 246 248 247 249 /* The minimum bpf_core_relo checked by the loader