diff options
author | Daniel Borkmann <daniel@iogearbox.net> | 2018-02-08 18:26:18 -0500 |
---|---|---|
committer | Daniel Borkmann <daniel@iogearbox.net> | 2018-02-08 18:26:28 -0500 |
commit | d977ae593b2d3f9ef0df795eda93f4e6bc92b323 (patch) | |
tree | 0e3c09034cb8bc79fea586681d4de51689a23325 /tools | |
parent | 69fe98edee48b4c3c7d90cb7eea1f1662202137d (diff) | |
parent | e3d91b0ca523d53158f435a3e13df7f0cb360ea2 (diff) |
Merge branch 'bpf-libbpf-relo-fix-and-tests'
Jesper Dangaard Brouer says:
====================
While playing with using libbpf for the Suricata project, we had
issues LLVM >= 4.0.1 generating ELF files that could not be loaded
with libbpf (tools/lib/bpf/).
During the troubleshooting phase, I wrote a test program and improved
the debugging output in libbpf. I turned this into a selftests
program, and it also serves as a code example for libbpf in itself.
I discovered that there are at least three ELF load issues with
libbpf. I left them as TODO comments in (tools/testing/selftests/bpf)
test_libbpf.sh. I've only fixed the load issue with eh_frames, and
other types of relo-section that does not have exec flags. We can
work on the other issues later.
====================
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/include/uapi/linux/bpf_common.h | 7 | ||||
-rw-r--r-- | tools/lib/bpf/libbpf.c | 51 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/Makefile | 12 | ||||
-rwxr-xr-x | tools/testing/selftests/bpf/test_libbpf.sh | 49 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/test_libbpf_open.c | 150 |
5 files changed, 253 insertions, 16 deletions
diff --git a/tools/include/uapi/linux/bpf_common.h b/tools/include/uapi/linux/bpf_common.h index 18be90725ab0..ee97668bdadb 100644 --- a/tools/include/uapi/linux/bpf_common.h +++ b/tools/include/uapi/linux/bpf_common.h | |||
@@ -15,9 +15,10 @@ | |||
15 | 15 | ||
16 | /* ld/ldx fields */ | 16 | /* ld/ldx fields */ |
17 | #define BPF_SIZE(code) ((code) & 0x18) | 17 | #define BPF_SIZE(code) ((code) & 0x18) |
18 | #define BPF_W 0x00 | 18 | #define BPF_W 0x00 /* 32-bit */ |
19 | #define BPF_H 0x08 | 19 | #define BPF_H 0x08 /* 16-bit */ |
20 | #define BPF_B 0x10 | 20 | #define BPF_B 0x10 /* 8-bit */ |
21 | /* eBPF BPF_DW 0x18 64-bit */ | ||
21 | #define BPF_MODE(code) ((code) & 0xe0) | 22 | #define BPF_MODE(code) ((code) & 0xe0) |
22 | #define BPF_IMM 0x00 | 23 | #define BPF_IMM 0x00 |
23 | #define BPF_ABS 0x20 | 24 | #define BPF_ABS 0x20 |
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index c64840365433..97073d649c1a 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c | |||
@@ -319,8 +319,8 @@ bpf_program__init(void *data, size_t size, char *section_name, int idx, | |||
319 | 319 | ||
320 | prog->section_name = strdup(section_name); | 320 | prog->section_name = strdup(section_name); |
321 | if (!prog->section_name) { | 321 | if (!prog->section_name) { |
322 | pr_warning("failed to alloc name for prog under section %s\n", | 322 | pr_warning("failed to alloc name for prog under section(%d) %s\n", |
323 | section_name); | 323 | idx, section_name); |
324 | goto errout; | 324 | goto errout; |
325 | } | 325 | } |
326 | 326 | ||
@@ -742,6 +742,24 @@ bpf_object__init_maps(struct bpf_object *obj) | |||
742 | return 0; | 742 | return 0; |
743 | } | 743 | } |
744 | 744 | ||
745 | static bool section_have_execinstr(struct bpf_object *obj, int idx) | ||
746 | { | ||
747 | Elf_Scn *scn; | ||
748 | GElf_Shdr sh; | ||
749 | |||
750 | scn = elf_getscn(obj->efile.elf, idx); | ||
751 | if (!scn) | ||
752 | return false; | ||
753 | |||
754 | if (gelf_getshdr(scn, &sh) != &sh) | ||
755 | return false; | ||
756 | |||
757 | if (sh.sh_flags & SHF_EXECINSTR) | ||
758 | return true; | ||
759 | |||
760 | return false; | ||
761 | } | ||
762 | |||
745 | static int bpf_object__elf_collect(struct bpf_object *obj) | 763 | static int bpf_object__elf_collect(struct bpf_object *obj) |
746 | { | 764 | { |
747 | Elf *elf = obj->efile.elf; | 765 | Elf *elf = obj->efile.elf; |
@@ -763,29 +781,29 @@ static int bpf_object__elf_collect(struct bpf_object *obj) | |||
763 | 781 | ||
764 | idx++; | 782 | idx++; |
765 | if (gelf_getshdr(scn, &sh) != &sh) { | 783 | if (gelf_getshdr(scn, &sh) != &sh) { |
766 | pr_warning("failed to get section header from %s\n", | 784 | pr_warning("failed to get section(%d) header from %s\n", |
767 | obj->path); | 785 | idx, obj->path); |
768 | err = -LIBBPF_ERRNO__FORMAT; | 786 | err = -LIBBPF_ERRNO__FORMAT; |
769 | goto out; | 787 | goto out; |
770 | } | 788 | } |
771 | 789 | ||
772 | name = elf_strptr(elf, ep->e_shstrndx, sh.sh_name); | 790 | name = elf_strptr(elf, ep->e_shstrndx, sh.sh_name); |
773 | if (!name) { | 791 | if (!name) { |
774 | pr_warning("failed to get section name from %s\n", | 792 | pr_warning("failed to get section(%d) name from %s\n", |
775 | obj->path); | 793 | idx, obj->path); |
776 | err = -LIBBPF_ERRNO__FORMAT; | 794 | err = -LIBBPF_ERRNO__FORMAT; |
777 | goto out; | 795 | goto out; |
778 | } | 796 | } |
779 | 797 | ||
780 | data = elf_getdata(scn, 0); | 798 | data = elf_getdata(scn, 0); |
781 | if (!data) { | 799 | if (!data) { |
782 | pr_warning("failed to get section data from %s(%s)\n", | 800 | pr_warning("failed to get section(%d) data from %s(%s)\n", |
783 | name, obj->path); | 801 | idx, name, obj->path); |
784 | err = -LIBBPF_ERRNO__FORMAT; | 802 | err = -LIBBPF_ERRNO__FORMAT; |
785 | goto out; | 803 | goto out; |
786 | } | 804 | } |
787 | pr_debug("section %s, size %ld, link %d, flags %lx, type=%d\n", | 805 | pr_debug("section(%d) %s, size %ld, link %d, flags %lx, type=%d\n", |
788 | name, (unsigned long)data->d_size, | 806 | idx, name, (unsigned long)data->d_size, |
789 | (int)sh.sh_link, (unsigned long)sh.sh_flags, | 807 | (int)sh.sh_link, (unsigned long)sh.sh_flags, |
790 | (int)sh.sh_type); | 808 | (int)sh.sh_type); |
791 | 809 | ||
@@ -825,6 +843,14 @@ static int bpf_object__elf_collect(struct bpf_object *obj) | |||
825 | } else if (sh.sh_type == SHT_REL) { | 843 | } else if (sh.sh_type == SHT_REL) { |
826 | void *reloc = obj->efile.reloc; | 844 | void *reloc = obj->efile.reloc; |
827 | int nr_reloc = obj->efile.nr_reloc + 1; | 845 | int nr_reloc = obj->efile.nr_reloc + 1; |
846 | int sec = sh.sh_info; /* points to other section */ | ||
847 | |||
848 | /* Only do relo for section with exec instructions */ | ||
849 | if (!section_have_execinstr(obj, sec)) { | ||
850 | pr_debug("skip relo %s(%d) for section(%d)\n", | ||
851 | name, idx, sec); | ||
852 | continue; | ||
853 | } | ||
828 | 854 | ||
829 | reloc = realloc(reloc, | 855 | reloc = realloc(reloc, |
830 | sizeof(*obj->efile.reloc) * nr_reloc); | 856 | sizeof(*obj->efile.reloc) * nr_reloc); |
@@ -840,6 +866,8 @@ static int bpf_object__elf_collect(struct bpf_object *obj) | |||
840 | obj->efile.reloc[n].shdr = sh; | 866 | obj->efile.reloc[n].shdr = sh; |
841 | obj->efile.reloc[n].data = data; | 867 | obj->efile.reloc[n].data = data; |
842 | } | 868 | } |
869 | } else { | ||
870 | pr_debug("skip section(%d) %s\n", idx, name); | ||
843 | } | 871 | } |
844 | if (err) | 872 | if (err) |
845 | goto out; | 873 | goto out; |
@@ -1119,8 +1147,7 @@ static int bpf_object__collect_reloc(struct bpf_object *obj) | |||
1119 | 1147 | ||
1120 | prog = bpf_object__find_prog_by_idx(obj, idx); | 1148 | prog = bpf_object__find_prog_by_idx(obj, idx); |
1121 | if (!prog) { | 1149 | if (!prog) { |
1122 | pr_warning("relocation failed: no %d section\n", | 1150 | pr_warning("relocation failed: no section(%d)\n", idx); |
1123 | idx); | ||
1124 | return -LIBBPF_ERRNO__RELOC; | 1151 | return -LIBBPF_ERRNO__RELOC; |
1125 | } | 1152 | } |
1126 | 1153 | ||
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 566d6adc172a..5c43c187f27c 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile | |||
@@ -13,6 +13,7 @@ endif | |||
13 | CFLAGS += -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(GENDIR) $(GENFLAGS) -I../../../include | 13 | CFLAGS += -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(GENDIR) $(GENFLAGS) -I../../../include |
14 | LDLIBS += -lcap -lelf -lrt -lpthread | 14 | LDLIBS += -lcap -lelf -lrt -lpthread |
15 | 15 | ||
16 | # Order correspond to 'make run_tests' order | ||
16 | TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \ | 17 | TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \ |
17 | test_align test_verifier_log test_dev_cgroup test_tcpbpf_user | 18 | test_align test_verifier_log test_dev_cgroup test_tcpbpf_user |
18 | 19 | ||
@@ -22,15 +23,24 @@ TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test | |||
22 | test_l4lb_noinline.o test_xdp_noinline.o test_stacktrace_map.o \ | 23 | test_l4lb_noinline.o test_xdp_noinline.o test_stacktrace_map.o \ |
23 | sample_map_ret0.o test_tcpbpf_kern.o | 24 | sample_map_ret0.o test_tcpbpf_kern.o |
24 | 25 | ||
25 | TEST_PROGS := test_kmod.sh test_xdp_redirect.sh test_xdp_meta.sh \ | 26 | # Order correspond to 'make run_tests' order |
27 | TEST_PROGS := test_kmod.sh \ | ||
28 | test_libbpf.sh \ | ||
29 | test_xdp_redirect.sh \ | ||
30 | test_xdp_meta.sh \ | ||
26 | test_offload.py | 31 | test_offload.py |
27 | 32 | ||
33 | # Compile but not part of 'make run_tests' | ||
34 | TEST_GEN_PROGS_EXTENDED = test_libbpf_open | ||
35 | |||
28 | include ../lib.mk | 36 | include ../lib.mk |
29 | 37 | ||
30 | BPFOBJ := $(OUTPUT)/libbpf.a cgroup_helpers.c | 38 | BPFOBJ := $(OUTPUT)/libbpf.a cgroup_helpers.c |
31 | 39 | ||
32 | $(TEST_GEN_PROGS): $(BPFOBJ) | 40 | $(TEST_GEN_PROGS): $(BPFOBJ) |
33 | 41 | ||
42 | $(TEST_GEN_PROGS_EXTENDED): $(OUTPUT)/libbpf.a | ||
43 | |||
34 | .PHONY: force | 44 | .PHONY: force |
35 | 45 | ||
36 | # force a rebuild of BPFOBJ when its dependencies are updated | 46 | # force a rebuild of BPFOBJ when its dependencies are updated |
diff --git a/tools/testing/selftests/bpf/test_libbpf.sh b/tools/testing/selftests/bpf/test_libbpf.sh new file mode 100755 index 000000000000..d97dc914cd49 --- /dev/null +++ b/tools/testing/selftests/bpf/test_libbpf.sh | |||
@@ -0,0 +1,49 @@ | |||
1 | #!/bin/sh | ||
2 | # SPDX-License-Identifier: GPL-2.0 | ||
3 | |||
4 | export TESTNAME=test_libbpf | ||
5 | |||
6 | # Determine selftest success via shell exit code | ||
7 | exit_handler() | ||
8 | { | ||
9 | if (( $? == 0 )); then | ||
10 | echo "selftests: $TESTNAME [PASS]"; | ||
11 | else | ||
12 | echo "$TESTNAME: failed at file $LAST_LOADED" 1>&2 | ||
13 | echo "selftests: $TESTNAME [FAILED]"; | ||
14 | fi | ||
15 | } | ||
16 | |||
17 | libbpf_open_file() | ||
18 | { | ||
19 | LAST_LOADED=$1 | ||
20 | if [ -n "$VERBOSE" ]; then | ||
21 | ./test_libbpf_open $1 | ||
22 | else | ||
23 | ./test_libbpf_open --quiet $1 | ||
24 | fi | ||
25 | } | ||
26 | |||
27 | # Exit script immediately (well catched by trap handler) if any | ||
28 | # program/thing exits with a non-zero status. | ||
29 | set -e | ||
30 | |||
31 | # (Use 'trap -l' to list meaning of numbers) | ||
32 | trap exit_handler 0 2 3 6 9 | ||
33 | |||
34 | libbpf_open_file test_l4lb.o | ||
35 | |||
36 | # TODO: fix libbpf to load noinline functions | ||
37 | # [warning] libbpf: incorrect bpf_call opcode | ||
38 | #libbpf_open_file test_l4lb_noinline.o | ||
39 | |||
40 | # TODO: fix test_xdp_meta.c to load with libbpf | ||
41 | # [warning] libbpf: test_xdp_meta.o doesn't provide kernel version | ||
42 | #libbpf_open_file test_xdp_meta.o | ||
43 | |||
44 | # TODO: fix libbpf to handle .eh_frame | ||
45 | # [warning] libbpf: relocation failed: no section(10) | ||
46 | #libbpf_open_file ../../../../samples/bpf/tracex3_kern.o | ||
47 | |||
48 | # Success | ||
49 | exit 0 | ||
diff --git a/tools/testing/selftests/bpf/test_libbpf_open.c b/tools/testing/selftests/bpf/test_libbpf_open.c new file mode 100644 index 000000000000..8fcd1c076add --- /dev/null +++ b/tools/testing/selftests/bpf/test_libbpf_open.c | |||
@@ -0,0 +1,150 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 | ||
2 | * Copyright (c) 2018 Jesper Dangaard Brouer, Red Hat Inc. | ||
3 | */ | ||
4 | static const char *__doc__ = | ||
5 | "Libbpf test program for loading BPF ELF object files"; | ||
6 | |||
7 | #include <stdlib.h> | ||
8 | #include <stdio.h> | ||
9 | #include <string.h> | ||
10 | #include <stdarg.h> | ||
11 | #include <bpf/libbpf.h> | ||
12 | #include <getopt.h> | ||
13 | |||
14 | static const struct option long_options[] = { | ||
15 | {"help", no_argument, NULL, 'h' }, | ||
16 | {"debug", no_argument, NULL, 'D' }, | ||
17 | {"quiet", no_argument, NULL, 'q' }, | ||
18 | {0, 0, NULL, 0 } | ||
19 | }; | ||
20 | |||
21 | static void usage(char *argv[]) | ||
22 | { | ||
23 | int i; | ||
24 | |||
25 | printf("\nDOCUMENTATION:\n%s\n\n", __doc__); | ||
26 | printf(" Usage: %s (options-see-below) BPF_FILE\n", argv[0]); | ||
27 | printf(" Listing options:\n"); | ||
28 | for (i = 0; long_options[i].name != 0; i++) { | ||
29 | printf(" --%-12s", long_options[i].name); | ||
30 | printf(" short-option: -%c", | ||
31 | long_options[i].val); | ||
32 | printf("\n"); | ||
33 | } | ||
34 | printf("\n"); | ||
35 | } | ||
36 | |||
37 | #define DEFINE_PRINT_FN(name, enabled) \ | ||
38 | static int libbpf_##name(const char *fmt, ...) \ | ||
39 | { \ | ||
40 | va_list args; \ | ||
41 | int ret; \ | ||
42 | \ | ||
43 | va_start(args, fmt); \ | ||
44 | if (enabled) { \ | ||
45 | fprintf(stderr, "[" #name "] "); \ | ||
46 | ret = vfprintf(stderr, fmt, args); \ | ||
47 | } \ | ||
48 | va_end(args); \ | ||
49 | return ret; \ | ||
50 | } | ||
51 | DEFINE_PRINT_FN(warning, 1) | ||
52 | DEFINE_PRINT_FN(info, 1) | ||
53 | DEFINE_PRINT_FN(debug, 1) | ||
54 | |||
55 | #define EXIT_FAIL_LIBBPF EXIT_FAILURE | ||
56 | #define EXIT_FAIL_OPTION 2 | ||
57 | |||
58 | int test_walk_progs(struct bpf_object *obj, bool verbose) | ||
59 | { | ||
60 | struct bpf_program *prog; | ||
61 | int cnt = 0; | ||
62 | |||
63 | bpf_object__for_each_program(prog, obj) { | ||
64 | cnt++; | ||
65 | if (verbose) | ||
66 | printf("Prog (count:%d) section_name: %s\n", cnt, | ||
67 | bpf_program__title(prog, false)); | ||
68 | } | ||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | int test_walk_maps(struct bpf_object *obj, bool verbose) | ||
73 | { | ||
74 | struct bpf_map *map; | ||
75 | int cnt = 0; | ||
76 | |||
77 | bpf_map__for_each(map, obj) { | ||
78 | cnt++; | ||
79 | if (verbose) | ||
80 | printf("Map (count:%d) name: %s\n", cnt, | ||
81 | bpf_map__name(map)); | ||
82 | } | ||
83 | return 0; | ||
84 | } | ||
85 | |||
86 | int test_open_file(char *filename, bool verbose) | ||
87 | { | ||
88 | struct bpf_object *bpfobj = NULL; | ||
89 | long err; | ||
90 | |||
91 | if (verbose) | ||
92 | printf("Open BPF ELF-file with libbpf: %s\n", filename); | ||
93 | |||
94 | /* Load BPF ELF object file and check for errors */ | ||
95 | bpfobj = bpf_object__open(filename); | ||
96 | err = libbpf_get_error(bpfobj); | ||
97 | if (err) { | ||
98 | char err_buf[128]; | ||
99 | libbpf_strerror(err, err_buf, sizeof(err_buf)); | ||
100 | if (verbose) | ||
101 | printf("Unable to load eBPF objects in file '%s': %s\n", | ||
102 | filename, err_buf); | ||
103 | return EXIT_FAIL_LIBBPF; | ||
104 | } | ||
105 | test_walk_progs(bpfobj, verbose); | ||
106 | test_walk_maps(bpfobj, verbose); | ||
107 | |||
108 | if (verbose) | ||
109 | printf("Close BPF ELF-file with libbpf: %s\n", | ||
110 | bpf_object__name(bpfobj)); | ||
111 | bpf_object__close(bpfobj); | ||
112 | |||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | int main(int argc, char **argv) | ||
117 | { | ||
118 | char filename[1024] = { 0 }; | ||
119 | bool verbose = 1; | ||
120 | int longindex = 0; | ||
121 | int opt; | ||
122 | |||
123 | libbpf_set_print(libbpf_warning, libbpf_info, NULL); | ||
124 | |||
125 | /* Parse commands line args */ | ||
126 | while ((opt = getopt_long(argc, argv, "hDq", | ||
127 | long_options, &longindex)) != -1) { | ||
128 | switch (opt) { | ||
129 | case 'D': | ||
130 | libbpf_set_print(libbpf_warning, libbpf_info, | ||
131 | libbpf_debug); | ||
132 | break; | ||
133 | case 'q': /* Use in scripting mode */ | ||
134 | verbose = 0; | ||
135 | break; | ||
136 | case 'h': | ||
137 | default: | ||
138 | usage(argv); | ||
139 | return EXIT_FAIL_OPTION; | ||
140 | } | ||
141 | } | ||
142 | if (optind >= argc) { | ||
143 | usage(argv); | ||
144 | printf("ERROR: Expected BPF_FILE argument after options\n"); | ||
145 | return EXIT_FAIL_OPTION; | ||
146 | } | ||
147 | snprintf(filename, sizeof(filename), "%s", argv[optind]); | ||
148 | |||
149 | return test_open_file(filename, verbose); | ||
150 | } | ||