On Thu, Sep 3, 2020 at 3:34 PM Hao Luo haoluo@google.com wrote:
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: Hao Luo haoluo@google.com
Logic looks correct, but I have complaints about libbpf logging consistency, please see suggestions below.
tools/lib/bpf/libbpf.c | 116 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 102 insertions(+), 14 deletions(-)
[...]
@@ -3119,6 +3130,8 @@ static int bpf_object__collect_externs(struct bpf_object *obj) vt->type = int_btf_id; vs->offset = off; vs->size = sizeof(int);
pr_debug("ksym var_secinfo: var '%s', type #%d, size %d, offset %d\n",
ext->name, vt->type, vs->size, vs->offset);
debug leftover?
} sec->size = off; }
@@ -5724,8 +5737,13 @@ bpf_program__relocate(struct bpf_program *prog, struct bpf_object *obj) insn[0].imm = obj->maps[obj->kconfig_map_idx].fd; insn[1].imm = ext->kcfg.data_off; } else /* EXT_KSYM */ {
insn[0].imm = (__u32)ext->ksym.addr;
insn[1].imm = ext->ksym.addr >> 32;
if (ext->ksym.type_id) { /* typed ksyms */
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;
} } break; case RELO_CALL:
@@ -6462,10 +6480,72 @@ static int bpf_object__read_kallsyms_file(struct bpf_object *obj) return err; }
+static int bpf_object__resolve_ksyms_btf_id(struct bpf_object *obj) +{
struct extern_desc *ext;
int i, id;
if (!obj->btf_vmlinux) {
pr_warn("support of typed ksyms needs kernel btf.\n");
return -ENOENT;
}
This check shouldn't be needed, you'd either successfully load btf_vmlinux by now or will fail earlier, because BTF is required but not found.
for (i = 0; i < obj->nr_extern; i++) {
const struct btf_type *targ_var, *targ_type;
__u32 targ_type_id, local_type_id;
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("no btf entry for ksym '%s' in vmlinux.\n",
ext->name);
please try to stick to consistent style of comments:
"extern (ksym) '%s': failed to find BTF ID in vmlinux BTF" or something like that
return -ESRCH;
}
/* find target type_id */
targ_var = btf__type_by_id(obj->btf_vmlinux, id);
targ_type = skip_mods_and_typedefs(obj->btf_vmlinux,
targ_var->type,
&targ_type_id);
/* find local type_id */
local_type_id = ext->ksym.type_id;
ret = bpf_core_types_are_compat(obj->btf_vmlinux, targ_type_id,
obj->btf, local_type_id);
you reversed the order, it's always local btf/id, then target btf/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);
targ_name = btf__name_by_offset(obj->btf_vmlinux,
targ_type->name_off);
local_name = btf__name_by_offset(obj->btf,
local_type->name_off);
it's a bit unfortunate that we get the name of an already resolved type, because if you have a typedef to anon struct, this will give you an empty string. I don't know how much of a problem that would be, so I think it's fine to leave it as is, and fix it if it's a problem in practice.
pr_warn("ksym '%s' expects type '%s' (vmlinux_btf_id: #%d), "
"but got '%s' (btf_id: #%d)\n", ext->name,
targ_name, targ_type_id, local_name, local_type_id);
same thing, please stay consistent in logging format. Check bpf_core_dump_spec() for how BTF type info is usually emitted throughout libbpf:
"extern (ksym): incompatible types, expected [%d] %s %s, but kernel has [%d] %s %s\n"
there is a btf_kind_str() helper to resolve kind to a string representation.
return -EINVAL;
}
ext->is_set = true;
ext->ksym.vmlinux_btf_id = id;
pr_debug("extern (ksym) %s=vmlinux_btf_id(#%d)\n", ext->name, id);
"extern (ksym) '%s': resolved to [%d] %s %s\n", similar to above suggestion. This "[%d]" format is very consistently used for BTF IDs throughout, so it will be familiar and recognizable for people that had to deal with this in libbpf logs.
}
return 0;
+}
static int bpf_object__resolve_externs(struct bpf_object *obj, const char *extra_kconfig) {
bool need_config = false, need_kallsyms = false;
bool need_kallsyms = false, need_vmlinux_btf = false;
bool need_config = false;
nit: doesn't make sense to change the existing source code line at all. Just add `bool need_vmlinux_btf = false;` on a new line? Or we can split all these bools into 3 separate lines, if you prefer.
struct extern_desc *ext; void *kcfg_data = NULL; int err, i;
[...]