Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

libbpf: Support creating light skeleton of either endianness

Track target endianness in 'struct bpf_gen' and process in-memory data in
native byte-order, but on finalization convert the embedded loader BPF
insns to target endianness.

The light skeleton also includes a target-accessed data blob which is
heterogeneous and thus difficult to convert to target byte-order on
finalization. Add support functions to convert data to target endianness
as it is added to the blob.

Also add additional debug logging for data blob structure details and
skeleton loading.

Signed-off-by: Tony Ambardar <tony.ambardar@gmail.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/569562e1d5bf1cce80a1f1a3882461ee2da1ffd5.1726475448.git.tony.ambardar@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Tony Ambardar and committed by
Alexei Starovoitov
8ca3323d 0aed726c

+137 -51
+1
tools/lib/bpf/bpf_gen_internal.h
··· 34 34 void *data_cur; 35 35 void *insn_start; 36 36 void *insn_cur; 37 + bool swapped_endian; 37 38 ssize_t cleanup_label; 38 39 __u32 nr_progs; 39 40 __u32 nr_maps;
+133 -50
tools/lib/bpf/gen_loader.c
··· 401 401 opts->insns_sz = gen->insn_cur - gen->insn_start; 402 402 opts->data = gen->data_start; 403 403 opts->data_sz = gen->data_cur - gen->data_start; 404 + 405 + /* use target endianness for embedded loader */ 406 + if (gen->swapped_endian) { 407 + struct bpf_insn *insn = (struct bpf_insn *)opts->insns; 408 + int insn_cnt = opts->insns_sz / sizeof(struct bpf_insn); 409 + 410 + for (i = 0; i < insn_cnt; i++) 411 + bpf_insn_bswap(insn++); 412 + } 404 413 } 405 414 return gen->error; 406 415 } ··· 423 414 free(gen); 424 415 } 425 416 417 + /* 418 + * Fields of bpf_attr are set to values in native byte-order before being 419 + * written to the target-bound data blob, and may need endian conversion. 420 + * This macro allows providing the correct value in situ more simply than 421 + * writing a separate converter for *all fields* of *all records* included 422 + * in union bpf_attr. Note that sizeof(rval) should match the assignment 423 + * target to avoid runtime problems. 424 + */ 425 + #define tgt_endian(rval) ({ \ 426 + typeof(rval) _val = (rval); \ 427 + if (gen->swapped_endian) { \ 428 + switch (sizeof(_val)) { \ 429 + case 1: break; \ 430 + case 2: _val = bswap_16(_val); break; \ 431 + case 4: _val = bswap_32(_val); break; \ 432 + case 8: _val = bswap_64(_val); break; \ 433 + default: pr_warn("unsupported bswap size!\n"); \ 434 + } \ 435 + } \ 436 + _val; \ 437 + }) 438 + 426 439 void bpf_gen__load_btf(struct bpf_gen *gen, const void *btf_raw_data, 427 440 __u32 btf_raw_size) 428 441 { ··· 453 422 union bpf_attr attr; 454 423 455 424 memset(&attr, 0, attr_size); 456 - pr_debug("gen: load_btf: size %d\n", btf_raw_size); 457 425 btf_data = add_data(gen, btf_raw_data, btf_raw_size); 458 426 459 - attr.btf_size = btf_raw_size; 427 + attr.btf_size = tgt_endian(btf_raw_size); 460 428 btf_load_attr = add_data(gen, &attr, attr_size); 429 + pr_debug("gen: load_btf: off %d size %d, attr: off %d size %d\n", 430 + btf_data, btf_raw_size, btf_load_attr, attr_size); 461 431 462 432 /* populate union bpf_attr with user provided log details */ 463 433 move_ctx2blob(gen, attr_field(btf_load_attr, btf_log_level), 4, ··· 489 457 union bpf_attr attr; 490 458 491 459 memset(&attr, 0, attr_size); 492 - attr.map_type = map_type; 493 - attr.key_size = key_size; 494 - attr.value_size = value_size; 495 - attr.map_flags = map_attr->map_flags; 496 - attr.map_extra = map_attr->map_extra; 460 + attr.map_type = tgt_endian(map_type); 461 + attr.key_size = tgt_endian(key_size); 462 + attr.value_size = tgt_endian(value_size); 463 + attr.map_flags = tgt_endian(map_attr->map_flags); 464 + attr.map_extra = tgt_endian(map_attr->map_extra); 497 465 if (map_name) 498 466 libbpf_strlcpy(attr.map_name, map_name, sizeof(attr.map_name)); 499 - attr.numa_node = map_attr->numa_node; 500 - attr.map_ifindex = map_attr->map_ifindex; 501 - attr.max_entries = max_entries; 502 - attr.btf_key_type_id = map_attr->btf_key_type_id; 503 - attr.btf_value_type_id = map_attr->btf_value_type_id; 504 - 505 - pr_debug("gen: map_create: %s idx %d type %d value_type_id %d\n", 506 - attr.map_name, map_idx, map_type, attr.btf_value_type_id); 467 + attr.numa_node = tgt_endian(map_attr->numa_node); 468 + attr.map_ifindex = tgt_endian(map_attr->map_ifindex); 469 + attr.max_entries = tgt_endian(max_entries); 470 + attr.btf_key_type_id = tgt_endian(map_attr->btf_key_type_id); 471 + attr.btf_value_type_id = tgt_endian(map_attr->btf_value_type_id); 507 472 508 473 map_create_attr = add_data(gen, &attr, attr_size); 509 - if (attr.btf_value_type_id) 474 + pr_debug("gen: map_create: %s idx %d type %d value_type_id %d, attr: off %d size %d\n", 475 + map_name, map_idx, map_type, map_attr->btf_value_type_id, 476 + map_create_attr, attr_size); 477 + 478 + if (map_attr->btf_value_type_id) 510 479 /* populate union bpf_attr with btf_fd saved in the stack earlier */ 511 480 move_stack2blob(gen, attr_field(map_create_attr, btf_fd), 4, 512 481 stack_off(btf_fd)); 513 - switch (attr.map_type) { 482 + switch (map_type) { 514 483 case BPF_MAP_TYPE_ARRAY_OF_MAPS: 515 484 case BPF_MAP_TYPE_HASH_OF_MAPS: 516 485 move_stack2blob(gen, attr_field(map_create_attr, inner_map_fd), 4, ··· 531 498 /* emit MAP_CREATE command */ 532 499 emit_sys_bpf(gen, BPF_MAP_CREATE, map_create_attr, attr_size); 533 500 debug_ret(gen, "map_create %s idx %d type %d value_size %d value_btf_id %d", 534 - attr.map_name, map_idx, map_type, value_size, 535 - attr.btf_value_type_id); 501 + map_name, map_idx, map_type, value_size, 502 + map_attr->btf_value_type_id); 536 503 emit_check_err(gen); 537 504 /* remember map_fd in the stack, if successful */ 538 505 if (map_idx < 0) { ··· 817 784 emit_ksym_relo_log(gen, relo, kdesc->ref); 818 785 } 819 786 820 - static __u32 src_reg_mask(void) 787 + static __u32 src_reg_mask(struct bpf_gen *gen) 821 788 { 822 - #if defined(__LITTLE_ENDIAN_BITFIELD) 823 - return 0x0f; /* src_reg,dst_reg,... */ 824 - #elif defined(__BIG_ENDIAN_BITFIELD) 825 - return 0xf0; /* dst_reg,src_reg,... */ 789 + #if defined(__LITTLE_ENDIAN_BITFIELD) /* src_reg,dst_reg,... */ 790 + return gen->swapped_endian ? 0xf0 : 0x0f; 791 + #elif defined(__BIG_ENDIAN_BITFIELD) /* dst_reg,src_reg,... */ 792 + return gen->swapped_endian ? 0x0f : 0xf0; 826 793 #else 827 794 #error "Unsupported bit endianness, cannot proceed" 828 795 #endif ··· 873 840 emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, 3)); 874 841 clear_src_reg: 875 842 /* clear bpf_object__relocate_data's src_reg assignment, otherwise we get a verifier failure */ 876 - reg_mask = src_reg_mask(); 843 + reg_mask = src_reg_mask(gen); 877 844 emit(gen, BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_8, offsetofend(struct bpf_insn, code))); 878 845 emit(gen, BPF_ALU32_IMM(BPF_AND, BPF_REG_9, reg_mask)); 879 846 emit(gen, BPF_STX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, offsetofend(struct bpf_insn, code))); ··· 964 931 cleanup_core_relo(gen); 965 932 } 966 933 934 + /* Convert func, line, and core relo info blobs to target endianness */ 935 + static void info_blob_bswap(struct bpf_gen *gen, int func_info, int line_info, 936 + int core_relos, struct bpf_prog_load_opts *load_attr) 937 + { 938 + struct bpf_func_info *fi = gen->data_start + func_info; 939 + struct bpf_line_info *li = gen->data_start + line_info; 940 + struct bpf_core_relo *cr = gen->data_start + core_relos; 941 + int i; 942 + 943 + for (i = 0; i < load_attr->func_info_cnt; i++) 944 + bpf_func_info_bswap(fi++); 945 + 946 + for (i = 0; i < load_attr->line_info_cnt; i++) 947 + bpf_line_info_bswap(li++); 948 + 949 + for (i = 0; i < gen->core_relo_cnt; i++) 950 + bpf_core_relo_bswap(cr++); 951 + } 952 + 967 953 void bpf_gen__prog_load(struct bpf_gen *gen, 968 954 enum bpf_prog_type prog_type, const char *prog_name, 969 955 const char *license, struct bpf_insn *insns, size_t insn_cnt, 970 956 struct bpf_prog_load_opts *load_attr, int prog_idx) 971 957 { 958 + int func_info_tot_sz = load_attr->func_info_cnt * 959 + load_attr->func_info_rec_size; 960 + int line_info_tot_sz = load_attr->line_info_cnt * 961 + load_attr->line_info_rec_size; 962 + int core_relo_tot_sz = gen->core_relo_cnt * 963 + sizeof(struct bpf_core_relo); 972 964 int prog_load_attr, license_off, insns_off, func_info, line_info, core_relos; 973 965 int attr_size = offsetofend(union bpf_attr, core_relo_rec_size); 974 966 union bpf_attr attr; 975 967 976 968 memset(&attr, 0, attr_size); 977 - pr_debug("gen: prog_load: type %d insns_cnt %zd progi_idx %d\n", 978 - prog_type, insn_cnt, prog_idx); 979 969 /* add license string to blob of bytes */ 980 970 license_off = add_data(gen, license, strlen(license) + 1); 981 971 /* add insns to blob of bytes */ 982 972 insns_off = add_data(gen, insns, insn_cnt * sizeof(struct bpf_insn)); 973 + pr_debug("gen: prog_load: prog_idx %d type %d insn off %d insns_cnt %zd license off %d\n", 974 + prog_idx, prog_type, insns_off, insn_cnt, license_off); 983 975 984 - attr.prog_type = prog_type; 985 - attr.expected_attach_type = load_attr->expected_attach_type; 986 - attr.attach_btf_id = load_attr->attach_btf_id; 987 - attr.prog_ifindex = load_attr->prog_ifindex; 976 + /* convert blob insns to target endianness */ 977 + if (gen->swapped_endian) { 978 + struct bpf_insn *insn = gen->data_start + insns_off; 979 + int i; 980 + 981 + for (i = 0; i < insn_cnt; i++, insn++) 982 + bpf_insn_bswap(insn); 983 + } 984 + 985 + attr.prog_type = tgt_endian(prog_type); 986 + attr.expected_attach_type = tgt_endian(load_attr->expected_attach_type); 987 + attr.attach_btf_id = tgt_endian(load_attr->attach_btf_id); 988 + attr.prog_ifindex = tgt_endian(load_attr->prog_ifindex); 988 989 attr.kern_version = 0; 989 - attr.insn_cnt = (__u32)insn_cnt; 990 - attr.prog_flags = load_attr->prog_flags; 990 + attr.insn_cnt = tgt_endian((__u32)insn_cnt); 991 + attr.prog_flags = tgt_endian(load_attr->prog_flags); 991 992 992 - attr.func_info_rec_size = load_attr->func_info_rec_size; 993 - attr.func_info_cnt = load_attr->func_info_cnt; 994 - func_info = add_data(gen, load_attr->func_info, 995 - attr.func_info_cnt * attr.func_info_rec_size); 993 + attr.func_info_rec_size = tgt_endian(load_attr->func_info_rec_size); 994 + attr.func_info_cnt = tgt_endian(load_attr->func_info_cnt); 995 + func_info = add_data(gen, load_attr->func_info, func_info_tot_sz); 996 + pr_debug("gen: prog_load: func_info: off %d cnt %d rec size %d\n", 997 + func_info, load_attr->func_info_cnt, 998 + load_attr->func_info_rec_size); 996 999 997 - attr.line_info_rec_size = load_attr->line_info_rec_size; 998 - attr.line_info_cnt = load_attr->line_info_cnt; 999 - line_info = add_data(gen, load_attr->line_info, 1000 - attr.line_info_cnt * attr.line_info_rec_size); 1000 + attr.line_info_rec_size = tgt_endian(load_attr->line_info_rec_size); 1001 + attr.line_info_cnt = tgt_endian(load_attr->line_info_cnt); 1002 + line_info = add_data(gen, load_attr->line_info, line_info_tot_sz); 1003 + pr_debug("gen: prog_load: line_info: off %d cnt %d rec size %d\n", 1004 + line_info, load_attr->line_info_cnt, 1005 + load_attr->line_info_rec_size); 1001 1006 1002 - attr.core_relo_rec_size = sizeof(struct bpf_core_relo); 1003 - attr.core_relo_cnt = gen->core_relo_cnt; 1004 - core_relos = add_data(gen, gen->core_relos, 1005 - attr.core_relo_cnt * attr.core_relo_rec_size); 1007 + attr.core_relo_rec_size = tgt_endian((__u32)sizeof(struct bpf_core_relo)); 1008 + attr.core_relo_cnt = tgt_endian(gen->core_relo_cnt); 1009 + core_relos = add_data(gen, gen->core_relos, core_relo_tot_sz); 1010 + pr_debug("gen: prog_load: core_relos: off %d cnt %d rec size %zd\n", 1011 + core_relos, gen->core_relo_cnt, 1012 + sizeof(struct bpf_core_relo)); 1013 + 1014 + /* convert all info blobs to target endianness */ 1015 + if (gen->swapped_endian) 1016 + info_blob_bswap(gen, func_info, line_info, core_relos, load_attr); 1006 1017 1007 1018 libbpf_strlcpy(attr.prog_name, prog_name, sizeof(attr.prog_name)); 1008 1019 prog_load_attr = add_data(gen, &attr, attr_size); 1020 + pr_debug("gen: prog_load: attr: off %d size %d\n", 1021 + prog_load_attr, attr_size); 1009 1022 1010 1023 /* populate union bpf_attr with a pointer to license */ 1011 1024 emit_rel_store(gen, attr_field(prog_load_attr, license), license_off); ··· 1119 1040 int zero = 0; 1120 1041 1121 1042 memset(&attr, 0, attr_size); 1122 - pr_debug("gen: map_update_elem: idx %d\n", map_idx); 1123 1043 1124 1044 value = add_data(gen, pvalue, value_size); 1125 1045 key = add_data(gen, &zero, sizeof(zero)); ··· 1146 1068 emit(gen, BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel)); 1147 1069 1148 1070 map_update_attr = add_data(gen, &attr, attr_size); 1071 + pr_debug("gen: map_update_elem: idx %d, value: off %d size %d, attr: off %d size %d\n", 1072 + map_idx, value, value_size, map_update_attr, attr_size); 1149 1073 move_blob2blob(gen, attr_field(map_update_attr, map_fd), 4, 1150 1074 blob_fd_array_off(gen, map_idx)); 1151 1075 emit_rel_store(gen, attr_field(map_update_attr, key), key); ··· 1164 1084 int attr_size = offsetofend(union bpf_attr, flags); 1165 1085 int map_update_attr, key; 1166 1086 union bpf_attr attr; 1087 + int tgt_slot; 1167 1088 1168 1089 memset(&attr, 0, attr_size); 1169 - pr_debug("gen: populate_outer_map: outer %d key %d inner %d\n", 1170 - outer_map_idx, slot, inner_map_idx); 1171 1090 1172 - key = add_data(gen, &slot, sizeof(slot)); 1091 + tgt_slot = tgt_endian(slot); 1092 + key = add_data(gen, &tgt_slot, sizeof(tgt_slot)); 1173 1093 1174 1094 map_update_attr = add_data(gen, &attr, attr_size); 1095 + pr_debug("gen: populate_outer_map: outer %d key %d inner %d, attr: off %d size %d\n", 1096 + outer_map_idx, slot, inner_map_idx, map_update_attr, attr_size); 1175 1097 move_blob2blob(gen, attr_field(map_update_attr, map_fd), 4, 1176 1098 blob_fd_array_off(gen, outer_map_idx)); 1177 1099 emit_rel_store(gen, attr_field(map_update_attr, key), key); ··· 1194 1112 union bpf_attr attr; 1195 1113 1196 1114 memset(&attr, 0, attr_size); 1197 - pr_debug("gen: map_freeze: idx %d\n", map_idx); 1198 1115 map_freeze_attr = add_data(gen, &attr, attr_size); 1116 + pr_debug("gen: map_freeze: idx %d, attr: off %d size %d\n", 1117 + map_idx, map_freeze_attr, attr_size); 1199 1118 move_blob2blob(gen, attr_field(map_freeze_attr, map_fd), 4, 1200 1119 blob_fd_array_off(gen, map_idx)); 1201 1120 /* emit MAP_FREEZE command */
+1
tools/lib/bpf/libbpf.c
··· 9134 9134 if (!gen) 9135 9135 return -ENOMEM; 9136 9136 gen->opts = opts; 9137 + gen->swapped_endian = !is_native_endianness(obj); 9137 9138 obj->gen_loader = gen; 9138 9139 return 0; 9139 9140 }
+2 -1
tools/lib/bpf/skel_internal.h
··· 351 351 attr.test.ctx_size_in = opts->ctx->sz; 352 352 err = skel_sys_bpf(BPF_PROG_RUN, &attr, test_run_attr_sz); 353 353 if (err < 0 || (int)attr.test.retval < 0) { 354 - opts->errstr = "failed to execute loader prog"; 355 354 if (err < 0) { 355 + opts->errstr = "failed to execute loader prog"; 356 356 set_err; 357 357 } else { 358 + opts->errstr = "error returned by loader prog"; 358 359 err = (int)attr.test.retval; 359 360 #ifndef __KERNEL__ 360 361 errno = -err;