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