1 // SPDX-License-Identifier: GPL-2.0
2 #include <test_progs.h>
3 #include "progs/core_reloc_types.h"
5 #include <sys/syscall.h>
7 #define STRUCT_TO_CHAR_PTR(struct_name) (const char *)&(struct struct_name)
9 #define FLAVORS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \
15 #define FLAVORS_CASE_COMMON(name) \
17 .bpf_obj_file = "test_core_reloc_flavors.o", \
18 .btf_src_file = "btf__core_reloc_" #name ".o" \
20 #define FLAVORS_CASE(name) { \
21 FLAVORS_CASE_COMMON(name), \
22 .input = FLAVORS_DATA(core_reloc_##name), \
23 .input_len = sizeof(struct core_reloc_##name), \
24 .output = FLAVORS_DATA(core_reloc_flavors), \
25 .output_len = sizeof(struct core_reloc_flavors), \
28 #define FLAVORS_ERR_CASE(name) { \
29 FLAVORS_CASE_COMMON(name), \
33 #define NESTING_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \
34 .a = { .a = { .a = 42 } }, \
35 .b = { .b = { .b = 0xc001 } }, \
38 #define NESTING_CASE_COMMON(name) \
40 .bpf_obj_file = "test_core_reloc_nesting.o", \
41 .btf_src_file = "btf__core_reloc_" #name ".o"
43 #define NESTING_CASE(name) { \
44 NESTING_CASE_COMMON(name), \
45 .input = NESTING_DATA(core_reloc_##name), \
46 .input_len = sizeof(struct core_reloc_##name), \
47 .output = NESTING_DATA(core_reloc_nesting), \
48 .output_len = sizeof(struct core_reloc_nesting) \
51 #define NESTING_ERR_CASE(name) { \
52 NESTING_CASE_COMMON(name), \
56 #define ARRAYS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \
58 .b = { [1] = { [2] = { [3] = 2 } } }, \
59 .c = { [1] = { .c = 3 } }, \
60 .d = { [0] = { [0] = { .d = 4 } } }, \
63 #define ARRAYS_CASE_COMMON(name) \
65 .bpf_obj_file = "test_core_reloc_arrays.o", \
66 .btf_src_file = "btf__core_reloc_" #name ".o"
68 #define ARRAYS_CASE(name) { \
69 ARRAYS_CASE_COMMON(name), \
70 .input = ARRAYS_DATA(core_reloc_##name), \
71 .input_len = sizeof(struct core_reloc_##name), \
72 .output = STRUCT_TO_CHAR_PTR(core_reloc_arrays_output) { \
79 .output_len = sizeof(struct core_reloc_arrays_output) \
82 #define ARRAYS_ERR_CASE(name) { \
83 ARRAYS_CASE_COMMON(name), \
87 #define PRIMITIVES_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \
95 #define PRIMITIVES_CASE_COMMON(name) \
97 .bpf_obj_file = "test_core_reloc_primitives.o", \
98 .btf_src_file = "btf__core_reloc_" #name ".o"
100 #define PRIMITIVES_CASE(name) { \
101 PRIMITIVES_CASE_COMMON(name), \
102 .input = PRIMITIVES_DATA(core_reloc_##name), \
103 .input_len = sizeof(struct core_reloc_##name), \
104 .output = PRIMITIVES_DATA(core_reloc_primitives), \
105 .output_len = sizeof(struct core_reloc_primitives), \
108 #define PRIMITIVES_ERR_CASE(name) { \
109 PRIMITIVES_CASE_COMMON(name), \
113 #define MODS_CASE(name) { \
114 .case_name = #name, \
115 .bpf_obj_file = "test_core_reloc_mods.o", \
116 .btf_src_file = "btf__core_reloc_" #name ".o", \
117 .input = STRUCT_TO_CHAR_PTR(core_reloc_##name) { \
127 .input_len = sizeof(struct core_reloc_##name), \
128 .output = STRUCT_TO_CHAR_PTR(core_reloc_mods_output) { \
129 .a = 1, .b = 2, .c = 3, .d = 4, \
130 .e = 5, .f = 6, .g = 7, .h = 8, \
132 .output_len = sizeof(struct core_reloc_mods_output), \
135 #define PTR_AS_ARR_CASE(name) { \
136 .case_name = #name, \
137 .bpf_obj_file = "test_core_reloc_ptr_as_arr.o", \
138 .btf_src_file = "btf__core_reloc_" #name ".o", \
139 .input = (const char *)&(struct core_reloc_##name []){ \
144 .input_len = 3 * sizeof(struct core_reloc_##name), \
145 .output = STRUCT_TO_CHAR_PTR(core_reloc_ptr_as_arr) { \
148 .output_len = sizeof(struct core_reloc_ptr_as_arr), \
151 #define INTS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \
162 #define INTS_CASE_COMMON(name) \
163 .case_name = #name, \
164 .bpf_obj_file = "test_core_reloc_ints.o", \
165 .btf_src_file = "btf__core_reloc_" #name ".o"
167 #define INTS_CASE(name) { \
168 INTS_CASE_COMMON(name), \
169 .input = INTS_DATA(core_reloc_##name), \
170 .input_len = sizeof(struct core_reloc_##name), \
171 .output = INTS_DATA(core_reloc_ints), \
172 .output_len = sizeof(struct core_reloc_ints), \
175 #define INTS_ERR_CASE(name) { \
176 INTS_CASE_COMMON(name), \
180 #define EXISTENCE_CASE_COMMON(name) \
181 .case_name = #name, \
182 .bpf_obj_file = "test_core_reloc_existence.o", \
183 .btf_src_file = "btf__core_reloc_" #name ".o", \
184 .relaxed_core_relocs = true
186 #define EXISTENCE_ERR_CASE(name) { \
187 EXISTENCE_CASE_COMMON(name), \
191 #define BITFIELDS_CASE_COMMON(objfile, test_name_prefix, name) \
192 .case_name = test_name_prefix#name, \
193 .bpf_obj_file = objfile, \
194 .btf_src_file = "btf__core_reloc_" #name ".o"
196 #define BITFIELDS_CASE(name, ...) { \
197 BITFIELDS_CASE_COMMON("test_core_reloc_bitfields_probed.o", \
199 .input = STRUCT_TO_CHAR_PTR(core_reloc_##name) __VA_ARGS__, \
200 .input_len = sizeof(struct core_reloc_##name), \
201 .output = STRUCT_TO_CHAR_PTR(core_reloc_bitfields_output) \
203 .output_len = sizeof(struct core_reloc_bitfields_output), \
205 BITFIELDS_CASE_COMMON("test_core_reloc_bitfields_direct.o", \
207 .input = STRUCT_TO_CHAR_PTR(core_reloc_##name) __VA_ARGS__, \
208 .input_len = sizeof(struct core_reloc_##name), \
209 .output = STRUCT_TO_CHAR_PTR(core_reloc_bitfields_output) \
211 .output_len = sizeof(struct core_reloc_bitfields_output), \
212 .direct_raw_tp = true, \
216 #define BITFIELDS_ERR_CASE(name) { \
217 BITFIELDS_CASE_COMMON("test_core_reloc_bitfields_probed.o", \
221 BITFIELDS_CASE_COMMON("test_core_reloc_bitfields_direct.o", \
223 .direct_raw_tp = true, \
227 #define SIZE_CASE_COMMON(name) \
228 .case_name = #name, \
229 .bpf_obj_file = "test_core_reloc_size.o", \
230 .btf_src_file = "btf__core_reloc_" #name ".o", \
231 .relaxed_core_relocs = true
233 #define SIZE_OUTPUT_DATA(type) \
234 STRUCT_TO_CHAR_PTR(core_reloc_size_output) { \
235 .int_sz = sizeof(((type *)0)->int_field), \
236 .struct_sz = sizeof(((type *)0)->struct_field), \
237 .union_sz = sizeof(((type *)0)->union_field), \
238 .arr_sz = sizeof(((type *)0)->arr_field), \
239 .arr_elem_sz = sizeof(((type *)0)->arr_field[0]), \
240 .ptr_sz = 8, /* always 8-byte pointer for BPF */ \
241 .enum_sz = sizeof(((type *)0)->enum_field), \
244 #define SIZE_CASE(name) { \
245 SIZE_CASE_COMMON(name), \
247 .output = SIZE_OUTPUT_DATA(struct core_reloc_##name), \
248 .output_len = sizeof(struct core_reloc_size_output), \
251 #define SIZE_ERR_CASE(name) { \
252 SIZE_CASE_COMMON(name), \
256 struct core_reloc_test_case {
257 const char *case_name;
258 const char *bpf_obj_file;
259 const char *btf_src_file;
265 bool relaxed_core_relocs;
269 static struct core_reloc_test_case test_cases[] = {
270 /* validate we can find kernel image and use its BTF for relocs */
272 .case_name = "kernel",
273 .bpf_obj_file = "test_core_reloc_kernel.o",
274 .btf_src_file = NULL, /* load from /lib/modules/$(uname -r) */
277 .output = STRUCT_TO_CHAR_PTR(core_reloc_kernel_output) {
278 .valid = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, },
279 .comm = "test_progs",
280 .comm_len = sizeof("test_progs"),
282 .output_len = sizeof(struct core_reloc_kernel_output),
285 /* validate BPF program can use multiple flavors to match against
286 * single target BTF type
288 FLAVORS_CASE(flavors),
290 FLAVORS_ERR_CASE(flavors__err_wrong_name),
292 /* various struct/enum nesting and resolution scenarios */
293 NESTING_CASE(nesting),
294 NESTING_CASE(nesting___anon_embed),
295 NESTING_CASE(nesting___struct_union_mixup),
296 NESTING_CASE(nesting___extra_nesting),
297 NESTING_CASE(nesting___dup_compat_types),
299 NESTING_ERR_CASE(nesting___err_missing_field),
300 NESTING_ERR_CASE(nesting___err_array_field),
301 NESTING_ERR_CASE(nesting___err_missing_container),
302 NESTING_ERR_CASE(nesting___err_nonstruct_container),
303 NESTING_ERR_CASE(nesting___err_array_container),
304 NESTING_ERR_CASE(nesting___err_dup_incompat_types),
305 NESTING_ERR_CASE(nesting___err_partial_match_dups),
306 NESTING_ERR_CASE(nesting___err_too_deep),
308 /* various array access relocation scenarios */
310 ARRAYS_CASE(arrays___diff_arr_dim),
311 ARRAYS_CASE(arrays___diff_arr_val_sz),
312 ARRAYS_CASE(arrays___equiv_zero_sz_arr),
313 ARRAYS_CASE(arrays___fixed_arr),
315 ARRAYS_ERR_CASE(arrays___err_too_small),
316 ARRAYS_ERR_CASE(arrays___err_too_shallow),
317 ARRAYS_ERR_CASE(arrays___err_non_array),
318 ARRAYS_ERR_CASE(arrays___err_wrong_val_type1),
319 ARRAYS_ERR_CASE(arrays___err_wrong_val_type2),
320 ARRAYS_ERR_CASE(arrays___err_bad_zero_sz_arr),
322 /* enum/ptr/int handling scenarios */
323 PRIMITIVES_CASE(primitives),
324 PRIMITIVES_CASE(primitives___diff_enum_def),
325 PRIMITIVES_CASE(primitives___diff_func_proto),
326 PRIMITIVES_CASE(primitives___diff_ptr_type),
328 PRIMITIVES_ERR_CASE(primitives___err_non_enum),
329 PRIMITIVES_ERR_CASE(primitives___err_non_int),
330 PRIMITIVES_ERR_CASE(primitives___err_non_ptr),
332 /* const/volatile/restrict and typedefs scenarios */
334 MODS_CASE(mods___mod_swap),
335 MODS_CASE(mods___typedefs),
337 /* handling "ptr is an array" semantics */
338 PTR_AS_ARR_CASE(ptr_as_arr),
339 PTR_AS_ARR_CASE(ptr_as_arr___diff_sz),
341 /* int signedness/sizing/bitfield handling */
343 INTS_CASE(ints___bool),
344 INTS_CASE(ints___reverse_sign),
346 /* validate edge cases of capturing relocations */
349 .bpf_obj_file = "test_core_reloc_misc.o",
350 .btf_src_file = "btf__core_reloc_misc.o",
351 .input = (const char *)&(struct core_reloc_misc_extensible[]){
353 { .a = 2 }, /* not read */
356 .input_len = 4 * sizeof(int),
357 .output = STRUCT_TO_CHAR_PTR(core_reloc_misc_output) {
360 .c = 0, /* BUG in clang, should be 3 */
362 .output_len = sizeof(struct core_reloc_misc_output),
365 /* validate field existence checks */
367 EXISTENCE_CASE_COMMON(existence),
368 .input = STRUCT_TO_CHAR_PTR(core_reloc_existence) {
375 .input_len = sizeof(struct core_reloc_existence),
376 .output = STRUCT_TO_CHAR_PTR(core_reloc_existence_output) {
388 .output_len = sizeof(struct core_reloc_existence_output),
391 EXISTENCE_CASE_COMMON(existence___minimal),
392 .input = STRUCT_TO_CHAR_PTR(core_reloc_existence___minimal) {
395 .input_len = sizeof(struct core_reloc_existence___minimal),
396 .output = STRUCT_TO_CHAR_PTR(core_reloc_existence_output) {
403 .b_value = 0xff000002u,
404 .c_value = 0xff000003u,
405 .arr_value = 0xff000004u,
406 .s_value = 0xff000005u,
408 .output_len = sizeof(struct core_reloc_existence_output),
411 EXISTENCE_ERR_CASE(existence__err_int_sz),
412 EXISTENCE_ERR_CASE(existence__err_int_type),
413 EXISTENCE_ERR_CASE(existence__err_int_kind),
414 EXISTENCE_ERR_CASE(existence__err_arr_kind),
415 EXISTENCE_ERR_CASE(existence__err_arr_value_type),
416 EXISTENCE_ERR_CASE(existence__err_struct_type),
418 /* bitfield relocation checks */
419 BITFIELDS_CASE(bitfields, {
428 BITFIELDS_CASE(bitfields___bit_sz_change, {
435 .s32 = -0x3FEDCBA987654321LL,
437 BITFIELDS_CASE(bitfields___bitfield_vs_int, {
438 .ub1 = 0xFEDCBA9876543210LL,
440 .ub7 = -0x7EDCBA987654321LL,
441 .sb4 = -0x6123456789ABCDELL,
444 .s32 = 0x0ADEADBEEFBADB0BLL,
446 BITFIELDS_CASE(bitfields___just_big_enough, {
448 .ub2 = 0x0812345678FEDCBALL,
450 BITFIELDS_ERR_CASE(bitfields___err_too_big_bitfield),
452 /* size relocation checks */
454 SIZE_CASE(size___diff_sz),
460 uint64_t my_pid_tgid;
463 static size_t roundup_page(size_t sz)
465 long page_size = sysconf(_SC_PAGE_SIZE);
466 return (sz + page_size - 1) / page_size * page_size;
469 void test_core_reloc(void)
471 const size_t mmap_sz = roundup_page(sizeof(struct data));
472 struct bpf_object_load_attr load_attr = {};
473 struct core_reloc_test_case *test_case;
474 const char *tp_name, *probe_name;
475 int err, duration = 0, i, equal;
476 struct bpf_link *link = NULL;
477 struct bpf_map *data_map;
478 struct bpf_program *prog;
479 struct bpf_object *obj;
480 uint64_t my_pid_tgid;
482 void *mmap_data = NULL;
484 my_pid_tgid = getpid() | ((uint64_t)syscall(SYS_gettid) << 32);
486 for (i = 0; i < ARRAY_SIZE(test_cases); i++) {
487 test_case = &test_cases[i];
488 if (!test__start_subtest(test_case->case_name))
491 DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts,
492 .relaxed_core_relocs = test_case->relaxed_core_relocs,
495 obj = bpf_object__open_file(test_case->bpf_obj_file, &opts);
496 if (CHECK(IS_ERR(obj), "obj_open", "failed to open '%s': %ld\n",
497 test_case->bpf_obj_file, PTR_ERR(obj)))
500 /* for typed raw tracepoints, NULL should be specified */
501 if (test_case->direct_raw_tp) {
502 probe_name = "tp_btf/sys_enter";
505 probe_name = "raw_tracepoint/sys_enter";
506 tp_name = "sys_enter";
509 prog = bpf_object__find_program_by_title(obj, probe_name);
510 if (CHECK(!prog, "find_probe",
511 "prog '%s' not found\n", probe_name))
515 load_attr.log_level = 0;
516 load_attr.target_btf_path = test_case->btf_src_file;
517 err = bpf_object__load_xattr(&load_attr);
518 if (test_case->fails) {
519 CHECK(!err, "obj_load_fail",
520 "should fail to load prog '%s'\n", probe_name);
523 if (CHECK(err, "obj_load",
524 "failed to load prog '%s': %d\n",
529 data_map = bpf_object__find_map_by_name(obj, "test_cor.bss");
530 if (CHECK(!data_map, "find_data_map", "data map not found\n"))
533 mmap_data = mmap(NULL, mmap_sz, PROT_READ | PROT_WRITE,
534 MAP_SHARED, bpf_map__fd(data_map), 0);
535 if (CHECK(mmap_data == MAP_FAILED, "mmap",
536 ".bss mmap failed: %d", errno)) {
542 memset(mmap_data, 0, sizeof(*data));
543 memcpy(data->in, test_case->input, test_case->input_len);
544 data->my_pid_tgid = my_pid_tgid;
546 link = bpf_program__attach_raw_tracepoint(prog, tp_name);
547 if (CHECK(IS_ERR(link), "attach_raw_tp", "err %ld\n",
551 /* trigger test run */
554 equal = memcmp(data->out, test_case->output,
555 test_case->output_len) == 0;
556 if (CHECK(!equal, "check_result",
557 "input/output data don't match\n")) {
560 for (j = 0; j < test_case->input_len; j++) {
561 printf("input byte #%d: 0x%02hhx\n",
562 j, test_case->input[j]);
564 for (j = 0; j < test_case->output_len; j++) {
565 printf("output byte #%d: EXP 0x%02hhx GOT 0x%02hhx\n",
566 j, test_case->output[j], data->out[j]);
573 CHECK_FAIL(munmap(mmap_data, mmap_sz));
576 if (!IS_ERR_OR_NULL(link)) {
577 bpf_link__destroy(link);
580 bpf_object__close(obj);