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

perf test: Add libbpf relocation checker

There's a bug in LLVM that it can generate unneeded relocation
information. See [1] and [2]. Libbpf should check the target section of
a relocation symbol.

This patch adds a testcase which references a global variable (BPF
doesn't support global variables). Before fixing libbpf, the new test
case can be loaded into kernel, the global variable acts like the first
map. It is incorrect.

Result:

# ~/perf test BPF
37: Test BPF filter :
37.1: Test basic BPF filtering : Ok
37.2: Test BPF prologue generation : Ok
37.3: Test BPF relocation checker : FAILED!

# ~/perf test -v BPF
...
libbpf: loading object '[bpf_relocation_test]' from buffer
libbpf: section .strtab, size 126, link 0, flags 0, type=3
libbpf: section .text, size 0, link 0, flags 6, type=1
libbpf: section .data, size 0, link 0, flags 3, type=1
libbpf: section .bss, size 0, link 0, flags 3, type=8
libbpf: section func=sys_write, size 104, link 0, flags 6, type=1
libbpf: found program func=sys_write
libbpf: section .relfunc=sys_write, size 16, link 10, flags 0, type=9
libbpf: section maps, size 16, link 0, flags 3, type=1
libbpf: maps in [bpf_relocation_test]: 16 bytes
libbpf: section license, size 4, link 0, flags 3, type=1
libbpf: license of [bpf_relocation_test] is GPL
libbpf: section version, size 4, link 0, flags 3, type=1
libbpf: kernel version of [bpf_relocation_test] is 40400
libbpf: section .symtab, size 144, link 1, flags 0, type=2
libbpf: map 0 is "my_table"
libbpf: collecting relocating info for: 'func=sys_write'
libbpf: relocation: insn_idx=7
Success unexpectedly: libbpf error when dealing with relocation
test child finished with -1
---- end ----
Test BPF filter subtest 2: FAILED!

[1] https://llvm.org/bugs/show_bug.cgi?id=26243
[2] https://patchwork.ozlabs.org/patch/571385/

Signed-off-by: Wang Nan <wangnan0@huawei.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: He Kuang <hekuang@huawei.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Li Zefan <lizefan@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Will Deacon <will.deacon@arm.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1453715801-7732-2-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Wang Nan and committed by
Arnaldo Carvalho de Melo
7b6982ce ab414dcd

+98 -12
+1 -1
tools/perf/Makefile.perf
··· 618 618 $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32 619 619 $(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)FEATURE-DUMP $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* \ 620 620 $(OUTPUT)util/intel-pt-decoder/inat-tables.c $(OUTPUT)fixdep \ 621 - $(OUTPUT)tests/llvm-src-{base,kbuild,prologue}.c 621 + $(OUTPUT)tests/llvm-src-{base,kbuild,prologue,relocation}.c 622 622 $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean 623 623 $(python-clean) 624 624
+1
tools/perf/tests/.gitignore
··· 1 1 llvm-src-base.c 2 2 llvm-src-kbuild.c 3 3 llvm-src-prologue.c 4 + llvm-src-relocation.c
+8 -1
tools/perf/tests/Build
··· 31 31 perf-y += parse-no-sample-id-all.o 32 32 perf-y += kmod-path.o 33 33 perf-y += thread-map.o 34 - perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o llvm-src-prologue.o 34 + perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o llvm-src-prologue.o llvm-src-relocation.o 35 35 perf-y += bpf.o 36 36 perf-y += topology.o 37 37 perf-y += cpumap.o ··· 56 56 $(call rule_mkdir) 57 57 $(Q)echo '#include <tests/llvm.h>' > $@ 58 58 $(Q)echo 'const char test_llvm__bpf_test_prologue_prog[] =' >> $@ 59 + $(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@ 60 + $(Q)echo ';' >> $@ 61 + 62 + $(OUTPUT)tests/llvm-src-relocation.c: tests/bpf-script-test-relocation.c tests/Build 63 + $(call rule_mkdir) 64 + $(Q)echo '#include <tests/llvm.h>' > $@ 65 + $(Q)echo 'const char test_llvm__bpf_test_relocation[] =' >> $@ 59 66 $(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@ 60 67 $(Q)echo ';' >> $@ 61 68
+50
tools/perf/tests/bpf-script-test-relocation.c
··· 1 + /* 2 + * bpf-script-test-relocation.c 3 + * Test BPF loader checking relocation 4 + */ 5 + #ifndef LINUX_VERSION_CODE 6 + # error Need LINUX_VERSION_CODE 7 + # error Example: for 4.2 kernel, put 'clang-opt="-DLINUX_VERSION_CODE=0x40200" into llvm section of ~/.perfconfig' 8 + #endif 9 + #define BPF_ANY 0 10 + #define BPF_MAP_TYPE_ARRAY 2 11 + #define BPF_FUNC_map_lookup_elem 1 12 + #define BPF_FUNC_map_update_elem 2 13 + 14 + static void *(*bpf_map_lookup_elem)(void *map, void *key) = 15 + (void *) BPF_FUNC_map_lookup_elem; 16 + static void *(*bpf_map_update_elem)(void *map, void *key, void *value, int flags) = 17 + (void *) BPF_FUNC_map_update_elem; 18 + 19 + struct bpf_map_def { 20 + unsigned int type; 21 + unsigned int key_size; 22 + unsigned int value_size; 23 + unsigned int max_entries; 24 + }; 25 + 26 + #define SEC(NAME) __attribute__((section(NAME), used)) 27 + struct bpf_map_def SEC("maps") my_table = { 28 + .type = BPF_MAP_TYPE_ARRAY, 29 + .key_size = sizeof(int), 30 + .value_size = sizeof(int), 31 + .max_entries = 1, 32 + }; 33 + 34 + int this_is_a_global_val; 35 + 36 + SEC("func=sys_write") 37 + int bpf_func__sys_write(void *ctx) 38 + { 39 + int key = 0; 40 + int value = 0; 41 + 42 + /* 43 + * Incorrect relocation. Should not allow this program be 44 + * loaded into kernel. 45 + */ 46 + bpf_map_update_elem(&this_is_a_global_val, &key, &value, 0); 47 + return 0; 48 + } 49 + char _license[] SEC("license") = "GPL"; 50 + int _version SEC("version") = LINUX_VERSION_CODE;
+21 -5
tools/perf/tests/bpf.c
··· 71 71 (NR_ITERS + 1) / 4, 72 72 }, 73 73 #endif 74 + { 75 + LLVM_TESTCASE_BPF_RELOCATION, 76 + "Test BPF relocation checker", 77 + "[bpf_relocation_test]", 78 + "fix 'perf test LLVM' first", 79 + "libbpf error when dealing with relocation", 80 + NULL, 81 + 0, 82 + }, 74 83 }; 75 84 76 85 static int do_test(struct bpf_object *obj, int (*func)(void), ··· 199 190 200 191 ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz, 201 192 bpf_testcase_table[idx].prog_id, 202 - true); 193 + true, NULL); 203 194 if (ret != TEST_OK || !obj_buf || !obj_buf_sz) { 204 195 pr_debug("Unable to get BPF object, %s\n", 205 196 bpf_testcase_table[idx].msg_compile_fail); ··· 211 202 212 203 obj = prepare_bpf(obj_buf, obj_buf_sz, 213 204 bpf_testcase_table[idx].name); 214 - if (!obj) { 205 + if ((!!bpf_testcase_table[idx].target_func) != (!!obj)) { 206 + if (!obj) 207 + pr_debug("Fail to load BPF object: %s\n", 208 + bpf_testcase_table[idx].msg_load_fail); 209 + else 210 + pr_debug("Success unexpectedly: %s\n", 211 + bpf_testcase_table[idx].msg_load_fail); 215 212 ret = TEST_FAIL; 216 213 goto out; 217 214 } 218 215 219 - ret = do_test(obj, 220 - bpf_testcase_table[idx].target_func, 221 - bpf_testcase_table[idx].expect_result); 216 + if (obj) 217 + ret = do_test(obj, 218 + bpf_testcase_table[idx].target_func, 219 + bpf_testcase_table[idx].expect_result); 222 220 out: 223 221 bpf__clear(); 224 222 return ret;
+13 -4
tools/perf/tests/llvm.c
··· 35 35 static struct { 36 36 const char *source; 37 37 const char *desc; 38 + bool should_load_fail; 38 39 } bpf_source_table[__LLVM_TESTCASE_MAX] = { 39 40 [LLVM_TESTCASE_BASE] = { 40 41 .source = test_llvm__bpf_base_prog, ··· 49 48 .source = test_llvm__bpf_test_prologue_prog, 50 49 .desc = "Compile source for BPF prologue generation test", 51 50 }, 51 + [LLVM_TESTCASE_BPF_RELOCATION] = { 52 + .source = test_llvm__bpf_test_relocation, 53 + .desc = "Compile source for BPF relocation test", 54 + .should_load_fail = true, 55 + }, 52 56 }; 53 - 54 57 55 58 int 56 59 test_llvm__fetch_bpf_obj(void **p_obj_buf, 57 60 size_t *p_obj_buf_sz, 58 61 enum test_llvm__testcase idx, 59 - bool force) 62 + bool force, 63 + bool *should_load_fail) 60 64 { 61 65 const char *source; 62 66 const char *desc; ··· 74 68 75 69 source = bpf_source_table[idx].source; 76 70 desc = bpf_source_table[idx].desc; 71 + if (should_load_fail) 72 + *should_load_fail = bpf_source_table[idx].should_load_fail; 77 73 78 74 perf_config(perf_config_cb, NULL); 79 75 ··· 144 136 int ret; 145 137 void *obj_buf = NULL; 146 138 size_t obj_buf_sz = 0; 139 + bool should_load_fail = false; 147 140 148 141 if ((subtest < 0) || (subtest >= __LLVM_TESTCASE_MAX)) 149 142 return TEST_FAIL; 150 143 151 144 ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz, 152 - subtest, false); 145 + subtest, false, &should_load_fail); 153 146 154 - if (ret == TEST_OK) { 147 + if (ret == TEST_OK && !should_load_fail) { 155 148 ret = test__bpf_parsing(obj_buf, obj_buf_sz); 156 149 if (ret != TEST_OK) { 157 150 pr_debug("Failed to parse test case '%s'\n",
+4 -1
tools/perf/tests/llvm.h
··· 7 7 extern const char test_llvm__bpf_base_prog[]; 8 8 extern const char test_llvm__bpf_test_kbuild_prog[]; 9 9 extern const char test_llvm__bpf_test_prologue_prog[]; 10 + extern const char test_llvm__bpf_test_relocation[]; 10 11 11 12 enum test_llvm__testcase { 12 13 LLVM_TESTCASE_BASE, 13 14 LLVM_TESTCASE_KBUILD, 14 15 LLVM_TESTCASE_BPF_PROLOGUE, 16 + LLVM_TESTCASE_BPF_RELOCATION, 15 17 __LLVM_TESTCASE_MAX, 16 18 }; 17 19 18 20 int test_llvm__fetch_bpf_obj(void **p_obj_buf, size_t *p_obj_buf_sz, 19 - enum test_llvm__testcase index, bool force); 21 + enum test_llvm__testcase index, bool force, 22 + bool *should_load_fail); 20 23 #endif