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
10 changes: 10 additions & 0 deletions include/uapi/linux/btf.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@
#define BTF_MAGIC 0xeB9F
#define BTF_VERSION 1

/* kind layout section consists of a struct btf_kind_layout for each known
* kind at BTF encoding time.
*/
struct btf_kind_layout {
__u8 info_sz; /* size of singular element after btf_type */
__u8 elem_sz; /* size of each of btf_vlen(t) elements */
};

struct btf_header {
__u16 magic;
__u8 version;
Expand All @@ -19,6 +27,8 @@ struct btf_header {
__u32 type_len; /* length of type section */
__u32 str_off; /* offset of string section */
__u32 str_len; /* length of string section */
__u32 kind_layout_off;/* offset of kind layout section */
__u32 kind_layout_len;/* length of kind layout section */
};

/* Max # of type identifier */
Expand Down
96 changes: 76 additions & 20 deletions kernel/bpf/btf.c
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ struct btf {
struct btf_id_dtor_kfunc_tab *dtor_kfunc_tab;
struct btf_struct_metas *struct_meta_tab;
struct btf_struct_ops_tab *struct_ops_tab;
struct btf_kind_layout *kind_layout;

/* split BTF support */
struct btf *base_btf;
Expand Down Expand Up @@ -5215,23 +5216,36 @@ static s32 btf_check_meta(struct btf_verifier_env *env,
return -EINVAL;
}

if (BTF_INFO_KIND(t->info) > BTF_KIND_MAX ||
BTF_INFO_KIND(t->info) == BTF_KIND_UNKN) {
if (!btf_name_offset_valid(env->btf, t->name_off)) {
btf_verifier_log(env, "[%u] Invalid name_offset:%u",
env->log_type_id, t->name_off);
return -EINVAL;
}

if (BTF_INFO_KIND(t->info) == BTF_KIND_UNKN) {
btf_verifier_log(env, "[%u] Invalid kind:%u",
env->log_type_id, BTF_INFO_KIND(t->info));
return -EINVAL;
}

if (!btf_name_offset_valid(env->btf, t->name_off)) {
btf_verifier_log(env, "[%u] Invalid name_offset:%u",
env->log_type_id, t->name_off);
if (BTF_INFO_KIND(t->info) > BTF_KIND_MAX && env->btf->kind_layout &&
(BTF_INFO_KIND(t->info) * sizeof(struct btf_kind_layout)) <
env->btf->hdr.kind_layout_len) {
btf_verifier_log(env, "[%u] unknown but required kind %u",
env->log_type_id,
BTF_INFO_KIND(t->info));
return -EINVAL;
} else {
if (BTF_INFO_KIND(t->info) > BTF_KIND_MAX) {
btf_verifier_log(env, "[%u] Invalid kind:%u",
env->log_type_id, BTF_INFO_KIND(t->info));
return -EINVAL;
}
var_meta_size = btf_type_ops(t)->check_meta(env, t, meta_left);
if (var_meta_size < 0)
return var_meta_size;
}

var_meta_size = btf_type_ops(t)->check_meta(env, t, meta_left);
if (var_meta_size < 0)
return var_meta_size;

meta_left -= var_meta_size;

return saved_meta_left - meta_left;
Expand Down Expand Up @@ -5405,7 +5419,8 @@ static int btf_parse_str_sec(struct btf_verifier_env *env)
start = btf->nohdr_data + hdr->str_off;
end = start + hdr->str_len;

if (end != btf->data + btf->data_size) {
if (hdr->hdr_len < sizeof(struct btf_header) &&
end != btf->data + btf->data_size) {
btf_verifier_log(env, "String section is not at the end");
return -EINVAL;
}
Expand All @@ -5426,9 +5441,41 @@ static int btf_parse_str_sec(struct btf_verifier_env *env)
return 0;
}

static int btf_parse_kind_layout_sec(struct btf_verifier_env *env)
{
const struct btf_header *hdr = &env->btf->hdr;
struct btf *btf = env->btf;
void *start, *end;

if (hdr->hdr_len < sizeof(struct btf_header) ||
hdr->kind_layout_len == 0)
return 0;

/* Kind layout section must align to 4 bytes */
if (hdr->kind_layout_off & (sizeof(u32) - 1)) {
btf_verifier_log(env, "Unaligned kind_layout_off");
return -EINVAL;
}
start = btf->nohdr_data + hdr->kind_layout_off;
end = start + hdr->kind_layout_len;

if (hdr->kind_layout_len < sizeof(struct btf_kind_layout)) {
btf_verifier_log(env, "Kind layout section is too small");
return -EINVAL;
}
if (end > btf->data + btf->data_size) {
btf_verifier_log(env, "Kind layout section is too big");
return -EINVAL;
}
btf->kind_layout = start;

return 0;
}

static const size_t btf_sec_info_offset[] = {
offsetof(struct btf_header, type_off),
offsetof(struct btf_header, str_off),
offsetof(struct btf_header, kind_layout_off),
};

static int btf_sec_info_cmp(const void *a, const void *b)
Expand All @@ -5443,44 +5490,49 @@ static int btf_check_sec_info(struct btf_verifier_env *env,
u32 btf_data_size)
{
struct btf_sec_info secs[ARRAY_SIZE(btf_sec_info_offset)];
u32 total, expected_total, i;
u32 nr_secs = ARRAY_SIZE(btf_sec_info_offset);
u32 total, expected_total, gap, i;
const struct btf_header *hdr;
const struct btf *btf;

btf = env->btf;
hdr = &btf->hdr;

if (hdr->hdr_len < sizeof(struct btf_header))
nr_secs--;

/* Populate the secs from hdr */
for (i = 0; i < ARRAY_SIZE(btf_sec_info_offset); i++)
for (i = 0; i < nr_secs; i++)
secs[i] = *(struct btf_sec_info *)((void *)hdr +
btf_sec_info_offset[i]);

sort(secs, ARRAY_SIZE(btf_sec_info_offset),
sort(secs, nr_secs,
sizeof(struct btf_sec_info), btf_sec_info_cmp, NULL);

/* Check for gaps and overlap among sections */
total = 0;
expected_total = btf_data_size - hdr->hdr_len;
for (i = 0; i < ARRAY_SIZE(btf_sec_info_offset); i++) {
for (i = 0; i < nr_secs; i++) {
if (expected_total < secs[i].off) {
btf_verifier_log(env, "Invalid section offset");
return -EINVAL;
}
if (total < secs[i].off) {
/* gap */
btf_verifier_log(env, "Unsupported section found");
return -EINVAL;
}
if (total > secs[i].off) {
btf_verifier_log(env, "Section overlap found");
return -EINVAL;
}
gap = secs[i].off - total;
if (gap >= 4) {
/* gap larger than alignment gap */
btf_verifier_log(env, "Unsupported section found");
return -EINVAL;
}
if (expected_total - total < secs[i].len) {
btf_verifier_log(env,
"Total section length too long");
return -EINVAL;
}
total += secs[i].len;
total += secs[i].len + gap;
}

/* There is data other than hdr and known sections */
Expand Down Expand Up @@ -5816,6 +5868,10 @@ static struct btf *btf_parse(const union bpf_attr *attr, bpfptr_t uattr, u32 uat
if (err)
goto errout;

err = btf_parse_kind_layout_sec(env);
if (err)
goto errout;

err = btf_parse_type_sec(env);
if (err)
goto errout;
Expand Down
2 changes: 2 additions & 0 deletions scripts/Makefile.btf
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ pahole-flags-$(call test-ge, $(pahole-ver), 126) = -j$(JOBS) --btf_features=enc

pahole-flags-$(call test-ge, $(pahole-ver), 130) += --btf_features=attributes

pahole-flags-$(call test-ge, $(pahole-ver), 131) += --btf_features=kind_layout

ifneq ($(KBUILD_EXTMOD),)
module-pahole-flags-$(call test-ge, $(pahole-ver), 128) += --btf_features=distilled_base
endif
Expand Down
28 changes: 26 additions & 2 deletions tools/bpf/bpftool/Documentation/bpftool-btf.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ BTF COMMANDS
| **bpftool** **btf help**
|
| *BTF_SRC* := { **id** *BTF_ID* | **prog** *PROG* | **map** *MAP* [{**key** | **value** | **kv** | **all**}] | **file** *FILE* }
| *FORMAT* := { **raw** | **c** [**unsorted**] }
| *FORMAT* := { **raw** | **c** [**unsorted**] | **meta** }
| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* | **name** *PROG_NAME* }

Expand Down Expand Up @@ -65,7 +65,8 @@ bpftool btf dump *BTF_SRC* [format *FORMAT*] [root_id *ROOT_ID*]
**format** option can be used to override default (raw) output format. Raw
(**raw**) or C-syntax (**c**) output formats are supported. With C-style
formatting, the output is sorted by default. Use the **unsorted** option
to avoid sorting the output.
to avoid sorting the output. BTF metadata can be displayed with the
**meta** option.

**root_id** option can be used to filter a dump to a single type and all
its dependent types. It cannot be used with any other types of filtering
Expand Down Expand Up @@ -267,3 +268,26 @@ All the standard ways to specify map or program are supported:
[104859] FUNC 'smbalert_work' type_id=9695 linkage=static
[104860] FUNC 'smbus_alert' type_id=71367 linkage=static
[104861] FUNC 'smbus_do_alert' type_id=84827 linkage=static

Display BTF metadata from file vmlinux

**# bpftool btf dump file vmlinux format meta**

::

size 5161076
magic 0xeb9f
version 1
flags 0x1
hdr_len 40
type_len 3036368
type_off 0
str_len 2124588
str_off 3036368
kind_layout_len 80
kind_layout_off 5160956
kind 0 UNKNOWN info_sz 0 elem_sz 0
kind 1 INT info_sz 0 elem_sz 0
kind 2 PTR info_sz 0 elem_sz 0
kind 3 ARRAY info_sz 0 elem_sz 0
kind 4 STRUCT info_sz 0 elem_sz 0
2 changes: 1 addition & 1 deletion tools/bpf/bpftool/bash-completion/bpftool
Original file line number Diff line number Diff line change
Expand Up @@ -950,7 +950,7 @@ _bpftool()
return 0
;;
format)
COMPREPLY=( $( compgen -W "c raw" -- "$cur" ) )
COMPREPLY=( $( compgen -W "c raw meta" -- "$cur" ) )
;;
root_id)
return 0;
Expand Down
94 changes: 91 additions & 3 deletions tools/bpf/bpftool/btf.c
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,90 @@ static int dump_btf_c(const struct btf *btf,
return err;
}

static int dump_btf_meta(const struct btf *btf)
{
const struct btf_header *hdr;
const struct btf_kind_layout *k;
__u8 i, nr_kinds = 0;
const void *data;
__u32 data_sz;

data = btf__raw_data(btf, &data_sz);
if (!data)
return -ENOMEM;
hdr = data;
if (json_output) {
jsonw_start_object(json_wtr); /* metadata object */
jsonw_uint_field(json_wtr, "size", data_sz);
jsonw_name(json_wtr, "header");
jsonw_start_object(json_wtr); /* header object */
jsonw_uint_field(json_wtr, "magic", hdr->magic);
jsonw_uint_field(json_wtr, "version", hdr->version);
jsonw_uint_field(json_wtr, "flags", hdr->flags);
jsonw_uint_field(json_wtr, "hdr_len", hdr->hdr_len);
jsonw_uint_field(json_wtr, "type_len", hdr->type_len);
jsonw_uint_field(json_wtr, "type_off", hdr->type_off);
jsonw_uint_field(json_wtr, "str_len", hdr->str_len);
jsonw_uint_field(json_wtr, "str_off", hdr->str_off);
} else {
printf("size %-10u\n", data_sz);
printf("magic 0x%-10x\nversion %-10d\nflags 0x%-10x\nhdr_len %-10u\n",
hdr->magic, hdr->version, hdr->flags, hdr->hdr_len);
printf("type_len %-10u\ntype_off %-10u\n", hdr->type_len, hdr->type_off);
printf("str_len %-10u\nstr_off %-10u\n", hdr->str_len, hdr->str_off);
}

if (hdr->hdr_len < sizeof(struct btf_header)) {
if (json_output) {
jsonw_end_object(json_wtr); /* end header object */
jsonw_end_object(json_wtr); /* end metadata object */
}
return 0;
}

data_sz -= hdr->hdr_len;

if (hdr->kind_layout_len > 0 && hdr->kind_layout_off > 0) {
if (hdr->kind_layout_off + hdr->kind_layout_len <= data_sz) {
k = (void *)hdr + hdr->hdr_len + hdr->kind_layout_off;
nr_kinds = hdr->kind_layout_len / sizeof(*k);
}
}
if (json_output) {
jsonw_uint_field(json_wtr, "kind_layout_len", hdr->kind_layout_len);
jsonw_uint_field(json_wtr, "kind_layout_offset", hdr->kind_layout_off);
jsonw_end_object(json_wtr); /* end header object */

if (nr_kinds > 0) {
jsonw_name(json_wtr, "kind_layouts");
jsonw_start_array(json_wtr);
for (i = 0; i < nr_kinds; i++) {
jsonw_start_object(json_wtr);
jsonw_uint_field(json_wtr, "kind", i);
if (i < NR_BTF_KINDS)
jsonw_string_field(json_wtr, "name", btf_kind_str[i]);
else
jsonw_null_field(json_wtr, "name");
jsonw_uint_field(json_wtr, "info_sz", k[i].info_sz);
jsonw_uint_field(json_wtr, "elem_sz", k[i].elem_sz);
jsonw_end_object(json_wtr);
}
jsonw_end_array(json_wtr);
}
jsonw_end_object(json_wtr); /* end metadata object */
} else {
printf("kind_layout_len %-10u\nkind_layout_off %-10u\n",
hdr->kind_layout_len, hdr->kind_layout_off);
for (i = 0; i < nr_kinds; i++) {
printf("kind %-4d %-10s info_sz %-4d elem_sz %-4d\n",
i, i < NR_BTF_KINDS ? btf_kind_str[i] : "?",
k[i].info_sz, k[i].elem_sz);
}
}

return 0;
}

static const char sysfs_vmlinux[] = "/sys/kernel/btf/vmlinux";

static struct btf *get_vmlinux_btf_from_sysfs(void)
Expand Down Expand Up @@ -880,7 +964,7 @@ static bool btf_is_kernel_module(__u32 btf_id)

static int do_dump(int argc, char **argv)
{
bool dump_c = false, sort_dump_c = true;
bool dump_c = false, sort_dump_c = true, dump_meta = false;
struct btf *btf = NULL, *base = NULL;
__u32 root_type_ids[MAX_ROOT_IDS];
bool have_id_filtering;
Expand Down Expand Up @@ -990,10 +1074,12 @@ static int do_dump(int argc, char **argv)
}
if (strcmp(*argv, "c") == 0) {
dump_c = true;
} else if (is_prefix(*argv, "meta")) {
dump_meta = true;
} else if (strcmp(*argv, "raw") == 0) {
dump_c = false;
} else {
p_err("unrecognized format specifier: '%s', possible values: raw, c",
p_err("unrecognized format specifier: '%s', possible values: raw, c, meta",
*argv);
err = -EINVAL;
goto done;
Expand Down Expand Up @@ -1072,6 +1158,8 @@ static int do_dump(int argc, char **argv)
goto done;
}
err = dump_btf_c(btf, root_type_ids, root_type_cnt, sort_dump_c);
} else if (dump_meta) {
err = dump_btf_meta(btf);
} else {
err = dump_btf_raw(btf, root_type_ids, root_type_cnt);
}
Expand Down Expand Up @@ -1446,7 +1534,7 @@ static int do_help(int argc, char **argv)
" %1$s %2$s help\n"
"\n"
" BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n"
" FORMAT := { raw | c [unsorted] }\n"
" FORMAT := { raw | c [unsorted] | meta }\n"
" " HELP_SPEC_MAP "\n"
" " HELP_SPEC_PROGRAM "\n"
" " HELP_SPEC_OPTIONS " |\n"
Expand Down
Loading
Loading