History

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. ====================

Daniel Borkmann committed 8 years ago

Commit d977ae52 parents 69fe98e e3d91b05 files · +253 −16Browse files

Unified · Split

5 files
  1. tools/include/uapi/linux/bpf_common.h +4 −3
  2. tools/lib/bpf/libbpf.c +39 −12
  3. tools/testing/selftests/bpf/Makefile +11 −1
  4. tools/testing/selftests/bpf/test_libbpf.sh +49 −0
  5. tools/testing/selftests/bpf/test_libbpf_open.c +150 −0
tools/include/uapi/linux/bpf_common.h+4−3
@@ -15,9 +15,10 @@ · 14 unmodified lines
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 0x0018#define BPF_W 0x00 /* 32-bit */
19#define BPF_H 0x0819#define BPF_H 0x08 /* 16-bit */
20#define BPF_B 0x1020#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 0x0023#define BPF_IMM 0x00
23#define BPF_ABS 0x2024#define BPF_ABS 0x20
tools/lib/bpf/libbpf.c+39−12
@@ -319,8 +319,8 @@ bpf_program__init(void *data, size_t size, char *section_name, int idx, · 318 unmodified lines
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) · 415 unmodified lines
742 return 0;742 return 0;
743}743}
744 744
745static 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
745static int bpf_object__elf_collect(struct bpf_object *obj)763static 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) · 15 unmodified lines
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) · 33 unmodified lines
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) · 9 unmodified lines
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) · 273 unmodified lines
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
tools/testing/selftests/bpf/Makefile+11−1
@@ -13,6 +13,7 @@ endif · 12 unmodified lines
13CFLAGS += -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(GENDIR) $(GENFLAGS) -I../../../include13CFLAGS += -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(GENDIR) $(GENFLAGS) -I../../../include
14LDLIBS += -lcap -lelf -lrt -lpthread14LDLIBS += -lcap -lelf -lrt -lpthread
15 15
16# Order correspond to 'make run_tests' order
16TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \17TEST_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_user18 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 · 3 unmodified lines
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.o24 sample_map_ret0.o test_tcpbpf_kern.o
24 25
25TEST_PROGS := test_kmod.sh test_xdp_redirect.sh test_xdp_meta.sh \26# Order correspond to 'make run_tests' order
27TEST_PROGS := test_kmod.sh \
28 test_libbpf.sh \
29 test_xdp_redirect.sh \
30 test_xdp_meta.sh \
26 test_offload.py31 test_offload.py
27 32
33# Compile but not part of 'make run_tests'
34TEST_GEN_PROGS_EXTENDED = test_libbpf_open
35
28include ../lib.mk36include ../lib.mk
29 37
30BPFOBJ := $(OUTPUT)/libbpf.a cgroup_helpers.c38BPFOBJ := $(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: force44.PHONY: force
35 45
36# force a rebuild of BPFOBJ when its dependencies are updated46# force a rebuild of BPFOBJ when its dependencies are updated
tools/testing/selftests/bpf/test_libbpf.sh+49−0
@@ -0,0 +1,49 @@
1#!/bin/sh
2# SPDX-License-Identifier: GPL-2.0
3
4export TESTNAME=test_libbpf
5
6# Determine selftest success via shell exit code
7exit_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
17libbpf_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.
29set -e
30
31# (Use 'trap -l' to list meaning of numbers)
32trap exit_handler 0 2 3 6 9
33
34libbpf_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
49exit 0
tools/testing/selftests/bpf/test_libbpf_open.c+150−0
@@ -0,0 +1,150 @@
1/* SPDX-License-Identifier: GPL-2.0
2 * Copyright (c) 2018 Jesper Dangaard Brouer, Red Hat Inc.
3 */
4static 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
14static 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
21static 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) \
38static 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}
51DEFINE_PRINT_FN(warning, 1)
52DEFINE_PRINT_FN(info, 1)
53DEFINE_PRINT_FN(debug, 1)
54
55#define EXIT_FAIL_LIBBPF EXIT_FAILURE
56#define EXIT_FAIL_OPTION 2
57
58int 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
72int 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
86int 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
116int 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}