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