Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 137 additions & 10 deletions kernel/bpf/btf.c
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ struct btf {
void *nohdr_data;
struct btf_header hdr;
u32 nr_types; /* includes VOID for base BTF */
u32 nr_sorted_types; /* exclude VOID for base BTF */
u32 types_size;
u32 data_size;
refcount_t refcnt;
Expand Down Expand Up @@ -494,6 +495,11 @@ static bool btf_type_is_modifier(const struct btf_type *t)
return false;
}

static int btf_start_id(const struct btf *btf)
{
return btf->start_id + (btf->base_btf ? 0 : 1);
}

bool btf_type_is_void(const struct btf_type *t)
{
return t == &btf_void;
Expand Down Expand Up @@ -544,24 +550,138 @@ u32 btf_nr_types(const struct btf *btf)
return total;
}

s32 btf_find_by_name_kind(const struct btf *btf, const char *name, u8 kind)
/* Anonymous types (with empty names) are considered greater than named types
* and are sorted after them. Two anonymous types are considered equal. Named
* types are compared lexicographically.
*/
static int btf_compare_type_names(const void *a, const void *b, void *priv)
{
struct btf *btf = (struct btf *)priv;
const struct btf_type *ta = btf_type_by_id(btf, *(__u32 *)a);
const struct btf_type *tb = btf_type_by_id(btf, *(__u32 *)b);
const char *na, *nb;

if (!ta->name_off && tb->name_off)
return 1;
if (ta->name_off && !tb->name_off)
return -1;
if (!ta->name_off && !tb->name_off)
return 0;

na = btf_name_by_offset(btf, ta->name_off);
nb = btf_name_by_offset(btf, tb->name_off);
return strcmp(na, nb);
}

