core_reloc.c (30338B)
1// SPDX-License-Identifier: GPL-2.0 2#include <test_progs.h> 3#include "progs/core_reloc_types.h" 4#include "bpf_testmod/bpf_testmod.h" 5#include <linux/limits.h> 6#include <sys/mman.h> 7#include <sys/syscall.h> 8#include <bpf/btf.h> 9 10static int duration = 0; 11 12#define STRUCT_TO_CHAR_PTR(struct_name) (const char *)&(struct struct_name) 13 14#define MODULES_CASE(name, pg_name, tp_name) { \ 15 .case_name = name, \ 16 .bpf_obj_file = "test_core_reloc_module.o", \ 17 .btf_src_file = NULL, /* find in kernel module BTFs */ \ 18 .input = "", \ 19 .input_len = 0, \ 20 .output = STRUCT_TO_CHAR_PTR(core_reloc_module_output) { \ 21 .read_ctx_sz = sizeof(struct bpf_testmod_test_read_ctx),\ 22 .read_ctx_exists = true, \ 23 .buf_exists = true, \ 24 .len_exists = true, \ 25 .off_exists = true, \ 26 .len = 123, \ 27 .off = 0, \ 28 .comm = "test_progs", \ 29 .comm_len = sizeof("test_progs"), \ 30 }, \ 31 .output_len = sizeof(struct core_reloc_module_output), \ 32 .prog_name = pg_name, \ 33 .raw_tp_name = tp_name, \ 34 .trigger = __trigger_module_test_read, \ 35 .needs_testmod = true, \ 36} 37 38#define FLAVORS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \ 39 .a = 42, \ 40 .b = 0xc001, \ 41 .c = 0xbeef, \ 42} 43 44#define FLAVORS_CASE_COMMON(name) \ 45 .case_name = #name, \ 46 .bpf_obj_file = "test_core_reloc_flavors.o", \ 47 .btf_src_file = "btf__core_reloc_" #name ".o", \ 48 .raw_tp_name = "sys_enter", \ 49 .prog_name = "test_core_flavors" \ 50 51#define FLAVORS_CASE(name) { \ 52 FLAVORS_CASE_COMMON(name), \ 53 .input = FLAVORS_DATA(core_reloc_##name), \ 54 .input_len = sizeof(struct core_reloc_##name), \ 55 .output = FLAVORS_DATA(core_reloc_flavors), \ 56 .output_len = sizeof(struct core_reloc_flavors), \ 57} 58 59#define FLAVORS_ERR_CASE(name) { \ 60 FLAVORS_CASE_COMMON(name), \ 61 .fails = true, \ 62} 63 64#define NESTING_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \ 65 .a = { .a = { .a = 42 } }, \ 66 .b = { .b = { .b = 0xc001 } }, \ 67} 68 69#define NESTING_CASE_COMMON(name) \ 70 .case_name = #name, \ 71 .bpf_obj_file = "test_core_reloc_nesting.o", \ 72 .btf_src_file = "btf__core_reloc_" #name ".o", \ 73 .raw_tp_name = "sys_enter", \ 74 .prog_name = "test_core_nesting" \ 75 76#define NESTING_CASE(name) { \ 77 NESTING_CASE_COMMON(name), \ 78 .input = NESTING_DATA(core_reloc_##name), \ 79 .input_len = sizeof(struct core_reloc_##name), \ 80 .output = NESTING_DATA(core_reloc_nesting), \ 81 .output_len = sizeof(struct core_reloc_nesting) \ 82} 83 84#define NESTING_ERR_CASE(name) { \ 85 NESTING_CASE_COMMON(name), \ 86 .fails = true, \ 87} 88 89#define ARRAYS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \ 90 .a = { [2] = 1 }, \ 91 .b = { [1] = { [2] = { [3] = 2 } } }, \ 92 .c = { [1] = { .c = 3 } }, \ 93 .d = { [0] = { [0] = { .d = 4 } } }, \ 94} 95 96#define ARRAYS_CASE_COMMON(name) \ 97 .case_name = #name, \ 98 .bpf_obj_file = "test_core_reloc_arrays.o", \ 99 .btf_src_file = "btf__core_reloc_" #name ".o", \ 100 .raw_tp_name = "sys_enter", \ 101 .prog_name = "test_core_arrays" \ 102 103#define ARRAYS_CASE(name) { \ 104 ARRAYS_CASE_COMMON(name), \ 105 .input = ARRAYS_DATA(core_reloc_##name), \ 106 .input_len = sizeof(struct core_reloc_##name), \ 107 .output = STRUCT_TO_CHAR_PTR(core_reloc_arrays_output) { \ 108 .a2 = 1, \ 109 .b123 = 2, \ 110 .c1c = 3, \ 111 .d00d = 4, \ 112 .f10c = 0, \ 113 }, \ 114 .output_len = sizeof(struct core_reloc_arrays_output) \ 115} 116 117#define ARRAYS_ERR_CASE(name) { \ 118 ARRAYS_CASE_COMMON(name), \ 119 .fails = true, \ 120} 121 122#define PRIMITIVES_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \ 123 .a = 1, \ 124 .b = 2, \ 125 .c = 3, \ 126 .d = (void *)4, \ 127 .f = (void *)5, \ 128} 129 130#define PRIMITIVES_CASE_COMMON(name) \ 131 .case_name = #name, \ 132 .bpf_obj_file = "test_core_reloc_primitives.o", \ 133 .btf_src_file = "btf__core_reloc_" #name ".o", \ 134 .raw_tp_name = "sys_enter", \ 135 .prog_name = "test_core_primitives" \ 136 137#define PRIMITIVES_CASE(name) { \ 138 PRIMITIVES_CASE_COMMON(name), \ 139 .input = PRIMITIVES_DATA(core_reloc_##name), \ 140 .input_len = sizeof(struct core_reloc_##name), \ 141 .output = PRIMITIVES_DATA(core_reloc_primitives), \ 142 .output_len = sizeof(struct core_reloc_primitives), \ 143} 144 145#define PRIMITIVES_ERR_CASE(name) { \ 146 PRIMITIVES_CASE_COMMON(name), \ 147 .fails = true, \ 148} 149 150#define MODS_CASE(name) { \ 151 .case_name = #name, \ 152 .bpf_obj_file = "test_core_reloc_mods.o", \ 153 .btf_src_file = "btf__core_reloc_" #name ".o", \ 154 .input = STRUCT_TO_CHAR_PTR(core_reloc_##name) { \ 155 .a = 1, \ 156 .b = 2, \ 157 .c = (void *)3, \ 158 .d = (void *)4, \ 159 .e = { [2] = 5 }, \ 160 .f = { [1] = 6 }, \ 161 .g = { .x = 7 }, \ 162 .h = { .y = 8 }, \ 163 }, \ 164 .input_len = sizeof(struct core_reloc_##name), \ 165 .output = STRUCT_TO_CHAR_PTR(core_reloc_mods_output) { \ 166 .a = 1, .b = 2, .c = 3, .d = 4, \ 167 .e = 5, .f = 6, .g = 7, .h = 8, \ 168 }, \ 169 .output_len = sizeof(struct core_reloc_mods_output), \ 170 .raw_tp_name = "sys_enter", \ 171 .prog_name = "test_core_mods", \ 172} 173 174#define PTR_AS_ARR_CASE(name) { \ 175 .case_name = #name, \ 176 .bpf_obj_file = "test_core_reloc_ptr_as_arr.o", \ 177 .btf_src_file = "btf__core_reloc_" #name ".o", \ 178 .input = (const char *)&(struct core_reloc_##name []){ \ 179 { .a = 1 }, \ 180 { .a = 2 }, \ 181 { .a = 3 }, \ 182 }, \ 183 .input_len = 3 * sizeof(struct core_reloc_##name), \ 184 .output = STRUCT_TO_CHAR_PTR(core_reloc_ptr_as_arr) { \ 185 .a = 3, \ 186 }, \ 187 .output_len = sizeof(struct core_reloc_ptr_as_arr), \ 188 .raw_tp_name = "sys_enter", \ 189 .prog_name = "test_core_ptr_as_arr", \ 190} 191 192#define INTS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \ 193 .u8_field = 1, \ 194 .s8_field = 2, \ 195 .u16_field = 3, \ 196 .s16_field = 4, \ 197 .u32_field = 5, \ 198 .s32_field = 6, \ 199 .u64_field = 7, \ 200 .s64_field = 8, \ 201} 202 203#define INTS_CASE_COMMON(name) \ 204 .case_name = #name, \ 205 .bpf_obj_file = "test_core_reloc_ints.o", \ 206 .btf_src_file = "btf__core_reloc_" #name ".o", \ 207 .raw_tp_name = "sys_enter", \ 208 .prog_name = "test_core_ints" 209 210#define INTS_CASE(name) { \ 211 INTS_CASE_COMMON(name), \ 212 .input = INTS_DATA(core_reloc_##name), \ 213 .input_len = sizeof(struct core_reloc_##name), \ 214 .output = INTS_DATA(core_reloc_ints), \ 215 .output_len = sizeof(struct core_reloc_ints), \ 216} 217 218#define INTS_ERR_CASE(name) { \ 219 INTS_CASE_COMMON(name), \ 220 .fails = true, \ 221} 222 223#define FIELD_EXISTS_CASE_COMMON(name) \ 224 .case_name = #name, \ 225 .bpf_obj_file = "test_core_reloc_existence.o", \ 226 .btf_src_file = "btf__core_reloc_" #name ".o", \ 227 .raw_tp_name = "sys_enter", \ 228 .prog_name = "test_core_existence" 229 230#define BITFIELDS_CASE_COMMON(objfile, test_name_prefix, name) \ 231 .case_name = test_name_prefix#name, \ 232 .bpf_obj_file = objfile, \ 233 .btf_src_file = "btf__core_reloc_" #name ".o" 234 235#define BITFIELDS_CASE(name, ...) { \ 236 BITFIELDS_CASE_COMMON("test_core_reloc_bitfields_probed.o", \ 237 "probed:", name), \ 238 .input = STRUCT_TO_CHAR_PTR(core_reloc_##name) __VA_ARGS__, \ 239 .input_len = sizeof(struct core_reloc_##name), \ 240 .output = STRUCT_TO_CHAR_PTR(core_reloc_bitfields_output) \ 241 __VA_ARGS__, \ 242 .output_len = sizeof(struct core_reloc_bitfields_output), \ 243 .raw_tp_name = "sys_enter", \ 244 .prog_name = "test_core_bitfields", \ 245}, { \ 246 BITFIELDS_CASE_COMMON("test_core_reloc_bitfields_direct.o", \ 247 "direct:", name), \ 248 .input = STRUCT_TO_CHAR_PTR(core_reloc_##name) __VA_ARGS__, \ 249 .input_len = sizeof(struct core_reloc_##name), \ 250 .output = STRUCT_TO_CHAR_PTR(core_reloc_bitfields_output) \ 251 __VA_ARGS__, \ 252 .output_len = sizeof(struct core_reloc_bitfields_output), \ 253 .prog_name = "test_core_bitfields_direct", \ 254} 255 256 257#define BITFIELDS_ERR_CASE(name) { \ 258 BITFIELDS_CASE_COMMON("test_core_reloc_bitfields_probed.o", \ 259 "probed:", name), \ 260 .fails = true, \ 261 .raw_tp_name = "sys_enter", \ 262 .prog_name = "test_core_bitfields", \ 263}, { \ 264 BITFIELDS_CASE_COMMON("test_core_reloc_bitfields_direct.o", \ 265 "direct:", name), \ 266 .fails = true, \ 267 .prog_name = "test_core_bitfields_direct", \ 268} 269 270#define SIZE_CASE_COMMON(name) \ 271 .case_name = #name, \ 272 .bpf_obj_file = "test_core_reloc_size.o", \ 273 .btf_src_file = "btf__core_reloc_" #name ".o", \ 274 .raw_tp_name = "sys_enter", \ 275 .prog_name = "test_core_size" 276 277#define SIZE_OUTPUT_DATA(type) \ 278 STRUCT_TO_CHAR_PTR(core_reloc_size_output) { \ 279 .int_sz = sizeof(((type *)0)->int_field), \ 280 .int_off = offsetof(type, int_field), \ 281 .struct_sz = sizeof(((type *)0)->struct_field), \ 282 .struct_off = offsetof(type, struct_field), \ 283 .union_sz = sizeof(((type *)0)->union_field), \ 284 .union_off = offsetof(type, union_field), \ 285 .arr_sz = sizeof(((type *)0)->arr_field), \ 286 .arr_off = offsetof(type, arr_field), \ 287 .arr_elem_sz = sizeof(((type *)0)->arr_field[1]), \ 288 .arr_elem_off = offsetof(type, arr_field[1]), \ 289 .ptr_sz = 8, /* always 8-byte pointer for BPF */ \ 290 .ptr_off = offsetof(type, ptr_field), \ 291 .enum_sz = sizeof(((type *)0)->enum_field), \ 292 .enum_off = offsetof(type, enum_field), \ 293 .float_sz = sizeof(((type *)0)->float_field), \ 294 .float_off = offsetof(type, float_field), \ 295 } 296 297#define SIZE_CASE(name) { \ 298 SIZE_CASE_COMMON(name), \ 299 .input_len = 0, \ 300 .output = SIZE_OUTPUT_DATA(struct core_reloc_##name), \ 301 .output_len = sizeof(struct core_reloc_size_output), \ 302} 303 304#define SIZE_ERR_CASE(name) { \ 305 SIZE_CASE_COMMON(name), \ 306 .fails = true, \ 307} 308 309#define TYPE_BASED_CASE_COMMON(name) \ 310 .case_name = #name, \ 311 .bpf_obj_file = "test_core_reloc_type_based.o", \ 312 .btf_src_file = "btf__core_reloc_" #name ".o", \ 313 .raw_tp_name = "sys_enter", \ 314 .prog_name = "test_core_type_based" 315 316#define TYPE_BASED_CASE(name, ...) { \ 317 TYPE_BASED_CASE_COMMON(name), \ 318 .output = STRUCT_TO_CHAR_PTR(core_reloc_type_based_output) \ 319 __VA_ARGS__, \ 320 .output_len = sizeof(struct core_reloc_type_based_output), \ 321} 322 323#define TYPE_BASED_ERR_CASE(name) { \ 324 TYPE_BASED_CASE_COMMON(name), \ 325 .fails = true, \ 326} 327 328#define TYPE_ID_CASE_COMMON(name) \ 329 .case_name = #name, \ 330 .bpf_obj_file = "test_core_reloc_type_id.o", \ 331 .btf_src_file = "btf__core_reloc_" #name ".o", \ 332 .raw_tp_name = "sys_enter", \ 333 .prog_name = "test_core_type_id" 334 335#define TYPE_ID_CASE(name, setup_fn) { \ 336 TYPE_ID_CASE_COMMON(name), \ 337 .output = STRUCT_TO_CHAR_PTR(core_reloc_type_id_output) {}, \ 338 .output_len = sizeof(struct core_reloc_type_id_output), \ 339 .setup = setup_fn, \ 340} 341 342#define TYPE_ID_ERR_CASE(name) { \ 343 TYPE_ID_CASE_COMMON(name), \ 344 .fails = true, \ 345} 346 347#define ENUMVAL_CASE_COMMON(name) \ 348 .case_name = #name, \ 349 .bpf_obj_file = "test_core_reloc_enumval.o", \ 350 .btf_src_file = "btf__core_reloc_" #name ".o", \ 351 .raw_tp_name = "sys_enter", \ 352 .prog_name = "test_core_enumval" 353 354#define ENUMVAL_CASE(name, ...) { \ 355 ENUMVAL_CASE_COMMON(name), \ 356 .output = STRUCT_TO_CHAR_PTR(core_reloc_enumval_output) \ 357 __VA_ARGS__, \ 358 .output_len = sizeof(struct core_reloc_enumval_output), \ 359} 360 361#define ENUMVAL_ERR_CASE(name) { \ 362 ENUMVAL_CASE_COMMON(name), \ 363 .fails = true, \ 364} 365 366struct core_reloc_test_case; 367 368typedef int (*setup_test_fn)(struct core_reloc_test_case *test); 369typedef int (*trigger_test_fn)(const struct core_reloc_test_case *test); 370 371struct core_reloc_test_case { 372 const char *case_name; 373 const char *bpf_obj_file; 374 const char *btf_src_file; 375 const char *input; 376 int input_len; 377 const char *output; 378 int output_len; 379 bool fails; 380 bool needs_testmod; 381 bool relaxed_core_relocs; 382 const char *prog_name; 383 const char *raw_tp_name; 384 setup_test_fn setup; 385 trigger_test_fn trigger; 386}; 387 388static int find_btf_type(const struct btf *btf, const char *name, __u32 kind) 389{ 390 int id; 391 392 id = btf__find_by_name_kind(btf, name, kind); 393 if (CHECK(id <= 0, "find_type_id", "failed to find '%s', kind %d: %d\n", name, kind, id)) 394 return -1; 395 396 return id; 397} 398 399static int setup_type_id_case_local(struct core_reloc_test_case *test) 400{ 401 struct core_reloc_type_id_output *exp = (void *)test->output; 402 struct btf *local_btf = btf__parse(test->bpf_obj_file, NULL); 403 struct btf *targ_btf = btf__parse(test->btf_src_file, NULL); 404 const struct btf_type *t; 405 const char *name; 406 int i; 407 408 if (!ASSERT_OK_PTR(local_btf, "local_btf") || !ASSERT_OK_PTR(targ_btf, "targ_btf")) { 409 btf__free(local_btf); 410 btf__free(targ_btf); 411 return -EINVAL; 412 } 413 414 exp->local_anon_struct = -1; 415 exp->local_anon_union = -1; 416 exp->local_anon_enum = -1; 417 exp->local_anon_func_proto_ptr = -1; 418 exp->local_anon_void_ptr = -1; 419 exp->local_anon_arr = -1; 420 421 for (i = 1; i < btf__type_cnt(local_btf); i++) 422 { 423 t = btf__type_by_id(local_btf, i); 424 /* we are interested only in anonymous types */ 425 if (t->name_off) 426 continue; 427 428 if (btf_is_struct(t) && btf_vlen(t) && 429 (name = btf__name_by_offset(local_btf, btf_members(t)[0].name_off)) && 430 strcmp(name, "marker_field") == 0) { 431 exp->local_anon_struct = i; 432 } else if (btf_is_union(t) && btf_vlen(t) && 433 (name = btf__name_by_offset(local_btf, btf_members(t)[0].name_off)) && 434 strcmp(name, "marker_field") == 0) { 435 exp->local_anon_union = i; 436 } else if (btf_is_enum(t) && btf_vlen(t) && 437 (name = btf__name_by_offset(local_btf, btf_enum(t)[0].name_off)) && 438 strcmp(name, "MARKER_ENUM_VAL") == 0) { 439 exp->local_anon_enum = i; 440 } else if (btf_is_ptr(t) && (t = btf__type_by_id(local_btf, t->type))) { 441 if (btf_is_func_proto(t) && (t = btf__type_by_id(local_btf, t->type)) && 442 btf_is_int(t) && (name = btf__name_by_offset(local_btf, t->name_off)) && 443 strcmp(name, "_Bool") == 0) { 444 /* ptr -> func_proto -> _Bool */ 445 exp->local_anon_func_proto_ptr = i; 446 } else if (btf_is_void(t)) { 447 /* ptr -> void */ 448 exp->local_anon_void_ptr = i; 449 } 450 } else if (btf_is_array(t) && (t = btf__type_by_id(local_btf, btf_array(t)->type)) && 451 btf_is_int(t) && (name = btf__name_by_offset(local_btf, t->name_off)) && 452 strcmp(name, "_Bool") == 0) { 453 /* _Bool[] */ 454 exp->local_anon_arr = i; 455 } 456 } 457 458 exp->local_struct = find_btf_type(local_btf, "a_struct", BTF_KIND_STRUCT); 459 exp->local_union = find_btf_type(local_btf, "a_union", BTF_KIND_UNION); 460 exp->local_enum = find_btf_type(local_btf, "an_enum", BTF_KIND_ENUM); 461 exp->local_int = find_btf_type(local_btf, "int", BTF_KIND_INT); 462 exp->local_struct_typedef = find_btf_type(local_btf, "named_struct_typedef", BTF_KIND_TYPEDEF); 463 exp->local_func_proto_typedef = find_btf_type(local_btf, "func_proto_typedef", BTF_KIND_TYPEDEF); 464 exp->local_arr_typedef = find_btf_type(local_btf, "arr_typedef", BTF_KIND_TYPEDEF); 465 466 btf__free(local_btf); 467 btf__free(targ_btf); 468 return 0; 469} 470 471static int setup_type_id_case_success(struct core_reloc_test_case *test) { 472 struct core_reloc_type_id_output *exp = (void *)test->output; 473 struct btf *targ_btf; 474 int err; 475 476 err = setup_type_id_case_local(test); 477 if (err) 478 return err; 479 480 targ_btf = btf__parse(test->btf_src_file, NULL); 481 482 exp->targ_struct = find_btf_type(targ_btf, "a_struct", BTF_KIND_STRUCT); 483 exp->targ_union = find_btf_type(targ_btf, "a_union", BTF_KIND_UNION); 484 exp->targ_enum = find_btf_type(targ_btf, "an_enum", BTF_KIND_ENUM); 485 exp->targ_int = find_btf_type(targ_btf, "int", BTF_KIND_INT); 486 exp->targ_struct_typedef = find_btf_type(targ_btf, "named_struct_typedef", BTF_KIND_TYPEDEF); 487 exp->targ_func_proto_typedef = find_btf_type(targ_btf, "func_proto_typedef", BTF_KIND_TYPEDEF); 488 exp->targ_arr_typedef = find_btf_type(targ_btf, "arr_typedef", BTF_KIND_TYPEDEF); 489 490 btf__free(targ_btf); 491 return 0; 492} 493 494static int setup_type_id_case_failure(struct core_reloc_test_case *test) 495{ 496 struct core_reloc_type_id_output *exp = (void *)test->output; 497 int err; 498 499 err = setup_type_id_case_local(test); 500 if (err) 501 return err; 502 503 exp->targ_struct = 0; 504 exp->targ_union = 0; 505 exp->targ_enum = 0; 506 exp->targ_int = 0; 507 exp->targ_struct_typedef = 0; 508 exp->targ_func_proto_typedef = 0; 509 exp->targ_arr_typedef = 0; 510 511 return 0; 512} 513 514static int __trigger_module_test_read(const struct core_reloc_test_case *test) 515{ 516 struct core_reloc_module_output *exp = (void *)test->output; 517 518 trigger_module_test_read(exp->len); 519 return 0; 520} 521 522 523static const struct core_reloc_test_case test_cases[] = { 524 /* validate we can find kernel image and use its BTF for relocs */ 525 { 526 .case_name = "kernel", 527 .bpf_obj_file = "test_core_reloc_kernel.o", 528 .btf_src_file = NULL, /* load from /lib/modules/$(uname -r) */ 529 .input = "", 530 .input_len = 0, 531 .output = STRUCT_TO_CHAR_PTR(core_reloc_kernel_output) { 532 .valid = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }, 533 .comm = "test_progs", 534 .comm_len = sizeof("test_progs"), 535 }, 536 .output_len = sizeof(struct core_reloc_kernel_output), 537 .raw_tp_name = "sys_enter", 538 .prog_name = "test_core_kernel", 539 }, 540 541 /* validate we can find kernel module BTF types for relocs/attach */ 542 MODULES_CASE("module_probed", "test_core_module_probed", "bpf_testmod_test_read"), 543 MODULES_CASE("module_direct", "test_core_module_direct", NULL), 544 545 /* validate BPF program can use multiple flavors to match against 546 * single target BTF type 547 */ 548 FLAVORS_CASE(flavors), 549 550 FLAVORS_ERR_CASE(flavors__err_wrong_name), 551 552 /* various struct/enum nesting and resolution scenarios */ 553 NESTING_CASE(nesting), 554 NESTING_CASE(nesting___anon_embed), 555 NESTING_CASE(nesting___struct_union_mixup), 556 NESTING_CASE(nesting___extra_nesting), 557 NESTING_CASE(nesting___dup_compat_types), 558 559 NESTING_ERR_CASE(nesting___err_missing_field), 560 NESTING_ERR_CASE(nesting___err_array_field), 561 NESTING_ERR_CASE(nesting___err_missing_container), 562 NESTING_ERR_CASE(nesting___err_nonstruct_container), 563 NESTING_ERR_CASE(nesting___err_array_container), 564 NESTING_ERR_CASE(nesting___err_dup_incompat_types), 565 NESTING_ERR_CASE(nesting___err_partial_match_dups), 566 NESTING_ERR_CASE(nesting___err_too_deep), 567 568 /* various array access relocation scenarios */ 569 ARRAYS_CASE(arrays), 570 ARRAYS_CASE(arrays___diff_arr_dim), 571 ARRAYS_CASE(arrays___diff_arr_val_sz), 572 ARRAYS_CASE(arrays___equiv_zero_sz_arr), 573 ARRAYS_CASE(arrays___fixed_arr), 574 575 ARRAYS_ERR_CASE(arrays___err_too_small), 576 ARRAYS_ERR_CASE(arrays___err_too_shallow), 577 ARRAYS_ERR_CASE(arrays___err_non_array), 578 ARRAYS_ERR_CASE(arrays___err_wrong_val_type), 579 ARRAYS_ERR_CASE(arrays___err_bad_zero_sz_arr), 580 581 /* enum/ptr/int handling scenarios */ 582 PRIMITIVES_CASE(primitives), 583 PRIMITIVES_CASE(primitives___diff_enum_def), 584 PRIMITIVES_CASE(primitives___diff_func_proto), 585 PRIMITIVES_CASE(primitives___diff_ptr_type), 586 587 PRIMITIVES_ERR_CASE(primitives___err_non_enum), 588 PRIMITIVES_ERR_CASE(primitives___err_non_int), 589 PRIMITIVES_ERR_CASE(primitives___err_non_ptr), 590 591 /* const/volatile/restrict and typedefs scenarios */ 592 MODS_CASE(mods), 593 MODS_CASE(mods___mod_swap), 594 MODS_CASE(mods___typedefs), 595 596 /* handling "ptr is an array" semantics */ 597 PTR_AS_ARR_CASE(ptr_as_arr), 598 PTR_AS_ARR_CASE(ptr_as_arr___diff_sz), 599 600 /* int signedness/sizing/bitfield handling */ 601 INTS_CASE(ints), 602 INTS_CASE(ints___bool), 603 INTS_CASE(ints___reverse_sign), 604 605 /* validate edge cases of capturing relocations */ 606 { 607 .case_name = "misc", 608 .bpf_obj_file = "test_core_reloc_misc.o", 609 .btf_src_file = "btf__core_reloc_misc.o", 610 .input = (const char *)&(struct core_reloc_misc_extensible[]){ 611 { .a = 1 }, 612 { .a = 2 }, /* not read */ 613 { .a = 3 }, 614 }, 615 .input_len = 4 * sizeof(int), 616 .output = STRUCT_TO_CHAR_PTR(core_reloc_misc_output) { 617 .a = 1, 618 .b = 1, 619 .c = 0, /* BUG in clang, should be 3 */ 620 }, 621 .output_len = sizeof(struct core_reloc_misc_output), 622 .raw_tp_name = "sys_enter", 623 .prog_name = "test_core_misc", 624 }, 625 626 /* validate field existence checks */ 627 { 628 FIELD_EXISTS_CASE_COMMON(existence), 629 .input = STRUCT_TO_CHAR_PTR(core_reloc_existence) { 630 .a = 1, 631 .b = 2, 632 .c = 3, 633 .arr = { 4 }, 634 .s = { .x = 5 }, 635 }, 636 .input_len = sizeof(struct core_reloc_existence), 637 .output = STRUCT_TO_CHAR_PTR(core_reloc_existence_output) { 638 .a_exists = 1, 639 .b_exists = 1, 640 .c_exists = 1, 641 .arr_exists = 1, 642 .s_exists = 1, 643 .a_value = 1, 644 .b_value = 2, 645 .c_value = 3, 646 .arr_value = 4, 647 .s_value = 5, 648 }, 649 .output_len = sizeof(struct core_reloc_existence_output), 650 }, 651 { 652 FIELD_EXISTS_CASE_COMMON(existence___minimal), 653 .input = STRUCT_TO_CHAR_PTR(core_reloc_existence___minimal) { 654 .a = 42, 655 }, 656 .input_len = sizeof(struct core_reloc_existence___minimal), 657 .output = STRUCT_TO_CHAR_PTR(core_reloc_existence_output) { 658 .a_exists = 1, 659 .b_exists = 0, 660 .c_exists = 0, 661 .arr_exists = 0, 662 .s_exists = 0, 663 .a_value = 42, 664 .b_value = 0xff000002u, 665 .c_value = 0xff000003u, 666 .arr_value = 0xff000004u, 667 .s_value = 0xff000005u, 668 }, 669 .output_len = sizeof(struct core_reloc_existence_output), 670 }, 671 { 672 FIELD_EXISTS_CASE_COMMON(existence___wrong_field_defs), 673 .input = STRUCT_TO_CHAR_PTR(core_reloc_existence___wrong_field_defs) { 674 }, 675 .input_len = sizeof(struct core_reloc_existence___wrong_field_defs), 676 .output = STRUCT_TO_CHAR_PTR(core_reloc_existence_output) { 677 .a_exists = 0, 678 .b_exists = 0, 679 .c_exists = 0, 680 .arr_exists = 0, 681 .s_exists = 0, 682 .a_value = 0xff000001u, 683 .b_value = 0xff000002u, 684 .c_value = 0xff000003u, 685 .arr_value = 0xff000004u, 686 .s_value = 0xff000005u, 687 }, 688 .output_len = sizeof(struct core_reloc_existence_output), 689 }, 690 691 /* bitfield relocation checks */ 692 BITFIELDS_CASE(bitfields, { 693 .ub1 = 1, 694 .ub2 = 2, 695 .ub7 = 96, 696 .sb4 = -7, 697 .sb20 = -0x76543, 698 .u32 = 0x80000000, 699 .s32 = -0x76543210, 700 }), 701 BITFIELDS_CASE(bitfields___bit_sz_change, { 702 .ub1 = 6, 703 .ub2 = 0xABCDE, 704 .ub7 = 1, 705 .sb4 = -1, 706 .sb20 = -0x17654321, 707 .u32 = 0xBEEF, 708 .s32 = -0x3FEDCBA987654321LL, 709 }), 710 BITFIELDS_CASE(bitfields___bitfield_vs_int, { 711 .ub1 = 0xFEDCBA9876543210LL, 712 .ub2 = 0xA6, 713 .ub7 = -0x7EDCBA987654321LL, 714 .sb4 = -0x6123456789ABCDELL, 715 .sb20 = 0xD00DLL, 716 .u32 = -0x76543, 717 .s32 = 0x0ADEADBEEFBADB0BLL, 718 }), 719 BITFIELDS_CASE(bitfields___just_big_enough, { 720 .ub1 = 0xFLL, 721 .ub2 = 0x0812345678FEDCBALL, 722 }), 723 BITFIELDS_ERR_CASE(bitfields___err_too_big_bitfield), 724 725 /* field size and offset relocation checks */ 726 SIZE_CASE(size), 727 SIZE_CASE(size___diff_sz), 728 SIZE_CASE(size___diff_offs), 729 SIZE_ERR_CASE(size___err_ambiguous), 730 731 /* validate type existence and size relocations */ 732 TYPE_BASED_CASE(type_based, { 733 .struct_exists = 1, 734 .union_exists = 1, 735 .enum_exists = 1, 736 .typedef_named_struct_exists = 1, 737 .typedef_anon_struct_exists = 1, 738 .typedef_struct_ptr_exists = 1, 739 .typedef_int_exists = 1, 740 .typedef_enum_exists = 1, 741 .typedef_void_ptr_exists = 1, 742 .typedef_func_proto_exists = 1, 743 .typedef_arr_exists = 1, 744 .struct_sz = sizeof(struct a_struct), 745 .union_sz = sizeof(union a_union), 746 .enum_sz = sizeof(enum an_enum), 747 .typedef_named_struct_sz = sizeof(named_struct_typedef), 748 .typedef_anon_struct_sz = sizeof(anon_struct_typedef), 749 .typedef_struct_ptr_sz = sizeof(struct_ptr_typedef), 750 .typedef_int_sz = sizeof(int_typedef), 751 .typedef_enum_sz = sizeof(enum_typedef), 752 .typedef_void_ptr_sz = sizeof(void_ptr_typedef), 753 .typedef_func_proto_sz = sizeof(func_proto_typedef), 754 .typedef_arr_sz = sizeof(arr_typedef), 755 }), 756 TYPE_BASED_CASE(type_based___all_missing, { 757 /* all zeros */ 758 }), 759 TYPE_BASED_CASE(type_based___diff_sz, { 760 .struct_exists = 1, 761 .union_exists = 1, 762 .enum_exists = 1, 763 .typedef_named_struct_exists = 1, 764 .typedef_anon_struct_exists = 1, 765 .typedef_struct_ptr_exists = 1, 766 .typedef_int_exists = 1, 767 .typedef_enum_exists = 1, 768 .typedef_void_ptr_exists = 1, 769 .typedef_func_proto_exists = 1, 770 .typedef_arr_exists = 1, 771 .struct_sz = sizeof(struct a_struct___diff_sz), 772 .union_sz = sizeof(union a_union___diff_sz), 773 .enum_sz = sizeof(enum an_enum___diff_sz), 774 .typedef_named_struct_sz = sizeof(named_struct_typedef___diff_sz), 775 .typedef_anon_struct_sz = sizeof(anon_struct_typedef___diff_sz), 776 .typedef_struct_ptr_sz = sizeof(struct_ptr_typedef___diff_sz), 777 .typedef_int_sz = sizeof(int_typedef___diff_sz), 778 .typedef_enum_sz = sizeof(enum_typedef___diff_sz), 779 .typedef_void_ptr_sz = sizeof(void_ptr_typedef___diff_sz), 780 .typedef_func_proto_sz = sizeof(func_proto_typedef___diff_sz), 781 .typedef_arr_sz = sizeof(arr_typedef___diff_sz), 782 }), 783 TYPE_BASED_CASE(type_based___incompat, { 784 .enum_exists = 1, 785 .enum_sz = sizeof(enum an_enum), 786 }), 787 TYPE_BASED_CASE(type_based___fn_wrong_args, { 788 .struct_exists = 1, 789 .struct_sz = sizeof(struct a_struct), 790 }), 791 792 /* BTF_TYPE_ID_LOCAL/BTF_TYPE_ID_TARGET tests */ 793 TYPE_ID_CASE(type_id, setup_type_id_case_success), 794 TYPE_ID_CASE(type_id___missing_targets, setup_type_id_case_failure), 795 796 /* Enumerator value existence and value relocations */ 797 ENUMVAL_CASE(enumval, { 798 .named_val1_exists = true, 799 .named_val2_exists = true, 800 .named_val3_exists = true, 801 .anon_val1_exists = true, 802 .anon_val2_exists = true, 803 .anon_val3_exists = true, 804 .named_val1 = 1, 805 .named_val2 = 2, 806 .anon_val1 = 0x10, 807 .anon_val2 = 0x20, 808 }), 809 ENUMVAL_CASE(enumval___diff, { 810 .named_val1_exists = true, 811 .named_val2_exists = true, 812 .named_val3_exists = true, 813 .anon_val1_exists = true, 814 .anon_val2_exists = true, 815 .anon_val3_exists = true, 816 .named_val1 = 101, 817 .named_val2 = 202, 818 .anon_val1 = 0x11, 819 .anon_val2 = 0x22, 820 }), 821 ENUMVAL_CASE(enumval___val3_missing, { 822 .named_val1_exists = true, 823 .named_val2_exists = true, 824 .named_val3_exists = false, 825 .anon_val1_exists = true, 826 .anon_val2_exists = true, 827 .anon_val3_exists = false, 828 .named_val1 = 111, 829 .named_val2 = 222, 830 .anon_val1 = 0x111, 831 .anon_val2 = 0x222, 832 }), 833 ENUMVAL_ERR_CASE(enumval___err_missing), 834}; 835 836struct data { 837 char in[256]; 838 char out[256]; 839 bool skip; 840 uint64_t my_pid_tgid; 841}; 842 843static size_t roundup_page(size_t sz) 844{ 845 long page_size = sysconf(_SC_PAGE_SIZE); 846 return (sz + page_size - 1) / page_size * page_size; 847} 848 849static int run_btfgen(const char *src_btf, const char *dst_btf, const char *objpath) 850{ 851 char command[4096]; 852 int n; 853 854 n = snprintf(command, sizeof(command), 855 "./bpftool gen min_core_btf %s %s %s", 856 src_btf, dst_btf, objpath); 857 if (n < 0 || n >= sizeof(command)) 858 return -1; 859 860 return system(command); 861} 862 863static void run_core_reloc_tests(bool use_btfgen) 864{ 865 const size_t mmap_sz = roundup_page(sizeof(struct data)); 866 DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts); 867 struct core_reloc_test_case *test_case, test_case_copy; 868 const char *tp_name, *probe_name; 869 int err, i, equal, fd; 870 struct bpf_link *link = NULL; 871 struct bpf_map *data_map; 872 struct bpf_program *prog; 873 struct bpf_object *obj; 874 uint64_t my_pid_tgid; 875 struct data *data; 876 void *mmap_data = NULL; 877 878 my_pid_tgid = getpid() | ((uint64_t)syscall(SYS_gettid) << 32); 879 880 for (i = 0; i < ARRAY_SIZE(test_cases); i++) { 881 char btf_file[] = "/tmp/core_reloc.btf.XXXXXX"; 882 883 test_case_copy = test_cases[i]; 884 test_case = &test_case_copy; 885 886 if (!test__start_subtest(test_case->case_name)) 887 continue; 888 889 if (test_case->needs_testmod && !env.has_testmod) { 890 test__skip(); 891 continue; 892 } 893 894 /* generate a "minimal" BTF file and use it as source */ 895 if (use_btfgen) { 896 897 if (!test_case->btf_src_file || test_case->fails) { 898 test__skip(); 899 continue; 900 } 901 902 fd = mkstemp(btf_file); 903 if (!ASSERT_GE(fd, 0, "btf_tmp")) 904 continue; 905 close(fd); /* we only need the path */ 906 err = run_btfgen(test_case->btf_src_file, btf_file, 907 test_case->bpf_obj_file); 908 if (!ASSERT_OK(err, "run_btfgen")) 909 continue; 910 911 test_case->btf_src_file = btf_file; 912 } 913 914 if (test_case->setup) { 915 err = test_case->setup(test_case); 916 if (CHECK(err, "test_setup", "test #%d setup failed: %d\n", i, err)) 917 continue; 918 } 919 920 if (test_case->btf_src_file) { 921 err = access(test_case->btf_src_file, R_OK); 922 if (!ASSERT_OK(err, "btf_src_file")) 923 continue; 924 } 925 926 open_opts.btf_custom_path = test_case->btf_src_file; 927 obj = bpf_object__open_file(test_case->bpf_obj_file, &open_opts); 928 if (!ASSERT_OK_PTR(obj, "obj_open")) 929 goto cleanup; 930 931 probe_name = test_case->prog_name; 932 tp_name = test_case->raw_tp_name; /* NULL for tp_btf */ 933 prog = bpf_object__find_program_by_name(obj, probe_name); 934 if (CHECK(!prog, "find_probe", 935 "prog '%s' not found\n", probe_name)) 936 goto cleanup; 937 938 err = bpf_object__load(obj); 939 if (err) { 940 if (!test_case->fails) 941 ASSERT_OK(err, "obj_load"); 942 goto cleanup; 943 } 944 945 data_map = bpf_object__find_map_by_name(obj, ".bss"); 946 if (CHECK(!data_map, "find_data_map", "data map not found\n")) 947 goto cleanup; 948 949 mmap_data = mmap(NULL, mmap_sz, PROT_READ | PROT_WRITE, 950 MAP_SHARED, bpf_map__fd(data_map), 0); 951 if (CHECK(mmap_data == MAP_FAILED, "mmap", 952 ".bss mmap failed: %d", errno)) { 953 mmap_data = NULL; 954 goto cleanup; 955 } 956 data = mmap_data; 957 958 memset(mmap_data, 0, sizeof(*data)); 959 if (test_case->input_len) 960 memcpy(data->in, test_case->input, test_case->input_len); 961 data->my_pid_tgid = my_pid_tgid; 962 963 link = bpf_program__attach_raw_tracepoint(prog, tp_name); 964 if (!ASSERT_OK_PTR(link, "attach_raw_tp")) 965 goto cleanup; 966 967 /* trigger test run */ 968 if (test_case->trigger) { 969 if (!ASSERT_OK(test_case->trigger(test_case), "test_trigger")) 970 goto cleanup; 971 } else { 972 usleep(1); 973 } 974 975 if (data->skip) { 976 test__skip(); 977 goto cleanup; 978 } 979 980 if (!ASSERT_FALSE(test_case->fails, "obj_load_should_fail")) 981 goto cleanup; 982 983 equal = memcmp(data->out, test_case->output, 984 test_case->output_len) == 0; 985 if (CHECK(!equal, "check_result", 986 "input/output data don't match\n")) { 987 int j; 988 989 for (j = 0; j < test_case->input_len; j++) { 990 printf("input byte #%d: 0x%02hhx\n", 991 j, test_case->input[j]); 992 } 993 for (j = 0; j < test_case->output_len; j++) { 994 printf("output byte #%d: EXP 0x%02hhx GOT 0x%02hhx\n", 995 j, test_case->output[j], data->out[j]); 996 } 997 goto cleanup; 998 } 999 1000cleanup: 1001 if (mmap_data) { 1002 CHECK_FAIL(munmap(mmap_data, mmap_sz)); 1003 mmap_data = NULL; 1004 } 1005 if (use_btfgen) 1006 remove(test_case->btf_src_file); 1007 bpf_link__destroy(link); 1008 link = NULL; 1009 bpf_object__close(obj); 1010 } 1011} 1012 1013void test_core_reloc(void) 1014{ 1015 run_core_reloc_tests(false); 1016} 1017 1018void test_core_reloc_btfgen(void) 1019{ 1020 run_core_reloc_tests(true); 1021}