selftests/bpf: Add CO-RE relo test for TYPE_ID_LOCAL/TYPE_ID_TARGET
authorAndrii Nakryiko <andriin@fb.com>
Wed, 19 Aug 2020 19:45:17 +0000 (12:45 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Wed, 19 Aug 2020 21:19:39 +0000 (14:19 -0700)
Add tests for BTF type ID relocations. To allow testing this, enhance
core_relo.c test runner to allow dynamic initialization of test inputs.
If Clang doesn't have necessary support for new functionality, test is
skipped.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Yonghong Song <yhs@fb.com>
Link: https://lore.kernel.org/bpf/20200819194519.3375898-4-andriin@fb.com
tools/testing/selftests/bpf/prog_tests/core_reloc.c
tools/testing/selftests/bpf/progs/btf__core_reloc_type_id.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/btf__core_reloc_type_id___missing_targets.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/core_reloc_types.h
tools/testing/selftests/bpf/progs/test_core_reloc_type_based.c
tools/testing/selftests/bpf/progs/test_core_reloc_type_id.c [new file with mode: 0644]

index b775ce0..ad55051 100644 (file)
@@ -3,6 +3,9 @@
 #include "progs/core_reloc_types.h"
 #include <sys/mman.h>
 #include <sys/syscall.h>
+#include <bpf/btf.h>
+
+static int duration = 0;
 
 #define STRUCT_TO_CHAR_PTR(struct_name) (const char *)&(struct struct_name)
 
        .fails = true,                                                  \
 }
 
