selftests/bpf: add bpf-gcc support
authorIlya Leoshkevich <iii@linux.ibm.com>
Thu, 12 Sep 2019 16:05:43 +0000 (18:05 +0200)
committerDaniel Borkmann <daniel@iogearbox.net>
Mon, 16 Sep 2019 07:32:00 +0000 (09:32 +0200)
Now that binutils and gcc support for BPF is upstream, make use of it in
BPF selftests using alu32-like approach. Share as much as possible of
CFLAGS calculation with clang.

Fixes only obvious issues, leaving more complex ones for later:
- Use gcc-provided bpf-helpers.h instead of manually defining the
  helpers, change bpf_helpers.h include guard to avoid conflict.
- Include <linux/stddef.h> for __always_inline.
- Add $(OUTPUT)/../usr/include to include path in order to use local
  kernel headers instead of system kernel headers when building with O=.

In order to activate the bpf-gcc support, one needs to configure
binutils and gcc with --target=bpf and make them available in $PATH. In
particular, gcc must be installed as `bpf-gcc`, which is the default.

Right now with binutils 25a2915e8dba and gcc r275589 only a handful of
tests work:

# ./test_progs_bpf_gcc
# Summary: 7/39 PASSED, 1 SKIPPED, 98 FAILED

The reason for those failures are as follows:

- Build errors:
  - `error: too many function arguments for eBPF` for __always_inline
    functions read_str_var and read_map_var - must be inlining issue,
    and for process_l3_headers_v6, which relies on optimizing away
    function arguments.
  - `error: indirect call in function, which are not supported by eBPF`
    where there are no obvious indirect calls in the source calls, e.g.
    in __encap_ipip_none.
  - `error: field 'lock' has incomplete type` for fields of `struct
    bpf_spin_lock` type - bpf_spin_lock is re#defined by bpf-helpers.h,
    so its usage is sensitive to order of #includes.
  - `error: eBPF stack limit exceeded` in sysctl_tcp_mem.
- Load errors:
  - Missing object files due to above build errors.
  - `libbpf: failed to create map (name: 'test_ver.bss')`.
  - `libbpf: object file doesn't contain bpf program`.
  - `libbpf: Program '.text' contains unrecognized relo data pointing to
    section 0`.
  - `libbpf: BTF is required, but is missing or corrupted` - no BTF
    support in gcc yet.

Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com>
Cc: Jose E. Marchesi <jose.marchesi@oracle.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
tools/testing/selftests/bpf/Makefile
tools/testing/selftests/bpf/bpf_helpers.h
tools/testing/selftests/bpf/progs/test_tc_edt.c

index 7f3196af1ae48056b0be2aecebbf9250877050b2..6889c19a628c238a90da948d5733056039998fbd 100644 (file)
@@ -17,6 +17,7 @@ LLC           ?= llc
 LLVM_OBJCOPY   ?= llvm-objcopy
 LLVM_READELF   ?= llvm-readelf
 BTF_PAHOLE     ?= pahole
+BPF_GCC                ?= $(shell command -v bpf-gcc;)
 CFLAGS += -g -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(BPFDIR) -I$(GENDIR) $(GENFLAGS) -I../../../include \
          -Dbpf_prog_load=bpf_prog_test_load \
          -Dbpf_load_program=bpf_test_load_program
@@ -46,6 +47,10 @@ ifneq ($(SUBREG_CODEGEN),)
 TEST_GEN_FILES += $(patsubst %.o,alu32/%.o, $(BPF_OBJ_FILES))
 endif
 
+ifneq ($(BPF_GCC),)
+TEST_GEN_FILES += $(patsubst %.o,bpf_gcc/%.o, $(BPF_OBJ_FILES))
+endif
+
 # Order correspond to 'make run_tests' order
 TEST_PROGS := test_kmod.sh \
        test_libbpf.sh \
@@ -137,16 +142,19 @@ endif
 #
 # Use '-idirafter': Don't interfere with include mechanics except where the
 # build would have failed anyways.
