summaryrefslogtreecommitdiff
path: root/tools/bpf/bpftool/btf.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/bpf/bpftool/btf.c')
-rw-r--r--tools/bpf/bpftool/btf.c160
1 files changed, 128 insertions, 32 deletions
diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
index 2636655ac180..6ef908adf3a4 100644
--- a/tools/bpf/bpftool/btf.c
+++ b/tools/bpf/bpftool/btf.c
@@ -28,6 +28,7 @@
#define FASTCALL_DECL_TAG "bpf_fastcall"
#define MAX_ROOT_IDS 16
+#define MAX_BTF_FILES 64
static const char * const btf_kind_str[NR_BTF_KINDS] = {
[BTF_KIND_UNKN] = "UNKNOWN",
@@ -178,8 +179,7 @@ static int dump_btf_type(const struct btf *btf, __u32 id,
case BTF_KIND_STRUCT:
case BTF_KIND_UNION: {
const struct btf_member *m = (const void *)(t + 1);
- __u16 vlen = BTF_INFO_VLEN(t->info);
- int i;
+ __u32 i, vlen = BTF_INFO_VLEN(t->info);
if (json_output) {
jsonw_uint_field(w, "size", t->size);
@@ -224,9 +224,8 @@ static int dump_btf_type(const struct btf *btf, __u32 id,
}
case BTF_KIND_ENUM: {
const struct btf_enum *v = (const void *)(t + 1);
- __u16 vlen = BTF_INFO_VLEN(t->info);
+ __u32 i, vlen = BTF_INFO_VLEN(t->info);
const char *encoding;
- int i;
encoding = btf_kflag(t) ? "SIGNED" : "UNSIGNED";
if (json_output) {
@@ -253,7 +252,7 @@ static int dump_btf_type(const struct btf *btf, __u32 id,
if (btf_kflag(t))
printf("\n\t'%s' val=%d", name, v->val);
else
- printf("\n\t'%s' val=%u", name, v->val);
+ printf("\n\t'%s' val=%u", name, (__u32)v->val);
}
}
if (json_output)
@@ -262,9 +261,8 @@ static int dump_btf_type(const struct btf *btf, __u32 id,
}
case BTF_KIND_ENUM64: {
const struct btf_enum64 *v = btf_enum64(t);
- __u16 vlen = btf_vlen(t);
+ __u32 i, vlen = btf_vlen(t);
const char *encoding;
- int i;
encoding = btf_kflag(t) ? "SIGNED" : "UNSIGNED";
if (json_output) {
@@ -324,8 +322,7 @@ static int dump_btf_type(const struct btf *btf, __u32 id,
}
case BTF_KIND_FUNC_PROTO: {
const struct btf_param *p = (const void *)(t + 1);
- __u16 vlen = BTF_INFO_VLEN(t->info);
- int i;
+ __u32 i, vlen = BTF_INFO_VLEN(t->info);
if (json_output) {
jsonw_uint_field(w, "ret_type_id", t->type);
@@ -368,8 +365,7 @@ static int dump_btf_type(const struct btf *btf, __u32 id,
case BTF_KIND_DATASEC: {
const struct btf_var_secinfo *v = (const void *)(t + 1);
const struct btf_type *vt;
- __u16 vlen = BTF_INFO_VLEN(t->info);
- int i;
+ __u32 i, vlen = BTF_INFO_VLEN(t->info);
if (json_output) {
jsonw_uint_field(w, "size", t->size);
@@ -674,7 +670,7 @@ static __u64 btf_name_hasher(__u64 hash, const struct btf *btf, __u32 name_off)
static __u64 btf_type_disambig_hash(const struct btf *btf, __u32 id, bool include_members)
{
const struct btf_type *t = btf__type_by_id(btf, id);
- int i;
+ __u32 i;
size_t hash = 0;
hash = btf_name_hasher(hash, btf, t->name_off);
@@ -878,6 +874,45 @@ static bool btf_is_kernel_module(__u32 btf_id)
return btf_info.kernel_btf && strncmp(btf_name, "vmlinux", sizeof(btf_name)) != 0;
}
+static struct btf *merge_btf_files(const char **files, int nr_files,
+ struct btf *vmlinux_base)
+{
+ struct btf *combined, *mod;
+ int ret;
+
+ combined = btf__new_empty_split(vmlinux_base);
+ if (!combined) {
+ p_err("failed to create combined BTF: %s", strerror(errno));
+ return NULL;
+ }
+
+ for (int j = 0; j < nr_files; j++) {
+ mod = btf__parse_split(files[j], vmlinux_base);
+ if (!mod) {
+ p_err("failed to load BTF from %s: %s", files[j], strerror(errno));
+ btf__free(combined);
+ return NULL;
+ }
+
+ ret = btf__add_btf(combined, mod);
+ btf__free(mod);
+ if (ret < 0) {
+ p_err("failed to merge BTF from %s: %s", files[j], strerror(-ret));
+ btf__free(combined);
+ return NULL;
+ }
+ }
+
+ ret = btf__dedup(combined, NULL);
+ if (ret) {
+ p_err("failed to dedup combined BTF: %s", strerror(-ret));
+ btf__free(combined);
+ return NULL;
+ }
+
+ return combined;
+}
+
static int do_dump(int argc, char **argv)
{
bool dump_c = false, sort_dump_c = true;
@@ -905,7 +940,8 @@ static int do_dump(int argc, char **argv)
return -1;
}
- fd = map_parse_fd_and_info(&argc, &argv, &info, &len);
+ fd = map_parse_fd_and_info(&argc, &argv, &info, &len,
+ BPF_F_RDONLY);
if (fd < 0)
return -1;
@@ -957,20 +993,76 @@ static int do_dump(int argc, char **argv)
NEXT_ARG();
} else if (is_prefix(src, "file")) {
const char sysfs_prefix[] = "/sys/kernel/btf/";
+ struct btf *vmlinux_base = base_btf;
+ const char *files[MAX_BTF_FILES];
+ int nr_files = 0;
- if (!base_btf &&
- strncmp(*argv, sysfs_prefix, sizeof(sysfs_prefix) - 1) == 0 &&
- strcmp(*argv, sysfs_vmlinux) != 0)
- base = get_vmlinux_btf_from_sysfs();
-
- btf = btf__parse_split(*argv, base ?: base_btf);
- if (!btf) {
- err = -errno;
- p_err("failed to load BTF from %s: %s",
- *argv, strerror(errno));
- goto done;
+ /* First grab our argument, filtering out the sysfs_vmlinux. */
+ if (strcmp(*argv, sysfs_vmlinux) != 0) {
+ files[nr_files++] = *argv;
+ } else {
+ p_info("skipping %s (will be loaded as base)", *argv);
}
NEXT_ARG();
+
+ while (argc && is_prefix(*argv, "file")) {
+ NEXT_ARG();
+ if (!REQ_ARGS(1)) {
+ err = -EINVAL;
+ goto done;
+ }
+ /* Filter out any sysfs vmlinux entries. */
+ if (strcmp(*argv, sysfs_vmlinux) == 0) {
+ p_info("skipping %s (will be loaded as base)", *argv);
+ NEXT_ARG();
+ continue;
+ }
+ if (nr_files >= MAX_BTF_FILES) {
+ p_err("too many BTF files (max %d)", MAX_BTF_FILES);
+ err = -E2BIG;
+ goto done;
+ }
+ files[nr_files++] = *argv;
+ NEXT_ARG();
+ }
+
+ /* Auto-detect vmlinux base if any file is from sysfs */
+ if (!vmlinux_base) {
+ for (int j = 0; j < nr_files; j++) {
+ if (strncmp(files[j], sysfs_prefix, sizeof(sysfs_prefix) - 1) == 0) {
+ base = get_vmlinux_btf_from_sysfs();
+ vmlinux_base = base;
+ break;
+ }
+ }
+ }
+
+ /* All files were the sysfs_vmlinux, handle it like we used to */
+ if (nr_files == 0) {
+ nr_files = 1;
+ files[0] = sysfs_vmlinux;
+ }
+
+ if (nr_files == 1) {
+ btf = btf__parse_split(files[0], base ?: base_btf);
+ if (!btf) {
+ err = -errno;
+ p_err("failed to load BTF from %s: %s", files[0], strerror(errno));
+ goto done;
+ }
+ } else {
+ if (!vmlinux_base) {
+ p_err("base BTF is required when merging multiple BTF files; use -B/--base-btf or use sysfs paths");
+ err = -EINVAL;
+ goto done;
+ }
+
+ btf = merge_btf_files(files, nr_files, vmlinux_base);
+ if (!btf) {
+ err = -errno;
+ goto done;
+ }
+ }
} else {
err = -1;
p_err("unrecognized BTF source specifier: '%s'", src);
@@ -1022,7 +1114,7 @@ static int do_dump(int argc, char **argv)
for (i = 0; i < root_type_cnt; i++) {
if (root_type_ids[i] == root_id) {
err = -EINVAL;
- p_err("duplicate root_id %d supplied", root_id);
+ p_err("duplicate root_id %u supplied", root_id);
goto done;
}
}
@@ -1118,10 +1210,13 @@ build_btf_type_table(struct hashmap *tab, enum bpf_obj_type type,
[BPF_OBJ_PROG] = "prog",
[BPF_OBJ_MAP] = "map",
};
+ LIBBPF_OPTS(bpf_get_fd_by_id_opts, opts_ro);
__u32 btf_id, id = 0;
int err;
int fd;
+ opts_ro.open_flags = BPF_F_RDONLY;
+
while (true) {
switch (type) {
case BPF_OBJ_PROG:
@@ -1132,7 +1227,7 @@ build_btf_type_table(struct hashmap *tab, enum bpf_obj_type type,
break;
default:
err = -1;
- p_err("unexpected object type: %d", type);
+ p_err("unexpected object type: %u", type);
goto err_free;
}
if (err) {
@@ -1151,11 +1246,11 @@ build_btf_type_table(struct hashmap *tab, enum bpf_obj_type type,
fd = bpf_prog_get_fd_by_id(id);
break;
case BPF_OBJ_MAP:
- fd = bpf_map_get_fd_by_id(id);
+ fd = bpf_map_get_fd_by_id_opts(id, &opts_ro);
break;
default:
err = -1;
- p_err("unexpected object type: %d", type);
+ p_err("unexpected object type: %u", type);
goto err_free;
}
if (fd < 0) {
@@ -1188,7 +1283,7 @@ build_btf_type_table(struct hashmap *tab, enum bpf_obj_type type,
break;
default:
err = -1;
- p_err("unexpected object type: %d", type);
+ p_err("unexpected object type: %u", type);
goto err_free;
}
if (!btf_id)
@@ -1254,12 +1349,12 @@ show_btf_plain(struct bpf_btf_info *info, int fd,
n = 0;
hashmap__for_each_key_entry(btf_prog_table, entry, info->id) {
- printf("%s%lu", n++ == 0 ? " prog_ids " : ",", entry->value);
+ printf("%s%lu", n++ == 0 ? " prog_ids " : ",", (unsigned long)entry->value);
}
n = 0;
hashmap__for_each_key_entry(btf_map_table, entry, info->id) {
- printf("%s%lu", n++ == 0 ? " map_ids " : ",", entry->value);
+ printf("%s%lu", n++ == 0 ? " map_ids " : ",", (unsigned long)entry->value);
}
emit_obj_refs_plain(refs_table, info->id, "\n\tpids ");
@@ -1441,7 +1536,8 @@ static int do_help(int argc, char **argv)
" %1$s %2$s dump BTF_SRC [format FORMAT] [root_id ROOT_ID]\n"
" %1$s %2$s help\n"
"\n"
- " BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n"
+ " BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] |\n"
+ " file FILE [file FILE]... }\n"
" FORMAT := { raw | c [unsorted] }\n"
" " HELP_SPEC_MAP "\n"
" " HELP_SPEC_PROGRAM "\n"