+#define TYPE_ID_CASE_COMMON(name)                                      \
+       .case_name = #name,                                             \
+       .bpf_obj_file = "test_core_reloc_type_id.o",                    \
+       .btf_src_file = "btf__core_reloc_" #name ".o"                   \
+
+#define TYPE_ID_CASE(name, setup_fn) {                                 \
+       TYPE_ID_CASE_COMMON(name),                                      \
+       .output = STRUCT_TO_CHAR_PTR(core_reloc_type_id_output) {},     \
+       .output_len = sizeof(struct core_reloc_type_id_output),         \
+       .setup = setup_fn,                                              \
+}
+
+#define TYPE_ID_ERR_CASE(name) {                                       \
+       TYPE_ID_CASE_COMMON(name),                                      \
+       .fails = true,                                                  \
+}
+
+struct core_reloc_test_case;
+
+typedef int (*setup_test_fn)(struct core_reloc_test_case *test);
+
 struct core_reloc_test_case {
        const char *case_name;
        const char *bpf_obj_file;
@@ -280,8 +304,136 @@ struct core_reloc_test_case {
        bool fails;
        bool relaxed_core_relocs;
        bool direct_raw_tp;
+       setup_test_fn setup;
 };
 
+static int find_btf_type(const struct btf *btf, const char *name, __u32 kind)
+{
+       int id;
+
+       id = btf__find_by_name_kind(btf, name, kind);
+       if (CHECK(id <= 0, "find_type_id", "failed to find '%s', kind %d: %d\n", name, kind, id))
+               return -1;
+
+       return id;
+}
+
+static int setup_type_id_case_local(struct core_reloc_test_case *test)
+{
+       struct core_reloc_type_id_output *exp = (void *)test->output;
+       struct btf *local_btf = btf__parse(test->bpf_obj_file, NULL);
+       struct btf *targ_btf = btf__parse(test->btf_src_file, NULL);
+       const struct btf_type *t;
+       const char *name;
+       int i;
+
+       if (CHECK(IS_ERR(local_btf), "local_btf", "failed: %ld\n", PTR_ERR(local_btf)) ||
+           CHECK(IS_ERR(targ_btf), "targ_btf", "failed: %ld\n", PTR_ERR(targ_btf))) {
+               btf__free(local_btf);
+               btf__free(targ_btf);
+               return -EINVAL;
+       }
+
+       exp->local_anon_struct = -1;
+       exp->local_anon_union = -1;
+       exp->local_anon_enum = -1;
+       exp->local_anon_func_proto_ptr = -1;
+       exp->local_anon_void_ptr = -1;
+       exp->local_anon_arr = -1;
+
+       for (i = 1; i <= btf__get_nr_types(local_btf); i++)
+       {
+               t = btf__type_by_id(local_btf, i);
+               /* we are interested only in anonymous types */
+               if (t->name_off)
+                       continue;
+
+               if (btf_is_struct(t) && btf_vlen(t) &&
+                   (name = btf__name_by_offset(local_btf, btf_members(t)[0].name_off)) &&
+                   strcmp(name, "marker_field") == 0) {
+                       exp->local_anon_struct = i;
+               } else if (btf_is_union(t) && btf_vlen(t) &&
+                        (name = btf__name_by_offset(local_btf, btf_members(t)[0].name_off)) &&
+                        strcmp(name, "marker_field") == 0) {
+                       exp->local_anon_union = i;
+               } else if (btf_is_enum(t) && btf_vlen(t) &&
+                        (name = btf__name_by_offset(local_btf, btf_enum(t)[0].name_off)) &&
+                        strcmp(name, "MARKER_ENUM_VAL") == 0) {
+                       exp->local_anon_enum = i;
+               } else if (btf_is_ptr(t) && (t = btf__type_by_id(local_btf, t->type))) {
+                       if (btf_is_func_proto(t) && (t = btf__type_by_id(local_btf, t->type)) &&
+                           btf_is_int(t) && (name = btf__name_by_offset(local_btf, t->name_off)) &&
+                           strcmp(name, "_Bool") == 0) {
+                               /* ptr -> func_proto -> _Bool */
+                               exp->local_anon_func_proto_ptr = i;
+                       } else if (btf_is_void(t)) {
+                               /* ptr -> void */
+                               exp->local_anon_void_ptr = i;
+                       }
+               } else if (btf_is_array(t) && (t = btf__type_by_id(local_btf, btf_array(t)->type)) &&
+                          btf_is_int(t) && (name = btf__name_by_offset(local_btf, t->name_off)) &&
+                          strcmp(name, "_Bool") == 0) {
+                       /* _Bool[] */
+                       exp->local_anon_arr = i;
+               }
+       }
+
+       exp->local_struct = find_btf_type(local_btf, "a_struct", BTF_KIND_STRUCT);
+       exp->local_union = find_btf_type(local_btf, "a_union", BTF_KIND_UNION);
+       exp->local_enum = find_btf_type(local_btf, "an_enum", BTF_KIND_ENUM);
+       exp->local_int = find_btf_type(local_btf, "int", BTF_KIND_INT);
+       exp->local_struct_typedef = find_btf_type(local_btf, "named_struct_typedef", BTF_KIND_TYPEDEF);
+       exp->local_func_proto_typedef = find_btf_type(local_btf, "func_proto_typedef", BTF_KIND_TYPEDEF);
+       exp->local_arr_typedef = find_btf_type(local_btf, "arr_typedef", BTF_KIND_TYPEDEF);
+
+       btf__free(local_btf);
+       btf__free(targ_btf);
+       return 0;
+}
+
+static int setup_type_id_case_success(struct core_reloc_test_case *test) {
+       struct core_reloc_type_id_output *exp = (void *)test->output;
+       struct btf *targ_btf = btf__parse(test->btf_src_file, NULL);
+       int err;
+
+       err = setup_type_id_case_local(test);
+       if (err)
+               return err;
+
+       targ_btf = btf__parse(test->btf_src_file, NULL);
+
+       exp->targ_struct = find_btf_type(targ_btf, "a_struct", BTF_KIND_STRUCT);
+       exp->targ_union = find_btf_type(targ_btf, "a_union", BTF_KIND_UNION);
+       exp->targ_enum = find_btf_type(targ_btf, "an_enum", BTF_KIND_ENUM);
+       exp->targ_int = find_btf_type(targ_btf, "int", BTF_KIND_INT);
+       exp->targ_struct_typedef = find_btf_type(targ_btf, "named_struct_typedef", BTF_KIND_TYPEDEF);
+       exp->targ_func_proto_typedef = find_btf_type(targ_btf, "func_proto_typedef", BTF_KIND_TYPEDEF);
+       exp->targ_arr_typedef = find_btf_type(targ_btf, "arr_typedef", BTF_KIND_TYPEDEF);
+
+       btf__free(targ_btf);
+       return 0;
+}
+
+static int setup_type_id_case_failure(struct core_reloc_test_case *test)
+{
+       struct core_reloc_type_id_output *exp = (void *)test->output;
+       int err;
+
+       err = setup_type_id_case_local(test);
+       if (err)
+               return err;
+
+       exp->targ_struct = 0;
+       exp->targ_union = 0;
+       exp->targ_enum = 0;
+       exp->targ_int = 0;
+       exp->targ_struct_typedef = 0;
+       exp->targ_func_proto_typedef = 0;
+       exp->targ_arr_typedef = 0;
+
+       return 0;
+}
+
 static struct core_reloc_test_case test_cases[] = {
        /* validate we can find kernel image and use its BTF for relocs */
        {
@@ -530,6 +682,10 @@ static struct core_reloc_test_case test_cases[] = {
                .struct_exists = 1,
                .struct_sz = sizeof(struct a_struct),
        }),
+
+       /* BTF_TYPE_ID_LOCAL/BTF_TYPE_ID_TARGET tests */
+       TYPE_ID_CASE(type_id, setup_type_id_case_success),
+       TYPE_ID_CASE(type_id___missing_targets, setup_type_id_case_failure),
 };
 
 struct data {
@@ -551,7 +707,7 @@ void test_core_reloc(void)
        struct bpf_object_load_attr load_attr = {};
        struct core_reloc_test_case *test_case;
        const char *tp_name, *probe_name;
-       int err, duration = 0, i, equal;
+       int err, i, equal;
        struct bpf_link *link = NULL;
        struct bpf_map *data_map;
        struct bpf_program *prog;
@@ -567,11 +723,13 @@ void test_core_reloc(void)
                if (!test__start_subtest(test_case->case_name))
                        continue;
 
-               DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts,
-                       .relaxed_core_relocs = test_case->relaxed_core_relocs,
-               );
+               if (test_case->setup) {
+                       err = test_case->setup(test_case);
+                       if (CHECK(err, "test_setup", "test #%d setup failed: %d\n", i, err))
+                               continue;
+               }
 
-               obj = bpf_object__open_file(test_case->bpf_obj_file, &opts);
+               obj = bpf_object__open_file(test_case->bpf_obj_file, NULL);
                if (CHECK(IS_ERR(obj), "obj_open", "failed to open '%s': %ld\n",
                          test_case->bpf_obj_file, PTR_ERR(obj)))
                        continue;
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_type_id.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_type_id.c
new file mode 100644 (file)
index 0000000..abbe5bd
--- /dev/null
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_type_id x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_type_id___missing_targets.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_type_id___missing_targets.c
new file mode 100644 (file)
index 0000000..24e7caf
--- /dev/null
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_type_id___missing_targets x) {}
index d998537..10afcc5 100644 (file)
@@ -1034,3 +1034,44 @@ struct core_reloc_type_based___fn_wrong_args {
        func_proto_typedef___fn_wrong_arg_cnt1 f6;
        func_proto_typedef___fn_wrong_arg_cnt2 f7;
 };
