mirror of https://gitee.com/openkylin/linux.git
selftests/bpf: Add CO-RE relo test for TYPE_ID_LOCAL/TYPE_ID_TARGET
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
This commit is contained in:
parent
124a892d1c
commit
4836bf5e2e
|
@ -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,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;
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_type_id x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_type_id___missing_targets x) {}
|
|
@ -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 */
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue