From e0a43aa3e4a40fb88d3fa75397da0bf1e60a6c45 Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Fri, 30 Aug 2019 12:00:37 +0100 Subject: [PATCH 1/4] tools: bpftool: ignore make built-in rules for getting kernel version Bpftool calls the toplevel Makefile to get the kernel version for the sources it is built from. But when the utility is built from the top of the kernel repository, it may dump the following error message for certain architectures (including x86): $ make tools/bpf [...] make[3]: *** [checkbin] Error 1 [...] This does not prevent bpftool compilation, but may feel disconcerting. The "checkbin" arch-dependent target is not supposed to be called for target "kernelversion", which is a simple "echo" of the version number. It turns out this is caused by the make invocation in tools/bpf/bpftool, which attempts to find implicit rules to apply. Extract from debug output: Reading makefiles... Reading makefile 'Makefile'... Reading makefile 'scripts/Kbuild.include' (search path) (no ~ expansion)... Reading makefile 'scripts/subarch.include' (search path) (no ~ expansion)... Reading makefile 'arch/x86/Makefile' (search path) (no ~ expansion)... Reading makefile 'scripts/Makefile.kcov' (search path) (no ~ expansion)... Reading makefile 'scripts/Makefile.gcc-plugins' (search path) (no ~ expansion)... Reading makefile 'scripts/Makefile.kasan' (search path) (no ~ expansion)... Reading makefile 'scripts/Makefile.extrawarn' (search path) (no ~ expansion)... Reading makefile 'scripts/Makefile.ubsan' (search path) (no ~ expansion)... Updating makefiles.... Considering target file 'scripts/Makefile.ubsan'. Looking for an implicit rule for 'scripts/Makefile.ubsan'. Trying pattern rule with stem 'Makefile.ubsan'. [...] Trying pattern rule with stem 'Makefile.ubsan'. Trying implicit prerequisite 'scripts/Makefile.ubsan.o'. Looking for a rule with intermediate file 'scripts/Makefile.ubsan.o'. Avoiding implicit rule recursion. Trying pattern rule with stem 'Makefile.ubsan'. Trying rule prerequisite 'prepare'. Trying rule prerequisite 'FORCE'. Found an implicit rule for 'scripts/Makefile.ubsan'. Considering target file 'prepare'. File 'prepare' does not exist. Considering target file 'prepare0'. File 'prepare0' does not exist. Considering target file 'archprepare'. File 'archprepare' does not exist. Considering target file 'archheaders'. File 'archheaders' does not exist. Finished prerequisites of target file 'archheaders'. Must remake target 'archheaders'. Putting child 0x55976f4f6980 (archheaders) PID 31743 on the chain. To avoid that, pass the -r and -R flags to eliminate the use of make built-in rules (and while at it, built-in variables) when running command "make kernelversion" from bpftool's Makefile. Signed-off-by: Quentin Monnet Reviewed-by: Jakub Kicinski Signed-off-by: Daniel Borkmann --- tools/bpf/bpftool/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile index f284c207765ad..cd0fc05464e7f 100644 --- a/tools/bpf/bpftool/Makefile +++ b/tools/bpf/bpftool/Makefile @@ -24,7 +24,7 @@ endif LIBBPF = $(BPF_PATH)libbpf.a -BPFTOOL_VERSION := $(shell make --no-print-directory -sC ../../.. kernelversion) +BPFTOOL_VERSION := $(shell make -rR --no-print-directory -sC ../../.. kernelversion) $(LIBBPF): FORCE $(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(OUTPUT) $(OUTPUT)libbpf.a From 45c5589d07158eb1d0b5b313314706024bf451c5 Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Fri, 30 Aug 2019 12:00:38 +0100 Subject: [PATCH 2/4] tools: bpftool: improve and check builds for different make invocations There are a number of alternative "make" invocations that can be used to compile bpftool. The following invocations are expected to work: - through the kbuild system, from the top of the repository (make tools/bpf) - by telling make to change to the bpftool directory (make -C tools/bpf/bpftool) - by building the BPF tools from tools/ (cd tools && make bpf) - by running make from bpftool directory (cd tools/bpf/bpftool && make) Additionally, setting the O or OUTPUT variables should tell the build system to use a custom output path, for each of these alternatives. The following patch fixes the following invocations: $ make tools/bpf $ make tools/bpf O= $ make -C tools/bpf/bpftool OUTPUT= $ make -C tools/bpf/bpftool O= $ cd tools/ && make bpf O= $ cd tools/bpf/bpftool && make OUTPUT= $ cd tools/bpf/bpftool && make O= After this commit, the build still fails for two variants when passing the OUTPUT variable: $ make tools/bpf OUTPUT= $ cd tools/ && make bpf OUTPUT= In order to remember and check what make invocations are supposed to work, and to document the ones which do not, a new script is added to the BPF selftests. Note that some invocations require the kernel to be configured, so the script skips them if no .config file is found. v2: - In make_and_clean(), set $ERROR to 1 when "make" returns non-zero, even if the binary was produced. - Run "make clean" from the correct directory (bpf/ instead of bpftool/, when relevant). Reported-by: Lorenz Bauer Signed-off-by: Quentin Monnet Reviewed-by: Jakub Kicinski Signed-off-by: Daniel Borkmann --- tools/bpf/bpftool/Makefile | 12 +- tools/testing/selftests/bpf/Makefile | 3 +- .../selftests/bpf/test_bpftool_build.sh | 143 ++++++++++++++++++ 3 files changed, 152 insertions(+), 6 deletions(-) create mode 100755 tools/testing/selftests/bpf/test_bpftool_build.sh diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile index cd0fc05464e7f..3fc82ff9b52cf 100644 --- a/tools/bpf/bpftool/Makefile +++ b/tools/bpf/bpftool/Makefile @@ -17,21 +17,23 @@ endif BPF_DIR = $(srctree)/tools/lib/bpf/ ifneq ($(OUTPUT),) - BPF_PATH = $(OUTPUT) + LIBBPF_OUTPUT = $(OUTPUT)/libbpf/ + LIBBPF_PATH = $(LIBBPF_OUTPUT) else - BPF_PATH = $(BPF_DIR) + LIBBPF_PATH = $(BPF_DIR) endif -LIBBPF = $(BPF_PATH)libbpf.a +LIBBPF = $(LIBBPF_PATH)libbpf.a BPFTOOL_VERSION := $(shell make -rR --no-print-directory -sC ../../.. kernelversion) $(LIBBPF): FORCE - $(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(OUTPUT) $(OUTPUT)libbpf.a + $(if $(LIBBPF_OUTPUT),@mkdir -p $(LIBBPF_OUTPUT)) + $(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(LIBBPF_OUTPUT) $(LIBBPF_OUTPUT)libbpf.a $(LIBBPF)-clean: $(call QUIET_CLEAN, libbpf) - $(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(OUTPUT) clean >/dev/null + $(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(LIBBPF_OUTPUT) clean >/dev/null prefix ?= /usr/local bash_compdir ?= /usr/share/bash-completion/completions diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 1faad0c3c3c92..c7595b4ed55da 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -63,7 +63,8 @@ TEST_PROGS := test_kmod.sh \ test_tcp_check_syncookie.sh \ test_tc_tunnel.sh \ test_tc_edt.sh \ - test_xdping.sh + test_xdping.sh \ + test_bpftool_build.sh TEST_PROGS_EXTENDED := with_addr.sh \ with_tunnels.sh \ diff --git a/tools/testing/selftests/bpf/test_bpftool_build.sh b/tools/testing/selftests/bpf/test_bpftool_build.sh new file mode 100755 index 0000000000000..4ba5a34bff568 --- /dev/null +++ b/tools/testing/selftests/bpf/test_bpftool_build.sh @@ -0,0 +1,143 @@ +#!/bin/bash +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) + +ERROR=0 +TMPDIR= + +# If one build fails, continue but return non-0 on exit. +return_value() { + if [ -d "$TMPDIR" ] ; then + rm -rf -- $TMPDIR + fi + exit $ERROR +} +trap return_value EXIT + +case $1 in + -h|--help) + echo -e "$0 [-j ]" + echo -e "\tTest the different ways of building bpftool." + echo -e "" + echo -e "\tOptions:" + echo -e "\t\t-j :\tPass -j flag to 'make'." + exit + ;; +esac + +J=$* + +# Assume script is located under tools/testing/selftests/bpf/. We want to start +# build attempts from the top of kernel repository. +SCRIPT_REL_PATH=$(realpath --relative-to=$PWD $0) +SCRIPT_REL_DIR=$(dirname $SCRIPT_REL_PATH) +KDIR_ROOT_DIR=$(realpath $PWD/$SCRIPT_REL_DIR/../../../../) +cd $KDIR_ROOT_DIR + +check() { + local dir=$(realpath $1) + + echo -n "binary: " + # Returns non-null if file is found (and "false" is run) + find $dir -type f -executable -name bpftool -print -exec false {} + && \ + ERROR=1 && printf "FAILURE: Did not find bpftool\n" +} + +make_and_clean() { + echo -e "\$PWD: $PWD" + echo -e "command: make -s $* >/dev/null" + make $J -s $* >/dev/null + if [ $? -ne 0 ] ; then + ERROR=1 + fi + if [ $# -ge 1 ] ; then + check ${@: -1} + else + check . + fi + ( + if [ $# -ge 1 ] ; then + cd ${@: -1} + fi + make -s clean + ) + echo +} + +make_with_tmpdir() { + local ARGS + + TMPDIR=$(mktemp -d) + if [ $# -ge 2 ] ; then + ARGS=${@:1:(($# - 1))} + fi + echo -e "\$PWD: $PWD" + echo -e "command: make -s $ARGS ${@: -1}=$TMPDIR/ >/dev/null" + make $J -s $ARGS ${@: -1}=$TMPDIR/ >/dev/null + if [ $? -ne 0 ] ; then + ERROR=1 + fi + check $TMPDIR + rm -rf -- $TMPDIR + echo +} + +echo "Trying to build bpftool" +echo -e "... through kbuild\n" + +if [ -f ".config" ] ; then + make_and_clean tools/bpf + + ## $OUTPUT is overwritten in kbuild Makefile, and thus cannot be passed + ## down from toplevel Makefile to bpftool's Makefile. + + # make_with_tmpdir tools/bpf OUTPUT + echo -e "skip: make tools/bpf OUTPUT= (not supported)\n" + + make_with_tmpdir tools/bpf O +else + echo -e "skip: make tools/bpf (no .config found)\n" + echo -e "skip: make tools/bpf OUTPUT= (not supported)\n" + echo -e "skip: make tools/bpf O= (no .config found)\n" +fi + +echo -e "... from kernel source tree\n" + +make_and_clean -C tools/bpf/bpftool + +make_with_tmpdir -C tools/bpf/bpftool OUTPUT + +make_with_tmpdir -C tools/bpf/bpftool O + +echo -e "... from tools/\n" +cd tools/ + +make_and_clean bpf + +## In tools/bpf/Makefile, function "descend" is called and passes $(O) and +## $(OUTPUT). We would like $(OUTPUT) to have "bpf/bpftool/" appended before +## calling bpftool's Makefile, but this is not the case as the "descend" +## function focuses on $(O)/$(subdir). However, in the present case, updating +## $(O) to have $(OUTPUT) recomputed from it in bpftool's Makefile does not +## work, because $(O) is not defined from command line and $(OUTPUT) is not +## updated in tools/scripts/Makefile.include. +## +## Workarounds would require to a) edit "descend" or use an alternative way to +## call bpftool's Makefile, b) modify the conditions to update $(OUTPUT) and +## other variables in tools/scripts/Makefile.include (at the risk of breaking +## the build of other tools), or c) append manually the "bpf/bpftool" suffix to +## $(OUTPUT) in bpf's Makefile, which may break if targets for other directories +## use "descend" in the future. + +# make_with_tmpdir bpf OUTPUT +echo -e "skip: make bpf OUTPUT= (not supported)\n" + +make_with_tmpdir bpf O + +echo -e "... from bpftool's dir\n" +cd bpf/bpftool + +make_and_clean + +make_with_tmpdir OUTPUT + +make_with_tmpdir O From fbdb620b7c674928a5713dbad9b07252ff929d0a Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Fri, 30 Aug 2019 12:00:39 +0100 Subject: [PATCH 3/4] tools: bpf: account for generated feature/ and libbpf/ directories When building "tools/bpf" from the top of the Linux repository, the build system passes a value for the $(OUTPUT) Makefile variable to tools/bpf/Makefile and tools/bpf/bpftool/Makefile, which results in generating "libbpf/" (for bpftool) and "feature/" (bpf and bpftool) directories inside the tree. This commit adds such directories to the relevant .gitignore files, and edits the Makefiles to ensure they are removed on "make clean". The use of "rm" is also made consistent throughout those Makefiles (relies on the $(RM) variable, use "--" to prevent interpreting $(OUTPUT)/$(DESTDIR) as options. v2: - New patch. Signed-off-by: Quentin Monnet Signed-off-by: Daniel Borkmann --- tools/bpf/.gitignore | 1 + tools/bpf/Makefile | 5 +++-- tools/bpf/bpftool/.gitignore | 2 ++ tools/bpf/bpftool/Makefile | 10 ++++++---- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/tools/bpf/.gitignore b/tools/bpf/.gitignore index dfe2bd5a4b953..59024197e71dd 100644 --- a/tools/bpf/.gitignore +++ b/tools/bpf/.gitignore @@ -1,4 +1,5 @@ FEATURE-DUMP.bpf +feature bpf_asm bpf_dbg bpf_exp.yacc.* diff --git a/tools/bpf/Makefile b/tools/bpf/Makefile index 53b60ad452f5d..fbf5e4a0cb9c9 100644 --- a/tools/bpf/Makefile +++ b/tools/bpf/Makefile @@ -81,10 +81,11 @@ $(OUTPUT)bpf_exp.lex.o: $(OUTPUT)bpf_exp.lex.c clean: bpftool_clean $(call QUIET_CLEAN, bpf-progs) - $(Q)rm -rf $(OUTPUT)*.o $(OUTPUT)bpf_jit_disasm $(OUTPUT)bpf_dbg \ + $(Q)$(RM) -r -- $(OUTPUT)*.o $(OUTPUT)bpf_jit_disasm $(OUTPUT)bpf_dbg \ $(OUTPUT)bpf_asm $(OUTPUT)bpf_exp.yacc.* $(OUTPUT)bpf_exp.lex.* $(call QUIET_CLEAN, core-gen) - $(Q)rm -f $(OUTPUT)FEATURE-DUMP.bpf + $(Q)$(RM) -- $(OUTPUT)FEATURE-DUMP.bpf + $(Q)$(RM) -r -- $(OUTPUT)feature install: $(PROGS) bpftool_install $(call QUIET_INSTALL, bpf_jit_disasm) diff --git a/tools/bpf/bpftool/.gitignore b/tools/bpf/bpftool/.gitignore index 8248b8dd89d4b..b13926432b84c 100644 --- a/tools/bpf/bpftool/.gitignore +++ b/tools/bpf/bpftool/.gitignore @@ -3,3 +3,5 @@ bpftool*.8 bpf-helpers.* FEATURE-DUMP.bpftool +feature +libbpf diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile index 3fc82ff9b52cf..b0c5a369f54aa 100644 --- a/tools/bpf/bpftool/Makefile +++ b/tools/bpf/bpftool/Makefile @@ -124,9 +124,11 @@ $(OUTPUT)%.o: %.c clean: $(LIBBPF)-clean $(call QUIET_CLEAN, bpftool) - $(Q)$(RM) $(OUTPUT)bpftool $(OUTPUT)*.o $(OUTPUT)*.d + $(Q)$(RM) -- $(OUTPUT)bpftool $(OUTPUT)*.o $(OUTPUT)*.d + $(Q)$(RM) -r -- $(OUTPUT)libbpf/ $(call QUIET_CLEAN, core-gen) - $(Q)$(RM) $(OUTPUT)FEATURE-DUMP.bpftool + $(Q)$(RM) -- $(OUTPUT)FEATURE-DUMP.bpftool + $(Q)$(RM) -r -- $(OUTPUT)feature/ install: $(OUTPUT)bpftool $(call QUIET_INSTALL, bpftool) @@ -137,8 +139,8 @@ install: $(OUTPUT)bpftool uninstall: $(call QUIET_UNINST, bpftool) - $(Q)$(RM) $(DESTDIR)$(prefix)/sbin/bpftool - $(Q)$(RM) $(DESTDIR)$(bash_compdir)/bpftool + $(Q)$(RM) -- $(DESTDIR)$(prefix)/sbin/bpftool + $(Q)$(RM) -- $(DESTDIR)$(bash_compdir)/bpftool doc: $(call descend,Documentation) From 5b84ad2e89215835ea6476ba05295e849b30886b Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Fri, 30 Aug 2019 12:00:40 +0100 Subject: [PATCH 4/4] tools: bpftool: do not link twice against libbpf.a in Makefile In bpftool's Makefile, $(LIBS) includes $(LIBBPF), therefore the library is used twice in the linking command. No need to have $(LIBBPF) (from $^) on that command, let's do with "$(OBJS) $(LIBS)" (but move $(LIBBPF) _before_ the -l flags in $(LIBS)). Signed-off-by: Ilya Leoshkevich Signed-off-by: Quentin Monnet Reviewed-by: Jakub Kicinski Signed-off-by: Daniel Borkmann --- tools/bpf/bpftool/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile index b0c5a369f54aa..39bc6f0f4f0bb 100644 --- a/tools/bpf/bpftool/Makefile +++ b/tools/bpf/bpftool/Makefile @@ -55,7 +55,7 @@ ifneq ($(EXTRA_LDFLAGS),) LDFLAGS += $(EXTRA_LDFLAGS) endif -LIBS = -lelf -lz $(LIBBPF) +LIBS = $(LIBBPF) -lelf -lz INSTALL ?= install RM ?= rm -f @@ -117,7 +117,7 @@ $(OUTPUT)disasm.o: $(srctree)/kernel/bpf/disasm.c $(OUTPUT)feature.o: | zdep $(OUTPUT)bpftool: $(OBJS) $(LIBBPF) - $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) + $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(OUTPUT)%.o: %.c $(QUIET_CC)$(COMPILE.c) -MMD -o $@ $<