+
+/*
+ * TYPE ID MAPPING (LOCAL AND TARGET)
+ */
+struct core_reloc_type_id_output {
+       int local_anon_struct;
+       int local_anon_union;
+       int local_anon_enum;
+       int local_anon_func_proto_ptr;
+       int local_anon_void_ptr;
+       int local_anon_arr;
+
+       int local_struct;
+       int local_union;
+       int local_enum;
+       int local_int;
+       int local_struct_typedef;
+       int local_func_proto_typedef;
+       int local_arr_typedef;
+
+       int targ_struct;
+       int targ_union;
+       int targ_enum;
+       int targ_int;
+       int targ_struct_typedef;
+       int targ_func_proto_typedef;
+       int targ_arr_typedef;
+};
+
+struct core_reloc_type_id {
+       struct a_struct f1;
+       union a_union f2;
+       enum an_enum f3;
+       named_struct_typedef f4;
+       func_proto_typedef f5;
+       arr_typedef f6;
+};
+
+struct core_reloc_type_id___missing_targets {
+       /* nothing */
+};
index 6ab259d..fb60f81 100644 (file)
@@ -48,20 +48,6 @@ typedef int (*func_proto_typedef)(long);
 
 typedef char arr_typedef[20];
 
-struct core_reloc_type_based {
-       struct a_struct f1;
-       union a_union f2;
-       enum an_enum f3;
-       named_struct_typedef f4;
-       anon_struct_typedef f5;
-       struct_ptr_typedef f6;
-       int_typedef f7;
-       enum_typedef f8;
-       void_ptr_typedef f9;
-       func_proto_typedef f10;
-       arr_typedef f11;
-};
-
 struct core_reloc_type_based_output {
        bool struct_exists;
        bool union_exists;
diff --git a/tools/testing/selftests/bpf/progs/test_core_reloc_type_id.c b/tools/testing/selftests/bpf/progs/test_core_reloc_type_id.c
new file mode 100644 (file)
index 0000000..23e6e6b
--- /dev/null
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2020 Facebook
+
+#include <linux/bpf.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_core_read.h>
+
+char _license[] SEC("license") = "GPL";
+
+struct {
+       char in[256];
+       char out[256];
+       bool skip;
+} data = {};
+
+/* some types are shared with test_core_reloc_type_based.c */
+struct a_struct {
+       int x;
+};
+
+union a_union {
+       int y;
+       int z;
+};
+
+enum an_enum {
+       AN_ENUM_VAL1 = 1,
+       AN_ENUM_VAL2 = 2,
+       AN_ENUM_VAL3 = 3,
+};
+
+typedef struct a_struct named_struct_typedef;
+
+typedef int (*func_proto_typedef)(long);
+
+typedef char arr_typedef[20];
+
+struct core_reloc_type_id_output {
+       int local_anon_struct;
+       int local_anon_union;
+       int local_anon_enum;
+       int local_anon_func_proto_ptr;
+       int local_anon_void_ptr;
+       int local_anon_arr;
+
+       int local_struct;
+       int local_union;
+       int local_enum;
+       int local_int;
+       int local_struct_typedef;
+       int local_func_proto_typedef;
+       int local_arr_typedef;
+
+       int targ_struct;
+       int targ_union;
+       int targ_enum;
+       int targ_int;
+       int targ_struct_typedef;
+       int targ_func_proto_typedef;
+       int targ_arr_typedef;
+};
+
+/* preserve types even if Clang doesn't support built-in */
+struct a_struct t1 = {};
+union a_union t2 = {};
+enum an_enum t3 = 0;
+named_struct_typedef t4 = {};
+func_proto_typedef t5 = 0;
+arr_typedef t6 = {};
+
+SEC("raw_tracepoint/sys_enter")
+int test_core_type_id(void *ctx)
+{
+       /* We use __builtin_btf_type_id() in this tests, but up until the time
+        * __builtin_preserve_type_info() was added it contained a bug that
+        * would make this test fail. The bug was fixed with addition of
+        * __builtin_preserve_type_info(), though, so that's what we are using
+        * to detect whether this test has to be executed, however strange
+        * that might look like.
+        */
+#if __has_builtin(__builtin_preserve_type_info)
+       struct core_reloc_type_id_output *out = (void *)&data.out;
+
+       out->local_anon_struct = bpf_core_type_id_local(struct { int marker_field; });
+       out->local_anon_union = bpf_core_type_id_local(union { int marker_field; });
+       out->local_anon_enum = bpf_core_type_id_local(enum { MARKER_ENUM_VAL = 123 });
+       out->local_anon_func_proto_ptr = bpf_core_type_id_local(_Bool(*)(int));
+       out->local_anon_void_ptr = bpf_core_type_id_local(void *);
+       out->local_anon_arr = bpf_core_type_id_local(_Bool[47]);
+
+       out->local_struct = bpf_core_type_id_local(struct a_struct);
+       out->local_union = bpf_core_type_id_local(union a_union);
+       out->local_enum = bpf_core_type_id_local(enum an_enum);
+       out->local_int = bpf_core_type_id_local(int);
+       out->local_struct_typedef = bpf_core_type_id_local(named_struct_typedef);
+       out->local_func_proto_typedef = bpf_core_type_id_local(func_proto_typedef);
+       out->local_arr_typedef = bpf_core_type_id_local(arr_typedef);
+
+       out->targ_struct = bpf_core_type_id_kernel(struct a_struct);
+       out->targ_union = bpf_core_type_id_kernel(union a_union);
+       out->targ_enum = bpf_core_type_id_kernel(enum an_enum);
+       out->targ_int = bpf_core_type_id_kernel(int);
+       out->targ_struct_typedef = bpf_core_type_id_kernel(named_struct_typedef);
+       out->targ_func_proto_typedef = bpf_core_type_id_kernel(func_proto_typedef);
+       out->targ_arr_typedef = bpf_core_type_id_kernel(arr_typedef);
+#else
+       data.skip = true;
+#endif
+
+       return 0;
+}