Commit d370bbe1 authored by Hao Luo's avatar Hao Luo Committed by Alexei Starovoitov

bpf/libbpf: BTF support for typed ksyms

If a ksym is defined with a type, libbpf will try to find the ksym's btf
information from kernel btf. If a valid btf entry for the ksym is found,
libbpf can pass in the found btf id to the verifier, which validates the
ksym's type and value.

Typeless ksyms (i.e. those defined as 'void') will not have such btf_id,
but it has the symbol's address (read from kallsyms) and its value is
treated as a raw pointer.
Signed-off-by: default avatarHao Luo <haoluo@google.com>
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
Acked-by: default avatarAndrii Nakryiko <andriin@fb.com>
Link: https://lore.kernel.org/bpf/20200929235049.2533242-3-haoluo@google.com
parent 4976b718
...@@ -390,6 +390,12 @@ struct extern_desc { ...@@ -390,6 +390,12 @@ struct extern_desc {
} kcfg; } kcfg;
struct { struct {
unsigned long long addr; unsigned long long addr;
/* target btf_id of the corresponding kernel var. */
int vmlinux_btf_id;
/* local btf_id of the ksym extern's type. */
__u32 type_id;
} ksym; } ksym;
}; };
}; };
...@@ -2522,12 +2528,23 @@ static int bpf_object__load_vmlinux_btf(struct bpf_object *obj) ...@@ -2522,12 +2528,23 @@ static int bpf_object__load_vmlinux_btf(struct bpf_object *obj)
{ {
bool need_vmlinux_btf = false; bool need_vmlinux_btf = false;
struct bpf_program *prog; struct bpf_program *prog;
int err; int i, err;
/* CO-RE relocations need kernel BTF */ /* CO-RE relocations need kernel BTF */
if (obj->btf_ext && obj->btf_ext->core_relo_info.len) if (obj->btf_ext && obj->btf_ext->core_relo_info.len)
need_vmlinux_btf = true; need_vmlinux_btf = true;
/* Support for typed ksyms needs kernel BTF */
for (i = 0; i < obj->nr_extern; i++) {
const struct extern_desc *ext;
ext = &obj->externs[i];
if (ext->type == EXT_KSYM && ext->ksym.type_id) {
need_vmlinux_btf = true;
break;
}
}
bpf_object__for_each_program(prog, obj) { bpf_object__for_each_program(prog, obj) {
if (!prog->load) if (!prog->load)
continue; continue;
...@@ -3156,16 +3173,10 @@ static int bpf_object__collect_externs(struct bpf_object *obj) ...@@ -3156,16 +3173,10 @@ static int bpf_object__collect_externs(struct bpf_object *obj)
return -ENOTSUP; return -ENOTSUP;
} }
} else if (strcmp(sec_name, KSYMS_SEC) == 0) { } else if (strcmp(sec_name, KSYMS_SEC) == 0) {
const struct btf_type *vt;
ksym_sec = sec; ksym_sec = sec;
ext->type = EXT_KSYM; ext->type = EXT_KSYM;
skip_mods_and_typedefs(obj->btf, t->type,
vt = skip_mods_and_typedefs(obj->btf, t->type, NULL); &ext->ksym.type_id);
if (!btf_is_void(vt)) {
pr_warn("extern (ksym) '%s' is not typeless (void)\n", ext_name);
return -ENOTSUP;
}
} else { } else {
pr_warn("unrecognized extern section '%s'\n", sec_name); pr_warn("unrecognized extern section '%s'\n", sec_name);
return -ENOTSUP; return -ENOTSUP;
...@@ -5800,8 +5811,13 @@ bpf_object__relocate_data(struct bpf_object *obj, struct bpf_program *prog) ...@@ -5800,8 +5811,13 @@ bpf_object__relocate_data(struct bpf_object *obj, struct bpf_program *prog)
insn[0].imm = obj->maps[obj->kconfig_map_idx].fd; insn[0].imm = obj->maps[obj->kconfig_map_idx].fd;
insn[1].imm = ext->kcfg.data_off; insn[1].imm = ext->kcfg.data_off;
} else /* EXT_KSYM */ { } else /* EXT_KSYM */ {
insn[0].imm = (__u32)ext->ksym.addr; if (ext->ksym.type_id) { /* typed ksyms */
insn[1].imm = ext->ksym.addr >> 32; insn[0].src_reg = BPF_PSEUDO_BTF_ID;
insn[0].imm = ext->ksym.vmlinux_btf_id;
} else { /* typeless ksyms */
insn[0].imm = (__u32)ext->ksym.addr;
insn[1].imm = ext->ksym.addr >> 32;
}
} }
relo->processed = true; relo->processed = true;
break; break;
...@@ -6933,10 +6949,72 @@ static int bpf_object__read_kallsyms_file(struct bpf_object *obj) ...@@ -6933,10 +6949,72 @@ static int bpf_object__read_kallsyms_file(struct bpf_object *obj)
return err; return err;
} }
static int bpf_object__resolve_ksyms_btf_id(struct bpf_object *obj)
{
struct extern_desc *ext;
int i, id;
for (i = 0; i < obj->nr_extern; i++) {
const struct btf_type *targ_var, *targ_type;
__u32 targ_type_id, local_type_id;
const char *targ_var_name;
int ret;
ext = &obj->externs[i];
if (ext->type != EXT_KSYM || !ext->ksym.type_id)
continue;
id = btf__find_by_name_kind(obj->btf_vmlinux, ext->name,
BTF_KIND_VAR);
if (id <= 0) {
pr_warn("extern (ksym) '%s': failed to find BTF ID in vmlinux BTF.\n",
ext->name);
return -ESRCH;
}
/* find local type_id */
local_type_id = ext->ksym.type_id;
/* find target type_id */
targ_var = btf__type_by_id(obj->btf_vmlinux, id);
targ_var_name = btf__name_by_offset(obj->btf_vmlinux,
targ_var->name_off);
targ_type = skip_mods_and_typedefs(obj->btf_vmlinux,
targ_var->type,
&targ_type_id);
ret = bpf_core_types_are_compat(obj->btf, local_type_id,
obj->btf_vmlinux, targ_type_id);
if (ret <= 0) {
const struct btf_type *local_type;
const char *targ_name, *local_name;
local_type = btf__type_by_id(obj->btf, local_type_id);
local_name = btf__name_by_offset(obj->btf,
local_type->name_off);
targ_name = btf__name_by_offset(obj->btf_vmlinux,
targ_type->name_off);
pr_warn("extern (ksym) '%s': incompatible types, expected [%d] %s %s, but kernel has [%d] %s %s\n",
ext->name, local_type_id,
btf_kind_str(local_type), local_name, targ_type_id,
btf_kind_str(targ_type), targ_name);
return -EINVAL;
}
ext->is_set = true;
ext->ksym.vmlinux_btf_id = id;
pr_debug("extern (ksym) '%s': resolved to [%d] %s %s\n",
ext->name, id, btf_kind_str(targ_var), targ_var_name);
}
return 0;
}
static int bpf_object__resolve_externs(struct bpf_object *obj, static int bpf_object__resolve_externs(struct bpf_object *obj,
const char *extra_kconfig) const char *extra_kconfig)
{ {
bool need_config = false, need_kallsyms = false; bool need_config = false, need_kallsyms = false;
bool need_vmlinux_btf = false;
struct extern_desc *ext; struct extern_desc *ext;
void *kcfg_data = NULL; void *kcfg_data = NULL;
int err, i; int err, i;
...@@ -6967,7 +7045,10 @@ static int bpf_object__resolve_externs(struct bpf_object *obj, ...@@ -6967,7 +7045,10 @@ static int bpf_object__resolve_externs(struct bpf_object *obj,
strncmp(ext->name, "CONFIG_", 7) == 0) { strncmp(ext->name, "CONFIG_", 7) == 0) {
need_config = true; need_config = true;
} else if (ext->type == EXT_KSYM) { } else if (ext->type == EXT_KSYM) {
need_kallsyms = true; if (ext->ksym.type_id)
need_vmlinux_btf = true;
else
need_kallsyms = true;
} else { } else {
pr_warn("unrecognized extern '%s'\n", ext->name); pr_warn("unrecognized extern '%s'\n", ext->name);
return -EINVAL; return -EINVAL;
...@@ -6996,6 +7077,11 @@ static int bpf_object__resolve_externs(struct bpf_object *obj, ...@@ -6996,6 +7077,11 @@ static int bpf_object__resolve_externs(struct bpf_object *obj,
if (err) if (err)
return -EINVAL; return -EINVAL;
} }
if (need_vmlinux_btf) {
err = bpf_object__resolve_ksyms_btf_id(obj);
if (err)
return -EINVAL;
}
for (i = 0; i < obj->nr_extern; i++) { for (i = 0; i < obj->nr_extern; i++) {
ext = &obj->externs[i]; ext = &obj->externs[i];
...@@ -7028,10 +7114,10 @@ int bpf_object__load_xattr(struct bpf_object_load_attr *attr) ...@@ -7028,10 +7114,10 @@ int bpf_object__load_xattr(struct bpf_object_load_attr *attr)
} }
err = bpf_object__probe_loading(obj); err = bpf_object__probe_loading(obj);
err = err ? : bpf_object__load_vmlinux_btf(obj);
err = err ? : bpf_object__resolve_externs(obj, obj->kconfig); err = err ? : bpf_object__resolve_externs(obj, obj->kconfig);
err = err ? : bpf_object__sanitize_and_load_btf(obj); err = err ? : bpf_object__sanitize_and_load_btf(obj);
err = err ? : bpf_object__sanitize_maps(obj); err = err ? : bpf_object__sanitize_maps(obj);
err = err ? : bpf_object__load_vmlinux_btf(obj);
err = err ? : bpf_object__init_kern_struct_ops_maps(obj); err = err ? : bpf_object__init_kern_struct_ops_maps(obj);
err = err ? : bpf_object__create_maps(obj); err = err ? : bpf_object__create_maps(obj);
err = err ? : bpf_object__relocate(obj, attr->target_btf_path); err = err ? : bpf_object__relocate(obj, attr->target_btf_path);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment