Skip to content

Commit d977ae5

Browse files
committed
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 <[email protected]>
2 parents 69fe98e + e3d91b0 commit d977ae5

File tree

5 files changed

+253
-16
lines changed

5 files changed

+253
-16
lines changed

tools/include/uapi/linux/bpf_common.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515

1616
/* ld/ldx fields */
1717
#define BPF_SIZE(code) ((code) & 0x18)
18-
#define BPF_W 0x00
19-
#define BPF_H 0x08
20-
#define BPF_B 0x10
18+
#define BPF_W 0x00 /* 32-bit */
19+
#define BPF_H 0x08 /* 16-bit */
20+
#define BPF_B 0x10 /* 8-bit */
21+
/* eBPF BPF_DW 0x18 64-bit */
2122
#define BPF_MODE(code) ((code) & 0xe0)
2223
#define BPF_IMM 0x00
2324
#define BPF_ABS 0x20

tools/lib/bpf/libbpf.c

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -319,8 +319,8 @@ bpf_program__init(void *data, size_t size, char *section_name, int idx,
319319

320320
prog->section_name = strdup(section_name);
321321
if (!prog->section_name) {
322-
pr_warning("failed to alloc name for prog under section %s\n",
323-
section_name);
322+
pr_warning("failed to alloc name for prog under section(%d) %s\n",
323+
idx, section_name);
324324
goto errout;
325325
}
326326

@@ -742,6 +742,24 @@ bpf_object__init_maps(struct bpf_object *obj)
742742
return 0;
743743
}
744744

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+
745763
static int bpf_object__elf_collect(struct bpf_object *obj)
746764
{
747765
Elf *elf = obj->efile.elf;
@@ -763,29 +781,29 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
763781

764782
idx++;
765783
if (gelf_getshdr(scn, &sh) != &sh) {
766-
pr_warning("failed to get section header from %s\n",
767-
obj->path);
784+
pr_warning("failed to get section(%d) header from %s\n",
785+
idx, obj->path);
768786
err = -LIBBPF_ERRNO__FORMAT;
769787
goto out;
770788
}
771789

772790
name = elf_strptr(elf, ep->e_shstrndx, sh.sh_name);
773791
if (!name) {
774-
pr_warning("failed to get section name from %s\n",
775-
obj->path);
792+
pr_warning("failed to get section(%d) name from %s\n",
793+
idx, obj->path);
776794
err = -LIBBPF_ERRNO__FORMAT;
777795
goto out;
778796
}
779797

780798
data = elf_getdata(scn, 0);
781799
if (!data) {
782-
pr_warning("failed to get section data from %s(%s)\n",
783-
name, obj->path);
800+
pr_warning("failed to get section(%d) data from %s(%s)\n",
801+
idx, name, obj->path);
784802
err = -LIBBPF_ERRNO__FORMAT;
785803
goto out;
786804
}
787-
pr_debug("section %s, size %ld, link %d, flags %lx, type=%d\n",
788-
name, (unsigned long)data->d_size,
805+
pr_debug("section(%d) %s, size %ld, link %d, flags %lx, type=%d\n",
806+
idx, name, (unsigned long)data->d_size,
789807
(int)sh.sh_link, (unsigned long)sh.sh_flags,
790808
(int)sh.sh_type);
791809

@@ -825,6 +843,14 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
825843
} else if (sh.sh_type == SHT_REL) {
826844
void *reloc = obj->efile.reloc;
827845
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+
}
828854

829855
reloc = realloc(reloc,
830856
sizeof(*obj->efile.reloc) * nr_reloc);
@@ -840,6 +866,8 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
840866
obj->efile.reloc[n].shdr = sh;
841867
obj->efile.reloc[n].data = data;
842868
}
869+
} else {
870+
pr_debug("skip section(%d) %s\n", idx, name);
843871
}
844872
if (err)
845873
goto out;
@@ -1119,8 +1147,7 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
11191147

11201148
prog = bpf_object__find_prog_by_idx(obj, idx);
11211149
if (!prog) {
1122-
pr_warning("relocation failed: no %d section\n",
1123-
idx);
1150+
pr_warning("relocation failed: no section(%d)\n", idx);
11241151
return -LIBBPF_ERRNO__RELOC;
11251152
}
11261153

tools/testing/selftests/bpf/Makefile

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ endif
1313
CFLAGS += -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(GENDIR) $(GENFLAGS) -I../../../include
1414
LDLIBS += -lcap -lelf -lrt -lpthread
1515

16+
# Order correspond to 'make run_tests' order
1617
TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \
1718
test_align test_verifier_log test_dev_cgroup test_tcpbpf_user
1819

@@ -22,15 +23,24 @@ TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test
2223
test_l4lb_noinline.o test_xdp_noinline.o test_stacktrace_map.o \
2324
sample_map_ret0.o test_tcpbpf_kern.o
2425

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 \
2631
test_offload.py
2732

33+
# Compile but not part of 'make run_tests'
34+
TEST_GEN_PROGS_EXTENDED = test_libbpf_open
35+
2836
include ../lib.mk
2937

3038
BPFOBJ := $(OUTPUT)/libbpf.a cgroup_helpers.c
3139

3240
$(TEST_GEN_PROGS): $(BPFOBJ)
3341

42+
$(TEST_GEN_PROGS_EXTENDED): $(OUTPUT)/libbpf.a
43+
3444
.PHONY: force
3545

3646
# force a rebuild of BPFOBJ when its dependencies are updated
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
}

0 commit comments

Comments
 (0)