/* Verifies that BTF types are sorted in ascending order according to their
* names, with named types appearing before anonymous types. If the ordering
* is correct, counts the number of named types and updates the BTF object's
* nr_sorted_types field. Note that vmlinux and kernel module BTFs are sorted
* during the building phase, so the validation logic only needs to count the
* named types.
*/
static void btf_check_sorted(struct btf *btf)
{
const struct btf_type *t;
const char *tname;
u32 i, total;
int i, n, k = 0, nr_sorted_types;
bool skip_cmp = btf_is_kernel(btf);

if (btf->nr_types < 2)
return;

nr_sorted_types = 0;
n = btf_nr_types(btf) - 1;
for (i = btf_start_id(btf); i < n; i++) {
k = i + 1;
if (!skip_cmp && btf_compare_type_names(&i, &k, btf) > 0)
return;

total = btf_nr_types(btf);
for (i = 1; i < total; i++) {
t = btf_type_by_id(btf, i);
if (BTF_INFO_KIND(t->info) != kind)
continue;
if (t->name_off)
nr_sorted_types++;
else if (skip_cmp)
break;
}

t = btf_type_by_id(btf, k);
if (t->name_off)
nr_sorted_types++;
if (nr_sorted_types)
btf->nr_sorted_types = nr_sorted_types;
}

static s32 btf_find_by_name_kind_bsearch(const struct btf *btf, const char *name,
s32 start_id, s32 end_id)
{
const struct btf_type *t;
const char *tname;
s32 l, r, m;

l = start_id;
r = end_id;
while (l <= r) {
m = l + (r - l) / 2;
t = btf_type_by_id(btf, m);
tname = btf_name_by_offset(btf, t->name_off);
if (!strcmp(tname, name))
return i;
if (strcmp(tname, name) >= 0) {
if (l == r)
return r;
r = m;
} else {
l = m + 1;
}
}

return -ENOENT;
return btf_nr_types(btf);
}

s32 btf_find_by_name_kind(const struct btf *btf, const char *name, u8 kind)
{
const struct btf *base_btf = btf_base_btf(btf);
const struct btf_type *t;
const char *tname;
int err = -ENOENT;

if (base_btf) {
err = btf_find_by_name_kind(base_btf, name, kind);
if (err > 0)
goto out;
}

if (btf->nr_sorted_types > 0) {
/* binary search */
s32 start_id, end_id;
u32 idx;

start_id = btf_start_id(btf);
end_id = start_id + btf->nr_sorted_types - 1;
idx = btf_find_by_name_kind_bsearch(btf, name, start_id, end_id);
for (; idx <= end_id; idx++) {
t = btf_type_by_id(btf, idx);
tname = btf_name_by_offset(btf, t->name_off);
if (strcmp(tname, name))
goto out;
if (BTF_INFO_KIND(t->info) == kind)
return idx;
}
} else {
/* linear search */
u32 i, total;

total = btf_nr_types(btf);
for (i = btf_start_id(btf); i < total; i++) {
t = btf_type_by_id(btf, i);
if (BTF_INFO_KIND(t->info) != kind)
continue;

tname = btf_name_by_offset(btf, t->name_off);
if (!strcmp(tname, name))
return i;
}
}

out:
return err;
}

s32 bpf_find_btf_id(const char *name, u32 kind, struct btf **btf_p)
Expand Down Expand Up @@ -5791,6 +5911,7 @@ static struct btf *btf_parse(const union bpf_attr *attr, bpfptr_t uattr, u32 uat
goto errout;
}
env->btf = btf;
btf->nr_sorted_types = 0;

data = kvmalloc(attr->btf_size, GFP_KERNEL | __GFP_NOWARN);
if (!data) {
Expand Down Expand Up @@ -5824,6 +5945,8 @@ static struct btf *btf_parse(const union bpf_attr *attr, bpfptr_t uattr, u32 uat
if (err)
goto errout;

btf_check_sorted(btf);

struct_meta_tab = btf_parse_struct_metas(&env->log, btf);
if (IS_ERR(struct_meta_tab)) {
err = PTR_ERR(struct_meta_tab);
Expand Down Expand Up @@ -6210,6 +6333,7 @@ static struct btf *btf_parse_base(struct btf_verifier_env *env, const char *name
btf->data = data;
btf->data_size = data_size;
btf->kernel_btf = true;
btf->nr_sorted_types = 0;
snprintf(btf->name, sizeof(btf->name), "%s", name);

err = btf_parse_hdr(env);
Expand All @@ -6230,6 +6354,7 @@ static struct btf *btf_parse_base(struct btf_verifier_env *env, const char *name
if (err)
goto errout;

btf_check_sorted(btf);
refcount_set(&btf->refcnt, 1);

return btf;
Expand Down Expand Up @@ -6327,6 +6452,7 @@ static struct btf *btf_parse_module(const char *module_name, const void *data,
btf->start_id = base_btf->nr_types;
btf->start_str_off = base_btf->hdr.str_len;
btf->kernel_btf = true;
btf->nr_sorted_types = 0;
snprintf(btf->name, sizeof(btf->name), "%s", module_name);

btf->data = kvmemdup(data, data_size, GFP_KERNEL | __GFP_NOWARN);
Expand Down Expand Up @@ -6363,6 +6489,7 @@ static struct btf *btf_parse_module(const char *module_name, const void *data,
}

btf_verifier_env_free(env);
btf_check_sorted(btf);
refcount_set(&btf->refcnt, 1);
return btf;

Expand Down
2 changes: 2 additions & 0 deletions scripts/Makefile.btf
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pahole-flags-$(call test-ge, $(pahole-ver), 130) += --btf_features=attributes

ifneq ($(KBUILD_EXTMOD),)
module-pahole-flags-$(call test-ge, $(pahole-ver), 128) += --btf_features=distilled_base
module-resolve_btfid-flags-y = --distilled_base
endif

endif
Expand All @@ -35,3 +36,4 @@ pahole-flags-$(CONFIG_PAHOLE_HAS_LANG_EXCLUDE) += --lang_exclude=rust

export PAHOLE_FLAGS := $(pahole-flags-y)
export MODULE_PAHOLE_FLAGS := $(module-pahole-flags-y)
export MODULE_RESOLVE_BTFID_FLAGS := $(module-resolve_btfid-flags-y)
1 change: 1 addition & 0 deletions scripts/Makefile.modfinal
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ quiet_cmd_btf_ko = BTF [M] $@
printf "Skipping BTF generation for %s due to unavailability of vmlinux\n" $@ 1>&2; \
else \
LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J $(PAHOLE_FLAGS) $(MODULE_PAHOLE_FLAGS) --btf_base $(objtree)/vmlinux $@; \
$(RESOLVE_BTFIDS) -b $(objtree)/vmlinux $(MODULE_RESOLVE_BTFID_FLAGS) --btf_sort $@; \
$(RESOLVE_BTFIDS) -b $(objtree)/vmlinux $@; \
fi;

Expand Down
1 change: 1 addition & 0 deletions scripts/link-vmlinux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ if is_enabled CONFIG_DEBUG_INFO_BTF; then
if is_enabled CONFIG_WERROR; then
RESOLVE_BTFIDS_ARGS=" --fatal_warnings "
fi
${RESOLVE_BTFIDS} ${RESOLVE_BTFIDS_ARGS} --btf_sort "${VMLINUX}"
${RESOLVE_BTFIDS} ${RESOLVE_BTFIDS_ARGS} "${VMLINUX}"
fi

Expand Down
Loading
Loading