// SPDX-License-Identifier: GPL-2.0 #include #include #include "bpftool_helpers.h" #define FUNC_NAME "write_fmt" #define KSYM_NAME "write_fmt_scx_simple__BpfStream_" /* The name of a function of Rust is a part of the name of the program in kallsyms */ static void test_func_name(void) { struct bpf_insn insns[] = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }; LIBBPF_OPTS(bpf_prog_load_opts, opts); int int_id, proto_id, prog_fd = -1, i; struct bpf_func_info func_info = {}; struct bpf_prog_info info = {}; unsigned long long addr; __u32 len = sizeof(info); char sym[128], *p = sym; struct btf *btf; btf = btf__new_empty(); if (!ASSERT_OK_PTR(btf, "btf")) return; int_id = btf__add_int(btf, "i32", 4, BTF_INT_SIGNED); ASSERT_GT(int_id, 0, "int"); proto_id = btf__add_func_proto(btf, int_id); ASSERT_GT(proto_id, 0, "proto"); ASSERT_OK(btf__add_func_param(btf, "ctx", int_id), "param"); func_info.type_id = btf__add_func(btf, FUNC_NAME, BTF_FUNC_STATIC, proto_id); ASSERT_GT(func_info.type_id, 0, "func"); if (!ASSERT_OK(btf__load_into_kernel(btf), "btf load")) goto out; opts.prog_btf_fd = btf__fd(btf); opts.func_info = &func_info; opts.func_info_cnt = 1; opts.func_info_rec_size = sizeof(func_info); prog_fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL", insns, ARRAY_SIZE(insns), &opts); if (!ASSERT_GE(prog_fd, 0, "prog load")) goto out; if (!ASSERT_OK(bpf_prog_get_info_by_fd(prog_fd, &info, &len), "prog info")) goto out; if (!info.jited_prog_len) { test__skip(); goto out; } p += sprintf(p, "bpf_prog_"); for (i = 0; i < BPF_TAG_SIZE; i++) p += sprintf(p, "%02x", info.tag[i]); sprintf(p, "_%s", KSYM_NAME); ASSERT_OK(kallsyms_find(sym, &addr), sym); out: if (prog_fd >= 0) close(prog_fd); btf__free(btf); } #define PIN_PATH "/sys/fs/bpf/btf_rust_piece" /* * A piece of a static that LLVM split has the type of the whole static. * It's the last variable in the section, so its type ends past the map value. */ static void test_piece(void) { LIBBPF_OPTS(bpf_map_create_opts, opts); int int_id, struct_id, var_id, piece_id, sec_id, map_fd = -1, key = 0; __u32 value[2] = { 0x11111111, 0x22222222 }; char line[256] = {}, out[1024] = {}; struct btf *btf; FILE *f = NULL; btf = btf__new_empty(); if (!ASSERT_OK_PTR(btf, "btf")) return; int_id = btf__add_int(btf, "u32", 4, 0); ASSERT_GT(int_id, 0, "int"); struct_id = btf__add_struct(btf, "Whole", 8); ASSERT_GT(struct_id, 0, "struct"); ASSERT_OK(btf__add_field(btf, "a", int_id, 0, 0), "field"); ASSERT_OK(btf__add_field(btf, "b", int_id, 32, 0), "field"); var_id = btf__add_var(btf, "CNT", BTF_VAR_STATIC, int_id); ASSERT_GT(var_id, 0, "var"); piece_id = btf__add_var(btf, "WHOLE.1", BTF_VAR_STATIC, struct_id); ASSERT_GT(piece_id, 0, "piece"); sec_id = btf__add_datasec(btf, ".bss", sizeof(value)); ASSERT_GT(sec_id, 0, "datasec"); ASSERT_OK(btf__add_datasec_var_info(btf, var_id, 0, 4), "var info"); ASSERT_OK(btf__add_datasec_var_info(btf, piece_id, 4, 4), "piece info"); if (!ASSERT_OK(btf__load_into_kernel(btf), "btf load")) goto out; opts.btf_fd = btf__fd(btf); opts.btf_value_type_id = sec_id; map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, ".bss", sizeof(key), sizeof(value), 1, &opts); if (!ASSERT_GE(map_fd, 0, "map create")) goto out; if (!ASSERT_OK(bpf_map_update_elem(map_fd, &key, value, 0), "map update")) goto out; /* the variable is printed, the piece is not */ unlink(PIN_PATH); if (!ASSERT_OK(bpf_obj_pin(map_fd, PIN_PATH), "pin")) goto out; f = fopen(PIN_PATH, "r"); if (!ASSERT_OK_PTR(f, "open")) goto out; while (fgets(line, sizeof(line), f) && line[0] == '#') ; ASSERT_HAS_SUBSTR(line, "286331153", "var"); ASSERT_NULL(strstr(line, "572662306"), "piece"); /* the same for bpftool */ if (!ASSERT_OK(get_bpftool_command_output("map dump pinned " PIN_PATH, out, sizeof(out)), "bpftool")) goto out; ASSERT_HAS_SUBSTR(out, "CNT", "var"); ASSERT_NULL(strstr(out, "WHOLE.1"), "piece"); out: if (f) fclose(f); unlink(PIN_PATH); if (map_fd >= 0) close(map_fd); btf__free(btf); } /* Serial: programs are not in kallsyms while another test sets bpf_jit_harden */ void serial_test_btf_rust(void) { if (test__start_subtest("func_name")) test_func_name(); if (test__start_subtest("piece")) test_piece(); }