-CLANG_SYS_INCLUDES := $(shell $(CLANG) -v -E - </dev/null 2>&1 \
+define get_sys_includes
+$(shell $(1) -v -E - </dev/null 2>&1 \
        | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }')
+endef
+CLANG_SYS_INCLUDES = $(call get_sys_includes,$(CLANG))
+BPF_CFLAGS = -I. -I./include/uapi -I../../../include/uapi \
+            -I$(OUTPUT)/../usr/include -D__TARGET_ARCH_$(SRCARCH)
 
-CLANG_FLAGS = -I. -I./include/uapi -I../../../include/uapi \
-             $(CLANG_SYS_INCLUDES) \
-             -Wno-compare-distinct-pointer-types \
-             -D__TARGET_ARCH_$(SRCARCH)
+CLANG_CFLAGS = $(CLANG_SYS_INCLUDES) \
+              -Wno-compare-distinct-pointer-types
 
-$(OUTPUT)/test_l4lb_noinline.o: CLANG_FLAGS += -fno-inline
-$(OUTPUT)/test_xdp_noinline.o: CLANG_FLAGS += -fno-inline
+$(OUTPUT)/test_l4lb_noinline.o: BPF_CFLAGS += -fno-inline
+$(OUTPUT)/test_xdp_noinline.o: BPF_CFLAGS += -fno-inline
 
 $(OUTPUT)/test_queue_map.o: test_queue_stack_map.h
 $(OUTPUT)/test_stack_map.o: test_queue_stack_map.h
@@ -163,12 +171,12 @@ BTF_LLVM_PROBE := $(shell echo "int main() { return 0; }" | \
                          /bin/rm -f ./llvm_btf_verify.o)
 
 ifneq ($(BTF_LLVM_PROBE),)
-       CLANG_FLAGS += -g
+       BPF_CFLAGS += -g
 else
 ifneq ($(BTF_LLC_PROBE),)
 ifneq ($(BTF_PAHOLE_PROBE),)
 ifneq ($(BTF_OBJCOPY_PROBE),)
-       CLANG_FLAGS += -g
+       BPF_CFLAGS += -g
        LLC_FLAGS += -mattr=dwarfris
        DWARF2BTF = y
 endif
@@ -202,8 +210,8 @@ $(ALU32_BUILD_DIR)/test_progs_32: prog_tests/*.c
 
 $(ALU32_BUILD_DIR)/%.o: progs/%.c $(ALU32_BUILD_DIR)/test_progs_32 \
                                        | $(ALU32_BUILD_DIR)
-       ($(CLANG) $(CLANG_FLAGS) -O2 -target bpf -emit-llvm -c $< -o - || \
-               echo "clang failed") | \
+       ($(CLANG) $(BPF_CFLAGS) $(CLANG_CFLAGS) -O2 -target bpf -emit-llvm \
+               -c $< -o - || echo "clang failed") | \
        $(LLC) -march=bpf -mattr=+alu32 -mcpu=$(CPU) $(LLC_FLAGS) \
                -filetype=obj -o $@
 ifeq ($(DWARF2BTF),y)
@@ -211,10 +219,37 @@ ifeq ($(DWARF2BTF),y)
 endif
 endif
 
+ifneq ($(BPF_GCC),)
+GCC_SYS_INCLUDES = $(call get_sys_includes,gcc)
+IS_LITTLE_ENDIAN = $(shell $(CC) -dM -E - </dev/null | \
+                       grep 'define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__')
+ifeq ($(IS_LITTLE_ENDIAN),)
+MENDIAN=-mbig-endian
+else
+MENDIAN=-mlittle-endian
+endif
+BPF_GCC_CFLAGS = $(GCC_SYS_INCLUDES) $(MENDIAN)
+BPF_GCC_BUILD_DIR = $(OUTPUT)/bpf_gcc
+TEST_CUSTOM_PROGS += $(BPF_GCC_BUILD_DIR)/test_progs_bpf_gcc
+$(BPF_GCC_BUILD_DIR):
+       mkdir -p $@
+
+$(BPF_GCC_BUILD_DIR)/urandom_read: $(OUTPUT)/urandom_read | $(BPF_GCC_BUILD_DIR)
+       cp $< $@
+
+$(BPF_GCC_BUILD_DIR)/test_progs_bpf_gcc: $(OUTPUT)/test_progs \
+                                        | $(BPF_GCC_BUILD_DIR)
+       cp $< $@
+
+$(BPF_GCC_BUILD_DIR)/%.o: progs/%.c $(BPF_GCC_BUILD_DIR)/test_progs_bpf_gcc \
+                         | $(BPF_GCC_BUILD_DIR)
+       $(BPF_GCC) $(BPF_CFLAGS) $(BPF_GCC_CFLAGS) -O2 -c $< -o $@
+endif
+
 # Have one program compiled without "-target bpf" to test whether libbpf loads
 # it successfully
 $(OUTPUT)/test_xdp.o: progs/test_xdp.c
-       ($(CLANG) $(CLANG_FLAGS) -O2 -emit-llvm -c $< -o - || \
+       ($(CLANG) $(BPF_CFLAGS) $(CLANG_CFLAGS) -O2 -emit-llvm -c $< -o - || \
                echo "clang failed") | \
        $(LLC) -march=bpf -mcpu=$(CPU) $(LLC_FLAGS) -filetype=obj -o $@
 ifeq ($(DWARF2BTF),y)
@@ -222,8 +257,8 @@ ifeq ($(DWARF2BTF),y)
 endif
 
 $(OUTPUT)/%.o: progs/%.c
-       ($(CLANG) $(CLANG_FLAGS) -O2 -target bpf -emit-llvm -c $< -o - || \
-               echo "clang failed") | \
+       ($(CLANG) $(BPF_CFLAGS) $(CLANG_CFLAGS) -O2 -target bpf -emit-llvm \
+               -c $< -o - || echo "clang failed") | \
        $(LLC) -march=bpf -mcpu=$(CPU) $(LLC_FLAGS) -filetype=obj -o $@
 ifeq ($(DWARF2BTF),y)
        $(BTF_PAHOLE) -J $@
@@ -282,6 +317,6 @@ $(VERIFIER_TESTS_H): $(VERIFIER_TEST_FILES) | $(VERIFIER_TESTS_DIR)
                  echo '#endif' \
                 ) > $(VERIFIER_TESTS_H))
 
-EXTRA_CLEAN := $(TEST_CUSTOM_PROGS) $(ALU32_BUILD_DIR) \
+EXTRA_CLEAN := $(TEST_CUSTOM_PROGS) $(ALU32_BUILD_DIR) $(BPF_GCC_BUILD_DIR) \
        $(VERIFIER_TESTS_H) $(PROG_TESTS_H) $(MAP_TESTS_H) \
        feature
index 6c4930bc6e2ec9fc3263e916865e1fa5e8069eec..54a50699bbfdac12652fb85b12007f78aaadee14 100644 (file)
@@ -1,12 +1,6 @@
 /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
-#ifndef __BPF_HELPERS_H
-#define __BPF_HELPERS_H
-
-/* helper macro to place programs, maps, license in
- * different sections in elf_bpf file. Section names
- * are interpreted by elf_bpf loader
- */
-#define SEC(NAME) __attribute__((section(NAME), used))
+#ifndef __BPF_HELPERS__
+#define __BPF_HELPERS__
 
 #define __uint(name, val) int (*name)[val]
 #define __type(name, val) val *name
                         ##__VA_ARGS__);                \
 })
 
+#ifdef __clang__
+
+/* helper macro to place programs, maps, license in
+ * different sections in elf_bpf file. Section names
+ * are interpreted by elf_bpf loader
+ */
+#define SEC(NAME) __attribute__((section(NAME), used))
+
 /* helper functions called from eBPF programs written in C */
 static void *(*bpf_map_lookup_elem)(void *map, const void *key) =
        (void *) BPF_FUNC_map_lookup_elem;
@@ -256,6 +258,12 @@ struct bpf_map_def {
        unsigned int numa_node;
 };
 
+#else
+
+#include <bpf-helpers.h>
+
+#endif
+
 #define BPF_ANNOTATE_KV_PAIR(name, type_key, type_val)         \
        struct ____btf_map_##name {                             \
                type_key key;                                   \
index 3af64c470d64328922b97d38f17993a1d33d2380..0961415ba4773ee9d396b9246ed6f88d051cddc3 100644 (file)
@@ -2,6 +2,7 @@
 #include <stdint.h>
 #include <linux/bpf.h>
 #include <linux/if_ether.h>
+#include <linux/stddef.h>
 #include <linux/in.h>
 #include <linux/ip.h>
 #include <linux/pkt_cls.h>