Commit 124a892d authored by Andrii Nakryiko's avatar Andrii Nakryiko Committed by Alexei Starovoitov

selftests/bpf: Test TYPE_EXISTS and TYPE_SIZE CO-RE relocations

Add selftests for TYPE_EXISTS and TYPE_SIZE relocations, testing correctness
of relocations and handling of type compatiblity/incompatibility.

If __builtin_preserve_type_info() is not supported by compiler, skip tests.
Signed-off-by: default avatarAndrii Nakryiko <andriin@fb.com>
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
Acked-by: default avatarYonghong Song <yhs@fb.com>
Link: https://lore.kernel.org/bpf/20200819194519.3375898-3-andriin@fb.com
parent 3fc32f40
...@@ -177,14 +177,13 @@ ...@@ -177,14 +177,13 @@
.fails = true, \ .fails = true, \
} }
#define EXISTENCE_CASE_COMMON(name) \ #define FIELD_EXISTS_CASE_COMMON(name) \
.case_name = #name, \ .case_name = #name, \
.bpf_obj_file = "test_core_reloc_existence.o", \ .bpf_obj_file = "test_core_reloc_existence.o", \
.btf_src_file = "btf__core_reloc_" #name ".o", \ .btf_src_file = "btf__core_reloc_" #name ".o" \
.relaxed_core_relocs = true
#define EXISTENCE_ERR_CASE(name) { \ #define FIELD_EXISTS_ERR_CASE(name) { \
EXISTENCE_CASE_COMMON(name), \ FIELD_EXISTS_CASE_COMMON(name), \
.fails = true, \ .fails = true, \
} }
...@@ -253,6 +252,23 @@ ...@@ -253,6 +252,23 @@
.fails = true, \ .fails = true, \
} }
#define TYPE_BASED_CASE_COMMON(name) \
.case_name = #name, \
.bpf_obj_file = "test_core_reloc_type_based.o", \
.btf_src_file = "btf__core_reloc_" #name ".o" \
#define TYPE_BASED_CASE(name, ...) { \
TYPE_BASED_CASE_COMMON(name), \
.output = STRUCT_TO_CHAR_PTR(core_reloc_type_based_output) \
__VA_ARGS__, \
.output_len = sizeof(struct core_reloc_type_based_output), \
}
#define TYPE_BASED_ERR_CASE(name) { \
TYPE_BASED_CASE_COMMON(name), \
.fails = true, \
}
struct core_reloc_test_case { struct core_reloc_test_case {
const char *case_name; const char *case_name;
const char *bpf_obj_file; const char *bpf_obj_file;
...@@ -364,7 +380,7 @@ static struct core_reloc_test_case test_cases[] = { ...@@ -364,7 +380,7 @@ static struct core_reloc_test_case test_cases[] = {
/* validate field existence checks */ /* validate field existence checks */
{ {
EXISTENCE_CASE_COMMON(existence), FIELD_EXISTS_CASE_COMMON(existence),
.input = STRUCT_TO_CHAR_PTR(core_reloc_existence) { .input = STRUCT_TO_CHAR_PTR(core_reloc_existence) {
.a = 1, .a = 1,
.b = 2, .b = 2,
...@@ -388,7 +404,7 @@ static struct core_reloc_test_case test_cases[] = { ...@@ -388,7 +404,7 @@ static struct core_reloc_test_case test_cases[] = {
.output_len = sizeof(struct core_reloc_existence_output), .output_len = sizeof(struct core_reloc_existence_output),
}, },
{ {
EXISTENCE_CASE_COMMON(existence___minimal), FIELD_EXISTS_CASE_COMMON(existence___minimal),
.input = STRUCT_TO_CHAR_PTR(core_reloc_existence___minimal) { .input = STRUCT_TO_CHAR_PTR(core_reloc_existence___minimal) {
.a = 42, .a = 42,
}, },
...@@ -408,12 +424,12 @@ static struct core_reloc_test_case test_cases[] = { ...@@ -408,12 +424,12 @@ static struct core_reloc_test_case test_cases[] = {
.output_len = sizeof(struct core_reloc_existence_output), .output_len = sizeof(struct core_reloc_existence_output),
}, },
EXISTENCE_ERR_CASE(existence__err_int_sz), FIELD_EXISTS_ERR_CASE(existence__err_int_sz),
EXISTENCE_ERR_CASE(existence__err_int_type), FIELD_EXISTS_ERR_CASE(existence__err_int_type),
EXISTENCE_ERR_CASE(existence__err_int_kind), FIELD_EXISTS_ERR_CASE(existence__err_int_kind),
EXISTENCE_ERR_CASE(existence__err_arr_kind), FIELD_EXISTS_ERR_CASE(existence__err_arr_kind),
EXISTENCE_ERR_CASE(existence__err_arr_value_type), FIELD_EXISTS_ERR_CASE(existence__err_arr_value_type),
EXISTENCE_ERR_CASE(existence__err_struct_type), FIELD_EXISTS_ERR_CASE(existence__err_struct_type),
/* bitfield relocation checks */ /* bitfield relocation checks */
BITFIELDS_CASE(bitfields, { BITFIELDS_CASE(bitfields, {
...@@ -453,11 +469,73 @@ static struct core_reloc_test_case test_cases[] = { ...@@ -453,11 +469,73 @@ static struct core_reloc_test_case test_cases[] = {
SIZE_CASE(size), SIZE_CASE(size),
SIZE_CASE(size___diff_sz), SIZE_CASE(size___diff_sz),
SIZE_ERR_CASE(size___err_ambiguous), SIZE_ERR_CASE(size___err_ambiguous),
/* validate type existence and size relocations */
TYPE_BASED_CASE(type_based, {
.struct_exists = 1,
.union_exists = 1,
.enum_exists = 1,
.typedef_named_struct_exists = 1,
.typedef_anon_struct_exists = 1,
.typedef_struct_ptr_exists = 1,
.typedef_int_exists = 1,
.typedef_enum_exists = 1,
.typedef_void_ptr_exists = 1,
.typedef_func_proto_exists = 1,
.typedef_arr_exists = 1,
.struct_sz = sizeof(struct a_struct),
.union_sz = sizeof(union a_union),
.enum_sz = sizeof(enum an_enum),
.typedef_named_struct_sz = sizeof(named_struct_typedef),
.typedef_anon_struct_sz = sizeof(anon_struct_typedef),
.typedef_struct_ptr_sz = sizeof(struct_ptr_typedef),
.typedef_int_sz = sizeof(int_typedef),
.typedef_enum_sz = sizeof(enum_typedef),
.typedef_void_ptr_sz = sizeof(void_ptr_typedef),
.typedef_func_proto_sz = sizeof(func_proto_typedef),
.typedef_arr_sz = sizeof(arr_typedef),
}),
TYPE_BASED_CASE(type_based___all_missing, {
/* all zeros */
}),
TYPE_BASED_CASE(type_based___diff_sz, {
.struct_exists = 1,
.union_exists = 1,
.enum_exists = 1,
.typedef_named_struct_exists = 1,
.typedef_anon_struct_exists = 1,
.typedef_struct_ptr_exists = 1,
.typedef_int_exists = 1,
.typedef_enum_exists = 1,
.typedef_void_ptr_exists = 1,
.typedef_func_proto_exists = 1,
.typedef_arr_exists = 1,
.struct_sz = sizeof(struct a_struct___diff_sz),
.union_sz = sizeof(union a_union___diff_sz),
.enum_sz = sizeof(enum an_enum___diff_sz),
.typedef_named_struct_sz = sizeof(named_struct_typedef___diff_sz),
.typedef_anon_struct_sz = sizeof(anon_struct_typedef___diff_sz),
.typedef_struct_ptr_sz = sizeof(struct_ptr_typedef___diff_sz),
.typedef_int_sz = sizeof(int_typedef___diff_sz),
.typedef_enum_sz = sizeof(enum_typedef___diff_sz),
.typedef_void_ptr_sz = sizeof(void_ptr_typedef___diff_sz),
.typedef_func_proto_sz = sizeof(func_proto_typedef___diff_sz),
.typedef_arr_sz = sizeof(arr_typedef___diff_sz),
}),
TYPE_BASED_CASE(type_based___incompat, {
.enum_exists = 1,
.enum_sz = sizeof(enum an_enum),
}),
TYPE_BASED_CASE(type_based___fn_wrong_args, {
.struct_exists = 1,
.struct_sz = sizeof(struct a_struct),
}),
}; };
struct data { struct data {
char in[256]; char in[256];
char out[256]; char out[256];
bool skip;
uint64_t my_pid_tgid; uint64_t my_pid_tgid;
}; };
...@@ -516,15 +594,10 @@ void test_core_reloc(void) ...@@ -516,15 +594,10 @@ void test_core_reloc(void)
load_attr.log_level = 0; load_attr.log_level = 0;
load_attr.target_btf_path = test_case->btf_src_file; load_attr.target_btf_path = test_case->btf_src_file;
err = bpf_object__load_xattr(&load_attr); err = bpf_object__load_xattr(&load_attr);
if (test_case->fails) { if (err) {
CHECK(!err, "obj_load_fail", if (!test_case->fails)
"should fail to load prog '%s'\n", probe_name); CHECK(false, "obj_load", "failed to load prog '%s': %d\n", probe_name, err);
goto cleanup; goto cleanup;
} else {
if (CHECK(err, "obj_load",
"failed to load prog '%s': %d\n",
probe_name, err))
goto cleanup;
} }
data_map = bpf_object__find_map_by_name(obj, "test_cor.bss"); data_map = bpf_object__find_map_by_name(obj, "test_cor.bss");
...@@ -552,6 +625,16 @@ void test_core_reloc(void) ...@@ -552,6 +625,16 @@ void test_core_reloc(void)
/* trigger test run */ /* trigger test run */
usleep(1); usleep(1);
if (data->skip) {
test__skip();
goto cleanup;
}
if (test_case->fails) {
CHECK(false, "obj_load_fail", "should fail to load prog '%s'\n", probe_name);
goto cleanup;
}
equal = memcmp(data->out, test_case->output, equal = memcmp(data->out, test_case->output,
test_case->output_len) == 0; test_case->output_len) == 0;
if (CHECK(!equal, "check_result", if (CHECK(!equal, "check_result",
......
#include "core_reloc_types.h"
void f(struct core_reloc_type_based x) {}
#include "core_reloc_types.h"
void f(struct core_reloc_type_based___all_missing x) {}
#include "core_reloc_types.h"
void f(struct core_reloc_type_based___diff_sz x) {}
#include "core_reloc_types.h"
void f(struct core_reloc_type_based___fn_wrong_args x) {}
#include "core_reloc_types.h"
void f(struct core_reloc_type_based___incompat x) {}
...@@ -652,7 +652,7 @@ struct core_reloc_misc_extensible { ...@@ -652,7 +652,7 @@ struct core_reloc_misc_extensible {
}; };
/* /*
* EXISTENCE * FIELD EXISTENCE
*/ */
struct core_reloc_existence_output { struct core_reloc_existence_output {
int a_exists; int a_exists;
...@@ -834,3 +834,203 @@ struct core_reloc_size___err_ambiguous2 { ...@@ -834,3 +834,203 @@ struct core_reloc_size___err_ambiguous2 {
void *ptr_field; void *ptr_field;
enum { VALUE___2 = 123 } enum_field; enum { VALUE___2 = 123 } enum_field;
}; };
/*
* TYPE EXISTENCE & SIZE
*/
struct core_reloc_type_based_output {
bool struct_exists;
bool union_exists;
bool enum_exists;
bool typedef_named_struct_exists;
bool typedef_anon_struct_exists;
bool typedef_struct_ptr_exists;
bool typedef_int_exists;
bool typedef_enum_exists;
bool typedef_void_ptr_exists;
bool typedef_func_proto_exists;
bool typedef_arr_exists;
int struct_sz;
int union_sz;
int enum_sz;
int typedef_named_struct_sz;
int typedef_anon_struct_sz;
int typedef_struct_ptr_sz;
int typedef_int_sz;
int typedef_enum_sz;
int typedef_void_ptr_sz;
int typedef_func_proto_sz;
int typedef_arr_sz;
};
struct a_struct {
int x;
};
union a_union {
int y;
int z;
};
typedef struct a_struct named_struct_typedef;
typedef struct { int x, y, z; } anon_struct_typedef;
typedef struct {
int a, b, c;
} *struct_ptr_typedef;
enum an_enum {
AN_ENUM_VAL1 = 1,
AN_ENUM_VAL2 = 2,
AN_ENUM_VAL3 = 3,
};
typedef int int_typedef;
typedef enum { TYPEDEF_ENUM_VAL1, TYPEDEF_ENUM_VAL2 } enum_typedef;
typedef void *void_ptr_typedef;
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;
};
/* no types in target */
struct core_reloc_type_based___all_missing {
};
/* different type sizes, extra modifiers, anon vs named enums, etc */
struct a_struct___diff_sz {
long x;
int y;
char z;
};
union a_union___diff_sz {
char yy;
char zz;
};
typedef struct a_struct___diff_sz named_struct_typedef___diff_sz;
typedef struct { long xx, yy, zzz; } anon_struct_typedef___diff_sz;
typedef struct {
char aa[1], bb[2], cc[3];
} *struct_ptr_typedef___diff_sz;
enum an_enum___diff_sz {
AN_ENUM_VAL1___diff_sz = 0x123412341234,
AN_ENUM_VAL2___diff_sz = 2,
};
typedef unsigned long int_typedef___diff_sz;
typedef enum an_enum___diff_sz enum_typedef___diff_sz;
typedef const void * const void_ptr_typedef___diff_sz;
typedef int_typedef___diff_sz (*func_proto_typedef___diff_sz)(char);
typedef int arr_typedef___diff_sz[2];
struct core_reloc_type_based___diff_sz {
struct a_struct___diff_sz f1;
union a_union___diff_sz f2;
enum an_enum___diff_sz f3;
named_struct_typedef___diff_sz f4;
anon_struct_typedef___diff_sz f5;
struct_ptr_typedef___diff_sz f6;
int_typedef___diff_sz f7;
enum_typedef___diff_sz f8;
void_ptr_typedef___diff_sz f9;
func_proto_typedef___diff_sz f10;
arr_typedef___diff_sz f11;
};
/* incompatibilities between target and local types */
union a_struct___incompat { /* union instead of struct */
int x;
};
struct a_union___incompat { /* struct instead of union */
int y;
int z;
};
/* typedef to union, not to struct */
typedef union a_struct___incompat named_struct_typedef___incompat;
/* typedef to void pointer, instead of struct */
typedef void *anon_struct_typedef___incompat;
/* extra pointer indirection */
typedef struct {
int a, b, c;
} **struct_ptr_typedef___incompat;
/* typedef of a struct with int, instead of int */
typedef struct { int x; } int_typedef___incompat;
/* typedef to func_proto, instead of enum */
typedef int (*enum_typedef___incompat)(void);
/* pointer to char instead of void */
typedef char *void_ptr_typedef___incompat;
/* void return type instead of int */
typedef void (*func_proto_typedef___incompat)(long);
/* multi-dimensional array instead of a single-dimensional */
typedef int arr_typedef___incompat[20][2];
struct core_reloc_type_based___incompat {
union a_struct___incompat f1;
struct a_union___incompat f2;
/* the only valid one is enum, to check that something still succeeds */
enum an_enum f3;
named_struct_typedef___incompat f4;
anon_struct_typedef___incompat f5;
struct_ptr_typedef___incompat f6;
int_typedef___incompat f7;
enum_typedef___incompat f8;
void_ptr_typedef___incompat f9;
func_proto_typedef___incompat f10;
arr_typedef___incompat f11;
};
/* func_proto with incompatible signature */
typedef void (*func_proto_typedef___fn_wrong_ret1)(long);
typedef int * (*func_proto_typedef___fn_wrong_ret2)(long);
typedef struct { int x; } int_struct_typedef;
typedef int_struct_typedef (*func_proto_typedef___fn_wrong_ret3)(long);
typedef int (*func_proto_typedef___fn_wrong_arg)(void *);
typedef int (*func_proto_typedef___fn_wrong_arg_cnt1)(long, long);
typedef int (*func_proto_typedef___fn_wrong_arg_cnt2)(void);
struct core_reloc_type_based___fn_wrong_args {
/* one valid type to make sure relos still work */
struct a_struct f1;
func_proto_typedef___fn_wrong_ret1 f2;
func_proto_typedef___fn_wrong_ret2 f3;
func_proto_typedef___fn_wrong_ret3 f4;
func_proto_typedef___fn_wrong_arg f5;
func_proto_typedef___fn_wrong_arg_cnt1 f6;
func_proto_typedef___fn_wrong_arg_cnt2 f7;
};
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include <linux/bpf.h> #include <linux/bpf.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <bpf/bpf_helpers.h> #include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h> #include <bpf/bpf_core_read.h>
...@@ -11,6 +12,7 @@ char _license[] SEC("license") = "GPL"; ...@@ -11,6 +12,7 @@ char _license[] SEC("license") = "GPL";
struct { struct {
char in[256]; char in[256];
char out[256]; char out[256];
bool skip;
uint64_t my_pid_tgid; uint64_t my_pid_tgid;
} data = {}; } data = {};
......
// 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 = {};
struct a_struct {
int x;
};
union a_union {
int y;
int z;
};
typedef struct a_struct named_struct_typedef;
typedef struct { int x, y, z; } anon_struct_typedef;
typedef struct {
int a, b, c;
} *struct_ptr_typedef;
enum an_enum {
AN_ENUM_VAL1 = 1,
AN_ENUM_VAL2 = 2,
AN_ENUM_VAL3 = 3,
};
typedef int int_typedef;
typedef enum { TYPEDEF_ENUM_VAL1, TYPEDEF_ENUM_VAL2 } enum_typedef;
typedef void *void_ptr_typedef;
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;
bool enum_exists;
bool typedef_named_struct_exists;
bool typedef_anon_struct_exists;
bool typedef_struct_ptr_exists;
bool typedef_int_exists;
bool typedef_enum_exists;
bool typedef_void_ptr_exists;
bool typedef_func_proto_exists;
bool typedef_arr_exists;
int struct_sz;
int union_sz;
int enum_sz;
int typedef_named_struct_sz;
int typedef_anon_struct_sz;
int typedef_struct_ptr_sz;
int typedef_int_sz;
int typedef_enum_sz;
int typedef_void_ptr_sz;
int typedef_func_proto_sz;
int typedef_arr_sz;
};
SEC("raw_tracepoint/sys_enter")
int test_core_type_based(void *ctx)
{
#if __has_builtin(__builtin_preserve_type_info)
struct core_reloc_type_based_output *out = (void *)&data.out;
out->struct_exists = bpf_core_type_exists(struct a_struct);
out->union_exists = bpf_core_type_exists(union a_union);
out->enum_exists = bpf_core_type_exists(enum an_enum);
out->typedef_named_struct_exists = bpf_core_type_exists(named_struct_typedef);
out->typedef_anon_struct_exists = bpf_core_type_exists(anon_struct_typedef);
out->typedef_struct_ptr_exists = bpf_core_type_exists(struct_ptr_typedef);
out->typedef_int_exists = bpf_core_type_exists(int_typedef);
out->typedef_enum_exists = bpf_core_type_exists(enum_typedef);
out->typedef_void_ptr_exists = bpf_core_type_exists(void_ptr_typedef);
out->typedef_func_proto_exists = bpf_core_type_exists(func_proto_typedef);
out->typedef_arr_exists = bpf_core_type_exists(arr_typedef);
out->struct_sz = bpf_core_type_size(struct a_struct);
out->union_sz = bpf_core_type_size(union a_union);
out->enum_sz = bpf_core_type_size(enum an_enum);
out->typedef_named_struct_sz = bpf_core_type_size(named_struct_typedef);
out->typedef_anon_struct_sz = bpf_core_type_size(anon_struct_typedef);
out->typedef_struct_ptr_sz = bpf_core_type_size(struct_ptr_typedef);
out->typedef_int_sz = bpf_core_type_size(int_typedef);
out->typedef_enum_sz = bpf_core_type_size(enum_typedef);
out->typedef_void_ptr_sz = bpf_core_type_size(void_ptr_typedef);
out->typedef_func_proto_sz = bpf_core_type_size(func_proto_typedef);
out->typedef_arr_sz = bpf_core_type_size(arr_typedef);
#else
data.skip = true;
#endif
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment