Add tests for BTF type ID relocations. To allow testing this, enhance
core_relo.c test runner to allow dynamic initialization of test inputs.

Signed-off-by: Andrii Nakryiko <andr...@fb.com>
---
 .../selftests/bpf/prog_tests/core_reloc.c     | 151 +++++++++++++++++-
 .../bpf/progs/btf__core_reloc_type_id.c       |   3 +
 ...tf__core_reloc_type_id___missing_targets.c |   3 +
 .../selftests/bpf/progs/core_reloc_types.h    |  40 +++++
 .../bpf/progs/test_core_reloc_type_based.c    |  14 --
 .../bpf/progs/test_core_reloc_type_id.c       |  94 +++++++++++
 6 files changed, 286 insertions(+), 19 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/progs/btf__core_reloc_type_id.c
 create mode 100644 
tools/testing/selftests/bpf/progs/btf__core_reloc_type_id___missing_targets.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_core_reloc_type_id.c

diff --git a/tools/testing/selftests/bpf/prog_tests/core_reloc.c 
b/tools/testing/selftests/bpf/prog_tests/core_reloc.c
index 04b691cc55c4..54b74a8d33bc 100644
--- a/tools/testing/selftests/bpf/prog_tests/core_reloc.c
+++ b/tools/testing/selftests/bpf/prog_tests/core_reloc.c
@@ -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)
 
@@ -269,6 +272,27 @@
        .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,119 @@ struct core_reloc_test_case {
        bool fails;
        bool relaxed_core_relocs;
        bool direct_raw_tp;
+       setup_test_fn setup;
 };
 
+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_success(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);
+
+       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);
+
+       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_success(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 +665,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 {
@@ -550,7 +689,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;
@@ -566,11 +705,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
index 000000000000..abbe5bddcefd
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_type_id.c
@@ -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
index 000000000000..24e7caf4f013
--- /dev/null
+++ 
b/tools/testing/selftests/bpf/progs/btf__core_reloc_type_id___missing_targets.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_type_id___missing_targets x) {}
diff --git a/tools/testing/selftests/bpf/progs/core_reloc_types.h 
b/tools/testing/selftests/bpf/progs/core_reloc_types.h
index 721c8b2ad6e3..10afcc5f219f 100644
--- a/tools/testing/selftests/bpf/progs/core_reloc_types.h
+++ b/tools/testing/selftests/bpf/progs/core_reloc_types.h
@@ -1035,3 +1035,43 @@ struct core_reloc_type_based___fn_wrong_args {
        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 */
+};
diff --git a/tools/testing/selftests/bpf/progs/test_core_reloc_type_based.c 
b/tools/testing/selftests/bpf/progs/test_core_reloc_type_based.c
index 0c0fabcda691..9600aa80d18a 100644
--- a/tools/testing/selftests/bpf/progs/test_core_reloc_type_based.c
+++ b/tools/testing/selftests/bpf/progs/test_core_reloc_type_based.c
@@ -47,20 +47,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
index 000000000000..b47c1e813729
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_core_reloc_type_id.c
@@ -0,0 +1,94 @@
+// 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];
+} 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;
+};
+
+SEC("raw_tracepoint/sys_enter")
+int test_core_type_id(void *ctx)
+{
+       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);
+
+       return 0;
+}
+
-- 
2.24.1

Reply via email to