AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/07/22 13:29 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch only modifies the build system for BPF selftests (Makefiles and a shell script). It does not make any functional changes to the kernel code.",
  "WorthFuzzing": false
}

1/1 2026/07/22 13:29 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 0e0ca90bb7fa373ec8f7e4ff1764f81eabcca966\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Wed Jul 22 13:29:21 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile\nindex b642ee489ea640..a3ab525f7dfabb 100644\n--- a/tools/testing/selftests/bpf/Makefile\n+++ b/tools/testing/selftests/bpf/Makefile\n@@ -1,96 +1,44 @@\n # SPDX-License-Identifier: GPL-2.0\n+#\n+# BPF selftests build.\n+#\n+# The build is split in two layers:\n+#\n+#   Makefile (this file)\n+#       Everything that exists once: knobs and toolchain setup, libbpf /\n+#       bpftool / resolve_btfids sub-builds, vmlinux.h, signing keys,\n+#       generated tests.h headers, shared helper objects, standalone test\n+#       binaries (bench, veristat, test_verifier, xsk*, ...), kernel test\n+#       modules, docs, and the kselftest lib.mk run/install/clean\n+#       contract.\n+#\n+#   Makefile.runner\n+#       One test-runner instance (test_progs, test_progs-\u003cflavor\u003e,\n+#       test_maps): BPF objects, skeleton headers, test objects and the\n+#       final runner binary. Invoked from here once per instance; each\n+#       invocation is an independent single-flavor namespace, so the\n+#       runner is plain make without $$-escaping or eval.\n+#\n+#   gen_bpf_skel.sh\n+#       Links BPF objects via bpftool and generates (light) skeletons;\n+#       one script instead of four near-identical make recipes.\n+#\n+# Shared definitions (paths, flags, permissive-mode helpers) live in\n+# Makefile.buildvars, included by both layers.\n+\n include ../../../build/Build.include\n include ../../../scripts/Makefile.arch\n include ../../../scripts/Makefile.include\n \n-CXX ?= $(CROSS_COMPILE)g++\n-OBJCOPY ?= $(CROSS_COMPILE)objcopy\n-\n-CURDIR := $(abspath .)\n-TOOLSDIR := $(abspath ../../..)\n-LIBDIR := $(TOOLSDIR)/lib\n-BPFDIR := $(LIBDIR)/bpf\n-TOOLSINCDIR := $(TOOLSDIR)/include\n-TOOLSARCHINCDIR := $(TOOLSDIR)/arch/$(SRCARCH)/include\n-BPFTOOLDIR := $(TOOLSDIR)/bpf/bpftool\n-APIDIR := $(TOOLSINCDIR)/uapi\n-ifneq ($(O),)\n-GENDIR := $(O)/include/generated\n-else\n-GENDIR := $(abspath ../../../../include/generated)\n-endif\n-GENHDR := $(GENDIR)/autoconf.h\n-PKG_CONFIG ?= $(CROSS_COMPILE)pkg-config\n-\n-ifneq ($(wildcard $(GENHDR)),)\n-  GENFLAGS := -DHAVE_GENHDR\n+# These probes gate the flavor list, which must be final before lib.mk is\n+# included; keep values consistent with Makefile.buildvars.\n+ifeq ($(origin BPF_GCC),undefined)\n+BPF_GCC := $(shell command -v bpf-gcc;)\n endif\n-\n-BPF_GCC\t\t?= $(shell command -v bpf-gcc;)\n-ifdef ASAN\n-SAN_CFLAGS \t?= -fsanitize=address -fno-omit-frame-pointer\n-else\n-SAN_CFLAGS\t?=\n-endif\n-SAN_LDFLAGS\t?= $(SAN_CFLAGS)\n-RELEASE\t\t?=\n-OPT_FLAGS\t?= $(if $(RELEASE),-O2,-O0)\n-\n-LIBELF_CFLAGS\t:= $(shell $(PKG_CONFIG) libelf --cflags 2\u003e/dev/null)\n-LIBELF_LIBS\t:= $(shell $(PKG_CONFIG) libelf --libs 2\u003e/dev/null || echo -lelf)\n-\n-SKIP_DOCS\t?=\n-SKIP_LLVM\t?=\n-SKIP_LIBBFD\t?=\n-SKIP_CRYPTO\t?=\n-\n-# When BPF_STRICT_BUILD is 1, any BPF object, skeleton, test object, or\n-# benchmark compilation failure is fatal. Set to 0 to tolerate failures\n-# and continue building the remaining tests.\n-BPF_STRICT_BUILD ?= 1\n-PERMISSIVE := $(filter 0,$(BPF_STRICT_BUILD))\n-\n-ifeq ($(srctree),)\n-srctree := $(patsubst %/,%,$(dir $(CURDIR)))\n-srctree := $(patsubst %/,%,$(dir $(srctree)))\n-srctree := $(patsubst %/,%,$(dir $(srctree)))\n-srctree := $(patsubst %/,%,$(dir $(srctree)))\n-endif\n-\n-COMMON_CFLAGS = -g $(OPT_FLAGS) -rdynamic -std=gnu11\t\t\t\t\\\n-\t  -Wall -Werror -fno-omit-frame-pointer\t\t\t\t\\\n-\t  -Wno-unused-but-set-variable\t\t\t\t\t\\\n-\t  $(GENFLAGS) $(SAN_CFLAGS) $(LIBELF_CFLAGS)\t\t\t\\\n-\t  -I$(CURDIR) -I$(INCLUDE_DIR) -I$(GENDIR) -I$(LIBDIR)\t\t\\\n-\t  -I$(TOOLSINCDIR) -I$(TOOLSARCHINCDIR) -I$(APIDIR) -I$(OUTPUT)\t\\\n-\t  -I$(CURDIR)/libarena/include\n-LDFLAGS += $(SAN_LDFLAGS)\n-LDLIBS += $(LIBELF_LIBS) -lz -lrt -lpthread\n-\n-PCAP_CFLAGS\t:= $(shell $(PKG_CONFIG) --cflags libpcap 2\u003e/dev/null \u0026\u0026 echo \"-DTRAFFIC_MONITOR=1\")\n-PCAP_LIBS\t:= $(shell $(PKG_CONFIG) --libs libpcap 2\u003e/dev/null)\n-LDLIBS += $(PCAP_LIBS)\n-CFLAGS += $(COMMON_CFLAGS) $(PCAP_CFLAGS)\n-\n-# Some utility functions use LLVM libraries\n-jit_disasm_helpers.c-CFLAGS = $(LLVM_CFLAGS)\n-\n-ifneq ($(LLVM),)\n-# Silence some warnings when compiled with clang\n-CFLAGS += -Wno-unused-command-line-argument\n-endif\n-\n-# Check whether bpf cpu=v4 is supported or not by clang\n ifneq ($(shell $(CLANG) --target=bpf -mcpu=help 2\u003e\u00261 | grep 'v4'),)\n CLANG_CPUV4 := 1\n endif\n \n-# Check whether clang supports BPF address sanitizer (requires LLVM 22+)\n-CLANG_HAS_ARENA_ASAN := $(shell echo 'int x;' | \\\n-\t$(CLANG) --target=bpf -fsanitize=kernel-address \\\n-\t-mllvm -asan-shadow-addr-space=1 \\\n-\t-x c -c - -o /dev/null 2\u003e/dev/null \u0026\u0026 echo 1)\n-\n # Order correspond to 'make run_tests' order\n TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_progs \\\n \ttest_sockmap \\\n@@ -102,16 +50,6 @@ TEST_INST_SUBDIRS := no_alu32\n ifneq ($(BPF_GCC),)\n TEST_GEN_PROGS += test_progs-bpf_gcc\n TEST_INST_SUBDIRS += bpf_gcc\n-\n-# The following tests contain C code that, although technically legal,\n-# triggers GCC warnings that cannot be disabled: declaration of\n-# anonymous struct types in function parameter lists.\n-progs/btf_dump_test_case_bitfields.c-bpf_gcc-CFLAGS := -Wno-error\n-progs/btf_dump_test_case_namespacing.c-bpf_gcc-CFLAGS := -Wno-error\n-progs/btf_dump_test_case_packing.c-bpf_gcc-CFLAGS := -Wno-error\n-progs/btf_dump_test_case_padding.c-bpf_gcc-CFLAGS := -Wno-error\n-progs/btf_dump_test_case_syntax.c-bpf_gcc-CFLAGS := -Wno-error\n-\n endif\n \n ifneq ($(CLANG_CPUV4),)\n@@ -132,10 +70,6 @@ TEST_PROGS := test_kmod.sh \\\n TEST_PROGS_EXTENDED := \\\n \tima_setup.sh verify_sig_setup.sh\n \n-TEST_KMODS := bpf_testmod.ko bpf_test_no_cfi.ko bpf_test_modorder_x.ko \\\n-\tbpf_test_modorder_y.ko bpf_test_rqspinlock.ko\n-TEST_KMOD_TARGETS = $(addprefix $(OUTPUT)/,$(TEST_KMODS))\n-\n # Compile but not part of 'make run_tests'\n TEST_GEN_PROGS_EXTENDED = \\\n \tbench \\\n@@ -148,11 +82,10 @@ TEST_GEN_PROGS_EXTENDED = \\\n \txdp_synproxy \\\n \txskxceiver\n \n-TEST_GEN_FILES += $(TEST_KMODS) liburandom_read.so urandom_read sign-file uprobe_multi\n+TEST_KMODS ?= bpf_testmod.ko bpf_test_no_cfi.ko bpf_test_modorder_x.ko \\\n+\tbpf_test_modorder_y.ko bpf_test_rqspinlock.ko\n \n-ifneq ($(V),1)\n-submake_extras := feature_display=0\n-endif\n+TEST_GEN_FILES += $(TEST_KMODS) liburandom_read.so urandom_read sign-file uprobe_multi\n \n # override lib.mk's default rules\n OVERRIDE_TARGETS := 1\n@@ -171,7 +104,7 @@ endef\n include ../lib.mk\n \n NON_CHECK_FEAT_TARGETS := clean docs-clean emit_tests\n-CHECK_FEAT := $(filter-out $(NON_CHECK_FEAT_TARGETS),$(or $(MAKECMDGOALS), \"none\"))\n+CHECK_FEAT := $(filter-out $(NON_CHECK_FEAT_TARGETS),$(or $(MAKECMDGOALS),all))\n ifneq ($(CHECK_FEAT),)\n FEATURE_USER := .selftests\n FEATURE_TESTS := llvm\n@@ -188,6 +121,8 @@ override OUTPUT := $(patsubst %/,%,$(OUTPUT))\n endif\n endif\n \n+include Makefile.buildvars\n+\n ifneq ($(SKIP_LLVM),1)\n ifeq ($(feature-llvm),1)\n   LLVM_CFLAGS  += -DHAVE_LLVM_SUPPORT\n@@ -213,36 +148,11 @@ ifeq ($(feature-llvm),1)\n endif\n endif\n \n-SCRATCH_DIR := $(OUTPUT)/tools\n-BUILD_DIR := $(SCRATCH_DIR)/build\n-INCLUDE_DIR := $(SCRATCH_DIR)/include\n-BPFOBJ := $(BUILD_DIR)/libbpf/libbpf.a\n-ifneq ($(CROSS_COMPILE),)\n-HOST_BUILD_DIR\t\t:= $(BUILD_DIR)/host\n-HOST_SCRATCH_DIR\t:= $(OUTPUT)/host-tools\n-HOST_INCLUDE_DIR\t:= $(HOST_SCRATCH_DIR)/include\n-else\n-HOST_BUILD_DIR\t\t:= $(BUILD_DIR)\n-HOST_SCRATCH_DIR\t:= $(SCRATCH_DIR)\n-HOST_INCLUDE_DIR\t:= $(INCLUDE_DIR)\n-endif\n-HOST_BPFOBJ := $(HOST_BUILD_DIR)/libbpf/libbpf.a\n-RESOLVE_BTFIDS := $(HOST_BUILD_DIR)/resolve_btfids/resolve_btfids\n-VMLINUX_BTF_PATHS ?= $(if $(O),$(O)/vmlinux)\t\t\t\t\\\n-\t\t     $(if $(KBUILD_OUTPUT),$(KBUILD_OUTPUT)/vmlinux)\t\\\n-\t\t     ../../../../vmlinux\t\t\t\t\\\n-\t\t     /sys/kernel/btf/vmlinux\t\t\t\t\\\n-\t\t     /boot/vmlinux-$(shell uname -r)\n-VMLINUX_BTF ?= $(abspath $(firstword $(wildcard $(VMLINUX_BTF_PATHS))))\n-ifeq ($(VMLINUX_BTF),)\n-$(error Cannot find a vmlinux for VMLINUX_BTF at any of \"$(VMLINUX_BTF_PATHS)\")\n-endif\n-\n # Define simple and short `make test_progs`, `make test_maps`, etc targets\n # to build individual tests.\n # NOTE: Semicolon at the end is critical to override lib.mk's default static\n # rule for binaries.\n-$(notdir $(TEST_GEN_PROGS) $(TEST_KMODS)\t\t\t\t\\\n+$(notdir $(TEST_GEN_PROGS) $(TEST_KMOD_TARGETS)\t\t\t\t\\\n \t $(TEST_GEN_PROGS_EXTENDED)): %: $(OUTPUT)/% ;\n \n # sort removes libbpf duplicates when not cross-building\n@@ -256,11 +166,14 @@ $(MAKE_DIRS):\n \n $(OUTPUT)/%.o: %.c\n \t$(call msg,CC,,$@)\n-\t$(Q)$(CC) $(CFLAGS) -c $(filter %.c,$^) $(LDLIBS) -o $@\n+\t$(Q)$(CC) $(CFLAGS) -c $(filter %.c,$^) -o $@\n \n+# Headers may appear among the prerequisites for dependency tracking;\n+# they must not reach the clang/gcc command line (clang treats a bare .h\n+# argument as a precompiled-header job and refuses -o).\n $(OUTPUT)/%:%.c\n \t$(call msg,BINARY,,$@)\n-\t$(Q)$(LINK.c) $^ $(LDLIBS) -o $@\n+\t$(Q)$(LINK.c) $(filter-out %.h,$^) $(LDLIBS) -o $@\n \n # LLVM's ld.lld doesn't support all the architectures, so use it only on x86\n ifeq ($(SRCARCH),$(filter $(SRCARCH),x86 riscv))\n@@ -297,6 +210,13 @@ $(OUTPUT)/sign-file: ../../../../scripts/sign-file.c\n \t\t  $\u003c -o $@ \\\n \t\t  $(shell $(PKG_CONFIG) --libs libcrypto 2\u003e /dev/null || echo -lcrypto)\n \n+# Linking uprobe_multi can fail due to relocation overflows on mips.\n+$(OUTPUT)/uprobe_multi: CFLAGS += $(if $(filter mips, $(ARCH)),-mxgot)\n+$(OUTPUT)/uprobe_multi: uprobe_multi.c uprobe_multi.ld\n+\t$(call msg,BINARY,,$@)\n+\t$(Q)$(CC) $(CFLAGS) -Wl,-T,uprobe_multi.ld -O0 $(LDFLAGS) \t\\\n+\t\t$(filter-out %.ld,$^) $(LDLIBS) -o $@\n+\n # This should really be a grouped target, but make versions before 4.3 don't\n # support that for regular rules. However, pattern matching rules are implicitly\n # treated as grouped even with older versions of make, so as a workaround, the\n@@ -313,40 +233,18 @@ $(TEST_KMOD_TARGETS): $(addprefix test_kmods/,$(TEST_KMODS))\n \t$(call msg,MOD,,$@)\n \t$(Q)$(if $(PERMISSIVE),if [ -f test_kmods/$(@F) ]; then )cp test_kmods/$(@F) $@$(if $(PERMISSIVE),; fi)\n \n-\n-DEFAULT_BPFTOOL := $(HOST_SCRATCH_DIR)/sbin/bpftool\n-ifneq ($(CROSS_COMPILE),)\n-CROSS_BPFTOOL := $(SCRATCH_DIR)/sbin/bpftool\n-TRUNNER_BPFTOOL := $(CROSS_BPFTOOL)\n-USE_BOOTSTRAP := \"\"\n-else\n-TRUNNER_BPFTOOL := $(DEFAULT_BPFTOOL)\n-USE_BOOTSTRAP := \"bootstrap/\"\n-endif\n-\n TEST_GEN_PROGS_EXTENDED += $(TRUNNER_BPFTOOL)\n \n $(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED): $(BPFOBJ)\n \n-TESTING_HELPERS\t:= $(OUTPUT)/testing_helpers.o\n-CGROUP_HELPERS\t:= $(OUTPUT)/cgroup_helpers.o\n-UNPRIV_HELPERS  := $(OUTPUT)/unpriv_helpers.o\n-TRACE_HELPERS\t:= $(OUTPUT)/trace_helpers.o\n-JSON_WRITER\t\t:= $(OUTPUT)/json_writer.o\n-CAP_HELPERS\t:= $(OUTPUT)/cap_helpers.o\n-NETWORK_HELPERS := $(OUTPUT)/network_helpers.o\n-\n-$(OUTPUT)/test_sockmap: $(CGROUP_HELPERS) $(TESTING_HELPERS)\n-$(OUTPUT)/test_tcpnotify_user: $(CGROUP_HELPERS) $(TESTING_HELPERS) $(TRACE_HELPERS)\n-$(OUTPUT)/test_sock_fields: $(CGROUP_HELPERS) $(TESTING_HELPERS)\n-$(OUTPUT)/test_tag: $(TESTING_HELPERS)\n-$(OUTPUT)/test_lirc_mode2_user: $(TESTING_HELPERS)\n-$(OUTPUT)/flow_dissector_load: $(TESTING_HELPERS)\n-$(OUTPUT)/test_maps: $(TESTING_HELPERS)\n-$(OUTPUT)/test_verifier: $(TESTING_HELPERS) $(CAP_HELPERS) $(UNPRIV_HELPERS)\n-$(OUTPUT)/xsk.o: $(BPFOBJ)\n-\n-BPFTOOL ?= $(DEFAULT_BPFTOOL)\n+# ---------------------------------------------------------------------\n+# Tool sub-builds: bpftool and libbpf (host and, when cross-compiling,\n+# target variants) and resolve_btfids.\n+#\n+# Note the deliberate asymmetry: the host libbpf builds without\n+# sanitizers and with the host toolchain.\n+# ---------------------------------------------------------------------\n+\n $(DEFAULT_BPFTOOL): $(wildcard $(BPFTOOLDIR)/*.[ch] $(BPFTOOLDIR)/Makefile)    \\\n \t\t    $(HOST_BPFOBJ) | $(HOST_BUILD_DIR)/bpftool\n \t$(Q)$(MAKE) $(submake_extras)  -C $(BPFTOOLDIR)\t\t\t       \\\n@@ -377,20 +275,6 @@ $(CROSS_BPFTOOL): $(wildcard $(BPFTOOLDIR)/*.[ch] $(BPFTOOLDIR)/Makefile)\t\\\n \t\t    prefix= DESTDIR=$(SCRATCH_DIR)/ install-bin\n endif\n \n-ifneq ($(SKIP_DOCS),1)\n-all: docs\n-endif\n-\n-docs:\n-\t$(Q)RST2MAN_OPTS=\"--exit-status=1\" $(MAKE) $(submake_extras)\t\\\n-\t            -f Makefile.docs\t\t\t\t\t\\\n-\t            prefix= OUTPUT=$(OUTPUT)/ DESTDIR=$(OUTPUT)/ $@\n-\n-docs-clean:\n-\t$(Q)$(MAKE) $(submake_extras)\t\t\t\t\t\\\n-\t            -f Makefile.docs\t\t\t\t\t\\\n-\t            prefix= OUTPUT=$(OUTPUT)/ DESTDIR=$(OUTPUT)/ $@\n-\n $(BPFOBJ): $(wildcard $(BPFDIR)/*.[ch] $(BPFDIR)/Makefile)\t\t       \\\n \t   $(APIDIR)/linux/bpf.h\t\t\t\t\t       \\\n \t   | $(BUILD_DIR)/libbpf\n@@ -412,19 +296,6 @@ $(HOST_BPFOBJ): $(wildcard $(BPFDIR)/*.[ch] $(BPFDIR)/Makefile)\t\t       \\\n \t\t    DESTDIR=$(HOST_SCRATCH_DIR)/ prefix= all install_headers\n endif\n \n-# vmlinux.h is first dumped to a temporary file and then compared to\n-# the previous version. This helps to avoid unnecessary re-builds of\n-# $(TRUNNER_BPF_OBJS)\n-$(INCLUDE_DIR)/vmlinux.h: $(VMLINUX_BTF) $(BPFTOOL) | $(INCLUDE_DIR)\n-ifeq ($(VMLINUX_H),)\n-\t$(call msg,GEN,,$@)\n-\t$(Q)$(BPFTOOL) btf dump file $(VMLINUX_BTF) format c \u003e $(INCLUDE_DIR)/.vmlinux.h.tmp\n-\t$(Q)cmp -s $(INCLUDE_DIR)/.vmlinux.h.tmp $@ || mv $(INCLUDE_DIR)/.vmlinux.h.tmp $@\n-else\n-\t$(call msg,CP,,$@)\n-\t$(Q)cp \"$(VMLINUX_H)\" $@\n-endif\n-\n $(RESOLVE_BTFIDS): $(HOST_BPFOBJ) | $(HOST_BUILD_DIR)/resolve_btfids\t\\\n \t\t       $(TOOLSDIR)/bpf/resolve_btfids/main.c\t\\\n \t\t       $(TOOLSDIR)/lib/rbtree.c\t\t\t\\\n@@ -439,374 +310,38 @@ $(RESOLVE_BTFIDS): $(HOST_BPFOBJ) | $(HOST_BUILD_DIR)/resolve_btfids\t\\\n \t\tHOSTPKG_CONFIG='$(PKG_CONFIG)' \\\n \t\tOUTPUT=$(HOST_BUILD_DIR)/resolve_btfids/ BPFOBJ=$(HOST_BPFOBJ)\n \n-# Get Clang's default includes on this system, as opposed to those seen by\n-# '--target=bpf'. This fixes \"missing\" files on some architectures/distros,\n-# such as asm/byteorder.h, asm/socket.h, asm/sockios.h, sys/cdefs.h etc.\n-#\n-# Use '-idirafter': Don't interfere with include mechanics except where the\n-# build would have failed anyways.\n-define get_sys_includes\n-$(shell $(1) $(2) -v -E - \u003c/dev/null 2\u003e\u00261 \\\n-\t| sed -n '/\u003c...\u003e search starts here:/,/End of search list./{ s| \\(/.*\\)|-idirafter \\1|p }') \\\n-$(shell $(1) $(2) -dM -E - \u003c/dev/null | grep '__riscv_xlen ' | awk '{printf(\"-D__riscv_xlen=%d -D__BITS_PER_LONG=%d\", $$3, $$3)}') \\\n-$(shell $(1) $(2) -dM -E - \u003c/dev/null | grep '__loongarch_grlen ' | awk '{printf(\"-D__BITS_PER_LONG=%d\", $$3)}') \\\n-$(shell $(1) $(2) -dM -E - \u003c/dev/null | grep -E 'MIPS(EL|EB)|_MIPS_SZ(PTR|LONG) |_MIPS_SIM |_ABI(O32|N32|64) ' | awk '{printf(\"-D%s=%s \", $$2, $$3)}')\n-endef\n-\n-# Determine target endianness.\n-IS_LITTLE_ENDIAN := $(shell $(CC) -dM -E - \u003c/dev/null | \\\n-\t\t\tgrep 'define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__')\n-MENDIAN:=$(if $(IS_LITTLE_ENDIAN),-mlittle-endian,-mbig-endian)\n-BPF_TARGET_ENDIAN:=$(if $(IS_LITTLE_ENDIAN),--target=bpfel,--target=bpfeb)\n-\n-ifneq ($(CROSS_COMPILE),)\n-CLANG_TARGET_ARCH = --target=$(notdir $(CROSS_COMPILE:%-=%))\n-endif\n-\n-CLANG_SYS_INCLUDES = $(call get_sys_includes,$(CLANG),$(CLANG_TARGET_ARCH))\n-BPF_CFLAGS = -g -Wall -Werror -D__TARGET_ARCH_$(SRCARCH) $(MENDIAN)\t\\\n-\t     -I$(INCLUDE_DIR) -I$(CURDIR) -I$(APIDIR)\t\t\t\\\n-\t     -I$(CURDIR)/libarena/include\t\t\t\t\\\n-\t     -I$(abspath $(OUTPUT)/../usr/include)\t\t\t\\\n-\t     -std=gnu11\t\t \t\t\t\t\t\\\n-\t     -fno-strict-aliasing \t\t\t\t\t\\\n-\t     -Wno-microsoft-anon-tag\t\t\t\t\t\\\n-\t     -fms-extensions\t\t\t\t\t\t\\\n-\t     -Wno-compare-distinct-pointer-types\t\t\t\\\n-\t     -Wno-initializer-overrides\t\t\t\t\t\\\n-\t     #\n-# TODO: enable me -Wsign-compare\n-\n-CLANG_CFLAGS = $(CLANG_SYS_INCLUDES)\n-\n-$(OUTPUT)/test_l4lb_noinline.o: BPF_CFLAGS += -fno-inline\n-$(OUTPUT)/test_xdp_noinline.o: BPF_CFLAGS += -fno-inline\n-\n-$(OUTPUT)/flow_dissector_load.o: flow_dissector_load.h\n-$(OUTPUT)/cgroup_getset_retval_hooks.o: cgroup_getset_retval_hooks.h\n-\n-# Build BPF object using Clang\n-# $1 - input .c file\n-# $2 - output .o file\n-# $3 - CFLAGS\n-# $4 - binary name\n-define CLANG_BPF_BUILD_RULE\n-\t$(call msg,CLNG-BPF,$4,$2)\n-\t$(Q)$(CLANG) $3 -O2 $(BPF_TARGET_ENDIAN) -c $1 -mcpu=v3 -o $2 $(if $(PERMISSIVE),|| \\\n-\t\t($(RM) $2; printf '  %-12s %s\\n' 'SKIP-BPF' '$(notdir $2)' 1\u003e\u00262))\n-endef\n-# Similar to CLANG_BPF_BUILD_RULE, but with disabled alu32\n-define CLANG_NOALU32_BPF_BUILD_RULE\n-\t$(call msg,CLNG-BPF,$4,$2)\n-\t$(Q)$(CLANG) $3 -O2 $(BPF_TARGET_ENDIAN) -c $1 -mcpu=v2 -o $2 $(if $(PERMISSIVE),|| \\\n-\t\t($(RM) $2; printf '  %-12s %s\\n' 'SKIP-BPF' '$(notdir $2)' 1\u003e\u00262))\n-endef\n-# Similar to CLANG_BPF_BUILD_RULE, but with cpu-v4\n-define CLANG_CPUV4_BPF_BUILD_RULE\n-\t$(call msg,CLNG-BPF,$4,$2)\n-\t$(Q)$(CLANG) $3 -O2 $(BPF_TARGET_ENDIAN) -c $1 -mcpu=v4 -o $2 $(if $(PERMISSIVE),|| \\\n-\t\t($(RM) $2; printf '  %-12s %s\\n' 'SKIP-BPF' '$(notdir $2)' 1\u003e\u00262))\n-endef\n-# Build BPF object using GCC\n-define GCC_BPF_BUILD_RULE\n-\t$(call msg,GCC-BPF,$4,$2)\n-\t$(Q)$(BPF_GCC) $3 -DBPF_NO_PRESERVE_ACCESS_INDEX -Wno-attributes -O2 -c $1 -o $2 $(if $(PERMISSIVE),|| \\\n-\t\t($(RM) $2; printf '  %-12s %s\\n' 'SKIP-BPF' '$(notdir $2)' 1\u003e\u00262))\n-endef\n-\n-SKEL_BLACKLIST := btf__% test_pinning_invalid.c test_sk_assign.c\n-\n-LINKED_SKELS := test_static_linked.skel.h linked_funcs.skel.h\t\t\\\n-\t\tlinked_vars.skel.h linked_maps.skel.h \t\t\t\\\n-\t\ttest_subskeleton.skel.h test_subskeleton_lib.skel.h\t\\\n-\t\ttest_usdt.skel.h tracing_multi.skel.h\t\t\t\\\n-\t\ttracing_multi_module.skel.h\t\t\t\t\\\n-\t\ttracing_multi_intersect.skel.h\t\t\t\t\\\n-\t\ttracing_multi_session.skel.h\n-\n-LSKELS := fexit_sleep.c trace_printk.c trace_vprintk.c map_ptr_kern.c \t\\\n-\tcore_kern.c core_kern_overflow.c test_ringbuf.c\t\t\t\\\n-\ttest_ringbuf_n.c test_ringbuf_map_key.c test_ringbuf_write.c    \\\n-\ttest_ringbuf_overwrite.c\n-\n-LSKELS_SIGNED := fentry_test.c fexit_test.c atomics.c\n-\n-# Generate both light skeleton and libbpf skeleton for these\n-LSKELS_EXTRA := test_ksyms_module.c test_ksyms_weak.c kfunc_call_test.c \\\n-\tkfunc_call_test_subprog.c\n-SKEL_BLACKLIST += $$(LSKELS) $$(LSKELS_SIGNED)\n-\n-test_static_linked.skel.h-deps := test_static_linked1.bpf.o test_static_linked2.bpf.o\n-linked_funcs.skel.h-deps := linked_funcs1.bpf.o linked_funcs2.bpf.o\n-linked_vars.skel.h-deps := linked_vars1.bpf.o linked_vars2.bpf.o\n-linked_maps.skel.h-deps := linked_maps1.bpf.o linked_maps2.bpf.o\n-# In the subskeleton case, we want the test_subskeleton_lib.subskel.h file\n-# but that's created as a side-effect of the skel.h generation.\n-test_subskeleton.skel.h-deps := test_subskeleton_lib2.bpf.o test_subskeleton_lib.bpf.o test_subskeleton.bpf.o\n-test_subskeleton_lib.skel.h-deps := test_subskeleton_lib2.bpf.o test_subskeleton_lib.bpf.o\n-test_usdt.skel.h-deps := test_usdt.bpf.o test_usdt_multispec.bpf.o\n-xsk_xdp_progs.skel.h-deps := xsk_xdp_progs.bpf.o\n-xdp_hw_metadata.skel.h-deps := xdp_hw_metadata.bpf.o\n-xdp_features.skel.h-deps := xdp_features.bpf.o\n-tracing_multi.skel.h-deps := tracing_multi_attach.bpf.o tracing_multi_check.bpf.o\n-tracing_multi_module.skel.h-deps := tracing_multi_attach_module.bpf.o tracing_multi_check.bpf.o\n-tracing_multi_intersect.skel.h-deps := tracing_multi_intersect_attach.bpf.o tracing_multi_check.bpf.o\n-tracing_multi_session.skel.h-deps := tracing_multi_session_attach.bpf.o tracing_multi_check.bpf.o\n-\n-LINKED_BPF_OBJS := $(foreach skel,$(LINKED_SKELS),$($(skel)-deps))\n-LINKED_BPF_SRCS := $(patsubst %.bpf.o,%.c,$(LINKED_BPF_OBJS))\n-\n-HEADERS_FOR_BPF_OBJS := $(wildcard $(BPFDIR)/*.bpf.h)\t\t\\\n-\t\t\t$(wildcard $(CURDIR)/libarena/include/*.[ch])\t\\\n-\t\t\t$(addprefix $(BPFDIR)/,\tbpf_core_read.h\t\\\n-\t\t\t                        bpf_endian.h\t\\\n-\t\t\t\t\t\tbpf_helpers.h\t\\\n-\t\t\t                        bpf_tracing.h)\n-\n-# Set up extra TRUNNER_XXX \"temporary\" variables in the environment (relies on\n-# $eval()) and pass control to DEFINE_TEST_RUNNER_RULES.\n-# Parameters:\n-# $1 - test runner base binary name (e.g., test_progs)\n-# $2 - test runner extra \"flavor\" (e.g., no_alu32, cpuv4, bpf_gcc, etc)\n-define DEFINE_TEST_RUNNER\n-\n-LSKEL_SIGN := -S -k $(PRIVATE_KEY) -i $(VERIFICATION_CERT)\n-TRUNNER_OUTPUT := $(OUTPUT)$(if $2,/)$2\n-TRUNNER_BINARY := $1$(if $2,-)$2\n-TRUNNER_TEST_OBJS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.test.o,\t\\\n-\t\t\t\t $$(notdir $$(wildcard $(TRUNNER_TESTS_DIR)/*.c)))\n-TRUNNER_EXTRA_OBJS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.o,\t\t\\\n-\t\t\t\t $$(filter %.c,$(TRUNNER_EXTRA_SOURCES)))\n-TRUNNER_LIB_OBJS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.o,\t\t\\\n-\t\t\t\t $$(filter %.c,$(TRUNNER_LIB_SOURCES)))\n-TRUNNER_EXTRA_HDRS := $$(filter %.h,$(TRUNNER_EXTRA_SOURCES))\n-TRUNNER_TESTS_HDR := $(TRUNNER_TESTS_DIR)/tests.h\n-TRUNNER_BPF_SRCS := $$(notdir $$(wildcard $(TRUNNER_BPF_PROGS_DIR)/*.c))\n-TRUNNER_BPF_OBJS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.bpf.o, $$(TRUNNER_BPF_SRCS))\n-TRUNNER_BPF_SKELS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.skel.h,\t\\\n-\t\t\t\t $$(filter-out $(SKEL_BLACKLIST) $(LINKED_BPF_SRCS),\\\n-\t\t\t\t\t       $$(TRUNNER_BPF_SRCS)))\n-TRUNNER_BPF_LSKELS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.lskel.h, $$(LSKELS) $$(LSKELS_EXTRA))\n-TRUNNER_BPF_SKELS_LINKED := $$(addprefix $$(TRUNNER_OUTPUT)/,$(LINKED_SKELS))\n-TRUNNER_BPF_LSKELS_SIGNED := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.lskel.h, $$(LSKELS_SIGNED))\n-TEST_GEN_FILES += $$(TRUNNER_BPF_OBJS)\n-\n-# Evaluate rules now with extra TRUNNER_XXX variables above already defined\n-$$(eval $$(call DEFINE_TEST_RUNNER_RULES,$1,$2))\n-\n-endef\n-\n-# Using TRUNNER_XXX variables, provided by callers of DEFINE_TEST_RUNNER and\n-# set up by DEFINE_TEST_RUNNER itself, create test runner build rules with:\n-# $1 - test runner base binary name (e.g., test_progs)\n-# $2 - test runner extra \"flavor\" (e.g., no_alu32, cpuv4, bpf_gcc, etc)\n-define DEFINE_TEST_RUNNER_RULES\n-\n-# Permissive build behaviour (skip-on-failure compile, partial-link) only\n-# applies to test_progs and its flavors; runners that use strong cross-object\n-# references (e.g. test_maps) keep strict semantics even when permissive.\n-# The check is inlined per-runner so $1 is substituted at $(call) time and\n-# the result is baked into each rule's recipe.\n-\n-ifeq ($($(TRUNNER_OUTPUT)-dir),)\n-$(TRUNNER_OUTPUT)-dir := y\n-$(TRUNNER_OUTPUT):\n-\t$$(call msg,MKDIR,,$$@)\n-\t$(Q)mkdir -p $$@\n-endif\n-\n-# ensure we set up BPF objects generation rule just once for a given\n-# input/output directory combination\n-ifeq ($($(TRUNNER_BPF_PROGS_DIR)$(if $2,-)$2-bpfobjs),)\n-$(TRUNNER_BPF_PROGS_DIR)$(if $2,-)$2-bpfobjs := y\n-$(TRUNNER_BPF_OBJS): $(TRUNNER_OUTPUT)/%.bpf.o:\t\t\t\t\\\n-\t\t     $(TRUNNER_BPF_PROGS_DIR)/%.c\t\t\t\\\n-\t\t     $(TRUNNER_BPF_PROGS_DIR)/*.h\t\t\t\\\n-\t\t     $$(INCLUDE_DIR)/vmlinux.h\t\t\t\t\\\n-\t\t     $(HEADERS_FOR_BPF_OBJS)\t\t\t\t\\\n-\t\t     | $(TRUNNER_OUTPUT) $$(BPFOBJ)\n-\t$$(call $(TRUNNER_BPF_BUILD_RULE),$$\u003c,$$@,\t\t\t\\\n-\t\t\t\t\t  $(TRUNNER_BPF_CFLAGS)         \\\n-\t\t\t\t\t  $$($$\u003c-CFLAGS)\t\t\\\n-\t\t\t\t\t  $$($$\u003c-$2-CFLAGS),$(TRUNNER_BINARY))\n-\n-$(TRUNNER_BPF_SKELS): %.skel.h: %.bpf.o $(BPFTOOL) | $(TRUNNER_OUTPUT)\n-\t$(Q)$(if $(PERMISSIVE),if [ ! -f $$\u003c ]; then\t\t\t\\\n-\t\t$$(RM) $$@ $$(@:.skel.h=.subskel.h);\t\t\t\\\n-\t\tprintf '  %-12s %s\\n' 'SKIP-SKEL' '$$(notdir $$@)' 1\u003e\u00262; \\\n-\t\texit 0;\t\t\t\t\t\t\t\\\n-\tfi;)\t\t\t\t\t\t\t\t\\\n-\tprintf '  %-12s %s\\n' 'GEN-SKEL' '[$(TRUNNER_BINARY)] $$(notdir $$@)' 1\u003e\u00262; \\\n-\t$$(BPFTOOL) gen object $$(\u003c:.o=.linked1.o) $$\u003c \u0026\u0026\t\t\\\n-\t$$(BPFTOOL) gen object $$(\u003c:.o=.linked2.o) $$(\u003c:.o=.linked1.o) \u0026\u0026 \\\n-\t$$(BPFTOOL) gen object $$(\u003c:.o=.linked3.o) $$(\u003c:.o=.linked2.o) \u0026\u0026 \\\n-\tdiff $$(\u003c:.o=.linked2.o) $$(\u003c:.o=.linked3.o) \u0026\u0026\t\t\\\n-\t$$(BPFTOOL) gen skeleton $$(\u003c:.o=.linked3.o) name $$(notdir $$(\u003c:.bpf.o=)) \u003e $$@ \u0026\u0026 \\\n-\t$$(BPFTOOL) gen subskeleton $$(\u003c:.o=.linked3.o) name $$(notdir $$(\u003c:.bpf.o=)) \u003e $$(@:.skel.h=.subskel.h) $(if $(PERMISSIVE),|| { \\\n-\t\t$$(RM) $$@ $$(@:.skel.h=.subskel.h); \\\n-\t\tprintf '  %-12s %s\\n' 'SKIP-SKEL' '$$(notdir $$@)' 1\u003e\u00262; \\\n-\t}) \u0026\u0026 \\\n-\trm -f $$(\u003c:.o=.linked1.o) $$(\u003c:.o=.linked2.o) $$(\u003c:.o=.linked3.o)\n-\n-$(TRUNNER_BPF_LSKELS): %.lskel.h: %.bpf.o $(BPFTOOL) | $(TRUNNER_OUTPUT)\n-\t$(Q)$(if $(PERMISSIVE),if [ ! -f $$\u003c ]; then\t\t\t\\\n-\t\t$$(RM) $$@;\t\t\t\t\t\t\\\n-\t\tprintf '  %-12s %s\\n' 'SKIP-SKEL' '$$(notdir $$@)' 1\u003e\u00262; \\\n-\t\texit 0;\t\t\t\t\t\t\t\\\n-\tfi;)\t\t\t\t\t\t\t\t\\\n-\tprintf '  %-12s %s\\n' 'GEN-SKEL' '[$(TRUNNER_BINARY)] $$(notdir $$@)' 1\u003e\u00262; \\\n-\t$$(BPFTOOL) gen object $$(\u003c:.o=.llinked1.o) $$\u003c \u0026\u0026\t\t\\\n-\t$$(BPFTOOL) gen object $$(\u003c:.o=.llinked2.o) $$(\u003c:.o=.llinked1.o) \u0026\u0026 \\\n-\t$$(BPFTOOL) gen object $$(\u003c:.o=.llinked3.o) $$(\u003c:.o=.llinked2.o) \u0026\u0026 \\\n-\tdiff $$(\u003c:.o=.llinked2.o) $$(\u003c:.o=.llinked3.o) \u0026\u0026\t\t\\\n-\t$$(BPFTOOL) gen skeleton -L $$(\u003c:.o=.llinked3.o) name $$(notdir $$(\u003c:.bpf.o=_lskel)) \u003e $$@ $(if $(PERMISSIVE),|| { \\\n-\t\t$$(RM) $$@; \\\n-\t\tprintf '  %-12s %s\\n' 'SKIP-SKEL' '$$(notdir $$@)' 1\u003e\u00262; \\\n-\t}) \u0026\u0026 \\\n-\trm -f $$(\u003c:.o=.llinked1.o) $$(\u003c:.o=.llinked2.o) $$(\u003c:.o=.llinked3.o)\n-\n-$(TRUNNER_BPF_LSKELS_SIGNED): %.lskel.h: %.bpf.o $(BPFTOOL) | $(TRUNNER_OUTPUT)\n-\t$(Q)$(if $(PERMISSIVE),if [ ! -f $$\u003c ]; then\t\t\t\\\n-\t\t$$(RM) $$@;\t\t\t\t\t\t\\\n-\t\tprintf '  %-12s %s\\n' 'SKIP-SKEL' '$$(notdir $$@)' 1\u003e\u00262; \\\n-\t\texit 0;\t\t\t\t\t\t\t\\\n-\tfi;)\t\t\t\t\t\t\t\t\\\n-\tprintf '  %-12s %s\\n' 'GEN-SKEL' '[$(TRUNNER_BINARY) (signed)] $$(notdir $$@)' 1\u003e\u00262; \\\n-\t$$(BPFTOOL) gen object $$(\u003c:.o=.llinked1.o) $$\u003c \u0026\u0026\t\t\\\n-\t$$(BPFTOOL) gen object $$(\u003c:.o=.llinked2.o) $$(\u003c:.o=.llinked1.o) \u0026\u0026 \\\n-\t$$(BPFTOOL) gen object $$(\u003c:.o=.llinked3.o) $$(\u003c:.o=.llinked2.o) \u0026\u0026 \\\n-\tdiff $$(\u003c:.o=.llinked2.o) $$(\u003c:.o=.llinked3.o) \u0026\u0026\t\t\\\n-\t$$(BPFTOOL) gen skeleton $(LSKEL_SIGN) $$(\u003c:.o=.llinked3.o) name $$(notdir $$(\u003c:.bpf.o=_lskel)) \u003e $$@ $(if $(PERMISSIVE),|| { \\\n-\t\t$$(RM) $$@; \\\n-\t\tprintf '  %-12s %s\\n' 'SKIP-SKEL' '$$(notdir $$@)' 1\u003e\u00262; \\\n-\t}) \u0026\u0026 \\\n-\trm -f $$(\u003c:.o=.llinked1.o) $$(\u003c:.o=.llinked2.o) $$(\u003c:.o=.llinked3.o)\n-\n-$(LINKED_BPF_OBJS): %: $(TRUNNER_OUTPUT)/%\n-\n-# .SECONDEXPANSION here allows to correctly expand %-deps variables as prerequisites\n-.SECONDEXPANSION:\n-$(TRUNNER_BPF_SKELS_LINKED): $(TRUNNER_OUTPUT)/%: $$$$(%-deps) $(BPFTOOL) | $(TRUNNER_OUTPUT)\n-\t$(Q)$(if $(PERMISSIVE),for f in $$(addprefix $(TRUNNER_OUTPUT)/,$$($$(@F)-deps)); do \\\n-\t\tif [ ! -f $$$$f ]; then\t\t\t\t\t\t\\\n-\t\t\t$$(RM) $$@ $$(@:.skel.h=.subskel.h);\t\t\\\n-\t\t\tprintf '  %-12s %s\\n' 'SKIP-SKEL' '$$(notdir $$@)' 1\u003e\u00262; \\\n-\t\t\texit 0;\t\t\t\t\t\t\t\\\n-\t\tfi;\t\t\t\t\t\t\t\t\\\n-\tdone;)\t\t\t\t\t\t\t\t\t\\\n-\tprintf '  %-12s %s\\n' 'LINK-BPF' '[$(TRUNNER_BINARY)] $$(notdir $$(@:.skel.h=.bpf.o))' 1\u003e\u00262; \\\n-\t$$(BPFTOOL) gen object $$(@:.skel.h=.linked1.o) $$(addprefix $(TRUNNER_OUTPUT)/,$$($$(@F)-deps)) \u0026\u0026 \\\n-\t$$(BPFTOOL) gen object $$(@:.skel.h=.linked2.o) $$(@:.skel.h=.linked1.o) \u0026\u0026 \\\n-\t$$(BPFTOOL) gen object $$(@:.skel.h=.linked3.o) $$(@:.skel.h=.linked2.o) \u0026\u0026 \\\n-\tdiff $$(@:.skel.h=.linked2.o) $$(@:.skel.h=.linked3.o) \u0026\u0026\t\\\n-\tprintf '  %-12s %s\\n' 'GEN-SKEL' '[$(TRUNNER_BINARY)] $$(notdir $$@)' 1\u003e\u00262 \u0026\u0026 \\\n-\t$$(BPFTOOL) gen skeleton $$(@:.skel.h=.linked3.o) name $$(notdir $$(@:.skel.h=)) \u003e $$@ \u0026\u0026 \\\n-\t$$(BPFTOOL) gen subskeleton $$(@:.skel.h=.linked3.o) name $$(notdir $$(@:.skel.h=)) \u003e $$(@:.skel.h=.subskel.h) $(if $(PERMISSIVE),|| { \\\n-\t\t$$(RM) $$@ $$(@:.skel.h=.subskel.h);\t\t\t\\\n-\t\tprintf '  %-12s %s\\n' 'SKIP-SKEL' '$$(notdir $$@)' 1\u003e\u00262; \\\n-\t}) \u0026\u0026\t\t\t\t\t\t\t\t\t\\\n-\trm -f $$(@:.skel.h=.linked1.o) $$(@:.skel.h=.linked2.o) $$(@:.skel.h=.linked3.o)\n-\n-# When the compiler generates a %.d file, only skel basenames (not\n-# full paths) are specified as prerequisites for corresponding %.o\n-# file. vpath directives below instruct make to search for skel files\n-# in TRUNNER_OUTPUT, if they are not present in the working directory.\n-vpath %.skel.h $(TRUNNER_OUTPUT)\n-vpath %.lskel.h $(TRUNNER_OUTPUT)\n-vpath %.subskel.h $(TRUNNER_OUTPUT)\n-\n+ifneq ($(SKIP_DOCS),1)\n+all: docs\n endif\n \n-# ensure we set up tests.h header generation rule just once\n-ifeq ($($(TRUNNER_TESTS_DIR)-tests-hdr),)\n-$(TRUNNER_TESTS_DIR)-tests-hdr := y\n-$(TRUNNER_TESTS_HDR): $(TRUNNER_TESTS_DIR)/*.c\n-\t$$(call msg,TEST-HDR,$(TRUNNER_BINARY),$$@)\n-\t$$(shell (echo '/* Generated header, do not edit */';\t\t\t\t\t\\\n-\t\t  sed -n -E 's/^void (serial_)?test_([a-zA-Z0-9_]+)\\((void)?\\).*/DEFINE_TEST(\\2)/p'\t\\\n-\t\t\t$(TRUNNER_TESTS_DIR)/*.c | sort ;\t\\\n-\t\t ) \u003e $$@)\n-endif\n+docs:\n+\t$(Q)RST2MAN_OPTS=\"--exit-status=1\" $(MAKE) $(submake_extras)\t\\\n+\t            -f Makefile.docs\t\t\t\t\t\\\n+\t            prefix= OUTPUT=$(OUTPUT)/ DESTDIR=$(OUTPUT)/ $@\n \n-$(TRUNNER_OUTPUT)/resolve_btfids.test.o: $(RESOLVE_BTFIDS) $(TRUNNER_OUTPUT)/btf_data.bpf.o\n-$(TRUNNER_OUTPUT)/resolve_btfids.test.o: private TEST_NEEDS_BTFIDS = 1\n-\n-# compile individual test files\n-# Note: we cd into output directory to ensure embedded BPF object is found\n-$(TRUNNER_TEST_OBJS): $(TRUNNER_OUTPUT)/%.test.o:\t\t\t\\\n-\t\t      $(TRUNNER_TESTS_DIR)/%.c\t\t\t\t\\\n-\t\t      | $(TRUNNER_OUTPUT)/%.test.d\n-\t$$(call msg,TEST-OBJ,$(TRUNNER_BINARY),$$@)\n-\t$(Q)(cd $$(@D) \u0026\u0026 $$(CC) -I. $$(CFLAGS) -MMD -MT $$@ -c $(CURDIR)/$$\u003c $$(LDLIBS) -o $$(@F)) $(if $(filter test_progs%,$1),$(if $(PERMISSIVE),|| \\\n-\t\t($(RM) $$@; printf '  %-12s %s\\n' 'SKIP-TEST' '$$(notdir $$@)' 1\u003e\u00262)))\n-\t$$(if $$(TEST_NEEDS_BTFIDS),\t\t\t\t\t\t\\\n-\t\t$(Q)if [ -f $$@ ]; then\t\t\t\t\t\t\\\n-\t\t$(if $(filter 1,$(V)),true,printf '  %-8s%s %s\\n' \"BTFIDS\" \" [$(TRUNNER_BINARY)]\" \"$$(notdir $$@)\"); \\\n-\t\t$(RESOLVE_BTFIDS) --btf $(TRUNNER_OUTPUT)/btf_data.bpf.o $$@;\t\\\n-\t\t$(RESOLVE_BTFIDS) --patch_btfids $$@.BTF_ids $$@;\t\t\\\n-\t\tfi)\n-\n-$(TRUNNER_TEST_OBJS:.o=.d): $(TRUNNER_OUTPUT)/%.test.d:\t\t\t\\\n-\t\t\t    $(TRUNNER_TESTS_DIR)/%.c\t\t\t\\\n-\t\t\t    $(TRUNNER_EXTRA_HDRS)\t\t\t\\\n-\t\t\t    $$(BPFOBJ) | $(TRUNNER_OUTPUT)\t\t\\\n-\t\t\t    $(TRUNNER_BPF_SKELS)\t\t\t\\\n-\t\t\t    $(TRUNNER_BPF_LSKELS)\t\t\t\\\n-\t\t\t    $(TRUNNER_BPF_LSKELS_SIGNED)\t\t\\\n-\t\t\t    $(TRUNNER_BPF_SKELS_LINKED)\n-\n-ifeq ($(filter clean docs-clean emit_tests,$(MAKECMDGOALS)),)\n-include $(wildcard $(TRUNNER_TEST_OBJS:.o=.d))\n-endif\n+docs-clean:\n+\t$(Q)$(MAKE) $(submake_extras)\t\t\t\t\t\\\n+\t            -f Makefile.docs\t\t\t\t\t\\\n+\t            prefix= OUTPUT=$(OUTPUT)/ DESTDIR=$(OUTPUT)/ $@\n \n-# add per extra obj CFGLAGS definitions\n-$(foreach N,$(patsubst $(TRUNNER_OUTPUT)/%.o,%,$(TRUNNER_EXTRA_OBJS)),\t\\\n-\t$(eval $(TRUNNER_OUTPUT)/$(N).o: CFLAGS += $($(N).c-CFLAGS)))\n-\n-$(TRUNNER_EXTRA_OBJS): $(TRUNNER_OUTPUT)/%.o:\t\t\t\t\\\n-\t\t       %.c\t\t\t\t\t\t\\\n-\t\t       $(TRUNNER_EXTRA_HDRS)\t\t\t\t\\\n-\t\t       $(VERIFY_SIG_HDR)\t\t\t\t\\\n-\t\t       $(TRUNNER_TESTS_HDR)\t\t\t\t\\\n-\t\t       $$(BPFOBJ) | $(TRUNNER_OUTPUT)\n-\t$$(call msg,EXT-OBJ,$(TRUNNER_BINARY),$$@)\n-\t$(Q)$$(CC) $$(CFLAGS) -c $$\u003c $$(LDLIBS) -o $$@\n-\n-$(TRUNNER_LIB_OBJS): $(TRUNNER_OUTPUT)/%.o:$(TOOLSDIR)/lib/%.c\n-\t$$(call msg,LIB-OBJ,$(TRUNNER_BINARY),$$@)\n-\t$(Q)$$(CC) $$(CFLAGS) -c $$\u003c $$(LDLIBS) -o $$@\n-\n-# non-flavored in-srctree builds receive special treatment, in particular, we\n-# do not need to copy extra resources (see e.g. test_btf_dump_case())\n-$(TRUNNER_BINARY)-extras: $(TRUNNER_EXTRA_FILES) | $(TRUNNER_OUTPUT)\n-ifneq ($2:$(OUTPUT),:$(shell pwd))\n-\t$$(call msg,EXT-COPY,$(TRUNNER_BINARY),$(TRUNNER_EXTRA_FILES))\n-\t$(Q)rsync -aq $(if $(PERMISSIVE),--ignore-missing-args) $$^ $(TRUNNER_OUTPUT)/\n+# vmlinux.h is first dumped to a temporary file and then compared to\n+# the previous version. This helps to avoid unnecessary re-builds of\n+# BPF objects.\n+$(INCLUDE_DIR)/vmlinux.h: $(VMLINUX_BTF) $(BPFTOOL) | $(INCLUDE_DIR)\n+ifeq ($(VMLINUX_H),)\n+\t$(call msg,GEN,,$@)\n+\t$(Q)$(BPFTOOL) btf dump file $(VMLINUX_BTF) format c \u003e $(INCLUDE_DIR)/.vmlinux.h.tmp\n+\t$(Q)cmp -s $(INCLUDE_DIR)/.vmlinux.h.tmp $@ || mv $(INCLUDE_DIR)/.vmlinux.h.tmp $@\n+else\n+\t$(call msg,CP,,$@)\n+\t$(Q)cp \"$(VMLINUX_H)\" $@\n endif\n \n-# some X.test.o files have runtime dependencies on Y.bpf.o files\n-$(OUTPUT)/$(TRUNNER_BINARY): | $(TRUNNER_BPF_OBJS)\n-\n-$(OUTPUT)/$(TRUNNER_BINARY): $(if $(filter test_progs%,$1),$(if $(PERMISSIVE),$$(wildcard $(TRUNNER_TEST_OBJS)),$(TRUNNER_TEST_OBJS)),$(TRUNNER_TEST_OBJS))\t\\\n-\t\t\t     $(TRUNNER_EXTRA_OBJS) $$(BPFOBJ)\t\t\\\n-\t\t\t     $(TRUNNER_LIB_OBJS)\t\t\t\\\n-\t\t\t     $(TRUNNER_BPFTOOL)\t\t\t\t\\\n-\t\t\t     $(OUTPUT)/veristat\t\t\t\t\\\n-\t\t\t     | $(TRUNNER_BINARY)-extras\t\t\t\\\n-\t\t\t     $(if $(filter test_progs%,$1),$(if $(PERMISSIVE),$(TRUNNER_TEST_OBJS)))\n-\t$$(call msg,BINARY,,$$@)\n-\t$(Q)$$(CC) $$(CFLAGS) $(if $(filter test_progs%,$1),$(if $(PERMISSIVE),$$(filter %.a %.o,$$(wildcard $(TRUNNER_TEST_OBJS)) $$(filter-out $(TRUNNER_TEST_OBJS),$$^)),$$(filter %.a %.o,$$^)),$$(filter %.a %.o,$$^)) $$(LDLIBS) $$(LLVM_LDLIBS) $$(LDFLAGS) $$(LLVM_LDFLAGS) -o $$@\n-\t$(Q)ln -sf $(if $2,..,.)/tools/build/bpftool/$(USE_BOOTSTRAP)bpftool \\\n-\t\t   $(OUTPUT)/$(if $2,$2/)bpftool\n-\n-endef\n-\n-VERIFY_SIG_SETUP := $(CURDIR)/verify_sig_setup.sh\n-VERIFY_SIG_HDR := verification_cert.h\n-VERIFICATION_CERT   := $(BUILD_DIR)/signing_key.der\n-PRIVATE_KEY := $(BUILD_DIR)/signing_key.pem\n-\n-$(VERIFICATION_CERT) $(PRIVATE_KEY): $(VERIFY_SIG_SETUP)\n+# One genkey run produces both files. A plain two-target rule is not\n+# grouped - under -j make would run genkey twice concurrently and the\n+# openssl invocations race; the pattern form is implicitly grouped even\n+# with make \u003c 4.3 (same workaround as the test_kmods rule).\n+$(BUILD_DIR)/%.pem $(BUILD_DIR)/%.der: $(VERIFY_SIG_SETUP)\n \t$(Q)mkdir -p $(BUILD_DIR)\n \t$(Q)$(VERIFY_SIG_SETUP) genkey $(BUILD_DIR)\n \n@@ -835,116 +370,183 @@ LIBARENA_BPF_DEPS := $(wildcard libarena/Makefile\t\t\\\n \t\t\t\t libarena/selftests/*\t\t\\\n \t\t\t\t libarena/*.bpf.o)\n \n-LIBARENA_SKEL := libarena/libarena.skel.h\n-\n $(LIBARENA_SKEL): $(INCLUDE_DIR)/vmlinux.h $(BPFOBJ) $(LIBARENA_BPF_DEPS)\n \t+$(MAKE) -C libarena libarena.skel.h $(LIBARENA_MAKE_ARGS)\n \n-ifneq ($(CLANG_HAS_ARENA_ASAN),)\n-LIBARENA_ASAN_SKEL := libarena/libarena_asan.skel.h\n-CFLAGS += -DHAS_BPF_ARENA_ASAN\n+libarena: $(LIBARENA_SKEL)\n \n+ifneq ($(CLANG_HAS_ARENA_ASAN),)\n $(LIBARENA_ASAN_SKEL): $(INCLUDE_DIR)/vmlinux.h $(BPFOBJ) $(LIBARENA_BPF_DEPS)\n \t+$(MAKE) -C libarena libarena_asan.skel.h $(LIBARENA_MAKE_ARGS)\n+\n+libarena_asan: $(LIBARENA_ASAN_SKEL)\n endif\n \n-# Define test_progs test runner.\n-TRUNNER_TESTS_DIR := prog_tests\n-TRUNNER_BPF_PROGS_DIR := progs\n-TRUNNER_EXTRA_SOURCES := test_progs.c\t\t\\\n-\t\t\t cgroup_helpers.c\t\\\n-\t\t\t trace_helpers.c\t\\\n-\t\t\t network_helpers.c\t\\\n-\t\t\t testing_helpers.c\t\\\n-\t\t\t btf_helpers.c\t\t\\\n-\t\t\t cap_helpers.c\t\t\\\n-\t\t\t unpriv_helpers.c \t\\\n-\t\t\t sysctl_helpers.c\t\\\n-\t\t\t netlink_helpers.c\t\\\n-\t\t\t jit_disasm_helpers.c\t\\\n-\t\t\t io_helpers.c\t\t\\\n-\t\t\t test_loader.c\t\t\\\n-\t\t\t xsk.c\t\t\t\\\n-\t\t\t disasm.c\t\t\\\n-\t\t\t disasm_helpers.c\t\\\n-\t\t\t json_writer.c \t\t\\\n-\t\t\t $(VERIFY_SIG_HDR)\t\t\\\n-\t\t\t flow_dissector_load.h\t\\\n-\t\t\t ip_check_defrag_frags.h\t\\\n-\t\t\t bpftool_helpers.c\t\\\n-\t\t\t usdt_1.c usdt_2.c\t\\\n-\t\t\t $(LIBARENA_SKEL)\t\\\n-\t\t\t $(LIBARENA_ASAN_SKEL)\n-TRUNNER_LIB_SOURCES := find_bit.c\n-TRUNNER_EXTRA_FILES := $(OUTPUT)/urandom_read\t\t\t\t\\\n-\t\t       $(OUTPUT)/liburandom_read.so\t\t\t\\\n-\t\t       $(OUTPUT)/xdp_synproxy\t\t\t\t\\\n-\t\t       $(OUTPUT)/sign-file\t\t\t\t\\\n-\t\t       $(OUTPUT)/uprobe_multi\t\t\t\t\\\n-\t\t       $(TEST_KMOD_TARGETS)\t\t\t\t\\\n-\t\t       ima_setup.sh \t\t\t\t\t\\\n-\t\t       $(VERIFY_SIG_SETUP)\t\t\t\t\\\n-\t\t       $(wildcard progs/btf_dump_test_case_*.c)\t\t\\\n-\t\t       $(wildcard progs/*.bpf.o)\n-TRUNNER_BPF_BUILD_RULE := CLANG_BPF_BUILD_RULE\n-TRUNNER_BPF_CFLAGS := $(BPF_CFLAGS) $(CLANG_CFLAGS) -DENABLE_ATOMICS_TESTS\n-$(eval $(call DEFINE_TEST_RUNNER,test_progs))\n-\n-# Define test_progs-no_alu32 test runner.\n-TRUNNER_BPF_BUILD_RULE := CLANG_NOALU32_BPF_BUILD_RULE\n-TRUNNER_BPF_CFLAGS := $(BPF_CFLAGS) $(CLANG_CFLAGS)\n-$(eval $(call DEFINE_TEST_RUNNER,test_progs,no_alu32))\n-\n-# Define test_progs-cpuv4 test runner.\n+# ---------------------------------------------------------------------\n+# Generated test list headers\n+# ---------------------------------------------------------------------\n+\n+define gen_tests_hdr\n+\t$(call msg,TEST-HDR,,$@)\n+\t$(Q)(echo '/* Generated header, do not edit */';\t\t\\\n+\t     sed -n -E 's/^void (serial_)?test_([a-zA-Z0-9_]+)\\((void)?\\).*/DEFINE_TEST(\\2)/p' \\\n+\t\t$(@D)/*.c | sort) \u003e $@\n+endef\n+\n+prog_tests/tests.h: $(wildcard prog_tests/*.c)\n+\t$(gen_tests_hdr)\n+\n+map_tests/tests.h: $(wildcard map_tests/*.c)\n+\t$(gen_tests_hdr)\n+\n+verifier/tests.h: verifier/*.c\n+\t$(Q)( cd verifier/; \\\n+\t      echo '/* Generated header, do not edit */'; \\\n+\t      echo '#ifdef FILL_ARRAY'; \\\n+\t      ls *.c 2\u003e /dev/null | sed -e 's@\\(.*\\)@#include \\\"\\1\\\"@'; \\\n+\t      echo '#endif' \\\n+\t    ) \u003e verifier/tests.h\n+\n+# ---------------------------------------------------------------------\n+# Shared helper objects, used by the standalone binaries below and (as\n+# prebuilt inputs) by the unflavored test_progs runner.\n+# ---------------------------------------------------------------------\n+\n+# Some utility functions use LLVM libraries\n+$(OUTPUT)/jit_disasm_helpers.o: CFLAGS += $(LLVM_CFLAGS)\n+\n+HELPER_OBJS := $(addprefix $(OUTPUT)/,testing_helpers.o cgroup_helpers.o \\\n+\t       trace_helpers.o network_helpers.o json_writer.o\t\t\\\n+\t       cap_helpers.o unpriv_helpers.o usdt_1.o usdt_2.o xsk.o)\n+\n+# $(BPFTOOL) is a prerequisite because its sub-make is what installs\n+# libbpf's internal headers (bpf/hashmap.h, bpf/libbpf_internal.h) into\n+# $(INCLUDE_DIR); without it, helpers including those headers race the\n+# install and can silently pick up the source-tree copies instead.\n+$(HELPER_OBJS): flow_dissector_load.h ip_check_defrag_frags.h\t\t\\\n+\t\t$(VERIFY_SIG_HDR) prog_tests/tests.h $(BPFOBJ) $(BPFTOOL)\n+\n+$(OUTPUT)/test_sockmap: $(OUTPUT)/cgroup_helpers.o $(OUTPUT)/testing_helpers.o\n+$(OUTPUT)/test_tcpnotify_user: $(OUTPUT)/cgroup_helpers.o\t\t\\\n+\t\t\t       $(OUTPUT)/testing_helpers.o\t\t\\\n+\t\t\t       $(OUTPUT)/trace_helpers.o\n+$(OUTPUT)/test_tag: $(OUTPUT)/testing_helpers.o\n+$(OUTPUT)/test_lirc_mode2_user: $(OUTPUT)/testing_helpers.o\n+$(OUTPUT)/flow_dissector_load: flow_dissector_load.h $(OUTPUT)/testing_helpers.o\n+\n+# ---------------------------------------------------------------------\n+# Test runner instances, one sub-make each (see Makefile.runner).\n+# ---------------------------------------------------------------------\n+\n+# The LLVM feature-probe results are exported to the runner sub-makes.\n+# CC is passed explicitly instead: exporting it would also leak lib.mk's\n+# CC into the libbpf/bpftool/resolve_btfids sub-builds, which compute\n+# their own.\n+export LLVM_CFLAGS LLVM_LDLIBS LLVM_LDFLAGS\n+\n+RUNNER_MAKE := $(MAKE) -f Makefile.runner OUTPUT=$(OUTPUT) CC='$(CC)'\n+\n+# Everything a runner instance references but does not know how to build.\n+RUNNER_PREREQS := $(INCLUDE_DIR)/vmlinux.h $(BPFOBJ) $(BPFTOOL)\t\t\\\n+\t\t  $(TRUNNER_BPFTOOL) $(RESOLVE_BTFIDS)\t\t\t\\\n+\t\t  $(OUTPUT)/veristat\t\t\t\t\t\\\n+\t\t  $(VERIFY_SIG_HDR) $(PRIVATE_KEY) $(VERIFICATION_CERT)\t\\\n+\t\t  $(LIBARENA_SKEL) $(LIBARENA_ASAN_SKEL)\t\t\\\n+\t\t  prog_tests/tests.h\t\t\t\t\t\\\n+\t\t  $(HELPER_OBJS)\t\t\t\t\t\\\n+\t\t  $(OUTPUT)/urandom_read $(OUTPUT)/liburandom_read.so\t\\\n+\t\t  $(OUTPUT)/xdp_synproxy $(OUTPUT)/sign-file\t\t\\\n+\t\t  $(OUTPUT)/uprobe_multi $(TEST_KMOD_TARGETS)\n+\n+FORCE:\n+\n+# The default flavor's skeletons are also inputs of bench, test_maps,\n+# xskxceiver, xdp_* and test_cpp; \"default-skels\" builds just those\n+# without the test_progs test objects and binary.\n+.PHONY: default-skels\n+default-skels: $(RUNNER_PREREQS)\n+\t+$(Q)$(RUNNER_MAKE) RUNNER=test_progs FLAVOR= TESTS_DIR=prog_tests \\\n+\t\tBPF_BUILD=clang BPF_MCPU=v3 BPF_DEFINES=-DENABLE_ATOMICS_TESTS skels\n+\n+$(OUTPUT)/test_progs: $(RUNNER_PREREQS) default-skels FORCE\n+\t+$(Q)$(RUNNER_MAKE) RUNNER=test_progs FLAVOR= TESTS_DIR=prog_tests \\\n+\t\tBPF_BUILD=clang BPF_MCPU=v3 BPF_DEFINES=-DENABLE_ATOMICS_TESTS\n+\n+$(OUTPUT)/test_progs-no_alu32: $(RUNNER_PREREQS) FORCE\n+\t+$(Q)$(RUNNER_MAKE) RUNNER=test_progs FLAVOR=no_alu32 TESTS_DIR=prog_tests \\\n+\t\tBPF_BUILD=clang BPF_MCPU=v2\n+\n ifneq ($(CLANG_CPUV4),)\n-TRUNNER_BPF_BUILD_RULE := CLANG_CPUV4_BPF_BUILD_RULE\n-TRUNNER_BPF_CFLAGS := $(BPF_CFLAGS) $(CLANG_CFLAGS) -DENABLE_ATOMICS_TESTS\n-$(eval $(call DEFINE_TEST_RUNNER,test_progs,cpuv4))\n+$(OUTPUT)/test_progs-cpuv4: $(RUNNER_PREREQS) FORCE\n+\t+$(Q)$(RUNNER_MAKE) RUNNER=test_progs FLAVOR=cpuv4 TESTS_DIR=prog_tests \\\n+\t\tBPF_BUILD=clang BPF_MCPU=v4 BPF_DEFINES=-DENABLE_ATOMICS_TESTS\n endif\n \n-# Define test_progs BPF-GCC-flavored test runner.\n ifneq ($(BPF_GCC),)\n-TRUNNER_BPF_BUILD_RULE := GCC_BPF_BUILD_RULE\n-TRUNNER_BPF_CFLAGS := $(BPF_CFLAGS) $(call get_sys_includes,gcc,)\n-$(eval $(call DEFINE_TEST_RUNNER,test_progs,bpf_gcc))\n-endif\n-\n-# Define test_maps test runner.\n-TRUNNER_TESTS_DIR := map_tests\n-TRUNNER_BPF_PROGS_DIR := progs\n-TRUNNER_EXTRA_SOURCES := test_maps.c\n-TRUNNER_LIB_SOURCES :=\n-TRUNNER_EXTRA_FILES :=\n-TRUNNER_BPF_BUILD_RULE := $$(error no BPF objects should be built)\n-TRUNNER_BPF_CFLAGS :=\n-$(eval $(call DEFINE_TEST_RUNNER,test_maps))\n-\n-# Define test_verifier test runner.\n-# It is much simpler than test_maps/test_progs and sufficiently different from\n-# them (e.g., test.h is using completely pattern), that it's worth just\n-# explicitly defining all the rules explicitly.\n-verifier/tests.h: verifier/*.c\n-\t$(shell ( cd verifier/; \\\n-\t\t  echo '/* Generated header, do not edit */'; \\\n-\t\t  echo '#ifdef FILL_ARRAY'; \\\n-\t\t  ls *.c 2\u003e /dev/null | sed -e 's@\\(.*\\)@#include \\\"\\1\\\"@'; \\\n-\t\t  echo '#endif' \\\n-\t\t) \u003e verifier/tests.h)\n-$(OUTPUT)/test_verifier: test_verifier.c verifier/tests.h $(BPFOBJ) | $(OUTPUT)\n+$(OUTPUT)/test_progs-bpf_gcc: $(RUNNER_PREREQS) FORCE\n+\t+$(Q)$(RUNNER_MAKE) RUNNER=test_progs FLAVOR=bpf_gcc TESTS_DIR=prog_tests \\\n+\t\tBPF_BUILD=gcc\n+endif\n+\n+# test_maps compiles map_tests/*.c against the default flavor's skeletons\n+$(OUTPUT)/test_maps: $(RUNNER_PREREQS) map_tests/tests.h default-skels FORCE\n+\t+$(Q)$(RUNNER_MAKE) RUNNER=test_maps FLAVOR= TESTS_DIR=map_tests \\\n+\t\tBPF_BUILD=none\n+\n+# Default-flavor BPF objects and skeletons consumed by targets in this\n+# Makefile (and by lib.mk's install rule) are produced by the runner\n+# sub-make above; the empty recipe plus the order-only prerequisite\n+# makes them available here without duplicating the runner's rules.\n+DEFAULT_BPF_OBJS := $(patsubst progs/%.c,$(OUTPUT)/%.bpf.o,$(wildcard progs/*.c))\n+TEST_GEN_FILES += $(DEFAULT_BPF_OBJS)\n+\n+BENCH_SKELS := $(addprefix $(OUTPUT)/,\t\t\t\t\t\\\n+\t       test_overhead.skel.h trigger_bench.skel.h\t\t\\\n+\t       ringbuf_bench.skel.h perfbuf_bench.skel.h\t\t\\\n+\t       bloom_filter_bench.skel.h bpf_loop_bench.skel.h\t\t\\\n+\t       strncmp_bench.skel.h bpf_hashmap_full_update_bench.skel.h\\\n+\t       local_storage_bench.skel.h\t\t\t\t\\\n+\t       local_storage_rcu_tasks_trace_bench.skel.h\t\t\\\n+\t       bench_local_storage_create.skel.h\t\t\t\\\n+\t       bpf_hashmap_lookup.skel.h htab_mem_bench.skel.h\t\t\\\n+\t       crypto_bench.skel.h bench_sockmap_prog.skel.h\t\t\\\n+\t       lpm_trie_bench.skel.h lpm_trie_map.skel.h\t\t\\\n+\t       bpf_nop_bench.skel.h xdp_lb_bench.skel.h)\n+TOP_SKELS := $(addprefix $(OUTPUT)/,xsk_xdp_progs.skel.h\t\t\\\n+\t     xdp_hw_metadata.skel.h xdp_features.skel.h\t\t\t\\\n+\t     test_core_extern.skel.h) $(BENCH_SKELS)\n+\n+# Empty-recipe delegation: \"default-skels\" (re)builds the file via the\n+# runner sub-make, after which make re-stats the target, so dependents\n+# see fresh timestamps within the same invocation. Dependents still\n+# rebuild on mtime only - the phony prerequisite does not cascade.\n+$(DEFAULT_BPF_OBJS) $(TOP_SKELS): default-skels ;\n+\n+# ---------------------------------------------------------------------\n+# Standalone test binaries\n+# ---------------------------------------------------------------------\n+\n+# test_verifier is much simpler than test_maps/test_progs and\n+# sufficiently different from them (e.g., its tests.h uses a completely\n+# different pattern), that it's worth just defining its rules explicitly.\n+$(OUTPUT)/test_verifier: test_verifier.c verifier/tests.h $(BPFOBJ)\t\\\n+\t\t\t $(OUTPUT)/testing_helpers.o\t\t\t\\\n+\t\t\t $(OUTPUT)/cap_helpers.o\t\t\t\\\n+\t\t\t $(OUTPUT)/unpriv_helpers.o\n \t$(call msg,BINARY,,$@)\n \t$(Q)$(CC) $(CFLAGS) $(filter %.a %.o %.c,$^) $(LDLIBS) -o $@\n \n # Include find_bit.c to compile xskxceiver.\n EXTRA_SRC := $(TOOLSDIR)/lib/find_bit.c prog_tests/test_xsk.c prog_tests/test_xsk.h\n-$(OUTPUT)/xskxceiver: $(EXTRA_SRC) xskxceiver.c xskxceiver.h $(OUTPUT)/network_helpers.o $(OUTPUT)/xsk.o $(OUTPUT)/xsk_xdp_progs.skel.h $(BPFOBJ) | $(OUTPUT)\n+$(OUTPUT)/xskxceiver: $(EXTRA_SRC) xskxceiver.c xskxceiver.h $(OUTPUT)/network_helpers.o $(OUTPUT)/xsk.o $(OUTPUT)/xsk_xdp_progs.skel.h $(BPFOBJ)\n \t$(call msg,BINARY,,$@)\n \t$(Q)$(CC) $(CFLAGS) $(filter %.a %.o %.c,$^) $(LDLIBS) -o $@\n \n-$(OUTPUT)/xdp_hw_metadata: xdp_hw_metadata.c $(OUTPUT)/network_helpers.o $(OUTPUT)/xsk.o $(OUTPUT)/xdp_hw_metadata.skel.h | $(OUTPUT)\n+$(OUTPUT)/xdp_hw_metadata: xdp_hw_metadata.c $(OUTPUT)/network_helpers.o $(OUTPUT)/xsk.o $(OUTPUT)/xdp_hw_metadata.skel.h\n \t$(call msg,BINARY,,$@)\n \t$(Q)$(CC) $(CFLAGS) $(filter %.a %.o %.c,$^) $(LDLIBS) -o $@\n \n-$(OUTPUT)/xdp_features: xdp_features.c $(OUTPUT)/network_helpers.o $(OUTPUT)/xdp_features.skel.h | $(OUTPUT)\n+$(OUTPUT)/xdp_features: xdp_features.c $(OUTPUT)/network_helpers.o $(OUTPUT)/xdp_features.skel.h\n \t$(call msg,BINARY,,$@)\n \t$(Q)$(CC) $(CFLAGS) $(filter %.a %.o %.c,$^) $(LDLIBS) -o $@\n \n@@ -956,11 +558,18 @@ $(OUTPUT)/test_cpp: test_cpp.cpp $(OUTPUT)/test_core_extern.skel.h $(BPFOBJ)\n \t$(call msg,CXX,,$@)\n \t$(Q)$(CXX) $(CXXFLAGS) $(filter %.a %.o %.cpp,$^) $(LDLIBS) -o $@\n \n-# Benchmark runner\n-$(OUTPUT)/bench_%.o: benchs/bench_%.c bench.h $(BPFOBJ)\n+# ---------------------------------------------------------------------\n+# Benchmark runner. The benchmark list is defined by the files in\n+# benchs/: every benchs/bench_*.c is compiled and linked in.\n+# ---------------------------------------------------------------------\n+\n+BENCH_OBJS := $(sort $(patsubst benchs/%.c,$(OUTPUT)/%.o,$(wildcard benchs/bench_*.c)))\n+\n+$(OUTPUT)/bench_%.o: benchs/bench_%.c bench.h $(BPFOBJ) | default-skels\n \t$(call msg,CC,,$@)\n-\t$(Q)$(CC) $(CFLAGS) -O2 -c $(filter %.c,$^) $(LDLIBS) -o $@ $(if $(PERMISSIVE),|| \\\n-\t\t($(RM) $@; printf '  %-12s %s\\n' 'SKIP-BENCH' '$(notdir $@)' 1\u003e\u00262))\n+\t$(Q)$(CC) $(CFLAGS) -O2 -c $(filter %.c,$^) -o $@ $(call skip_on_fail,BENCH)\n+\n+# benchmark-specific generated-skeleton dependencies\n $(OUTPUT)/bench_rename.o: $(OUTPUT)/test_overhead.skel.h\n $(OUTPUT)/bench_trigger.o: $(OUTPUT)/trigger_bench.skel.h\n $(OUTPUT)/bench_ringbufs.o: $(OUTPUT)/ringbuf_bench.skel.h \\\n@@ -980,37 +589,19 @@ $(OUTPUT)/bench_lpm_trie_map.o: $(OUTPUT)/lpm_trie_bench.skel.h $(OUTPUT)/lpm_tr\n $(OUTPUT)/bench_bpf_nop.o: $(OUTPUT)/bpf_nop_bench.skel.h bench_bpf_timing.h\n $(OUTPUT)/bench_xdp_lb.o: $(OUTPUT)/xdp_lb_bench.skel.h bench_bpf_timing.h\n $(OUTPUT)/bench_bpf_timing.o: bench_bpf_timing.h\n+\n $(OUTPUT)/bench.o: bench.h testing_helpers.h $(BPFOBJ)\n $(OUTPUT)/bench: LDLIBS += -lm\n-$(OUTPUT)/bench: $(OUTPUT)/bench.o \\\n-\t\t $(TESTING_HELPERS) \\\n-\t\t $(TRACE_HELPERS) \\\n-\t\t $(CGROUP_HELPERS) \\\n-\t\t $(OUTPUT)/bench_count.o \\\n-\t\t $(OUTPUT)/bench_rename.o \\\n-\t\t $(OUTPUT)/bench_trigger.o \\\n-\t\t $(OUTPUT)/bench_ringbufs.o \\\n-\t\t $(OUTPUT)/bench_bloom_filter_map.o \\\n-\t\t $(OUTPUT)/bench_bpf_loop.o \\\n-\t\t $(OUTPUT)/bench_strncmp.o \\\n-\t\t $(OUTPUT)/bench_bpf_hashmap_full_update.o \\\n-\t\t $(OUTPUT)/bench_local_storage.o \\\n-\t\t $(OUTPUT)/bench_local_storage_rcu_tasks_trace.o \\\n-\t\t $(OUTPUT)/bench_bpf_hashmap_lookup.o \\\n-\t\t $(OUTPUT)/bench_local_storage_create.o \\\n-\t\t $(OUTPUT)/bench_htab_mem.o \\\n-\t\t $(OUTPUT)/bench_bpf_crypto.o \\\n-\t\t $(OUTPUT)/bench_sockmap.o \\\n-\t\t $(OUTPUT)/bench_lpm_trie_map.o \\\n-\t\t $(OUTPUT)/bench_bpf_timing.o \\\n-\t\t $(OUTPUT)/bench_bpf_nop.o \\\n-\t\t $(OUTPUT)/bench_xdp_lb.o \\\n-\t\t $(OUTPUT)/usdt_1.o \\\n-\t\t $(OUTPUT)/usdt_2.o \\\n-\t\t #\n+$(OUTPUT)/bench: $(OUTPUT)/bench.o\t\t\\\n+\t\t $(OUTPUT)/testing_helpers.o\t\\\n+\t\t $(OUTPUT)/trace_helpers.o\t\\\n+\t\t $(OUTPUT)/cgroup_helpers.o\t\\\n+\t\t $(OUTPUT)/usdt_1.o\t\t\\\n+\t\t $(OUTPUT)/usdt_2.o\t\t\\\n+\t\t $(BENCH_OBJS)\n \t$(call msg,BINARY,,$@)\n-\t$(Q)$(CC) $(CFLAGS) $(LDFLAGS) $(filter %.a %.o,$^) $(LDLIBS) -o $@ $(if $(PERMISSIVE),|| \\\n-\t\t($(RM) $@; printf '  %-12s %s\\n' 'SKIP-LINK' '$(notdir $@) (some benchmarks may have been skipped)' 1\u003e\u00262))\n+\t$(Q)$(CC) $(CFLAGS) $(LDFLAGS) $(filter %.a %.o,$^) $(LDLIBS) -o $@ \\\n+\t\t$(call skip_on_fail,LINK,,(some benchmarks may have been skipped))\n \n # This works around GCC warning about snprintf truncating strings like:\n #\n@@ -1022,13 +613,6 @@ $(OUTPUT)/veristat: $(OUTPUT)/veristat.o\n \t$(call msg,BINARY,,$@)\n \t$(Q)$(CC) $(CFLAGS) $(LDFLAGS) $(filter %.a %.o,$^) $(LDLIBS) -o $@\n \n-# Linking uprobe_multi can fail due to relocation overflows on mips.\n-$(OUTPUT)/uprobe_multi: CFLAGS += $(if $(filter mips, $(ARCH)),-mxgot)\n-$(OUTPUT)/uprobe_multi: uprobe_multi.c uprobe_multi.ld\n-\t$(call msg,BINARY,,$@)\n-\t$(Q)$(CC) $(CFLAGS) -Wl,-T,uprobe_multi.ld -O0 $(LDFLAGS) \t\\\n-\t\t$(filter-out %.ld,$^) $(LDLIBS) -o $@\n-\n EXTRA_CLEAN := $(SCRATCH_DIR) $(HOST_SCRATCH_DIR)\t\t\t\\\n \tprog_tests/tests.h map_tests/tests.h verifier/tests.h\t\t\\\n \tfeature bpftool $(TEST_KMOD_TARGETS)\t\t\t\t\\\n@@ -1038,7 +622,7 @@ EXTRA_CLEAN := $(SCRATCH_DIR) $(HOST_SCRATCH_DIR)\t\t\t\\\n \t\t\t       liburandom_read.so)\t\t\t\\\n \t$(OUTPUT)/FEATURE-DUMP.selftests\n \n-.PHONY: docs docs-clean\n+.PHONY: docs docs-clean libarena libarena_asan\n \n # Delete partially updated (corrupted) files on error\n .DELETE_ON_ERROR:\n@@ -1062,9 +646,3 @@ override define INSTALL_RULE\n \t\t\t$(INSTALL_PATH)/$$DIR;\t\t\t\t  \\\n \tdone\n endef\n-\n-libarena: $(LIBARENA_SKEL)\n-\n-ifneq ($(CLANG_HAS_ARENA_ASAN),)\n-libarena_asan: $(LIBARENA_ASAN_SKEL)\n-endif\ndiff --git a/tools/testing/selftests/bpf/Makefile.buildvars b/tools/testing/selftests/bpf/Makefile.buildvars\nnew file mode 100644\nindex 00000000000000..ea975feef0136f\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/Makefile.buildvars\n@@ -0,0 +1,204 @@\n+# SPDX-License-Identifier: GPL-2.0\n+#\n+# Shared toolchain, path and flag definitions for the BPF selftests build.\n+# Included by both Makefile (the top level) and Makefile.runner (one\n+# test-runner instance).\n+#\n+# Requirements at include time:\n+#   - $(OUTPUT) is set to an absolute path\n+#   - tools/build/Build.include, tools/scripts/Makefile.arch and\n+#     tools/scripts/Makefile.include have been included (for SRCARCH etc.)\n+\n+CXX ?= $(CROSS_COMPILE)g++\n+PKG_CONFIG ?= $(CROSS_COMPILE)pkg-config\n+\n+TOOLSDIR := $(abspath ../../..)\n+LIBDIR := $(TOOLSDIR)/lib\n+BPFDIR := $(LIBDIR)/bpf\n+TOOLSINCDIR := $(TOOLSDIR)/include\n+TOOLSARCHINCDIR := $(TOOLSDIR)/arch/$(SRCARCH)/include\n+BPFTOOLDIR := $(TOOLSDIR)/bpf/bpftool\n+APIDIR := $(TOOLSINCDIR)/uapi\n+ifneq ($(O),)\n+GENDIR := $(O)/include/generated\n+else\n+GENDIR := $(abspath ../../../../include/generated)\n+endif\n+GENHDR := $(GENDIR)/autoconf.h\n+\n+ifneq ($(wildcard $(GENHDR)),)\n+  GENFLAGS := -DHAVE_GENHDR\n+endif\n+\n+ifeq ($(srctree),)\n+srctree := $(abspath ../../../..)\n+endif\n+\n+# Immediate (with an override-preserving guard): a plain ?= stays\n+# recursively expanded and would re-run 'command -v' on every expansion.\n+ifeq ($(origin BPF_GCC),undefined)\n+BPF_GCC\t\t:= $(shell command -v bpf-gcc;)\n+endif\n+ifdef ASAN\n+SAN_CFLAGS\t?= -fsanitize=address -fno-omit-frame-pointer\n+else\n+SAN_CFLAGS\t?=\n+endif\n+SAN_LDFLAGS\t?= $(SAN_CFLAGS)\n+RELEASE\t\t?=\n+OPT_FLAGS\t?= $(if $(RELEASE),-O2,-O0)\n+\n+LIBELF_CFLAGS\t:= $(shell $(PKG_CONFIG) libelf --cflags 2\u003e/dev/null)\n+LIBELF_LIBS\t:= $(shell $(PKG_CONFIG) libelf --libs 2\u003e/dev/null || echo -lelf)\n+\n+SKIP_DOCS\t?=\n+SKIP_LLVM\t?=\n+SKIP_LIBBFD\t?=\n+SKIP_CRYPTO\t?=\n+\n+# When BPF_STRICT_BUILD is 1, any BPF object, skeleton, test object, or\n+# benchmark compilation failure is fatal. Set to 0 to tolerate failures\n+# and continue building the remaining tests.\n+BPF_STRICT_BUILD ?= 1\n+PERMISSIVE := $(filter 0,$(BPF_STRICT_BUILD))\n+\n+# Permissive-mode recipe suffix: on failure, remove the target, emit a\n+# SKIP marker and report success so the rest of the build continues.\n+# $(1) - SKIP tag (BPF, TEST, BENCH, SKEL, LINK, ...)\n+# $(2) - optional extra files to remove alongside the target\n+# $(3) - optional note appended to the SKIP message\n+skip_on_fail = $(if $(PERMISSIVE),|| { $(RM) $@ $(2); \\\n+\tprintf '  %-12s %s\\n' 'SKIP-$(1)' '$(notdir $@)$(if $(3), $(3))' 1\u003e\u00262; })\n+\n+# Kernel test modules; TEST_KMODS= on the command line disables them.\n+TEST_KMODS ?= bpf_testmod.ko bpf_test_no_cfi.ko bpf_test_modorder_x.ko \\\n+\tbpf_test_modorder_y.ko bpf_test_rqspinlock.ko\n+TEST_KMOD_TARGETS = $(addprefix $(OUTPUT)/,$(TEST_KMODS))\n+\n+# Output-tree layout. Defined before COMMON_CFLAGS on purpose: CFLAGS\n+# below is expanded immediately and references $(INCLUDE_DIR).\n+SCRATCH_DIR := $(OUTPUT)/tools\n+BUILD_DIR := $(SCRATCH_DIR)/build\n+INCLUDE_DIR := $(SCRATCH_DIR)/include\n+BPFOBJ := $(BUILD_DIR)/libbpf/libbpf.a\n+ifneq ($(CROSS_COMPILE),)\n+HOST_BUILD_DIR\t\t:= $(BUILD_DIR)/host\n+HOST_SCRATCH_DIR\t:= $(OUTPUT)/host-tools\n+HOST_INCLUDE_DIR\t:= $(HOST_SCRATCH_DIR)/include\n+else\n+HOST_BUILD_DIR\t\t:= $(BUILD_DIR)\n+HOST_SCRATCH_DIR\t:= $(SCRATCH_DIR)\n+HOST_INCLUDE_DIR\t:= $(INCLUDE_DIR)\n+endif\n+HOST_BPFOBJ := $(HOST_BUILD_DIR)/libbpf/libbpf.a\n+RESOLVE_BTFIDS := $(HOST_BUILD_DIR)/resolve_btfids/resolve_btfids\n+\n+DEFAULT_BPFTOOL := $(HOST_SCRATCH_DIR)/sbin/bpftool\n+ifneq ($(CROSS_COMPILE),)\n+CROSS_BPFTOOL := $(SCRATCH_DIR)/sbin/bpftool\n+TRUNNER_BPFTOOL := $(CROSS_BPFTOOL)\n+USE_BOOTSTRAP :=\n+else\n+TRUNNER_BPFTOOL := $(DEFAULT_BPFTOOL)\n+USE_BOOTSTRAP := bootstrap/\n+endif\n+BPFTOOL ?= $(DEFAULT_BPFTOOL)\n+\n+COMMON_CFLAGS = -g $(OPT_FLAGS) -rdynamic -std=gnu11\t\t\t\\\n+\t  -Wall -Werror -fno-omit-frame-pointer\t\t\t\t\\\n+\t  -Wno-unused-but-set-variable\t\t\t\t\t\\\n+\t  $(GENFLAGS) $(SAN_CFLAGS) $(LIBELF_CFLAGS)\t\t\t\\\n+\t  -I$(CURDIR) -I$(INCLUDE_DIR) -I$(GENDIR) -I$(LIBDIR)\t\t\\\n+\t  -I$(TOOLSINCDIR) -I$(TOOLSARCHINCDIR) -I$(APIDIR) -I$(OUTPUT)\t\\\n+\t  -I$(CURDIR)/libarena/include\n+LDFLAGS += $(SAN_LDFLAGS)\n+LDLIBS += $(LIBELF_LIBS) -lz -lrt -lpthread\n+\n+PCAP_CFLAGS\t:= $(shell $(PKG_CONFIG) --cflags libpcap 2\u003e/dev/null \u0026\u0026 echo \"-DTRAFFIC_MONITOR=1\")\n+PCAP_LIBS\t:= $(shell $(PKG_CONFIG) --libs libpcap 2\u003e/dev/null)\n+LDLIBS += $(PCAP_LIBS)\n+# Prepend rather than append: the include-search order must stay\n+# \"$(COMMON_CFLAGS) first, lib.mk additions after\" regardless of whether\n+# lib.mk was included before this file (top level) or not at all (runner).\n+CFLAGS := $(COMMON_CFLAGS) $(PCAP_CFLAGS) $(CFLAGS)\n+\n+ifneq ($(LLVM),)\n+# Silence some warnings when compiled with clang\n+CFLAGS += -Wno-unused-command-line-argument\n+endif\n+\n+# Check whether clang supports BPF address sanitizer (requires LLVM 22+)\n+CLANG_HAS_ARENA_ASAN := $(shell echo 'int x;' | \\\n+\t$(CLANG) --target=bpf -fsanitize=kernel-address \\\n+\t-mllvm -asan-shadow-addr-space=1 \\\n+\t-x c -c - -o /dev/null 2\u003e/dev/null \u0026\u0026 echo 1)\n+ifneq ($(CLANG_HAS_ARENA_ASAN),)\n+CFLAGS += -DHAS_BPF_ARENA_ASAN\n+endif\n+\n+VMLINUX_BTF_PATHS ?= $(if $(O),$(O)/vmlinux)\t\t\t\t\\\n+\t\t     $(if $(KBUILD_OUTPUT),$(KBUILD_OUTPUT)/vmlinux)\t\\\n+\t\t     ../../../../vmlinux\t\t\t\t\\\n+\t\t     /sys/kernel/btf/vmlinux\t\t\t\t\\\n+\t\t     /boot/vmlinux-$(shell uname -r)\n+VMLINUX_BTF ?= $(abspath $(firstword $(wildcard $(VMLINUX_BTF_PATHS))))\n+ifeq ($(VMLINUX_BTF),)\n+$(error Cannot find a vmlinux for VMLINUX_BTF at any of \"$(VMLINUX_BTF_PATHS)\")\n+endif\n+\n+# Get Clang's default includes on this system, as opposed to those seen by\n+# '--target=bpf'. This fixes \"missing\" files on some architectures/distros,\n+# such as asm/byteorder.h, asm/socket.h, asm/sockios.h, sys/cdefs.h etc.\n+#\n+# Use '-idirafter': Don't interfere with include mechanics except where the\n+# build would have failed anyways.\n+define get_sys_includes\n+$(shell $(1) $(2) -v -E - \u003c/dev/null 2\u003e\u00261 \\\n+\t| sed -n '/\u003c...\u003e search starts here:/,/End of search list./{ s| \\(/.*\\)|-idirafter \\1|p }') \\\n+$(shell $(1) $(2) -dM -E - \u003c/dev/null | grep '__riscv_xlen ' | awk '{printf(\"-D__riscv_xlen=%d -D__BITS_PER_LONG=%d\", $$3, $$3)}') \\\n+$(shell $(1) $(2) -dM -E - \u003c/dev/null | grep '__loongarch_grlen ' | awk '{printf(\"-D__BITS_PER_LONG=%d\", $$3)}') \\\n+$(shell $(1) $(2) -dM -E - \u003c/dev/null | grep -E 'MIPS(EL|EB)|_MIPS_SZ(PTR|LONG) |_MIPS_SIM |_ABI(O32|N32|64) ' | awk '{printf(\"-D%s=%s \", $$2, $$3)}')\n+endef\n+\n+# Determine target endianness.\n+IS_LITTLE_ENDIAN := $(shell $(CC) -dM -E - \u003c/dev/null | \\\n+\t\t\tgrep 'define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__')\n+MENDIAN := $(if $(IS_LITTLE_ENDIAN),-mlittle-endian,-mbig-endian)\n+BPF_TARGET_ENDIAN := $(if $(IS_LITTLE_ENDIAN),--target=bpfel,--target=bpfeb)\n+\n+ifneq ($(CROSS_COMPILE),)\n+CLANG_TARGET_ARCH = --target=$(notdir $(CROSS_COMPILE:%-=%))\n+endif\n+\n+CLANG_SYS_INCLUDES := $(call get_sys_includes,$(CLANG),$(CLANG_TARGET_ARCH))\n+BPF_CFLAGS = -g -Wall -Werror -D__TARGET_ARCH_$(SRCARCH) $(MENDIAN)\t\\\n+\t     -I$(INCLUDE_DIR) -I$(CURDIR) -I$(APIDIR)\t\t\t\\\n+\t     -I$(CURDIR)/libarena/include\t\t\t\t\\\n+\t     -I$(abspath $(OUTPUT)/../usr/include)\t\t\t\\\n+\t     -std=gnu11\t\t \t\t\t\t\t\\\n+\t     -fno-strict-aliasing \t\t\t\t\t\\\n+\t     -Wno-microsoft-anon-tag\t\t\t\t\t\\\n+\t     -fms-extensions\t\t\t\t\t\t\\\n+\t     -Wno-compare-distinct-pointer-types\t\t\t\\\n+\t     -Wno-initializer-overrides\t\t\t\t\t\\\n+\t     #\n+# TODO: enable me -Wsign-compare\n+\n+CLANG_CFLAGS = $(CLANG_SYS_INCLUDES)\n+\n+# Signing key/cert shared by the signed light skeletons and test_progs.\n+VERIFY_SIG_SETUP := $(CURDIR)/verify_sig_setup.sh\n+VERIFY_SIG_HDR := verification_cert.h\n+VERIFICATION_CERT := $(BUILD_DIR)/signing_key.der\n+PRIVATE_KEY := $(BUILD_DIR)/signing_key.pem\n+\n+# libarena skeletons, built by the libarena/ sub-make (driven by the top\n+# level Makefile); test_progs and its flavors depend on the headers.\n+LIBARENA_SKEL := libarena/libarena.skel.h\n+ifneq ($(CLANG_HAS_ARENA_ASAN),)\n+LIBARENA_ASAN_SKEL := libarena/libarena_asan.skel.h\n+endif\n+\n+ifneq ($(V),1)\n+submake_extras := feature_display=0\n+endif\ndiff --git a/tools/testing/selftests/bpf/Makefile.runner b/tools/testing/selftests/bpf/Makefile.runner\nnew file mode 100644\nindex 00000000000000..4c58cc51f2514e\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/Makefile.runner\n@@ -0,0 +1,313 @@\n+# SPDX-License-Identifier: GPL-2.0\n+#\n+# Build one BPF test-runner instance: test_progs, one of its flavors\n+# (no_alu32, cpuv4, bpf_gcc), or test_maps.\n+#\n+# Each instance is an independent sub-make with its own output directory,\n+# so everything here is written in plain make - no $$-escaping, no eval,\n+# no per-flavor guards. This file is always invoked by the main Makefile,\n+# never directly: all shared prerequisites (libbpf, bpftool, vmlinux.h,\n+# veristat, helper objects, signing key, generated tests.h, extra\n+# binaries) are built by the main Makefile *before* this one runs and are\n+# referenced below as plain files.\n+#\n+# Parameters (all passed on the sub-make command line):\n+#   OUTPUT      absolute path of the selftests output directory\n+#   RUNNER      base binary name: test_progs | test_maps\n+#   FLAVOR      flavor suffix: empty | no_alu32 | cpuv4 | bpf_gcc\n+#   TESTS_DIR   directory with the test sources: prog_tests | map_tests\n+#   BPF_BUILD   how to build progs/*.c BPF objects: clang | gcc | none\n+#   BPF_MCPU    clang -mcpu= value: v2 | v3 | v4\n+#   BPF_DEFINES extra defines for BPF objects (e.g. -DENABLE_ATOMICS_TESTS)\n+#   CC          C compiler (as resolved by lib.mk in the main Makefile)\n+#   LLVM_CFLAGS/LLVM_LDLIBS/LLVM_LDFLAGS\n+#               results of the top-level LLVM feature probe\n+\n+include ../../../build/Build.include\n+include ../../../scripts/Makefile.arch\n+include ../../../scripts/Makefile.include\n+\n+# Same message helpers as ../lib.mk, which only the top level includes.\n+ifeq ($(V),1)\n+Q =\n+msg =\n+else\n+Q = @\n+msg = @printf '  %-8s%s %s%s\\n' \"$(1)\" \"$(if $(2), [$(2)])\" \"$(notdir $(3))\" \"$(if $(4), $(4))\";\n+MAKEFLAGS += --no-print-directory\n+endif\n+\n+include Makefile.buildvars\n+\n+# Keep in sync with the CFLAGS/LDFLAGS additions in ../lib.mk.\n+CFLAGS += -D_GNU_SOURCE=\n+CFLAGS += -I$(srctree)/tools/testing/selftests\n+CFLAGS += $(USERCFLAGS)\n+LDFLAGS += $(USERLDFLAGS)\n+ifneq ($(LLVM),)\n+CFLAGS += -Wno-address-of-packed-member\n+CFLAGS += -Wno-gnu-variable-sized-type-not-at-end\n+endif\n+\n+BINARY := $(RUNNER)$(if $(FLAVOR),-$(FLAVOR))\n+RDIR   := $(OUTPUT)$(if $(FLAVOR),/$(FLAVOR))\n+\n+all: $(OUTPUT)/$(BINARY)\n+.PHONY: all\n+\n+# Delete partially updated (corrupted) files on error\n+.DELETE_ON_ERROR:\n+\n+# Permissive skip-on-failure of individual tests applies to test_progs and\n+# its flavors only; test_maps uses strong cross-object references and keeps\n+# strict semantics even when BPF_STRICT_BUILD=0.\n+PERMISSIVE_TESTS := $(if $(filter test_progs,$(RUNNER)),$(PERMISSIVE))\n+\n+$(RDIR):\n+\t$(call msg,MKDIR,,$@)\n+\t$(Q)mkdir -p $@\n+\n+# ---------------------------------------------------------------------\n+# Per-runner sources\n+# ---------------------------------------------------------------------\n+\n+ifeq ($(RUNNER),test_progs)\n+EXTRA_SOURCES := test_progs.c cgroup_helpers.c trace_helpers.c\t\t\\\n+\t\t network_helpers.c testing_helpers.c btf_helpers.c\t\\\n+\t\t cap_helpers.c unpriv_helpers.c sysctl_helpers.c\t\\\n+\t\t netlink_helpers.c jit_disasm_helpers.c io_helpers.c\t\\\n+\t\t test_loader.c xsk.c disasm.c disasm_helpers.c\t\t\\\n+\t\t json_writer.c bpftool_helpers.c usdt_1.c usdt_2.c\n+LIB_SOURCES := find_bit.c\n+EXTRA_HDRS := flow_dissector_load.h ip_check_defrag_frags.h\t\t\\\n+\t      $(VERIFY_SIG_HDR) $(LIBARENA_SKEL) $(LIBARENA_ASAN_SKEL)\n+EXTRA_FILES := $(OUTPUT)/urandom_read $(OUTPUT)/liburandom_read.so\t\\\n+\t       $(OUTPUT)/xdp_synproxy $(OUTPUT)/sign-file\t\t\\\n+\t       $(OUTPUT)/uprobe_multi\t\t\t\t\t\\\n+\t       $(TEST_KMOD_TARGETS)\t\t\t\t\t\\\n+\t       ima_setup.sh $(VERIFY_SIG_SETUP)\t\t\t\t\\\n+\t       $(wildcard progs/btf_dump_test_case_*.c)\t\t\t\\\n+\t       $(wildcard progs/*.bpf.o)\n+endif\n+\n+ifeq ($(RUNNER),test_maps)\n+EXTRA_SOURCES := test_maps.c testing_helpers.c\n+endif\n+\n+# Note on the unflavored runners, which share $(OUTPUT) with the main\n+# Makefile: helper objects the main Makefile also uses (and lists in the\n+# runner prerequisites) are already built and fresh when a runner starts,\n+# so the rules below find them up to date and never write them.\n+\n+TESTS_HDR := $(TESTS_DIR)/tests.h\n+\n+TEST_SRCS  := $(notdir $(wildcard $(TESTS_DIR)/*.c))\n+TEST_OBJS  := $(patsubst %.c,$(RDIR)/%.test.o,$(TEST_SRCS))\n+TEST_DEPS  := $(TEST_OBJS:.o=.d)\n+EXTRA_OBJS := $(patsubst %.c,$(RDIR)/%.o,$(filter %.c,$(EXTRA_SOURCES)))\n+LIB_OBJS   := $(patsubst %.c,$(RDIR)/%.o,$(LIB_SOURCES))\n+\n+# ---------------------------------------------------------------------\n+# BPF objects and skeletons\n+# ---------------------------------------------------------------------\n+\n+ifneq ($(BPF_BUILD),none)\n+\n+BPF_SRCS := $(notdir $(wildcard progs/*.c))\n+BPF_OBJS := $(patsubst %.c,$(RDIR)/%.bpf.o,$(BPF_SRCS))\n+\n+# Which tests want a light skeleton is derived from the test sources\n+# themselves: an '#include \"foo.lskel.h\"' line is the single source of\n+# truth. Tests that include both foo.skel.h and foo.lskel.h get both.\n+skel_includes = $(shell grep -rhoE '\"[a-zA-Z0-9_]+\\.$(1)\"' prog_tests benchs map_tests *.c *.cpp 2\u003e/dev/null)\n+LSKEL_BASES := $(sort $(patsubst \"%.lskel.h\",%,$(call skel_includes,lskel.h)))\n+SKEL_DEMANDED := $(sort $(patsubst \"%.skel.h\",%,$(call skel_includes,skel.h)))\n+\n+# Signing is not expressed in the include, so the signed subset stays\n+# explicit.\n+LSKELS_SIGNED := fentry_test fexit_test atomics\n+\n+# BPF objects that cannot have a regular skeleton, plus lskel-only tests\n+# (light-skeleton users whose sources never include the .skel.h form).\n+SKEL_BLACKLIST := btf__% test_pinning_invalid.c test_sk_assign.c \\\n+\t\t  $(addsuffix .c,$(filter-out $(SKEL_DEMANDED),$(LSKEL_BASES)))\n+\n+# Skeletons made by linking multiple BPF objects together. The %-deps\n+# mapping is the only hand-maintained metadata: it cannot be derived from\n+# the sources.\n+test_static_linked.skel.h-deps := test_static_linked1.bpf.o test_static_linked2.bpf.o\n+linked_funcs.skel.h-deps := linked_funcs1.bpf.o linked_funcs2.bpf.o\n+linked_vars.skel.h-deps := linked_vars1.bpf.o linked_vars2.bpf.o\n+linked_maps.skel.h-deps := linked_maps1.bpf.o linked_maps2.bpf.o\n+# In the subskeleton case, we want the test_subskeleton_lib.subskel.h file\n+# but that's created as a side-effect of the skel.h generation.\n+test_subskeleton.skel.h-deps := test_subskeleton_lib2.bpf.o test_subskeleton_lib.bpf.o test_subskeleton.bpf.o\n+test_subskeleton_lib.skel.h-deps := test_subskeleton_lib2.bpf.o test_subskeleton_lib.bpf.o\n+test_usdt.skel.h-deps := test_usdt.bpf.o test_usdt_multispec.bpf.o\n+tracing_multi.skel.h-deps := tracing_multi_attach.bpf.o tracing_multi_check.bpf.o\n+tracing_multi_module.skel.h-deps := tracing_multi_attach_module.bpf.o tracing_multi_check.bpf.o\n+tracing_multi_intersect.skel.h-deps := tracing_multi_intersect_attach.bpf.o tracing_multi_check.bpf.o\n+tracing_multi_session.skel.h-deps := tracing_multi_session_attach.bpf.o tracing_multi_check.bpf.o\n+\n+LINKED_SKELS := test_static_linked.skel.h linked_funcs.skel.h\t\t\\\n+\t\tlinked_vars.skel.h linked_maps.skel.h\t\t\t\\\n+\t\ttest_subskeleton.skel.h test_subskeleton_lib.skel.h\t\\\n+\t\ttest_usdt.skel.h tracing_multi.skel.h\t\t\t\\\n+\t\ttracing_multi_module.skel.h\t\t\t\t\\\n+\t\ttracing_multi_intersect.skel.h\t\t\t\t\\\n+\t\ttracing_multi_session.skel.h\n+LINKED_BPF_SRCS := $(patsubst %.bpf.o,%.c,$(foreach skel,$(LINKED_SKELS),$($(skel)-deps)))\n+\n+SKELS := $(patsubst %.c,$(RDIR)/%.skel.h,\t\t\t\t\\\n+\t   $(filter-out $(SKEL_BLACKLIST) $(LINKED_BPF_SRCS),$(BPF_SRCS)))\n+LSKELS_H        := $(patsubst %,$(RDIR)/%.lskel.h,$(filter-out $(LSKELS_SIGNED),$(LSKEL_BASES)))\n+LSKELS_SIGNED_H := $(patsubst %,$(RDIR)/%.lskel.h,$(LSKELS_SIGNED))\n+LINKED_SKELS_H  := $(addprefix $(RDIR)/,$(LINKED_SKELS))\n+ALL_SKELS := $(SKELS) $(LSKELS_H) $(LSKELS_SIGNED_H) $(LINKED_SKELS_H)\n+\n+HEADERS_FOR_BPF_OBJS := $(wildcard $(BPFDIR)/*.bpf.h)\t\t\t\\\n+\t\t\t$(wildcard $(CURDIR)/libarena/include/*.[ch])\t\\\n+\t\t\t$(addprefix $(BPFDIR)/,\tbpf_core_read.h\t\t\\\n+\t\t\t\t\t\tbpf_endian.h\t\t\\\n+\t\t\t\t\t\tbpf_helpers.h\t\t\\\n+\t\t\t\t\t\tbpf_tracing.h)\n+\n+# Per-source BPF compilation flag overrides ($\u003c-CFLAGS / $\u003c-$(FLAVOR)-CFLAGS).\n+# GCC cannot disable \"declaration of anonymous struct in parameter list\"\n+# warnings triggered by these (technically legal) test programs.\n+progs/btf_dump_test_case_bitfields.c-bpf_gcc-CFLAGS := -Wno-error\n+progs/btf_dump_test_case_namespacing.c-bpf_gcc-CFLAGS := -Wno-error\n+progs/btf_dump_test_case_packing.c-bpf_gcc-CFLAGS := -Wno-error\n+progs/btf_dump_test_case_padding.c-bpf_gcc-CFLAGS := -Wno-error\n+progs/btf_dump_test_case_syntax.c-bpf_gcc-CFLAGS := -Wno-error\n+\n+ifeq ($(BPF_BUILD),gcc)\n+BPF_CC_MSG := GCC-BPF\n+# Note: sys includes come from the *host* gcc, not $(BPF_GCC) - bpf-gcc's\n+# own search path lacks the host's asm-generic/ headers. Computed once\n+# here: bpf_cc below is deferred and expands per compiled object.\n+GCC_SYS_INCLUDES := $(call get_sys_includes,gcc,)\n+bpf_cc = $(BPF_GCC) $(BPF_CFLAGS) $(GCC_SYS_INCLUDES)\t\t\t\\\n+\t $(BPF_DEFINES) $($\u003c-CFLAGS) $($\u003c-$(FLAVOR)-CFLAGS)\t\t\\\n+\t -DBPF_NO_PRESERVE_ACCESS_INDEX -Wno-attributes -O2 -c $\u003c -o $@\n+else\n+BPF_CC_MSG := CLNG-BPF\n+bpf_cc = $(CLANG) $(BPF_CFLAGS) $(CLANG_CFLAGS) $(BPF_DEFINES)\t\t\\\n+\t $($\u003c-CFLAGS) $($\u003c-$(FLAVOR)-CFLAGS)\t\t\t\t\\\n+\t -O2 $(BPF_TARGET_ENDIAN) -c $\u003c -mcpu=$(BPF_MCPU) -o $@\n+endif\n+\n+$(BPF_OBJS): $(RDIR)/%.bpf.o: progs/%.c progs/*.h\t\t\t\\\n+\t     $(INCLUDE_DIR)/vmlinux.h $(HEADERS_FOR_BPF_OBJS)\t\t\\\n+\t     | $(RDIR) $(BPFOBJ)\n+\t$(call msg,$(BPF_CC_MSG),$(BINARY),$@)\n+\t$(Q)$(bpf_cc) $(call skip_on_fail,BPF)\n+\n+# gen_bpf_skel.sh picks the bpftool binary up from the environment.\n+export BPFTOOL\n+GEN_SKEL := ./gen_bpf_skel.sh\n+\n+$(SKELS): $(RDIR)/%.skel.h: $(RDIR)/%.bpf.o $(BPFTOOL) | $(RDIR)\n+\t$(Q)$(GEN_SKEL) --tag '$(BINARY)' --name $* --infix linked\t\\\n+\t\t--skel $@ --subskel $(@:.skel.h=.subskel.h) $\u003c\t\t\\\n+\t\t$(call skip_on_fail,SKEL,$(@:.skel.h=.subskel.h))\n+\n+$(LSKELS_H): $(RDIR)/%.lskel.h: $(RDIR)/%.bpf.o $(BPFTOOL) | $(RDIR)\n+\t$(Q)$(GEN_SKEL) --tag '$(BINARY)' --name $*_lskel --infix llinked \\\n+\t\t--lskel --skel $@ $\u003c $(call skip_on_fail,SKEL)\n+\n+$(LSKELS_SIGNED_H): $(RDIR)/%.lskel.h: $(RDIR)/%.bpf.o $(BPFTOOL)\t\\\n+\t\t    $(PRIVATE_KEY) $(VERIFICATION_CERT) | $(RDIR)\n+\t$(Q)$(GEN_SKEL) --tag '$(BINARY) (signed)' --name $*_lskel\t\\\n+\t\t--infix llinked\t\t\t\t\t\t\\\n+\t\t--sign '-S -k $(PRIVATE_KEY) -i $(VERIFICATION_CERT)'\t\\\n+\t\t--skel $@ $\u003c $(call skip_on_fail,SKEL)\n+\n+.SECONDEXPANSION:\n+$(LINKED_SKELS_H): $(RDIR)/%.skel.h: $$(addprefix $(RDIR)/,$$($$*.skel.h-deps)) \\\n+\t\t   $(BPFTOOL) | $(RDIR)\n+\t$(Q)$(GEN_SKEL) --tag '$(BINARY)' --name $* --infix linked\t\\\n+\t\t--skel $@ --subskel $(@:.skel.h=.subskel.h)\t\t\\\n+\t\t$(addprefix $(RDIR)/,$($*.skel.h-deps))\t\t\t\\\n+\t\t$(call skip_on_fail,SKEL,$(@:.skel.h=.subskel.h))\n+\n+endif # BPF_BUILD != none\n+\n+.PHONY: skels\n+skels: $(ALL_SKELS)\n+\n+# When the compiler generates a %.d file, only skel basenames (not full\n+# paths) are specified as prerequisites for the corresponding %.o file.\n+# The vpath directives instruct make to also search for skel files in\n+# $(RDIR) if they are not present in the working directory.\n+vpath %.skel.h $(RDIR)\n+vpath %.lskel.h $(RDIR)\n+vpath %.subskel.h $(RDIR)\n+\n+# ---------------------------------------------------------------------\n+# Test objects and runner binary\n+# ---------------------------------------------------------------------\n+\n+$(RDIR)/resolve_btfids.test.o: $(RESOLVE_BTFIDS) $(RDIR)/btf_data.bpf.o\n+$(RDIR)/resolve_btfids.test.o: private TEST_NEEDS_BTFIDS = 1\n+\n+# Compile individual test files.\n+# Note: we cd into the output directory to ensure embedded BPF objects\n+# are found. The %.test.d dependency files are a side effect of the -MMD\n+# below; the separate no-recipe rule for them orders the first\n+# compilation after skeleton generation and re-orders test object\n+# compilation when headers change.\n+$(TEST_OBJS): $(RDIR)/%.test.o: $(TESTS_DIR)/%.c | $(RDIR)/%.test.d\n+\t$(call msg,TEST-OBJ,$(BINARY),$@)\n+\t$(Q)(cd $(@D) \u0026\u0026 $(CC) -I. $(CFLAGS) -MMD -MT $@ -c $(CURDIR)/$\u003c -o $(@F)) \\\n+\t\t$(if $(PERMISSIVE_TESTS),$(call skip_on_fail,TEST))\n+\t$(if $(TEST_NEEDS_BTFIDS),\t\t\t\t\t\\\n+\t\t$(Q)if [ -f $@ ]; then\t\t\t\t\t\\\n+\t\t$(if $(filter 1,$(V)),true,printf '  %-8s%s %s\\n' \"BTFIDS\" \" [$(BINARY)]\" \"$(notdir $@)\"); \\\n+\t\t$(RESOLVE_BTFIDS) --btf $(RDIR)/btf_data.bpf.o $@;\t\\\n+\t\t$(RESOLVE_BTFIDS) --patch_btfids $@.BTF_ids $@;\t\t\\\n+\t\tfi)\n+\n+$(TEST_DEPS): $(RDIR)/%.test.d: $(TESTS_DIR)/%.c $(EXTRA_HDRS) $(BPFOBJ) \\\n+\t      | $(RDIR) $(ALL_SKELS)\n+\n+include $(wildcard $(TEST_DEPS))\n+\n+$(RDIR)/jit_disasm_helpers.o: CFLAGS += $(LLVM_CFLAGS)\n+\n+$(EXTRA_OBJS): $(RDIR)/%.o: %.c $(EXTRA_HDRS) $(TESTS_HDR) $(BPFOBJ) | $(RDIR)\n+\t$(call msg,EXT-OBJ,$(BINARY),$@)\n+\t$(Q)$(CC) $(CFLAGS) -c $\u003c -o $@\n+\n+$(LIB_OBJS): $(RDIR)/%.o: $(TOOLSDIR)/lib/%.c | $(RDIR)\n+\t$(call msg,LIB-OBJ,$(BINARY),$@)\n+\t$(Q)$(CC) $(CFLAGS) -c $\u003c -o $@\n+\n+# Non-flavored in-srctree builds receive special treatment, in particular\n+# we do not need to copy extra resources (see e.g. test_btf_dump_case()).\n+.PHONY: extras\n+extras: $(if $(PERMISSIVE),$(wildcard $(EXTRA_FILES)),$(EXTRA_FILES)) | $(RDIR)\n+ifneq ($(FLAVOR):$(OUTPUT),:$(CURDIR))\n+ifneq ($(strip $(EXTRA_FILES)),)\n+\t$(call msg,EXT-COPY,$(BINARY),$(notdir $(EXTRA_FILES)))\n+\t$(Q)rsync -aq $(if $(PERMISSIVE),--ignore-missing-args) $(EXTRA_FILES) $(RDIR)/\n+endif\n+endif\n+\n+# In permissive mode, link whatever test objects were successfully built\n+# (their compilation may have been skipped); the wildcard in the recipe\n+# picks up the survivors at link time.\n+# Prerequisite order is also link order (test objects, extra objects,\n+# libbpf, lib objects).\n+$(OUTPUT)/$(BINARY): $(if $(PERMISSIVE_TESTS),,$(TEST_OBJS))\t\t\\\n+\t\t     $(EXTRA_OBJS) $(BPFOBJ) $(LIB_OBJS)\t\t\\\n+\t\t     $(TRUNNER_BPFTOOL) $(OUTPUT)/veristat\t\t\\\n+\t\t     | extras $(BPF_OBJS)\t\t\t\t\\\n+\t\t     $(if $(PERMISSIVE_TESTS),$(TEST_OBJS))\n+\t$(call msg,BINARY,,$@)\n+\t$(Q)$(CC) $(CFLAGS)\t\t\t\t\t\t\\\n+\t\t$(if $(PERMISSIVE_TESTS),$(wildcard $(TEST_OBJS)))\t\\\n+\t\t$(filter %.a %.o,$^)\t\t\t\t\t\\\n+\t\t$(LDLIBS) $(LLVM_LDLIBS) $(LDFLAGS) $(LLVM_LDFLAGS) -o $@\n+\t$(Q)ln -sf $(if $(FLAVOR),..,.)/tools/build/bpftool/$(USE_BOOTSTRAP)bpftool \\\n+\t\t   $(RDIR)/bpftool\ndiff --git a/tools/testing/selftests/bpf/gen_bpf_skel.sh b/tools/testing/selftests/bpf/gen_bpf_skel.sh\nnew file mode 100755\nindex 00000000000000..0b237f63967dd6\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/gen_bpf_skel.sh\n@@ -0,0 +1,97 @@\n+#!/bin/bash\n+# SPDX-License-Identifier: GPL-2.0\n+#\n+# Link BPF object file(s) with \"bpftool gen object\" and generate a\n+# skeleton (or light skeleton) header from the result.\n+#\n+# Usage:\n+#   BPFTOOL=\u003cbpftool\u003e gen_bpf_skel.sh --name NAME --skel OUT [options] OBJ...\n+#\n+#   --name NAME       skeleton object name (\"name NAME\" for bpftool)\n+#   --skel OUT        output header (\"foo.skel.h\" or \"foo.lskel.h\")\n+#   --subskel OUT     also generate a subskeleton header into OUT\n+#   --lskel           generate a light skeleton (bpftool gen skeleton -L)\n+#   --sign 'ARGS'     extra \"gen skeleton\" args for signing (-S -k .. -i ..)\n+#   --tag TAG         build-log tag, e.g. \"test_progs-no_alu32\"\n+#   --infix STR       intermediate file infix (default: linked). Callers\n+#                     generating both a .skel.h and a .lskel.h from the\n+#                     same .bpf.o MUST use distinct infixes, or parallel\n+#                     builds race on the intermediate files.\n+#   --no-determinism-check\n+#                     link the inputs once instead of three times.\n+#                     By default the object is re-linked twice more and\n+#                     the 2nd and 3rd results compared, as a regression\n+#                     test for the determinism of \"bpftool gen object\".\n+#\n+# The bpftool binary is taken from $BPFTOOL (default: bpftool from PATH).\n+# On failure all outputs and intermediates are removed and the script\n+# exits non-zero; permissive-mode skipping is the caller's business\n+# (see skip_on_fail in Makefile.buildvars).\n+\n+set -u\n+\n+bpftool=${BPFTOOL:-bpftool}\n+name='' skel='' subskel='' tag='' sign_args='' infix=linked\n+lskel=0 det_check=1\n+\n+while [ $# -gt 0 ]; do\n+\tcase \"$1\" in\n+\t--name)\t\tname=$2; shift 2 ;;\n+\t--skel)\t\tskel=$2; shift 2 ;;\n+\t--subskel)\tsubskel=$2; shift 2 ;;\n+\t--tag)\t\ttag=$2; shift 2 ;;\n+\t--infix)\tinfix=$2; shift 2 ;;\n+\t--sign)\t\tsign_args=$2; shift 2 ;;\n+\t--lskel)\tlskel=1; shift ;;\n+\t--no-determinism-check) det_check=0; shift ;;\n+\t--)\t\tshift; break ;;\n+\t-*)\t\techo \"$0: unknown option: $1\" \u003e\u00262; exit 1 ;;\n+\t*)\t\tbreak ;;\n+\tesac\n+done\n+\n+if [ -z \"$name\" ] || [ -z \"$skel\" ] || [ $# -eq 0 ]; then\n+\techo \"usage: $0 --name NAME --skel OUT [options] OBJ...\" \u003e\u00262\n+\texit 1\n+fi\n+\n+base=${skel%.skel.h}\n+base=${base%.lskel.h}\n+t1=$base.${infix}1.o\n+t2=$base.${infix}2.o\n+t3=$base.${infix}3.o\n+\n+fail() {\n+\trm -f \"$skel\" ${subskel:+\"$subskel\"} \"$t1\" \"$t2\" \"$t3\"\n+\texit 1\n+}\n+\n+if [ $# -gt 1 ]; then\n+\tprintf '  %-12s %s\\n' 'LINK-BPF' \"${tag:+[$tag] }$(basename \"$base\").bpf.o\" \u003e\u00262\n+fi\n+printf '  %-12s %s\\n' 'GEN-SKEL' \"${tag:+[$tag] }$(basename \"$skel\")\" \u003e\u00262\n+\n+\"$bpftool\" gen object \"$t1\" \"$@\" || fail\n+final=$t1\n+if [ \"$det_check\" -eq 1 ]; then\n+\t\"$bpftool\" gen object \"$t2\" \"$t1\" || fail\n+\t\"$bpftool\" gen object \"$t3\" \"$t2\" || fail\n+\tif ! cmp -s \"$t2\" \"$t3\"; then\n+\t\techo \"$0: bpftool gen object is not deterministic for $skel\" \u003e\u00262\n+\t\tfail\n+\tfi\n+\tfinal=$t3\n+fi\n+\n+flags=\n+[ \"$lskel\" -eq 1 ] \u0026\u0026 flags=-L\n+# $sign_args intentionally unquoted: it is a list of bpftool arguments\n+# shellcheck disable=SC2086\n+\"$bpftool\" gen skeleton $sign_args $flags \"$final\" name \"$name\" \u003e \"$skel\" || fail\n+\n+if [ -n \"$subskel\" ]; then\n+\t\"$bpftool\" gen subskeleton \"$final\" name \"$name\" \u003e \"$subskel\" || fail\n+fi\n+\n+rm -f \"$t1\" \"$t2\" \"$t3\"\n+exit 0\n"
}

2/1 2026/07/22 13:29 agent patch-evaluator
0m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch only modifies the build system for BPF selftests (Makefiles and a shell script). It does not make any functional changes to the kernel code.",
  "WorthFuzzing": false
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Do not list too generic configs (we already have them enabled). Only list those that
   specifically cover the modified code. List them in the EnableConfigs output array,
   and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit 0e0ca90bb7fa373ec8f7e4ff1764f81eabcca966
Author: syz-cluster <triage@syzkaller.com>
Date:   Wed Jul 22 13:29:21 2026 +0000

    syz-cluster: applied patch under review

diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index b642ee489ea640..a3ab525f7dfabb 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -1,96 +1,44 @@
 # SPDX-License-Identifier: GPL-2.0
+#
+# BPF selftests build.
+#
+# The build is split in two layers:
+#
+#   Makefile (this file)
+#       Everything that exists once: knobs and toolchain setup, libbpf /
+#       bpftool / resolve_btfids sub-builds, vmlinux.h, signing keys,
+#       generated tests.h headers, shared helper objects, standalone test
+#       binaries (bench, veristat, test_verifier, xsk*, ...), kernel test
+#       modules, docs, and the kselftest lib.mk run/install/clean
+#       contract.
+#
+#   Makefile.runner
+#       One test-runner instance (test_progs, test_progs-<flavor>,
+#       test_maps): BPF objects, skeleton headers, test objects and the
+#       final runner binary. Invoked from here once per instance; each
+#       invocation is an independent single-flavor namespace, so the
+#       runner is plain make without $$-escaping or eval.
+#
+#   gen_bpf_skel.sh
+#       Links BPF objects via bpftool and generates (light) skeletons;
+#       one script instead of four near-identical make recipes.
+#
+# Shared definitions (paths, flags, permissive-mode helpers) live in
+# Makefile.buildvars, included by both layers.
+
 include ../../../build/Build.include
 include ../../../scripts/Makefile.arch
 include ../../../scripts/Makefile.include
 
-CXX ?= $(CROSS_COMPILE)g++
-OBJCOPY ?= $(CROSS_COMPILE)objcopy
-
-CURDIR := $(abspath .)
-TOOLSDIR := $(abspath ../../..)
-LIBDIR := $(TOOLSDIR)/lib
-BPFDIR := $(LIBDIR)/bpf
-TOOLSINCDIR := $(TOOLSDIR)/include
-TOOLSARCHINCDIR := $(TOOLSDIR)/arch/$(SRCARCH)/include
-BPFTOOLDIR := $(TOOLSDIR)/bpf/bpftool
-APIDIR := $(TOOLSINCDIR)/uapi
-ifneq ($(O),)
-GENDIR := $(O)/include/generated
-else
-GENDIR := $(abspath ../../../../include/generated)
-endif
-GENHDR := $(GENDIR)/autoconf.h
-PKG_CONFIG ?= $(CROSS_COMPILE)pkg-config
-
-ifneq ($(wildcard $(GENHDR)),)
-  GENFLAGS := -DHAVE_GENHDR
+# These probes gate the flavor list, which must be final before lib.mk is
+# included; keep values consistent with Makefile.buildvars.
+ifeq ($(origin BPF_GCC),undefined)
+BPF_GCC := $(shell command -v bpf-gcc;)
 endif
-
-BPF_GCC		?= $(shell command -v bpf-gcc;)
-ifdef ASAN
-SAN_CFLAGS 	?= -fsanitize=address -fno-omit-frame-pointer
-else
-SAN_CFLAGS	?=
-endif
-SAN_LDFLAGS	?= $(SAN_CFLAGS)
-RELEASE		?=
-OPT_FLAGS	?= $(if $(RELEASE),-O2,-O0)
-
-LIBELF_CFLAGS	:= $(shell $(PKG_CONFIG) libelf --cflags 2>/dev/null)
-LIBELF_LIBS	:= $(shell $(PKG_CONFIG) libelf --libs 2>/dev/null || echo -lelf)
-
-SKIP_DOCS	?=
-SKIP_LLVM	?=
-SKIP_LIBBFD	?=
-SKIP_CRYPTO	?=
-
-# When BPF_STRICT_BUILD is 1, any BPF object, skeleton, test object, or
-# benchmark compilation failure is fatal. Set to 0 to tolerate failures
-# and continue building the remaining tests.
-BPF_STRICT_BUILD ?= 1
-PERMISSIVE := $(filter 0,$(BPF_STRICT_BUILD))
-
-ifeq ($(srctree),)
-srctree := $(patsubst %/,%,$(dir $(CURDIR)))
-srctree := $(patsubst %/,%,$(dir $(srctree)))
-srctree := $(patsubst %/,%,$(dir $(srctree)))
-srctree := $(patsubst %/,%,$(dir $(srctree)))
-endif
-
-COMMON_CFLAGS = -g $(OPT_FLAGS) -rdynamic -std=gnu11				\
-	  -Wall -Werror -fno-omit-frame-pointer				\
-	  -Wno-unused-but-set-variable					\
-	  $(GENFLAGS) $(SAN_CFLAGS) $(LIBELF_CFLAGS)			\
-	  -I$(CURDIR) -I$(INCLUDE_DIR) -I$(GENDIR) -I$(LIBDIR)		\
-	  -I$(TOOLSINCDIR) -I$(TOOLSARCHINCDIR) -I$(APIDIR) -I$(OUTPUT)	\
-	  -I$(CURDIR)/libarena/include
-LDFLAGS += $(SAN_LDFLAGS)
-LDLIBS += $(LIBELF_LIBS) -lz -lrt -lpthread
-
-PCAP_CFLAGS	:= $(shell $(PKG_CONFIG) --cflags libpcap 2>/dev/null && echo "-DTRAFFIC_MONITOR=1")
-PCAP_LIBS	:= $(shell $(PKG_CONFIG) --libs libpcap 2>/dev/null)
-LDLIBS += $(PCAP_LIBS)
-CFLAGS += $(COMMON_CFLAGS) $(PCAP_CFLAGS)
-
-# Some utility functions use LLVM libraries
-jit_disasm_helpers.c-CFLAGS = $(LLVM_CFLAGS)
-
-ifneq ($(LLVM),)
-# Silence some warnings when compiled with clang
-CFLAGS += -Wno-unused-command-line-argument
-endif
-
-# Check whether bpf cpu=v4 is supported or not by clang
 ifneq ($(shell $(CLANG) --target=bpf -mcpu=help 2>&1 | grep 'v4'),)
 CLANG_CPUV4 := 1
 endif
 
-# Check whether clang supports BPF address sanitizer (requires LLVM 22+)
-CLANG_HAS_ARENA_ASAN := $(shell echo 'int x;' | \
-	$(CLANG) --target=bpf -fsanitize=kernel-address \
-	-mllvm -asan-shadow-addr-space=1 \
-	-x c -c - -o /dev/null 2>/dev/null && echo 1)
-
 # Order correspond to 'make run_tests' order
 TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_progs \
 	test_sockmap \
@@ -102,16 +50,6 @@ TEST_INST_SUBDIRS := no_alu32
 ifneq ($(BPF_GCC),)
 TEST_GEN_PROGS += test_progs-bpf_gcc
 TEST_INST_SUBDIRS += bpf_gcc
-
-# The following tests contain C code that, although technically legal,
-# triggers GCC warnings that cannot be disabled: declaration of
-# anonymous struct types in function parameter lists.
-progs/btf_dump_test_case_bitfields.c-bpf_gcc-CFLAGS := -Wno-error
-progs/btf_dump_test_case_namespacing.c-bpf_gcc-CFLAGS := -Wno-error
-progs/btf_dump_test_case_packing.c-bpf_gcc-CFLAGS := -Wno-error
-progs/btf_dump_test_case_padding.c-bpf_gcc-CFLAGS := -Wno-error
-progs/btf_dump_test_case_syntax.c-bpf_gcc-CFLAGS := -Wno-error
-
 endif
 
 ifneq ($(CLANG_CPUV4),)
@@ -132,10 +70,6 @@ TEST_PROGS := test_kmod.sh \
 TEST_PROGS_EXTENDED := \
 	ima_setup.sh verify_sig_setup.sh
 
-TEST_KMODS := bpf_testmod.ko bpf_test_no_cfi.ko bpf_test_modorder_x.ko \
-	bpf_test_modorder_y.ko bpf_test_rqspinlock.ko
-TEST_KMOD_TARGETS = $(addprefix $(OUTPUT)/,$(TEST_KMODS))
-
 # Compile but not part of 'make run_tests'
 TEST_GEN_PROGS_EXTENDED = \
 	bench \
@@ -148,11 +82,10 @@ TEST_GEN_PROGS_EXTENDED = \
 	xdp_synproxy \
 	xskxceiver
 
-TEST_GEN_FILES += $(TEST_KMODS) liburandom_read.so urandom_read sign-file uprobe_multi
+TEST_KMODS ?= bpf_testmod.ko bpf_test_no_cfi.ko bpf_test_modorder_x.ko \
+	bpf_test_modorder_y.ko bpf_test_rqspinlock.ko
 
-ifneq ($(V),1)
-submake_extras := feature_display=0
-endif
+TEST_GEN_FILES += $(TEST_KMODS) liburandom_read.so urandom_read sign-file uprobe_multi
 
 # override lib.mk's default rules
 OVERRIDE_TARGETS := 1
@@ -171,7 +104,7 @@ endef
 include ../lib.mk
 
 NON_CHECK_FEAT_TARGETS := clean docs-clean emit_tests
-CHECK_FEAT := $(filter-out $(NON_CHECK_FEAT_TARGETS),$(or $(MAKECMDGOALS), "none"))
+CHECK_FEAT := $(filter-out $(NON_CHECK_FEAT_TARGETS),$(or $(MAKECMDGOALS),all))
 ifneq ($(CHECK_FEAT),)
 FEATURE_USER := .selftests
 FEATURE_TESTS := llvm
@@ -188,6 +121,8 @@ override OUTPUT := $(patsubst %/,%,$(OUTPUT))
 endif
 endif
 
+include Makefile.buildvars
+
 ifneq ($(SKIP_LLVM),1)
 ifeq ($(feature-llvm),1)
   LLVM_CFLAGS  += -DHAVE_LLVM_SUPPORT
@@ -213,36 +148,11 @@ ifeq ($(feature-llvm),1)
 endif
 endif
 
-SCRATCH_DIR := $(OUTPUT)/tools
-BUILD_DIR := $(SCRATCH_DIR)/build
-INCLUDE_DIR := $(SCRATCH_DIR)/include
-BPFOBJ := $(BUILD_DIR)/libbpf/libbpf.a
-ifneq ($(CROSS_COMPILE),)
-HOST_BUILD_DIR		:= $(BUILD_DIR)/host
-HOST_SCRATCH_DIR	:= $(OUTPUT)/host-tools
-HOST_INCLUDE_DIR	:= $(HOST_SCRATCH_DIR)/include
-else
-HOST_BUILD_DIR		:= $(BUILD_DIR)
-HOST_SCRATCH_DIR	:= $(SCRATCH_DIR)
-HOST_INCLUDE_DIR	:= $(INCLUDE_DIR)
-endif
-HOST_BPFOBJ := $(HOST_BUILD_DIR)/libbpf/libbpf.a
-RESOLVE_BTFIDS := $(HOST_BUILD_DIR)/resolve_btfids/resolve_btfids
-VMLINUX_BTF_PATHS ?= $(if $(O),$(O)/vmlinux)				\
-		     $(if $(KBUILD_OUTPUT),$(KBUILD_OUTPUT)/vmlinux)	\
-		     ../../../../vmlinux				\
-		     /sys/kernel/btf/vmlinux				\
-		     /boot/vmlinux-$(shell uname -r)
-VMLINUX_BTF ?= $(abspath $(firstword $(wildcard $(VMLINUX_BTF_PATHS))))
-ifeq ($(VMLINUX_BTF),)
-$(error Cannot find a vmlinux for VMLINUX_BTF at any of "$(VMLINUX_BTF_PATHS)")
-endif
-
 # Define simple and short `make test_progs`, `make test_maps`, etc targets
 # to build individual tests.
 # NOTE: Semicolon at the end is critical to override lib.mk's default static
 # rule for binaries.
-$(notdir $(TEST_GEN_PROGS) $(TEST_KMODS)				\
+$(notdir $(TEST_GEN_PROGS) $(TEST_KMOD_TARGETS)				\
 	 $(TEST_GEN_PROGS_EXTENDED)): %: $(OUTPUT)/% ;
 
 # sort removes libbpf duplicates when not cross-building
@@ -256,11 +166,14 @@ $(MAKE_DIRS):
 
 $(OUTPUT)/%.o: %.c
 	$(call msg,CC,,$@)
-	$(Q)$(CC) $(CFLAGS) -c $(filter %.c,$^) $(LDLIBS) -o $@
+	$(Q)$(CC) $(CFLAGS) -c $(filter %.c,$^) -o $@
 
+# Headers may appear among the prerequisites for dependency tracking;
+# they must not reach the clang/gcc command line (clang treats a bare .h
+# argument as a precompiled-header job and refuses -o).
 $(OUTPUT)/%:%.c
 	$(call msg,BINARY,,$@)
-	$(Q)$(LINK.c) $^ $(LDLIBS) -o $@
+	$(Q)$(LINK.c) $(filter-out %.h,$^) $(LDLIBS) -o $@
 
 # LLVM's ld.lld doesn't support all the architectures, so use it only on x86
 ifeq ($(SRCARCH),$(filter $(SRCARCH),x86 riscv))
@@ -297,6 +210,13 @@ $(OUTPUT)/sign-file: ../../../../scripts/sign-file.c
 		  $< -o $@ \
 		  $(shell $(PKG_CONFIG) --libs libcrypto 2> /dev/null || echo -lcrypto)
 
+# Linking uprobe_multi can fail due to relocation overflows on mips.
+$(OUTPUT)/uprobe_multi: CFLAGS += $(if $(filter mips, $(ARCH)),-mxgot)
+$(OUTPUT)/uprobe_multi: uprobe_multi.c uprobe_multi.ld
+	$(call msg,BINARY,,$@)
+	$(Q)$(CC) $(CFLAGS) -Wl,-T,uprobe_multi.ld -O0 $(LDFLAGS) 	\
+		$(filter-out %.ld,$^) $(LDLIBS) -o $@
+
 # This should really be a grouped target, but make versions before 4.3 don't
 # support that for regular rules. However, pattern matching rules are implicitly
 # treated as grouped even with older versions of make, so as a workaround, the
@@ -313,40 +233,18 @@ $(TEST_KMOD_TARGETS): $(addprefix test_kmods/,$(TEST_KMODS))
 	$(call msg,MOD,,$@)
 	$(Q)$(if $(PERMISSIVE),if [ -f test_kmods/$(@F) ]; then )cp test_kmods/$(@F) $@$(if $(PERMISSIVE),; fi)
 
-
-DEFAULT_BPFTOOL := $(HOST_SCRATCH_DIR)/sbin/bpftool
-ifneq ($(CROSS_COMPILE),)
-CROSS_BPFTOOL := $(SCRATCH_DIR)/sbin/bpftool
-TRUNNER_BPFTOOL := $(CROSS_BPFTOOL)
-USE_BOOTSTRAP := ""
-else
-TRUNNER_BPFTOOL := $(DEFAULT_BPFTOOL)
-USE_BOOTSTRAP := "bootstrap/"
-endif
-
 TEST_GEN_PROGS_EXTENDED += $(TRUNNER_BPFTOOL)
 
 $(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED): $(BPFOBJ)
 
-TESTING_HELPERS	:= $(OUTPUT)/testing_helpers.o
-CGROUP_HELPERS	:= $(OUTPUT)/cgroup_helpers.o
-UNPRIV_HELPERS  := $(OUTPUT)/unpriv_helpers.o
-TRACE_HELPERS	:= $(OUTPUT)/trace_helpers.o
-JSON_WRITER		:= $(OUTPUT)/json_writer.o
-CAP_HELPERS	:= $(OUTPUT)/cap_helpers.o
-NETWORK_HELPERS := $(OUTPUT)/network_helpers.o
-
-$(OUTPUT)/test_sockmap: $(CGROUP_HELPERS) $(TESTING_HELPERS)
-$(OUTPUT)/test_tcpnotify_user: $(CGROUP_HELPERS) $(TESTING_HELPERS) $(TRACE_HELPERS)
-$(OUTPUT)/test_sock_fields: $(CGROUP_HELPERS) $(TESTING_HELPERS)
-$(OUTPUT)/test_tag: $(TESTING_HELPERS)
-$(OUTPUT)/test_lirc_mode2_user: $(TESTING_HELPERS)
-$(OUTPUT)/flow_dissector_load: $(TESTING_HELPERS)
-$(OUTPUT)/test_maps: $(TESTING_HELPERS)
-$(OUTPUT)/test_verifier: $(TESTING_HELPERS) $(CAP_HELPERS) $(UNPRIV_HELPERS)
-$(OUTPUT)/xsk.o: $(BPFOBJ)
-
-BPFTOOL ?= $(DEFAULT_BPFTOOL)
+# ---------------------------------------------------------------------
+# Tool sub-builds: bpftool and libbpf (host and, when cross-compiling,
+# target variants) and resolve_btfids.
+#
+# Note the deliberate asymmetry: the host libbpf builds without
+# sanitizers and with the host toolchain.
+# ---------------------------------------------------------------------
+
 $(DEFAULT_BPFTOOL): $(wildcard $(BPFTOOLDIR)/*.[ch] $(BPFTOOLDIR)/Makefile)    \
 		    $(HOST_BPFOBJ) | $(HOST_BUILD_DIR)/bpftool
 	$(Q)$(MAKE) $(submake_extras)  -C $(BPFTOOLDIR)			       \
@@ -377,20 +275,6 @@ $(CROSS_BPFTOOL): $(wildcard $(BPFTOOLDIR)/*.[ch] $(BPFTOOLDIR)/Makefile)	\
 		    prefix= DESTDIR=$(SCRATCH_DIR)/ install-bin
 endif
 
-ifneq ($(SKIP_DOCS),1)
-all: docs
-endif
-
-docs:
-	$(Q)RST2MAN_OPTS="--exit-status=1" $(MAKE) $(submake_extras)	\
-	            -f Makefile.docs					\
-	            prefix= OUTPUT=$(OUTPUT)/ DESTDIR=$(OUTPUT)/ $@
-
-docs-clean:
-	$(Q)$(MAKE) $(submake_extras)					\
-	            -f Makefile.docs					\
-	            prefix= OUTPUT=$(OUTPUT)/ DESTDIR=$(OUTPUT)/ $@
-
 $(BPFOBJ): $(wildcard $(BPFDIR)/*.[ch] $(BPFDIR)/Makefile)		       \
 	   $(APIDIR)/linux/bpf.h					       \
 	   | $(BUILD_DIR)/libbpf
@@ -412,19 +296,6 @@ $(HOST_BPFOBJ): $(wildcard $(BPFDIR)/*.[ch] $(BPFDIR)/Makefile)		       \
 		    DESTDIR=$(HOST_SCRATCH_DIR)/ prefix= all install_headers
 endif
 
-# vmlinux.h is first dumped to a temporary file and then compared to
-# the previous version. This helps to avoid unnecessary re-builds of
-# $(TRUNNER_BPF_OBJS)
-$(INCLUDE_DIR)/vmlinux.h: $(VMLINUX_BTF) $(BPFTOOL) | $(INCLUDE_DIR)
-ifeq ($(VMLINUX_H),)
-	$(call msg,GEN,,$@)
-	$(Q)$(BPFTOOL) btf dump file $(VMLINUX_BTF) format c > $(INCLUDE_DIR)/.vmlinux.h.tmp
-	$(Q)cmp -s $(INCLUDE_DIR)/.vmlinux.h.tmp $@ || mv $(INCLUDE_DIR)/.vmlinux.h.tmp $@
-else
-	$(call msg,CP,,$@)
-	$(Q)cp "$(VMLINUX_H)" $@
-endif
-
 $(RESOLVE_BTFIDS): $(HOST_BPFOBJ) | $(HOST_BUILD_DIR)/resolve_btfids	\
 		       $(TOOLSDIR)/bpf/resolve_btfids/main.c	\
 		       $(TOOLSDIR)/lib/rbtree.c			\
@@ -439,374 +310,38 @@ $(RESOLVE_BTFIDS): $(HOST_BPFOBJ) | $(HOST_BUILD_DIR)/resolve_btfids	\
 		HOSTPKG_CONFIG='$(PKG_CONFIG)' \
 		OUTPUT=$(HOST_BUILD_DIR)/resolve_btfids/ BPFOBJ=$(HOST_BPFOBJ)
 
-# Get Clang's default includes on this system, as opposed to those seen by
-# '--target=bpf'. This fixes "missing" files on some architectures/distros,
-# such as asm/byteorder.h, asm/socket.h, asm/sockios.h, sys/cdefs.h etc.
-#
-# Use '-idirafter': Don't interfere with include mechanics except where the
-# build would have failed anyways.
-define get_sys_includes
-$(shell $(1) $(2) -v -E - </dev/null 2>&1 \
-	| sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') \
-$(shell $(1) $(2) -dM -E - </dev/null | grep '__riscv_xlen ' | awk '{printf("-D__riscv_xlen=%d -D__BITS_PER_LONG=%d", $$3, $$3)}') \
-$(shell $(1) $(2) -dM -E - </dev/null | grep '__loongarch_grlen ' | awk '{printf("-D__BITS_PER_LONG=%d", $$3)}') \
-$(shell $(1) $(2) -dM -E - </dev/null | grep -E 'MIPS(EL|EB)|_MIPS_SZ(PTR|LONG) |_MIPS_SIM |_ABI(O32|N32|64) ' | awk '{printf("-D%s=%s ", $$2, $$3)}')
-endef
-
-# Determine target endianness.
-IS_LITTLE_ENDIAN := $(shell $(CC) -dM -E - </dev/null | \
-			grep 'define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__')
-MENDIAN:=$(if $(IS_LITTLE_ENDIAN),-mlittle-endian,-mbig-endian)
-BPF_TARGET_ENDIAN:=$(if $(IS_LITTLE_ENDIAN),--target=bpfel,--target=bpfeb)
-
-ifneq ($(CROSS_COMPILE),)
-CLANG_TARGET_ARCH = --target=$(notdir $(CROSS_COMPILE:%-=%))
-endif
-
-CLANG_SYS_INCLUDES = $(call get_sys_includes,$(CLANG),$(CLANG_TARGET_ARCH))
-BPF_CFLAGS = -g -Wall -Werror -D__TARGET_ARCH_$(SRCARCH) $(MENDIAN)	\
-	     -I$(INCLUDE_DIR) -I$(CURDIR) -I$(APIDIR)			\
-	     -I$(CURDIR)/libarena/include				\
-	     -I$(abspath $(OUTPUT)/../usr/include)			\
-	     -std=gnu11		 					\
-	     -fno-strict-aliasing 					\
-	     -Wno-microsoft-anon-tag					\
-	     -fms-extensions						\
-	     -Wno-compare-distinct-pointer-types			\
-	     -Wno-initializer-overrides					\
-	     #
-# TODO: enable me -Wsign-compare
-
-CLANG_CFLAGS = $(CLANG_SYS_INCLUDES)
-
-$(OUTPUT)/test_l4lb_noinline.o: BPF_CFLAGS += -fno-inline
-$(OUTPUT)/test_xdp_noinline.o: BPF_CFLAGS += -fno-inline
-
-$(OUTPUT)/flow_dissector_load.o: flow_dissector_load.h
-$(OUTPUT)/cgroup_getset_retval_hooks.o: cgroup_getset_retval_hooks.h
-
-# Build BPF object using Clang
-# $1 - input .c file
-# $2 - output .o file
-# $3 - CFLAGS
-# $4 - binary name
-define CLANG_BPF_BUILD_RULE
-	$(call msg,CLNG-BPF,$4,$2)
-	$(Q)$(CLANG) $3 -O2 $(BPF_TARGET_ENDIAN) -c $1 -mcpu=v3 -o $2 $(if $(PERMISSIVE),|| \
-		($(RM) $2; printf '  %-12s %s\n' 'SKIP-BPF' '$(notdir $2)' 1>&2))
-endef
-# Similar to CLANG_BPF_BUILD_RULE, but with disabled alu32
-define CLANG_NOALU32_BPF_BUILD_RULE
-	$(call msg,CLNG-BPF,$4,$2)
-	$(Q)$(CLANG) $3 -O2 $(BPF_TARGET_ENDIAN) -c $1 -mcpu=v2 -o $2 $(if $(PERMISSIVE),|| \
-		($(RM) $2; printf '  %-12s %s\n' 'SKIP-BPF' '$(notdir $2)' 1>&2))
-endef
-# Similar to CLANG_BPF_BUILD_RULE, but with cpu-v4
-define CLANG_CPUV4_BPF_BUILD_RULE
-	$(call msg,CLNG-BPF,$4,$2)
-	$(Q)$(CLANG) $3 -O2 $(BPF_TARGET_ENDIAN) -c $1 -mcpu=v4 -o $2 $(if $(PERMISSIVE),|| \
-		($(RM) $2; printf '  %-12s %s\n' 'SKIP-BPF' '$(notdir $2)' 1>&2))
-endef
-# Build BPF object using GCC
-define GCC_BPF_BUILD_RULE
-	$(call msg,GCC-BPF,$4,$2)
-	$(Q)$(BPF_GCC) $3 -DBPF_NO_PRESERVE_ACCESS_INDEX -Wno-attributes -O2 -c $1 -o $2 $(if $(PERMISSIVE),|| \
-		($(RM) $2; printf '  %-12s %s\n' 'SKIP-BPF' '$(notdir $2)' 1>&2))
-endef
-
-SKEL_BLACKLIST := btf__% test_pinning_invalid.c test_sk_assign.c
-
-LINKED_SKELS := test_static_linked.skel.h linked_funcs.skel.h		\
-		linked_vars.skel.h linked_maps.skel.h 			\
-		test_subskeleton.skel.h test_subskeleton_lib.skel.h	\
-		test_usdt.skel.h tracing_multi.skel.h			\
-		tracing_multi_module.skel.h				\
-		tracing_multi_intersect.skel.h				\
-		tracing_multi_session.skel.h
-
-LSKELS := fexit_sleep.c trace_printk.c trace_vprintk.c map_ptr_kern.c 	\
-	core_kern.c core_kern_overflow.c test_ringbuf.c			\
-	test_ringbuf_n.c test_ringbuf_map_key.c test_ringbuf_write.c    \
-	test_ringbuf_overwrite.c
-
-LSKELS_SIGNED := fentry_test.c fexit_test.c atomics.c
-
-# Generate both light skeleton and libbpf skeleton for these
-LSKELS_EXTRA := test_ksyms_module.c test_ksyms_weak.c kfunc_call_test.c \
-	kfunc_call_test_subprog.c
-SKEL_BLACKLIST += $$(LSKELS) $$(LSKELS_SIGNED)
-
-test_static_linked.skel.h-deps := test_static_linked1.bpf.o test_static_linked2.bpf.o
-linked_funcs.skel.h-deps := linked_funcs1.bpf.o linked_funcs2.bpf.o
-linked_vars.skel.h-deps := linked_vars1.bpf.o linked_vars2.bpf.o
-linked_maps.skel.h-deps := linked_maps1.bpf.o linked_maps2.bpf.o
-# In the subskeleton case, we want the test_subskeleton_lib.subskel.h file
-# but that's created as a side-effect of the skel.h generation.
-test_subskeleton.skel.h-deps := test_subskeleton_lib2.bpf.o test_subskeleton_lib.bpf.o test_subskeleton.bpf.o
-test_subskeleton_lib.skel.h-deps := test_subskeleton_lib2.bpf.o test_subskeleton_lib.bpf.o
-test_usdt.skel.h-deps := test_usdt.bpf.o test_usdt_multispec.bpf.o
-xsk_xdp_progs.skel.h-deps := xsk_xdp_progs.bpf.o
-xdp_hw_metadata.skel.h-deps := xdp_hw_metadata.bpf.o
-xdp_features.skel.h-deps := xdp_features.bpf.o
-tracing_multi.skel.h-deps := tracing_multi_attach.bpf.o tracing_multi_check.bpf.o
-tracing_multi_module.skel.h-deps := tracing_multi_attach_module.bpf.o tracing_multi_check.bpf.o
-tracing_multi_intersect.skel.h-deps := tracing_multi_intersect_attach.bpf.o tracing_multi_check.bpf.o
-tracing_multi_session.skel.h-deps := tracing_multi_session_attach.bpf.o tracing_multi_check.bpf.o
-
-LINKED_BPF_OBJS := $(foreach skel,$(LINKED_SKELS),$($(skel)-deps))
-LINKED_BPF_SRCS := $(patsubst %.bpf.o,%.c,$(LINKED_BPF_OBJS))
-
-HEADERS_FOR_BPF_OBJS := $(wildcard $(BPFDIR)/*.bpf.h)		\
-			$(wildcard $(CURDIR)/libarena/include/*.[ch])	\
-			$(addprefix $(BPFDIR)/,	bpf_core_read.h	\
-			                        bpf_endian.h	\
-						bpf_helpers.h	\
-			                        bpf_tracing.h)
-
-# Set up extra TRUNNER_XXX "temporary" variables in the environment (relies on
-# $eval()) and pass control to DEFINE_TEST_RUNNER_RULES.
-# Parameters:
-# $1 - test runner base binary name (e.g., test_progs)
-# $2 - test runner extra "flavor" (e.g., no_alu32, cpuv4, bpf_gcc, etc)
-define DEFINE_TEST_RUNNER
-
-LSKEL_SIGN := -S -k $(PRIVATE_KEY) -i $(VERIFICATION_CERT)
-TRUNNER_OUTPUT := $(OUTPUT)$(if $2,/)$2
-TRUNNER_BINARY := $1$(if $2,-)$2
-TRUNNER_TEST_OBJS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.test.o,	\
-				 $$(notdir $$(wildcard $(TRUNNER_TESTS_DIR)/*.c)))
-TRUNNER_EXTRA_OBJS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.o,		\
-				 $$(filter %.c,$(TRUNNER_EXTRA_SOURCES)))
-TRUNNER_LIB_OBJS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.o,		\
-				 $$(filter %.c,$(TRUNNER_LIB_SOURCES)))
-TRUNNER_EXTRA_HDRS := $$(filter %.h,$(TRUNNER_EXTRA_SOURCES))
-TRUNNER_TESTS_HDR := $(TRUNNER_TESTS_DIR)/tests.h
-TRUNNER_BPF_SRCS := $$(notdir $$(wildcard $(TRUNNER_BPF_PROGS_DIR)/*.c))
-TRUNNER_BPF_OBJS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.bpf.o, $$(TRUNNER_BPF_SRCS))
-TRUNNER_BPF_SKELS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.skel.h,	\
-				 $$(filter-out $(SKEL_BLACKLIST) $(LINKED_BPF_SRCS),\
-					       $$(TRUNNER_BPF_SRCS)))
-TRUNNER_BPF_LSKELS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.lskel.h, $$(LSKELS) $$(LSKELS_EXTRA))
-TRUNNER_BPF_SKELS_LINKED := $$(addprefix $$(TRUNNER_OUTPUT)/,$(LINKED_SKELS))
-TRUNNER_BPF_LSKELS_SIGNED := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.lskel.h, $$(LSKELS_SIGNED))
-TEST_GEN_FILES += $$(TRUNNER_BPF_OBJS)
-
-# Evaluate rules now with extra TRUNNER_XXX variables above already defined
-$$(eval $$(call DEFINE_TEST_RUNNER_RULES,$1,$2))
-
-endef
-
-# Using TRUNNER_XXX variables, provided by callers of DEFINE_TEST_RUNNER and
-# set up by DEFINE_TEST_RUNNER itself, create test runner build rules with:
-# $1 - test runner base binary name (e.g., test_progs)
-# $2 - test runner extra "flavor" (e.g., no_alu32, cpuv4, bpf_gcc, etc)
-define DEFINE_TEST_RUNNER_RULES
-
-# Permissive build behaviour (skip-on-failure compile, partial-link) only
-# applies to test_progs and its flavors; runners that use strong cross-object
-# references (e.g. test_maps) keep strict semantics even when permissive.
-# The check is inlined per-runner so $1 is substituted at $(call) time and
-# the result is baked into each rule's recipe.
-
-ifeq ($($(TRUNNER_OUTPUT)-dir),)
-$(TRUNNER_OUTPUT)-dir := y
-$(TRUNNER_OUTPUT):
-	$$(call msg,MKDIR,,$$@)
-	$(Q)mkdir -p $$@
-endif
-
-# ensure we set up BPF objects generation rule just once for a given
-# input/output directory combination
-ifeq ($($(TRUNNER_BPF_PROGS_DIR)$(if $2,-)$2-bpfobjs),)
-$(TRUNNER_BPF_PROGS_DIR)$(if $2,-)$2-bpfobjs := y
-$(TRUNNER_BPF_OBJS): $(TRUNNER_OUTPUT)/%.bpf.o:				\
-		     $(TRUNNER_BPF_PROGS_DIR)/%.c			\
-		     $(TRUNNER_BPF_PROGS_DIR)/*.h			\
-		     $$(INCLUDE_DIR)/vmlinux.h				\
-		     $(HEADERS_FOR_BPF_OBJS)				\
-		     | $(TRUNNER_OUTPUT) $$(BPFOBJ)
-	$$(call $(TRUNNER_BPF_BUILD_RULE),$$<,$$@,			\
-					  $(TRUNNER_BPF_CFLAGS)         \
-					  $$($$<-CFLAGS)		\
-					  $$($$<-$2-CFLAGS),$(TRUNNER_BINARY))
-
-$(TRUNNER_BPF_SKELS): %.skel.h: %.bpf.o $(BPFTOOL) | $(TRUNNER_OUTPUT)
-	$(Q)$(if $(PERMISSIVE),if [ ! -f $$< ]; then			\
-		$$(RM) $$@ $$(@:.skel.h=.subskel.h);			\
-		printf '  %-12s %s\n' 'SKIP-SKEL' '$$(notdir $$@)' 1>&2; \
-		exit 0;							\
-	fi;)								\
-	printf '  %-12s %s\n' 'GEN-SKEL' '[$(TRUNNER_BINARY)] $$(notdir $$@)' 1>&2; \
-	$$(BPFTOOL) gen object $$(<:.o=.linked1.o) $$< &&		\
-	$$(BPFTOOL) gen object $$(<:.o=.linked2.o) $$(<:.o=.linked1.o) && \
-	$$(BPFTOOL) gen object $$(<:.o=.linked3.o) $$(<:.o=.linked2.o) && \
-	diff $$(<:.o=.linked2.o) $$(<:.o=.linked3.o) &&		\
-	$$(BPFTOOL) gen skeleton $$(<:.o=.linked3.o) name $$(notdir $$(<:.bpf.o=)) > $$@ && \
-	$$(BPFTOOL) gen subskeleton $$(<:.o=.linked3.o) name $$(notdir $$(<:.bpf.o=)) > $$(@:.skel.h=.subskel.h) $(if $(PERMISSIVE),|| { \
-		$$(RM) $$@ $$(@:.skel.h=.subskel.h); \
-		printf '  %-12s %s\n' 'SKIP-SKEL' '$$(notdir $$@)' 1>&2; \
-	}) && \
-	rm -f $$(<:.o=.linked1.o) $$(<:.o=.linked2.o) $$(<:.o=.linked3.o)
-
-$(TRUNNER_BPF_LSKELS): %.lskel.h: %.bpf.o $(BPFTOOL) | $(TRUNNER_OUTPUT)
-	$(Q)$(if $(PERMISSIVE),if [ ! -f $$< ]; then			\
-		$$(RM) $$@;						\
-		printf '  %-12s %s\n' 'SKIP-SKEL' '$$(notdir $$@)' 1>&2; \
-		exit 0;							\
-	fi;)								\
-	printf '  %-12s %s\n' 'GEN-SKEL' '[$(TRUNNER_BINARY)] $$(notdir $$@)' 1>&2; \
-	$$(BPFTOOL) gen object $$(<:.o=.llinked1.o) $$< &&		\
-	$$(BPFTOOL) gen object $$(<:.o=.llinked2.o) $$(<:.o=.llinked1.o) && \
-	$$(BPFTOOL) gen object $$(<:.o=.llinked3.o) $$(<:.o=.llinked2.o) && \
-	diff $$(<:.o=.llinked2.o) $$(<:.o=.llinked3.o) &&		\
-	$$(BPFTOOL) gen skeleton -L $$(<:.o=.llinked3.o) name $$(notdir $$(<:.bpf.o=_lskel)) > $$@ $(if $(PERMISSIVE),|| { \
-		$$(RM) $$@; \
-		printf '  %-12s %s\n' 'SKIP-SKEL' '$$(notdir $$@)' 1>&2; \
-	}) && \
-	rm -f $$(<:.o=.llinked1.o) $$(<:.o=.llinked2.o) $$(<:.o=.llinked3.o)
-
-$(TRUNNER_BPF_LSKELS_SIGNED): %.lskel.h: %.bpf.o $(BPFTOOL) | $(TRUNNER_OUTPUT)
-	$(Q)$(if $(PERMISSIVE),if [ ! -f $$< ]; then			\
-		$$(RM) $$@;						\
-		printf '  %-12s %s\n' 'SKIP-SKEL' '$$(notdir $$@)' 1>&2; \
-		exit 0;							\
-	fi;)								\
-	printf '  %-12s %s\n' 'GEN-SKEL' '[$(TRUNNER_BINARY) (signed)] $$(notdir $$@)' 1>&2; \
-	$$(BPFTOOL) gen object $$(<:.o=.llinked1.o) $$< &&		\
-	$$(BPFTOOL) gen object $$(<:.o=.llinked2.o) $$(<:.o=.llinked1.o) && \
-	$$(BPFTOOL) gen object $$(<:.o=.llinked3.o) $$(<:.o=.llinked2.o) && \
-	diff $$(<:.o=.llinked2.o) $$(<:.o=.llinked3.o) &&		\
-	$$(BPFTOOL) gen skeleton $(LSKEL_SIGN) $$(<:.o=.llinked3.o) name $$(notdir $$(<:.bpf.o=_lskel)) > $$@ $(if $(PERMISSIVE),|| { \
-		$$(RM) $$@; \
-		printf '  %-12s %s\n' 'SKIP-SKEL' '$$(notdir $$@)' 1>&2; \
-	}) && \
-	rm -f $$(<:.o=.llinked1.o) $$(<:.o=.llinked2.o) $$(<:.o=.llinked3.o)
-
-$(LINKED_BPF_OBJS): %: $(TRUNNER_OUTPUT)/%
-
-# .SECONDEXPANSION here allows to correctly expand %-deps variables as prerequisites
-.SECONDEXPANSION:
-$(TRUNNER_BPF_SKELS_LINKED): $(TRUNNER_OUTPUT)/%: $$$$(%-deps) $(BPFTOOL) | $(TRUNNER_OUTPUT)
-	$(Q)$(if $(PERMISSIVE),for f in $$(addprefix $(TRUNNER_OUTPUT)/,$$($$(@F)-deps)); do \
-		if [ ! -f $$$$f ]; then						\
-			$$(RM) $$@ $$(@:.skel.h=.subskel.h);		\
-			printf '  %-12s %s\n' 'SKIP-SKEL' '$$(notdir $$@)' 1>&2; \
-			exit 0;							\
-		fi;								\
-	done;)									\
-	printf '  %-12s %s\n' 'LINK-BPF' '[$(TRUNNER_BINARY)] $$(notdir $$(@:.skel.h=.bpf.o))' 1>&2; \
-	$$(BPFTOOL) gen object $$(@:.skel.h=.linked1.o) $$(addprefix $(TRUNNER_OUTPUT)/,$$($$(@F)-deps)) && \
-	$$(BPFTOOL) gen object $$(@:.skel.h=.linked2.o) $$(@:.skel.h=.linked1.o) && \
-	$$(BPFTOOL) gen object $$(@:.skel.h=.linked3.o) $$(@:.skel.h=.linked2.o) && \
-	diff $$(@:.skel.h=.linked2.o) $$(@:.skel.h=.linked3.o) &&	\
-	printf '  %-12s %s\n' 'GEN-SKEL' '[$(TRUNNER_BINARY)] $$(notdir $$@)' 1>&2 && \
-	$$(BPFTOOL) gen skeleton $$(@:.skel.h=.linked3.o) name $$(notdir $$(@:.skel.h=)) > $$@ && \
-	$$(BPFTOOL) gen subskeleton $$(@:.skel.h=.linked3.o) name $$(notdir $$(@:.skel.h=)) > $$(@:.skel.h=.subskel.h) $(if $(PERMISSIVE),|| { \
-		$$(RM) $$@ $$(@:.skel.h=.subskel.h);			\
-		printf '  %-12s %s\n' 'SKIP-SKEL' '$$(notdir $$@)' 1>&2; \
-	}) &&									\
-	rm -f $$(@:.skel.h=.linked1.o) $$(@:.skel.h=.linked2.o) $$(@:.skel.h=.linked3.o)
-
-# When the compiler generates a %.d file, only skel basenames (not
-# full paths) are specified as prerequisites for corresponding %.o
-# file. vpath directives below instruct make to search for skel files
-# in TRUNNER_OUTPUT, if they are not present in the working directory.
-vpath %.skel.h $(TRUNNER_OUTPUT)
-vpath %.lskel.h $(TRUNNER_OUTPUT)
-vpath %.subskel.h $(TRUNNER_OUTPUT)
-
+ifneq ($(SKIP_DOCS),1)
+all: docs
 endif
 
-# ensure we set up tests.h header generation rule just once
-ifeq ($($(TRUNNER_TESTS_DIR)-tests-hdr),)
-$(TRUNNER_TESTS_DIR)-tests-hdr := y
-$(TRUNNER_TESTS_HDR): $(TRUNNER_TESTS_DIR)/*.c
-	$$(call msg,TEST-HDR,$(TRUNNER_BINARY),$$@)
-	$$(shell (echo '/* Generated header, do not edit */';					\
-		  sed -n -E 's/^void (serial_)?test_([a-zA-Z0-9_]+)\((void)?\).*/DEFINE_TEST(\2)/p'	\
-			$(TRUNNER_TESTS_DIR)/*.c | sort ;	\
-		 ) > $$@)
-endif
+docs:
+	$(Q)RST2MAN_OPTS="--exit-status=1" $(MAKE) $(submake_extras)	\
+	            -f Makefile.docs					\
+	            prefix= OUTPUT=$(OUTPUT)/ DESTDIR=$(OUTPUT)/ $@
 
-$(TRUNNER_OUTPUT)/resolve_btfids.test.o: $(RESOLVE_BTFIDS) $(TRUNNER_OUTPUT)/btf_data.bpf.o
-$(TRUNNER_OUTPUT)/resolve_btfids.test.o: private TEST_NEEDS_BTFIDS = 1
-
-# compile individual test files
-# Note: we cd into output directory to ensure embedded BPF object is found
-$(TRUNNER_TEST_OBJS): $(TRUNNER_OUTPUT)/%.test.o:			\
-		      $(TRUNNER_TESTS_DIR)/%.c				\
-		      | $(TRUNNER_OUTPUT)/%.test.d
-	$$(call msg,TEST-OBJ,$(TRUNNER_BINARY),$$@)
-	$(Q)(cd $$(@D) && $$(CC) -I. $$(CFLAGS) -MMD -MT $$@ -c $(CURDIR)/$$< $$(LDLIBS) -o $$(@F)) $(if $(filter test_progs%,$1),$(if $(PERMISSIVE),|| \
-		($(RM) $$@; printf '  %-12s %s\n' 'SKIP-TEST' '$$(notdir $$@)' 1>&2)))
-	$$(if $$(TEST_NEEDS_BTFIDS),						\
-		$(Q)if [ -f $$@ ]; then						\
-		$(if $(filter 1,$(V)),true,printf '  %-8s%s %s\n' "BTFIDS" " [$(TRUNNER_BINARY)]" "$$(notdir $$@)"); \
-		$(RESOLVE_BTFIDS) --btf $(TRUNNER_OUTPUT)/btf_data.bpf.o $$@;	\
-		$(RESOLVE_BTFIDS) --patch_btfids $$@.BTF_ids $$@;		\
-		fi)
-
-$(TRUNNER_TEST_OBJS:.o=.d): $(TRUNNER_OUTPUT)/%.test.d:			\
-			    $(TRUNNER_TESTS_DIR)/%.c			\
-			    $(TRUNNER_EXTRA_HDRS)			\
-			    $$(BPFOBJ) | $(TRUNNER_OUTPUT)		\
-			    $(TRUNNER_BPF_SKELS)			\
-			    $(TRUNNER_BPF_LSKELS)			\
-			    $(TRUNNER_BPF_LSKELS_SIGNED)		\
-			    $(TRUNNER_BPF_SKELS_LINKED)
-
-ifeq ($(filter clean docs-clean emit_tests,$(MAKECMDGOALS)),)
-include $(wildcard $(TRUNNER_TEST_OBJS:.o=.d))
-endif
+docs-clean:
+	$(Q)$(MAKE) $(submake_extras)					\
+	            -f Makefile.docs					\
+	            prefix= OUTPUT=$(OUTPUT)/ DESTDIR=$(OUTPUT)/ $@
 
-# add per extra obj CFGLAGS definitions
-$(foreach N,$(patsubst $(TRUNNER_OUTPUT)/%.o,%,$(TRUNNER_EXTRA_OBJS)),	\
-	$(eval $(TRUNNER_OUTPUT)/$(N).o: CFLAGS += $($(N).c-CFLAGS)))
-
-$(TRUNNER_EXTRA_OBJS): $(TRUNNER_OUTPUT)/%.o:				\
-		       %.c						\
-		       $(TRUNNER_EXTRA_HDRS)				\
-		       $(VERIFY_SIG_HDR)				\
-		       $(TRUNNER_TESTS_HDR)				\
-		       $$(BPFOBJ) | $(TRUNNER_OUTPUT)
-	$$(call msg,EXT-OBJ,$(TRUNNER_BINARY),$$@)
-	$(Q)$$(CC) $$(CFLAGS) -c $$< $$(LDLIBS) -o $$@
-
-$(TRUNNER_LIB_OBJS): $(TRUNNER_OUTPUT)/%.o:$(TOOLSDIR)/lib/%.c
-	$$(call msg,LIB-OBJ,$(TRUNNER_BINARY),$$@)
-	$(Q)$$(CC) $$(CFLAGS) -c $$< $$(LDLIBS) -o $$@
-
-# non-flavored in-srctree builds receive special treatment, in particular, we
-# do not need to copy extra resources (see e.g. test_btf_dump_case())
-$(TRUNNER_BINARY)-extras: $(TRUNNER_EXTRA_FILES) | $(TRUNNER_OUTPUT)
-ifneq ($2:$(OUTPUT),:$(shell pwd))
-	$$(call msg,EXT-COPY,$(TRUNNER_BINARY),$(TRUNNER_EXTRA_FILES))
-	$(Q)rsync -aq $(if $(PERMISSIVE),--ignore-missing-args) $$^ $(TRUNNER_OUTPUT)/
+# vmlinux.h is first dumped to a temporary file and then compared to
+# the previous version. This helps to avoid unnecessary re-builds of
+# BPF objects.
+$(INCLUDE_DIR)/vmlinux.h: $(VMLINUX_BTF) $(BPFTOOL) | $(INCLUDE_DIR)
+ifeq ($(VMLINUX_H),)
+	$(call msg,GEN,,$@)
+	$(Q)$(BPFTOOL) btf dump file $(VMLINUX_BTF) format c > $(INCLUDE_DIR)/.vmlinux.h.tmp
+	$(Q)cmp -s $(INCLUDE_DIR)/.vmlinux.h.tmp $@ || mv $(INCLUDE_DIR)/.vmlinux.h.tmp $@
+else
+	$(call msg,CP,,$@)
+	$(Q)cp "$(VMLINUX_H)" $@
 endif
 
-# some X.test.o files have runtime dependencies on Y.bpf.o files
-$(OUTPUT)/$(TRUNNER_BINARY): | $(TRUNNER_BPF_OBJS)
-
-$(OUTPUT)/$(TRUNNER_BINARY): $(if $(filter test_progs%,$1),$(if $(PERMISSIVE),$$(wildcard $(TRUNNER_TEST_OBJS)),$(TRUNNER_TEST_OBJS)),$(TRUNNER_TEST_OBJS))	\
-			     $(TRUNNER_EXTRA_OBJS) $$(BPFOBJ)		\
-			     $(TRUNNER_LIB_OBJS)			\
-			     $(TRUNNER_BPFTOOL)				\
-			     $(OUTPUT)/veristat				\
-			     | $(TRUNNER_BINARY)-extras			\
-			     $(if $(filter test_progs%,$1),$(if $(PERMISSIVE),$(TRUNNER_TEST_OBJS)))
-	$$(call msg,BINARY,,$$@)
-	$(Q)$$(CC) $$(CFLAGS) $(if $(filter test_progs%,$1),$(if $(PERMISSIVE),$$(filter %.a %.o,$$(wildcard $(TRUNNER_TEST_OBJS)) $$(filter-out $(TRUNNER_TEST_OBJS),$$^)),$$(filter %.a %.o,$$^)),$$(filter %.a %.o,$$^)) $$(LDLIBS) $$(LLVM_LDLIBS) $$(LDFLAGS) $$(LLVM_LDFLAGS) -o $$@
-	$(Q)ln -sf $(if $2,..,.)/tools/build/bpftool/$(USE_BOOTSTRAP)bpftool \
-		   $(OUTPUT)/$(if $2,$2/)bpftool
-
-endef
-
-VERIFY_SIG_SETUP := $(CURDIR)/verify_sig_setup.sh
-VERIFY_SIG_HDR := verification_cert.h
-VERIFICATION_CERT   := $(BUILD_DIR)/signing_key.der
-PRIVATE_KEY := $(BUILD_DIR)/signing_key.pem
-
-$(VERIFICATION_CERT) $(PRIVATE_KEY): $(VERIFY_SIG_SETUP)
+# One genkey run produces both files. A plain two-target rule is not
+# grouped - under -j make would run genkey twice concurrently and the
+# openssl invocations race; the pattern form is implicitly grouped even
+# with make < 4.3 (same workaround as the test_kmods rule).
+$(BUILD_DIR)/%.pem $(BUILD_DIR)/%.der: $(VERIFY_SIG_SETUP)
 	$(Q)mkdir -p $(BUILD_DIR)
 	$(Q)$(VERIFY_SIG_SETUP) genkey $(BUILD_DIR)
 
@@ -835,116 +370,183 @@ LIBARENA_BPF_DEPS := $(wildcard libarena/Makefile		\
 				 libarena/selftests/*		\
 				 libarena/*.bpf.o)
 
-LIBARENA_SKEL := libarena/libarena.skel.h
-
 $(LIBARENA_SKEL): $(INCLUDE_DIR)/vmlinux.h $(BPFOBJ) $(LIBARENA_BPF_DEPS)
 	+$(MAKE) -C libarena libarena.skel.h $(LIBARENA_MAKE_ARGS)
 
-ifneq ($(CLANG_HAS_ARENA_ASAN),)
-LIBARENA_ASAN_SKEL := libarena/libarena_asan.skel.h
-CFLAGS += -DHAS_BPF_ARENA_ASAN
+libarena: $(LIBARENA_SKEL)
 
+ifneq ($(CLANG_HAS_ARENA_ASAN),)
 $(LIBARENA_ASAN_SKEL): $(INCLUDE_DIR)/vmlinux.h $(BPFOBJ) $(LIBARENA_BPF_DEPS)
 	+$(MAKE) -C libarena libarena_asan.skel.h $(LIBARENA_MAKE_ARGS)
+
+libarena_asan: $(LIBARENA_ASAN_SKEL)
 endif
 
-# Define test_progs test runner.
-TRUNNER_TESTS_DIR := prog_tests
-TRUNNER_BPF_PROGS_DIR := progs
-TRUNNER_EXTRA_SOURCES := test_progs.c		\
-			 cgroup_helpers.c	\
-			 trace_helpers.c	\
-			 network_helpers.c	\
-			 testing_helpers.c	\
-			 btf_helpers.c		\
-			 cap_helpers.c		\
-			 unpriv_helpers.c 	\
-			 sysctl_helpers.c	\
-			 netlink_helpers.c	\
-			 jit_disasm_helpers.c	\
-			 io_helpers.c		\
-			 test_loader.c		\
-			 xsk.c			\
-			 disasm.c		\
-			 disasm_helpers.c	\
-			 json_writer.c 		\
-			 $(VERIFY_SIG_HDR)		\
-			 flow_dissector_load.h	\
-			 ip_check_defrag_frags.h	\
-			 bpftool_helpers.c	\
-			 usdt_1.c usdt_2.c	\
-			 $(LIBARENA_SKEL)	\
-			 $(LIBARENA_ASAN_SKEL)
-TRUNNER_LIB_SOURCES := find_bit.c
-TRUNNER_EXTRA_FILES := $(OUTPUT)/urandom_read				\
-		       $(OUTPUT)/liburandom_read.so			\
-		       $(OUTPUT)/xdp_synproxy				\
-		       $(OUTPUT)/sign-file				\
-		       $(OUTPUT)/uprobe_multi				\
-		       $(TEST_KMOD_TARGETS)				\
-		       ima_setup.sh 					\
-		       $(VERIFY_SIG_SETUP)				\
-		       $(wildcard progs/btf_dump_test_case_*.c)		\
-		       $(wildcard progs/*.bpf.o)
-TRUNNER_BPF_BUILD_RULE := CLANG_BPF_BUILD_RULE
-TRUNNER_BPF_CFLAGS := $(BPF_CFLAGS) $(CLANG_CFLAGS) -DENABLE_ATOMICS_TESTS
-$(eval $(call DEFINE_TEST_RUNNER,test_progs))
-
-# Define test_progs-no_alu32 test runner.
-TRUNNER_BPF_BUILD_RULE := CLANG_NOALU32_BPF_BUILD_RULE
-TRUNNER_BPF_CFLAGS := $(BPF_CFLAGS) $(CLANG_CFLAGS)
-$(eval $(call DEFINE_TEST_RUNNER,test_progs,no_alu32))
-
-# Define test_progs-cpuv4 test runner.
+# ---------------------------------------------------------------------
+# Generated test list headers
+# ---------------------------------------------------------------------
+
+define gen_tests_hdr
+	$(call msg,TEST-HDR,,$@)
+	$(Q)(echo '/* Generated header, do not edit */';		\
+	     sed -n -E 's/^void (serial_)?test_([a-zA-Z0-9_]+)\((void)?\).*/DEFINE_TEST(\2)/p' \
+		$(@D)/*.c | sort) > $@
+endef
+
+prog_tests/tests.h: $(wildcard prog_tests/*.c)
+	$(gen_tests_hdr)
+
+map_tests/tests.h: $(wildcard map_tests/*.c)
+	$(gen_tests_hdr)
+
+verifier/tests.h: verifier/*.c
+	$(Q)( cd verifier/; \
+	      echo '/* Generated header, do not edit */'; \
+	      echo '#ifdef FILL_ARRAY'; \
+	      ls *.c 2> /dev/null | sed -e 's@\(.*\)@#include \"\1\"@'; \
+	      echo '#endif' \
+	    ) > verifier/tests.h
+
+# ---------------------------------------------------------------------
+# Shared helper objects, used by the standalone binaries below and (as
+# prebuilt inputs) by the unflavored test_progs runner.
+# ---------------------------------------------------------------------
+
+# Some utility functions use LLVM libraries
+$(OUTPUT)/jit_disasm_helpers.o: CFLAGS += $(LLVM_CFLAGS)
+
+HELPER_OBJS := $(addprefix $(OUTPUT)/,testing_helpers.o cgroup_helpers.o \
+	       trace_helpers.o network_helpers.o json_writer.o		\
+	       cap_helpers.o unpriv_helpers.o usdt_1.o usdt_2.o xsk.o)
+
+# $(BPFTOOL) is a prerequisite because its sub-make is what installs
+# libbpf's internal headers (bpf/hashmap.h, bpf/libbpf_internal.h) into
+# $(INCLUDE_DIR); without it, helpers including those headers race the
+# install and can silently pick up the source-tree copies instead.
+$(HELPER_OBJS): flow_dissector_load.h ip_check_defrag_frags.h		\
+		$(VERIFY_SIG_HDR) prog_tests/tests.h $(BPFOBJ) $(BPFTOOL)
+
+$(OUTPUT)/test_sockmap: $(OUTPUT)/cgroup_helpers.o $(OUTPUT)/testing_helpers.o
+$(OUTPUT)/test_tcpnotify_user: $(OUTPUT)/cgroup_helpers.o		\
+			       $(OUTPUT)/testing_helpers.o		\
+			       $(OUTPUT)/trace_helpers.o
+$(OUTPUT)/test_tag: $(OUTPUT)/testing_helpers.o
+$(OUTPUT)/test_lirc_mode2_user: $(OUTPUT)/testing_helpers.o
+$(OUTPUT)/flow_dissector_load: flow_dissector_load.h $(OUTPUT)/testing_helpers.o
+
+# ---------------------------------------------------------------------
+# Test runner instances, one sub-make each (see Makefile.runner).
+# ---------------------------------------------------------------------
+
+# The LLVM feature-probe results are exported to the runner sub-makes.
+# CC is passed explicitly instead: exporting it would also leak lib.mk's
+# CC into the libbpf/bpftool/resolve_btfids sub-builds, which compute
+# their own.
+export LLVM_CFLAGS LLVM_LDLIBS LLVM_LDFLAGS
+
+RUNNER_MAKE := $(MAKE) -f Makefile.runner OUTPUT=$(OUTPUT) CC='$(CC)'
+
+# Everything a runner instance references but does not know how to build.
+RUNNER_PREREQS := $(INCLUDE_DIR)/vmlinux.h $(BPFOBJ) $(BPFTOOL)		\
+		  $(TRUNNER_BPFTOOL) $(RESOLVE_BTFIDS)			\
+		  $(OUTPUT)/veristat					\
+		  $(VERIFY_SIG_HDR) $(PRIVATE_KEY) $(VERIFICATION_CERT)	\
+		  $(LIBARENA_SKEL) $(LIBARENA_ASAN_SKEL)		\
+		  prog_tests/tests.h					\
+		  $(HELPER_OBJS)					\
+		  $(OUTPUT)/urandom_read $(OUTPUT)/liburandom_read.so	\
+		  $(OUTPUT)/xdp_synproxy $(OUTPUT)/sign-file		\
+		  $(OUTPUT)/uprobe_multi $(TEST_KMOD_TARGETS)
+
+FORCE:
+
+# The default flavor's skeletons are also inputs of bench, test_maps,
+# xskxceiver, xdp_* and test_cpp; "default-skels" builds just those
+# without the test_progs test objects and binary.
+.PHONY: default-skels
+default-skels: $(RUNNER_PREREQS)
+	+$(Q)$(RUNNER_MAKE) RUNNER=test_progs FLAVOR= TESTS_DIR=prog_tests \
+		BPF_BUILD=clang BPF_MCPU=v3 BPF_DEFINES=-DENABLE_ATOMICS_TESTS skels
+
+$(OUTPUT)/test_progs: $(RUNNER_PREREQS) default-skels FORCE
+	+$(Q)$(RUNNER_MAKE) RUNNER=test_progs FLAVOR= TESTS_DIR=prog_tests \
+		BPF_BUILD=clang BPF_MCPU=v3 BPF_DEFINES=-DENABLE_ATOMICS_TESTS
+
+$(OUTPUT)/test_progs-no_alu32: $(RUNNER_PREREQS) FORCE
+	+$(Q)$(RUNNER_MAKE) RUNNER=test_progs FLAVOR=no_alu32 TESTS_DIR=prog_tests \
+		BPF_BUILD=clang BPF_MCPU=v2
+
 ifneq ($(CLANG_CPUV4),)
-TRUNNER_BPF_BUILD_RULE := CLANG_CPUV4_BPF_BUILD_RULE
-TRUNNER_BPF_CFLAGS := $(BPF_CFLAGS) $(CLANG_CFLAGS) -DENABLE_ATOMICS_TESTS
-$(eval $(call DEFINE_TEST_RUNNER,test_progs,cpuv4))
+$(OUTPUT)/test_progs-cpuv4: $(RUNNER_PREREQS) FORCE
+	+$(Q)$(RUNNER_MAKE) RUNNER=test_progs FLAVOR=cpuv4 TESTS_DIR=prog_tests \
+		BPF_BUILD=clang BPF_MCPU=v4 BPF_DEFINES=-DENABLE_ATOMICS_TESTS
 endif
 
-# Define test_progs BPF-GCC-flavored test runner.
 ifneq ($(BPF_GCC),)
-TRUNNER_BPF_BUILD_RULE := GCC_BPF_BUILD_RULE
-TRUNNER_BPF_CFLAGS := $(BPF_CFLAGS) $(call get_sys_includes,gcc,)
-$(eval $(call DEFINE_TEST_RUNNER,test_progs,bpf_gcc))
-endif
-
-# Define test_maps test runner.
-TRUNNER_TESTS_DIR := map_tests
-TRUNNER_BPF_PROGS_DIR := progs
-TRUNNER_EXTRA_SOURCES := test_maps.c
-TRUNNER_LIB_SOURCES :=
-TRUNNER_EXTRA_FILES :=
-TRUNNER_BPF_BUILD_RULE := $$(error no BPF objects should be built)
-TRUNNER_BPF_CFLAGS :=
-$(eval $(call DEFINE_TEST_RUNNER,test_maps))
-
-# Define test_verifier test runner.
-# It is much simpler than test_maps/test_progs and sufficiently different from
-# them (e.g., test.h is using completely pattern), that it's worth just
-# explicitly defining all the rules explicitly.
-verifier/tests.h: verifier/*.c
-	$(shell ( cd verifier/; \
-		  echo '/* Generated header, do not edit */'; \
-		  echo '#ifdef FILL_ARRAY'; \
-		  ls *.c 2> /dev/null | sed -e 's@\(.*\)@#include \"\1\"@'; \
-		  echo '#endif' \
-		) > verifier/tests.h)
-$(OUTPUT)/test_verifier: test_verifier.c verifier/tests.h $(BPFOBJ) | $(OUTPUT)
+$(OUTPUT)/test_progs-bpf_gcc: $(RUNNER_PREREQS) FORCE
+	+$(Q)$(RUNNER_MAKE) RUNNER=test_progs FLAVOR=bpf_gcc TESTS_DIR=prog_tests \
+		BPF_BUILD=gcc
+endif
+
+# test_maps compiles map_tests/*.c against the default flavor's skeletons
+$(OUTPUT)/test_maps: $(RUNNER_PREREQS) map_tests/tests.h default-skels FORCE
+	+$(Q)$(RUNNER_MAKE) RUNNER=test_maps FLAVOR= TESTS_DIR=map_tests \
+		BPF_BUILD=none
+
+# Default-flavor BPF objects and skeletons consumed by targets in this
+# Makefile (and by lib.mk's install rule) are produced by the runner
+# sub-make above; the empty recipe plus the order-only prerequisite
+# makes them available here without duplicating the runner's rules.
+DEFAULT_BPF_OBJS := $(patsubst progs/%.c,$(OUTPUT)/%.bpf.o,$(wildcard progs/*.c))
+TEST_GEN_FILES += $(DEFAULT_BPF_OBJS)
+
+BENCH_SKELS := $(addprefix $(OUTPUT)/,					\
+	       test_overhead.skel.h trigger_bench.skel.h		\
+	       ringbuf_bench.skel.h perfbuf_bench.skel.h		\
+	       bloom_filter_bench.skel.h bpf_loop_bench.skel.h		\
+	       strncmp_bench.skel.h bpf_hashmap_full_update_bench.skel.h\
+	       local_storage_bench.skel.h				\
+	       local_storage_rcu_tasks_trace_bench.skel.h		\
+	       bench_local_storage_create.skel.h			\
+	       bpf_hashmap_lookup.skel.h htab_mem_bench.skel.h		\
+	       crypto_bench.skel.h bench_sockmap_prog.skel.h		\
+	       lpm_trie_bench.skel.h lpm_trie_map.skel.h		\
+	       bpf_nop_bench.skel.h xdp_lb_bench.skel.h)
+TOP_SKELS := $(addprefix $(OUTPUT)/,xsk_xdp_progs.skel.h		\
+	     xdp_hw_metadata.skel.h xdp_features.skel.h			\
+	     test_core_extern.skel.h) $(BENCH_SKELS)
+
+# Empty-recipe delegation: "default-skels" (re)builds the file via the
+# runner sub-make, after which make re-stats the target, so dependents
+# see fresh timestamps within the same invocation. Dependents still
+# rebuild on mtime only - the phony prerequisite does not cascade.
+$(DEFAULT_BPF_OBJS) $(TOP_SKELS): default-skels ;
+
+# ---------------------------------------------------------------------
+# Standalone test binaries
+# ---------------------------------------------------------------------
+
+# test_verifier is much simpler than test_maps/test_progs and
+# sufficiently different from them (e.g., its tests.h uses a completely
+# different pattern), that it's worth just defining its rules explicitly.
+$(OUTPUT)/test_verifier: test_verifier.c verifier/tests.h $(BPFOBJ)	\
+			 $(OUTPUT)/testing_helpers.o			\
+			 $(OUTPUT)/cap_helpers.o			\
+			 $(OUTPUT)/unpriv_helpers.o
 	$(call msg,BINARY,,$@)
 	$(Q)$(CC) $(CFLAGS) $(filter %.a %.o %.c,$^) $(LDLIBS) -o $@
 
 # Include find_bit.c to compile xskxceiver.
 EXTRA_SRC := $(TOOLSDIR)/lib/find_bit.c prog_tests/test_xsk.c prog_tests/test_xsk.h
-$(OUTPUT)/xskxceiver: $(EXTRA_SRC) xskxceiver.c xskxceiver.h $(OUTPUT)/network_helpers.o $(OUTPUT)/xsk.o $(OUTPUT)/xsk_xdp_progs.skel.h $(BPFOBJ) | $(OUTPUT)
+$(OUTPUT)/xskxceiver: $(EXTRA_SRC) xskxceiver.c xskxceiver.h $(OUTPUT)/network_helpers.o $(OUTPUT)/xsk.o $(OUTPUT)/xsk_xdp_progs.skel.h $(BPFOBJ)
 	$(call msg,BINARY,,$@)
 	$(Q)$(CC) $(CFLAGS) $(filter %.a %.o %.c,$^) $(LDLIBS) -o $@
 
-$(OUTPUT)/xdp_hw_metadata: xdp_hw_metadata.c $(OUTPUT)/network_helpers.o $(OUTPUT)/xsk.o $(OUTPUT)/xdp_hw_metadata.skel.h | $(OUTPUT)
+$(OUTPUT)/xdp_hw_metadata: xdp_hw_metadata.c $(OUTPUT)/network_helpers.o $(OUTPUT)/xsk.o $(OUTPUT)/xdp_hw_metadata.skel.h
 	$(call msg,BINARY,,$@)
 	$(Q)$(CC) $(CFLAGS) $(filter %.a %.o %.c,$^) $(LDLIBS) -o $@
 
-$(OUTPUT)/xdp_features: xdp_features.c $(OUTPUT)/network_helpers.o $(OUTPUT)/xdp_features.skel.h | $(OUTPUT)
+$(OUTPUT)/xdp_features: xdp_features.c $(OUTPUT)/network_helpers.o $(OUTPUT)/xdp_features.skel.h
 	$(call msg,BINARY,,$@)
 	$(Q)$(CC) $(CFLAGS) $(filter %.a %.o %.c,$^) $(LDLIBS) -o $@
 
@@ -956,11 +558,18 @@ $(OUTPUT)/test_cpp: test_cpp.cpp $(OUTPUT)/test_core_extern.skel.h $(BPFOBJ)
 	$(call msg,CXX,,$@)
 	$(Q)$(CXX) $(CXXFLAGS) $(filter %.a %.o %.cpp,$^) $(LDLIBS) -o $@
 
-# Benchmark runner
-$(OUTPUT)/bench_%.o: benchs/bench_%.c bench.h $(BPFOBJ)
+# ---------------------------------------------------------------------
+# Benchmark runner. The benchmark list is defined by the files in
+# benchs/: every benchs/bench_*.c is compiled and linked in.
+# ---------------------------------------------------------------------
+
+BENCH_OBJS := $(sort $(patsubst benchs/%.c,$(OUTPUT)/%.o,$(wildcard benchs/bench_*.c)))
+
+$(OUTPUT)/bench_%.o: benchs/bench_%.c bench.h $(BPFOBJ) | default-skels
 	$(call msg,CC,,$@)
-	$(Q)$(CC) $(CFLAGS) -O2 -c $(filter %.c,$^) $(LDLIBS) -o $@ $(if $(PERMISSIVE),|| \
-		($(RM) $@; printf '  %-12s %s\n' 'SKIP-BENCH' '$(notdir $@)' 1>&2))
+	$(Q)$(CC) $(CFLAGS) -O2 -c $(filter %.c,$^) -o $@ $(call skip_on_fail,BENCH)
+
+# benchmark-specific generated-skeleton dependencies
 $(OUTPUT)/bench_rename.o: $(OUTPUT)/test_overhead.skel.h
 $(OUTPUT)/bench_trigger.o: $(OUTPUT)/trigger_bench.skel.h
 $(OUTPUT)/bench_ringbufs.o: $(OUTPUT)/ringbuf_bench.skel.h \
@@ -980,37 +589,19 @@ $(OUTPUT)/bench_lpm_trie_map.o: $(OUTPUT)/lpm_trie_bench.skel.h $(OUTPUT)/lpm_tr
 $(OUTPUT)/bench_bpf_nop.o: $(OUTPUT)/bpf_nop_bench.skel.h bench_bpf_timing.h
 $(OUTPUT)/bench_xdp_lb.o: $(OUTPUT)/xdp_lb_bench.skel.h bench_bpf_timing.h
 $(OUTPUT)/bench_bpf_timing.o: bench_bpf_timing.h
+
 $(OUTPUT)/bench.o: bench.h testing_helpers.h $(BPFOBJ)
 $(OUTPUT)/bench: LDLIBS += -lm
-$(OUTPUT)/bench: $(OUTPUT)/bench.o \
-		 $(TESTING_HELPERS) \
-		 $(TRACE_HELPERS) \
-		 $(CGROUP_HELPERS) \
-		 $(OUTPUT)/bench_count.o \
-		 $(OUTPUT)/bench_rename.o \
-		 $(OUTPUT)/bench_trigger.o \
-		 $(OUTPUT)/bench_ringbufs.o \
-		 $(OUTPUT)/bench_bloom_filter_map.o \
-		 $(OUTPUT)/bench_bpf_loop.o \
-		 $(OUTPUT)/bench_strncmp.o \
-		 $(OUTPUT)/bench_bpf_hashmap_full_update.o \
-		 $(OUTPUT)/bench_local_storage.o \
-		 $(OUTPUT)/bench_local_storage_rcu_tasks_trace.o \
-		 $(OUTPUT)/bench_bpf_hashmap_lookup.o \
-		 $(OUTPUT)/bench_local_storage_create.o \
-		 $(OUTPUT)/bench_htab_mem.o \
-		 $(OUTPUT)/bench_bpf_crypto.o \
-		 $(OUTPUT)/bench_sockmap.o \
-		 $(OUTPUT)/bench_lpm_trie_map.o \
-		 $(OUTPUT)/bench_bpf_timing.o \
-		 $(OUTPUT)/bench_bpf_nop.o \
-		 $(OUTPUT)/bench_xdp_lb.o \
-		 $(OUTPUT)/usdt_1.o \
-		 $(OUTPUT)/usdt_2.o \
-		 #
+$(OUTPUT)/bench: $(OUTPUT)/bench.o		\
+		 $(OUTPUT)/testing_helpers.o	\
+		 $(OUTPUT)/trace_helpers.o	\
+		 $(OUTPUT)/cgroup_helpers.o	\
+		 $(OUTPUT)/usdt_1.o		\
+		 $(OUTPUT)/usdt_2.o		\
+		 $(BENCH_OBJS)
 	$(call msg,BINARY,,$@)
-	$(Q)$(CC) $(CFLAGS) $(LDFLAGS) $(filter %.a %.o,$^) $(LDLIBS) -o $@ $(if $(PERMISSIVE),|| \
-		($(RM) $@; printf '  %-12s %s\n' 'SKIP-LINK' '$(notdir $@) (some benchmarks may have been skipped)' 1>&2))
+	$(Q)$(CC) $(CFLAGS) $(LDFLAGS) $(filter %.a %.o,$^) $(LDLIBS) -o $@ \
+		$(call skip_on_fail,LINK,,(some benchmarks may have been skipped))
 
 # This works around GCC warning about snprintf truncating strings like:
 #
@@ -1022,13 +613,6 @@ $(OUTPUT)/veristat: $(OUTPUT)/veristat.o
 	$(call msg,BINARY,,$@)
 	$(Q)$(CC) $(CFLAGS) $(LDFLAGS) $(filter %.a %.o,$^) $(LDLIBS) -o $@
 
-# Linking uprobe_multi can fail due to relocation overflows on mips.
-$(OUTPUT)/uprobe_multi: CFLAGS += $(if $(filter mips, $(ARCH)),-mxgot)
-$(OUTPUT)/uprobe_multi: uprobe_multi.c uprobe_multi.ld
-	$(call msg,BINARY,,$@)
-	$(Q)$(CC) $(CFLAGS) -Wl,-T,uprobe_multi.ld -O0 $(LDFLAGS) 	\
-		$(filter-out %.ld,$^) $(LDLIBS) -o $@
-
 EXTRA_CLEAN := $(SCRATCH_DIR) $(HOST_SCRATCH_DIR)			\
 	prog_tests/tests.h map_tests/tests.h verifier/tests.h		\
 	feature bpftool $(TEST_KMOD_TARGETS)				\
@@ -1038,7 +622,7 @@ EXTRA_CLEAN := $(SCRATCH_DIR) $(HOST_SCRATCH_DIR)			\
 			       liburandom_read.so)			\
 	$(OUTPUT)/FEATURE-DUMP.selftests
 
-.PHONY: docs docs-clean
+.PHONY: docs docs-clean libarena libarena_asan
 
 # Delete partially updated (corrupted) files on error
 .DELETE_ON_ERROR:
@@ -1062,9 +646,3 @@ override define INSTALL_RULE
 			$(INSTALL_PATH)/$$DIR;				  \
 	done
 endef
-
-libarena: $(LIBARENA_SKEL)
-
-ifneq ($(CLANG_HAS_ARENA_ASAN),)
-libarena_asan: $(LIBARENA_ASAN_SKEL)
-endif
diff --git a/tools/testing/selftests/bpf/Makefile.buildvars b/tools/testing/selftests/bpf/Makefile.buildvars
new file mode 100644
index 00000000000000..ea975feef0136f
--- /dev/null
+++ b/tools/testing/selftests/bpf/Makefile.buildvars
@@ -0,0 +1,204 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Shared toolchain, path and flag definitions for the BPF selftests build.
+# Included by both Makefile (the top level) and Makefile.runner (one
+# test-runner instance).
+#
+# Requirements at include time:
+#   - $(OUTPUT) is set to an absolute path
+#   - tools/build/Build.include, tools/scripts/Makefile.arch and
+#     tools/scripts/Makefile.include have been included (for SRCARCH etc.)
+
+CXX ?= $(CROSS_COMPILE)g++
+PKG_CONFIG ?= $(CROSS_COMPILE)pkg-config
+
+TOOLSDIR := $(abspath ../../..)
+LIBDIR := $(TOOLSDIR)/lib
+BPFDIR := $(LIBDIR)/bpf
+TOOLSINCDIR := $(TOOLSDIR)/include
+TOOLSARCHINCDIR := $(TOOLSDIR)/arch/$(SRCARCH)/include
+BPFTOOLDIR := $(TOOLSDIR)/bpf/bpftool
+APIDIR := $(TOOLSINCDIR)/uapi
+ifneq ($(O),)
+GENDIR := $(O)/include/generated
+else
+GENDIR := $(abspath ../../../../include/generated)
+endif
+GENHDR := $(GENDIR)/autoconf.h
+
+ifneq ($(wildcard $(GENHDR)),)
+  GENFLAGS := -DHAVE_GENHDR
+endif
+
+ifeq ($(srctree),)
+srctree := $(abspath ../../../..)
+endif
+
+# Immediate (with an override-preserving guard): a plain ?= stays
+# recursively expanded and would re-run 'command -v' on every expansion.
+ifeq ($(origin BPF_GCC),undefined)
+BPF_GCC		:= $(shell command -v bpf-gcc;)
+endif
+ifdef ASAN
+SAN_CFLAGS	?= -fsanitize=address -fno-omit-frame-pointer
+else
+SAN_CFLAGS	?=
+endif
+SAN_LDFLAGS	?= $(SAN_CFLAGS)
+RELEASE		?=
+OPT_FLAGS	?= $(if $(RELEASE),-O2,-O0)
+
+LIBELF_CFLAGS	:= $(shell $(PKG_CONFIG) libelf --cflags 2>/dev/null)
+LIBELF_LIBS	:= $(shell $(PKG_CONFIG) libelf --libs 2>/dev/null || echo -lelf)
+
+SKIP_DOCS	?=
+SKIP_LLVM	?=
+SKIP_LIBBFD	?=
+SKIP_CRYPTO	?=
+
+# When BPF_STRICT_BUILD is 1, any BPF object, skeleton, test object, or
+# benchmark compilation failure is fatal. Set to 0 to tolerate failures
+# and continue building the remaining tests.
+BPF_STRICT_BUILD ?= 1
+PERMISSIVE := $(filter 0,$(BPF_STRICT_BUILD))
+
+# Permissive-mode recipe suffix: on failure, remove the target, emit a
+# SKIP marker and report success so the rest of the build continues.
+# $(1) - SKIP tag (BPF, TEST, BENCH, SKEL, LINK, ...)
+# $(2) - optional extra files to remove alongside the target
+# $(3) - optional note appended to the SKIP message
+skip_on_fail = $(if $(PERMISSIVE),|| { $(RM) $@ $(2); \
+	printf '  %-12s %s\n' 'SKIP-$(1)' '$(notdir $@)$(if $(3), $(3))' 1>&2; })
+
+# Kernel test modules; TEST_KMODS= on the command line disables them.
+TEST_KMODS ?= bpf_testmod.ko bpf_test_no_cfi.ko bpf_test_modorder_x.ko \
+	bpf_test_modorder_y.ko bpf_test_rqspinlock.ko
+TEST_KMOD_TARGETS = $(addprefix $(OUTPUT)/,$(TEST_KMODS))
+
+# Output-tree layout. Defined before COMMON_CFLAGS on purpose: CFLAGS
+# below is expanded immediately and references $(INCLUDE_DIR).
+SCRATCH_DIR := $(OUTPUT)/tools
+BUILD_DIR := $(SCRATCH_DIR)/build
+INCLUDE_DIR := $(SCRATCH_DIR)/include
+BPFOBJ := $(BUILD_DIR)/libbpf/libbpf.a
+ifneq ($(CROSS_COMPILE),)
+HOST_BUILD_DIR		:= $(BUILD_DIR)/host
+HOST_SCRATCH_DIR	:= $(OUTPUT)/host-tools
+HOST_INCLUDE_DIR	:= $(HOST_SCRATCH_DIR)/include
+else
+HOST_BUILD_DIR		:= $(BUILD_DIR)
+HOST_SCRATCH_DIR	:= $(SCRATCH_DIR)
+HOST_INCLUDE_DIR	:= $(INCLUDE_DIR)
+endif
+HOST_BPFOBJ := $(HOST_BUILD_DIR)/libbpf/libbpf.a
+RESOLVE_BTFIDS := $(HOST_BUILD_DIR)/resolve_btfids/resolve_btfids
+
+DEFAULT_BPFTOOL := $(HOST_SCRATCH_DIR)/sbin/bpftool
+ifneq ($(CROSS_COMPILE),)
+CROSS_BPFTOOL := $(SCRATCH_DIR)/sbin/bpftool
+TRUNNER_BPFTOOL := $(CROSS_BPFTOOL)
+USE_BOOTSTRAP :=
+else
+TRUNNER_BPFTOOL := $(DEFAULT_BPFTOOL)
+USE_BOOTSTRAP := bootstrap/
+endif
+BPFTOOL ?= $(DEFAULT_BPFTOOL)
+
+COMMON_CFLAGS = -g $(OPT_FLAGS) -rdynamic -std=gnu11			\
+	  -Wall -Werror -fno-omit-frame-pointer				\
+	  -Wno-unused-but-set-variable					\
+	  $(GENFLAGS) $(SAN_CFLAGS) $(LIBELF_CFLAGS)			\
+	  -I$(CURDIR) -I$(INCLUDE_DIR) -I$(GENDIR) -I$(LIBDIR)		\
+	  -I$(TOOLSINCDIR) -I$(TOOLSARCHINCDIR) -I$(APIDIR) -I$(OUTPUT)	\
+	  -I$(CURDIR)/libarena/include
+LDFLAGS += $(SAN_LDFLAGS)
+LDLIBS += $(LIBELF_LIBS) -lz -lrt -lpthread
+
+PCAP_CFLAGS	:= $(shell $(PKG_CONFIG) --cflags libpcap 2>/dev/null && echo "-DTRAFFIC_MONITOR=1")
+PCAP_LIBS	:= $(shell $(PKG_CONFIG) --libs libpcap 2>/dev/null)
+LDLIBS += $(PCAP_LIBS)
+# Prepend rather than append: the include-search order must stay
+# "$(COMMON_CFLAGS) first, lib.mk additions after" regardless of whether
+# lib.mk was included before this file (top level) or not at all (runner).
+CFLAGS := $(COMMON_CFLAGS) $(PCAP_CFLAGS) $(CFLAGS)
+
+ifneq ($(LLVM),)
+# Silence some warnings when compiled with clang
+CFLAGS += -Wno-unused-command-line-argument
+endif
+
+# Check whether clang supports BPF address sanitizer (requires LLVM 22+)
+CLANG_HAS_ARENA_ASAN := $(shell echo 'int x;' | \
+	$(CLANG) --target=bpf -fsanitize=kernel-address \
+	-mllvm -asan-shadow-addr-space=1 \
+	-x c -c - -o /dev/null 2>/dev/null && echo 1)
+ifneq ($(CLANG_HAS_ARENA_ASAN),)
+CFLAGS += -DHAS_BPF_ARENA_ASAN
+endif
+
+VMLINUX_BTF_PATHS ?= $(if $(O),$(O)/vmlinux)				\
+		     $(if $(KBUILD_OUTPUT),$(KBUILD_OUTPUT)/vmlinux)	\
+		     ../../../../vmlinux				\
+		     /sys/kernel/btf/vmlinux				\
+		     /boot/vmlinux-$(shell uname -r)
+VMLINUX_BTF ?= $(abspath $(firstword $(wildcard $(VMLINUX_BTF_PATHS))))
+ifeq ($(VMLINUX_BTF),)
+$(error Cannot find a vmlinux for VMLINUX_BTF at any of "$(VMLINUX_BTF_PATHS)")
+endif
+
+# Get Clang's default includes on this system, as opposed to those seen by
+# '--target=bpf'. This fixes "missing" files on some architectures/distros,
+# such as asm/byteorder.h, asm/socket.h, asm/sockios.h, sys/cdefs.h etc.
+#
+# Use '-idirafter': Don't interfere with include mechanics except where the
+# build would have failed anyways.
+define get_sys_includes
+$(shell $(1) $(2) -v -E - </dev/null 2>&1 \
+	| sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') \
+$(shell $(1) $(2) -dM -E - </dev/null | grep '__riscv_xlen ' | awk '{printf("-D__riscv_xlen=%d -D__BITS_PER_LONG=%d", $$3, $$3)}') \
+$(shell $(1) $(2) -dM -E - </dev/null | grep '__loongarch_grlen ' | awk '{printf("-D__BITS_PER_LONG=%d", $$3)}') \
+$(shell $(1) $(2) -dM -E - </dev/null | grep -E 'MIPS(EL|EB)|_MIPS_SZ(PTR|LONG) |_MIPS_SIM |_ABI(O32|N32|64) ' | awk '{printf("-D%s=%s ", $$2, $$3)}')
+endef
+
+# Determine target endianness.
+IS_LITTLE_ENDIAN := $(shell $(CC) -dM -E - </dev/null | \
+			grep 'define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__')
+MENDIAN := $(if $(IS_LITTLE_ENDIAN),-mlittle-endian,-mbig-endian)
+BPF_TARGET_ENDIAN := $(if $(IS_LITTLE_ENDIAN),--target=bpfel,--target=bpfeb)
+
+ifneq ($(CROSS_COMPILE),)
+CLANG_TARGET_ARCH = --target=$(notdir $(CROSS_COMPILE:%-=%))
+endif
+
+CLANG_SYS_INCLUDES := $(call get_sys_includes,$(CLANG),$(CLANG_TARGET_ARCH))
+BPF_CFLAGS = -g -Wall -Werror -D__TARGET_ARCH_$(SRCARCH) $(MENDIAN)	\
+	     -I$(INCLUDE_DIR) -I$(CURDIR) -I$(APIDIR)			\
+	     -I$(CURDIR)/libarena/include				\
+	     -I$(abspath $(OUTPUT)/../usr/include)			\
+	     -std=gnu11		 					\
+	     -fno-strict-aliasing 					\
+	     -Wno-microsoft-anon-tag					\
+	     -fms-extensions						\
+	     -Wno-compare-distinct-pointer-types			\
+	     -Wno-initializer-overrides					\
+	     #
+# TODO: enable me -Wsign-compare
+
+CLANG_CFLAGS = $(CLANG_SYS_INCLUDES)
+
+# Signing key/cert shared by the signed light skeletons and test_progs.
+VERIFY_SIG_SETUP := $(CURDIR)/verify_sig_setup.sh
+VERIFY_SIG_HDR := verification_cert.h
+VERIFICATION_CERT := $(BUILD_DIR)/signing_key.der
+PRIVATE_KEY := $(BUILD_DIR)/signing_key.pem
+
+# libarena skeletons, built by the libarena/ sub-make (driven by the top
+# level Makefile); test_progs and its flavors depend on the headers.
+LIBARENA_SKEL := libarena/libarena.skel.h
+ifneq ($(CLANG_HAS_ARENA_ASAN),)
+LIBARENA_ASAN_SKEL := libarena/libarena_asan.skel.h
+endif
+
+ifneq ($(V),1)
+submake_extras := feature_display=0
+endif
diff --git a/tools/testing/selftests/bpf/Makefile.runner b/tools/testing/selftests/bpf/Makefile.runner
new file mode 100644
index 00000000000000..4c58cc51f2514e
--- /dev/null
+++ b/tools/testing/selftests/bpf/Makefile.runner
@@ -0,0 +1,313 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Build one BPF test-runner instance: test_progs, one of its flavors
+# (no_alu32, cpuv4, bpf_gcc), or test_maps.
+#
+# Each instance is an independent sub-make with its own output directory,
+# so everything here is written in plain make - no $$-escaping, no eval,
+# no per-flavor guards. This file is always invoked by the main Makefile,
+# never directly: all shared prerequisites (libbpf, bpftool, vmlinux.h,
+# veristat, helper objects, signing key, generated tests.h, extra
+# binaries) are built by the main Makefile *before* this one runs and are
+# referenced below as plain files.
+#
+# Parameters (all passed on the sub-make command line):
+#   OUTPUT      absolute path of the selftests output directory
+#   RUNNER      base binary name: test_progs | test_maps
+#   FLAVOR      flavor suffix: empty | no_alu32 | cpuv4 | bpf_gcc
+#   TESTS_DIR   directory with the test sources: prog_tests | map_tests
+#   BPF_BUILD   how to build progs/*.c BPF objects: clang | gcc | none
+#   BPF_MCPU    clang -mcpu= value: v2 | v3 | v4
+#   BPF_DEFINES extra defines for BPF objects (e.g. -DENABLE_ATOMICS_TESTS)
+#   CC          C compiler (as resolved by lib.mk in the main Makefile)
+#   LLVM_CFLAGS/LLVM_LDLIBS/LLVM_LDFLAGS
+#               results of the top-level LLVM feature probe
+
+include ../../../build/Build.include
+include ../../../scripts/Makefile.arch
+include ../../../scripts/Makefile.include
+
+# Same message helpers as ../lib.mk, which only the top level includes.
+ifeq ($(V),1)
+Q =
+msg =
+else
+Q = @
+msg = @printf '  %-8s%s %s%s\n' "$(1)" "$(if $(2), [$(2)])" "$(notdir $(3))" "$(if $(4), $(4))";
+MAKEFLAGS += --no-print-directory
+endif
+
+include Makefile.buildvars
+
+# Keep in sync with the CFLAGS/LDFLAGS additions in ../lib.mk.
+CFLAGS += -D_GNU_SOURCE=
+CFLAGS += -I$(srctree)/tools/testing/selftests
+CFLAGS += $(USERCFLAGS)
+LDFLAGS += $(USERLDFLAGS)
+ifneq ($(LLVM),)
+CFLAGS += -Wno-address-of-packed-member
+CFLAGS += -Wno-gnu-variable-sized-type-not-at-end
+endif
+
+BINARY := $(RUNNER)$(if $(FLAVOR),-$(FLAVOR))
+RDIR   := $(OUTPUT)$(if $(FLAVOR),/$(FLAVOR))
+
+all: $(OUTPUT)/$(BINARY)
+.PHONY: all
+
+# Delete partially updated (corrupted) files on error
+.DELETE_ON_ERROR:
+
+# Permissive skip-on-failure of individual tests applies to test_progs and
+# its flavors only; test_maps uses strong cross-object references and keeps
+# strict semantics even when BPF_STRICT_BUILD=0.
+PERMISSIVE_TESTS := $(if $(filter test_progs,$(RUNNER)),$(PERMISSIVE))
+
+$(RDIR):
+	$(call msg,MKDIR,,$@)
+	$(Q)mkdir -p $@
+
+# ---------------------------------------------------------------------
+# Per-runner sources
+# ---------------------------------------------------------------------
+
+ifeq ($(RUNNER),test_progs)
+EXTRA_SOURCES := test_progs.c cgroup_helpers.c trace_helpers.c		\
+		 network_helpers.c testing_helpers.c btf_helpers.c	\
+		 cap_helpers.c unpriv_helpers.c sysctl_helpers.c	\
+		 netlink_helpers.c jit_disasm_helpers.c io_helpers.c	\
+		 test_loader.c xsk.c disasm.c disasm_helpers.c		\
+		 json_writer.c bpftool_helpers.c usdt_1.c usdt_2.c
+LIB_SOURCES := find_bit.c
+EXTRA_HDRS := flow_dissector_load.h ip_check_defrag_frags.h		\
+	      $(VERIFY_SIG_HDR) $(LIBARENA_SKEL) $(LIBARENA_ASAN_SKEL)
+EXTRA_FILES := $(OUTPUT)/urandom_read $(OUTPUT)/liburandom_read.so	\
+	       $(OUTPUT)/xdp_synproxy $(OUTPUT)/sign-file		\
+	       $(OUTPUT)/uprobe_multi					\
+	       $(TEST_KMOD_TARGETS)					\
+	       ima_setup.sh $(VERIFY_SIG_SETUP)				\
+	       $(wildcard progs/btf_dump_test_case_*.c)			\
+	       $(wildcard progs/*.bpf.o)
+endif
+
+ifeq ($(RUNNER),test_maps)
+EXTRA_SOURCES := test_maps.c testing_helpers.c
+endif
+
+# Note on the unflavored runners, which share $(OUTPUT) with the main
+# Makefile: helper objects the main Makefile also uses (and lists in the
+# runner prerequisites) are already built and fresh when a runner starts,
+# so the rules below find them up to date and never write them.
+
+TESTS_HDR := $(TESTS_DIR)/tests.h
+
+TEST_SRCS  := $(notdir $(wildcard $(TESTS_DIR)/*.c))
+TEST_OBJS  := $(patsubst %.c,$(RDIR)/%.test.o,$(TEST_SRCS))
+TEST_DEPS  := $(TEST_OBJS:.o=.d)
+EXTRA_OBJS := $(patsubst %.c,$(RDIR)/%.o,$(filter %.c,$(EXTRA_SOURCES)))
+LIB_OBJS   := $(patsubst %.c,$(RDIR)/%.o,$(LIB_SOURCES))
+
+# ---------------------------------------------------------------------
+# BPF objects and skeletons
+# ---------------------------------------------------------------------
+
+ifneq ($(BPF_BUILD),none)
+
+BPF_SRCS := $(notdir $(wildcard progs/*.c))
+BPF_OBJS := $(patsubst %.c,$(RDIR)/%.bpf.o,$(BPF_SRCS))
+
+# Which tests want a light skeleton is derived from the test sources
+# themselves: an '#include "foo.lskel.h"' line is the single source of
+# truth. Tests that include both foo.skel.h and foo.lskel.h get both.
+skel_includes = $(shell grep -rhoE '"[a-zA-Z0-9_]+\.$(1)"' prog_tests benchs map_tests *.c *.cpp 2>/dev/null)
+LSKEL_BASES := $(sort $(patsubst "%.lskel.h",%,$(call skel_includes,lskel.h)))
+SKEL_DEMANDED := $(sort $(patsubst "%.skel.h",%,$(call skel_includes,skel.h)))
+
+# Signing is not expressed in the include, so the signed subset stays
+# explicit.
+LSKELS_SIGNED := fentry_test fexit_test atomics
+
+# BPF objects that cannot have a regular skeleton, plus lskel-only tests
+# (light-skeleton users whose sources never include the .skel.h form).
+SKEL_BLACKLIST := btf__% test_pinning_invalid.c test_sk_assign.c \
+		  $(addsuffix .c,$(filter-out $(SKEL_DEMANDED),$(LSKEL_BASES)))
+
+# Skeletons made by linking multiple BPF objects together. The %-deps
+# mapping is the only hand-maintained metadata: it cannot be derived from
+# the sources.
+test_static_linked.skel.h-deps := test_static_linked1.bpf.o test_static_linked2.bpf.o
+linked_funcs.skel.h-deps := linked_funcs1.bpf.o linked_funcs2.bpf.o
+linked_vars.skel.h-deps := linked_vars1.bpf.o linked_vars2.bpf.o
+linked_maps.skel.h-deps := linked_maps1.bpf.o linked_maps2.bpf.o
+# In the subskeleton case, we want the test_subskeleton_lib.subskel.h file
+# but that's created as a side-effect of the skel.h generation.
+test_subskeleton.skel.h-deps := test_subskeleton_lib2.bpf.o test_subskeleton_lib.bpf.o test_subskeleton.bpf.o
+test_subskeleton_lib.skel.h-deps := test_subskeleton_lib2.bpf.o test_subskeleton_lib.bpf.o
+test_usdt.skel.h-deps := test_usdt.bpf.o test_usdt_multispec.bpf.o
+tracing_multi.skel.h-deps := tracing_multi_attach.bpf.o tracing_multi_check.bpf.o
+tracing_multi_module.skel.h-deps := tracing_multi_attach_module.bpf.o tracing_multi_check.bpf.o
+tracing_multi_intersect.skel.h-deps := tracing_multi_intersect_attach.bpf.o tracing_multi_check.bpf.o
+tracing_multi_session.skel.h-deps := tracing_multi_session_attach.bpf.o tracing_multi_check.bpf.o
+
+LINKED_SKELS := test_static_linked.skel.h linked_funcs.skel.h		\
+		linked_vars.skel.h linked_maps.skel.h			\
+		test_subskeleton.skel.h test_subskeleton_lib.skel.h	\
+		test_usdt.skel.h tracing_multi.skel.h			\
+		tracing_multi_module.skel.h				\
+		tracing_multi_intersect.skel.h				\
+		tracing_multi_session.skel.h
+LINKED_BPF_SRCS := $(patsubst %.bpf.o,%.c,$(foreach skel,$(LINKED_SKELS),$($(skel)-deps)))
+
+SKELS := $(patsubst %.c,$(RDIR)/%.skel.h,				\
+	   $(filter-out $(SKEL_BLACKLIST) $(LINKED_BPF_SRCS),$(BPF_SRCS)))
+LSKELS_H        := $(patsubst %,$(RDIR)/%.lskel.h,$(filter-out $(LSKELS_SIGNED),$(LSKEL_BASES)))
+LSKELS_SIGNED_H := $(patsubst %,$(RDIR)/%.lskel.h,$(LSKELS_SIGNED))
+LINKED_SKELS_H  := $(addprefix $(RDIR)/,$(LINKED_SKELS))
+ALL_SKELS := $(SKELS) $(LSKELS_H) $(LSKELS_SIGNED_H) $(LINKED_SKELS_H)
+
+HEADERS_FOR_BPF_OBJS := $(wildcard $(BPFDIR)/*.bpf.h)			\
+			$(wildcard $(CURDIR)/libarena/include/*.[ch])	\
+			$(addprefix $(BPFDIR)/,	bpf_core_read.h		\
+						bpf_endian.h		\
+						bpf_helpers.h		\
+						bpf_tracing.h)
+
+# Per-source BPF compilation flag overrides ($<-CFLAGS / $<-$(FLAVOR)-CFLAGS).
+# GCC cannot disable "declaration of anonymous struct in parameter list"
+# warnings triggered by these (technically legal) test programs.
+progs/btf_dump_test_case_bitfields.c-bpf_gcc-CFLAGS := -Wno-error
+progs/btf_dump_test_case_namespacing.c-bpf_gcc-CFLAGS := -Wno-error
+progs/btf_dump_test_case_packing.c-bpf_gcc-CFLAGS := -Wno-error
+progs/btf_dump_test_case_padding.c-bpf_gcc-CFLAGS := -Wno-error
+progs/btf_dump_test_case_syntax.c-bpf_gcc-CFLAGS := -Wno-error
+
+ifeq ($(BPF_BUILD),gcc)
+BPF_CC_MSG := GCC-BPF
+# Note: sys includes come from the *host* gcc, not $(BPF_GCC) - bpf-gcc's
+# own search path lacks the host's asm-generic/ headers. Computed once
+# here: bpf_cc below is deferred and expands per compiled object.
+GCC_SYS_INCLUDES := $(call get_sys_includes,gcc,)
+bpf_cc = $(BPF_GCC) $(BPF_CFLAGS) $(GCC_SYS_INCLUDES)			\
+	 $(BPF_DEFINES) $($<-CFLAGS) $($<-$(FLAVOR)-CFLAGS)		\
+	 -DBPF_NO_PRESERVE_ACCESS_INDEX -Wno-attributes -O2 -c $< -o $@
+else
+BPF_CC_MSG := CLNG-BPF
+bpf_cc = $(CLANG) $(BPF_CFLAGS) $(CLANG_CFLAGS) $(BPF_DEFINES)		\
+	 $($<-CFLAGS) $($<-$(FLAVOR)-CFLAGS)				\
+	 -O2 $(BPF_TARGET_ENDIAN) -c $< -mcpu=$(BPF_MCPU) -o $@
+endif
+
+$(BPF_OBJS): $(RDIR)/%.bpf.o: progs/%.c progs/*.h			\
+	     $(INCLUDE_DIR)/vmlinux.h $(HEADERS_FOR_BPF_OBJS)		\
+	     | $(RDIR) $(BPFOBJ)
+	$(call msg,$(BPF_CC_MSG),$(BINARY),$@)
+	$(Q)$(bpf_cc) $(call skip_on_fail,BPF)
+
+# gen_bpf_skel.sh picks the bpftool binary up from the environment.
+export BPFTOOL
+GEN_SKEL := ./gen_bpf_skel.sh
+
+$(SKELS): $(RDIR)/%.skel.h: $(RDIR)/%.bpf.o $(BPFTOOL) | $(RDIR)
+	$(Q)$(GEN_SKEL) --tag '$(BINARY)' --name $* --infix linked	\
+		--skel $@ --subskel $(@:.skel.h=.subskel.h) $<		\
+		$(call skip_on_fail,SKEL,$(@:.skel.h=.subskel.h))
+
+$(LSKELS_H): $(RDIR)/%.lskel.h: $(RDIR)/%.bpf.o $(BPFTOOL) | $(RDIR)
+	$(Q)$(GEN_SKEL) --tag '$(BINARY)' --name $*_lskel --infix llinked \
+		--lskel --skel $@ $< $(call skip_on_fail,SKEL)
+
+$(LSKELS_SIGNED_H): $(RDIR)/%.lskel.h: $(RDIR)/%.bpf.o $(BPFTOOL)	\
+		    $(PRIVATE_KEY) $(VERIFICATION_CERT) | $(RDIR)
+	$(Q)$(GEN_SKEL) --tag '$(BINARY) (signed)' --name $*_lskel	\
+		--infix llinked						\
+		--sign '-S -k $(PRIVATE_KEY) -i $(VERIFICATION_CERT)'	\
+		--skel $@ $< $(call skip_on_fail,SKEL)
+
+.SECONDEXPANSION:
+$(LINKED_SKELS_H): $(RDIR)/%.skel.h: $$(addprefix $(RDIR)/,$$($$*.skel.h-deps)) \
+		   $(BPFTOOL) | $(RDIR)
+	$(Q)$(GEN_SKEL) --tag '$(BINARY)' --name $* --infix linked	\
+		--skel $@ --subskel $(@:.skel.h=.subskel.h)		\
+		$(addprefix $(RDIR)/,$($*.skel.h-deps))			\
+		$(call skip_on_fail,SKEL,$(@:.skel.h=.subskel.h))
+
+endif # BPF_BUILD != none
+
+.PHONY: skels
+skels: $(ALL_SKELS)
+
+# When the compiler generates a %.d file, only skel basenames (not full
+# paths) are specified as prerequisites for the corresponding %.o file.
+# The vpath directives instruct make to also search for skel files in
+# $(RDIR) if they are not present in the working directory.
+vpath %.skel.h $(RDIR)
+vpath %.lskel.h $(RDIR)
+vpath %.subskel.h $(RDIR)
+
+# ---------------------------------------------------------------------
+# Test objects and runner binary
+# ---------------------------------------------------------------------
+
+$(RDIR)/resolve_btfids.test.o: $(RESOLVE_BTFIDS) $(RDIR)/btf_data.bpf.o
+$(RDIR)/resolve_btfids.test.o: private TEST_NEEDS_BTFIDS = 1
+
+# Compile individual test files.
+# Note: we cd into the output directory to ensure embedded BPF objects
+# are found. The %.test.d dependency files are a side effect of the -MMD
+# below; the separate no-recipe rule for them orders the first
+# compilation after skeleton generation and re-orders test object
+# compilation when headers change.
+$(TEST_OBJS): $(RDIR)/%.test.o: $(TESTS_DIR)/%.c | $(RDIR)/%.test.d
+	$(call msg,TEST-OBJ,$(BINARY),$@)
+	$(Q)(cd $(@D) && $(CC) -I. $(CFLAGS) -MMD -MT $@ -c $(CURDIR)/$< -o $(@F)) \
+		$(if $(PERMISSIVE_TESTS),$(call skip_on_fail,TEST))
+	$(if $(TEST_NEEDS_BTFIDS),					\
+		$(Q)if [ -f $@ ]; then					\
+		$(if $(filter 1,$(V)),true,printf '  %-8s%s %s\n' "BTFIDS" " [$(BINARY)]" "$(notdir $@)"); \
+		$(RESOLVE_BTFIDS) --btf $(RDIR)/btf_data.bpf.o $@;	\
+		$(RESOLVE_BTFIDS) --patch_btfids $@.BTF_ids $@;		\
+		fi)
+
+$(TEST_DEPS): $(RDIR)/%.test.d: $(TESTS_DIR)/%.c $(EXTRA_HDRS) $(BPFOBJ) \
+	      | $(RDIR) $(ALL_SKELS)
+
+include $(wildcard $(TEST_DEPS))
+
+$(RDIR)/jit_disasm_helpers.o: CFLAGS += $(LLVM_CFLAGS)
+
+$(EXTRA_OBJS): $(RDIR)/%.o: %.c $(EXTRA_HDRS) $(TESTS_HDR) $(BPFOBJ) | $(RDIR)
+	$(call msg,EXT-OBJ,$(BINARY),$@)
+	$(Q)$(CC) $(CFLAGS) -c $< -o $@
+
+$(LIB_OBJS): $(RDIR)/%.o: $(TOOLSDIR)/lib/%.c | $(RDIR)
+	$(call msg,LIB-OBJ,$(BINARY),$@)
+	$(Q)$(CC) $(CFLAGS) -c $< -o $@
+
+# Non-flavored in-srctree builds receive special treatment, in particular
+# we do not need to copy extra resources (see e.g. test_btf_dump_case()).
+.PHONY: extras
+extras: $(if $(PERMISSIVE),$(wildcard $(EXTRA_FILES)),$(EXTRA_FILES)) | $(RDIR)
+ifneq ($(FLAVOR):$(OUTPUT),:$(CURDIR))
+ifneq ($(strip $(EXTRA_FILES)),)
+	$(call msg,EXT-COPY,$(BINARY),$(notdir $(EXTRA_FILES)))
+	$(Q)rsync -aq $(if $(PERMISSIVE),--ignore-missing-args) $(EXTRA_FILES) $(RDIR)/
+endif
+endif
+
+# In permissive mode, link whatever test objects were successfully built
+# (their compilation may have been skipped); the wildcard in the recipe
+# picks up the survivors at link time.
+# Prerequisite order is also link order (test objects, extra objects,
+# libbpf, lib objects).
+$(OUTPUT)/$(BINARY): $(if $(PERMISSIVE_TESTS),,$(TEST_OBJS))		\
+		     $(EXTRA_OBJS) $(BPFOBJ) $(LIB_OBJS)		\
+		     $(TRUNNER_BPFTOOL) $(OUTPUT)/veristat		\
+		     | extras $(BPF_OBJS)				\
+		     $(if $(PERMISSIVE_TESTS),$(TEST_OBJS))
+	$(call msg,BINARY,,$@)
+	$(Q)$(CC) $(CFLAGS)						\
+		$(if $(PERMISSIVE_TESTS),$(wildcard $(TEST_OBJS)))	\
+		$(filter %.a %.o,$^)					\
+		$(LDLIBS) $(LLVM_LDLIBS) $(LDFLAGS) $(LLVM_LDFLAGS) -o $@
+	$(Q)ln -sf $(if $(FLAVOR),..,.)/tools/build/bpftool/$(USE_BOOTSTRAP)bpftool \
+		   $(RDIR)/bpftool
diff --git a/tools/testing/selftests/bpf/gen_bpf_skel.sh b/tools/testing/selftests/bpf/gen_bpf_skel.sh
new file mode 100755
index 00000000000000..0b237f63967dd6
--- /dev/null
+++ b/tools/testing/selftests/bpf/gen_bpf_skel.sh
@@ -0,0 +1,97 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Link BPF object file(s) with "bpftool gen object" and generate a
+# skeleton (or light skeleton) header from the result.
+#
+# Usage:
+#   BPFTOOL=<bpftool> gen_bpf_skel.sh --name NAME --skel OUT [options] OBJ...
+#
+#   --name NAME       skeleton object name ("name NAME" for bpftool)
+#   --skel OUT        output header ("foo.skel.h" or "foo.lskel.h")
+#   --subskel OUT     also generate a subskeleton header into OUT
+#   --lskel           generate a light skeleton (bpftool gen skeleton -L)
+#   --sign 'ARGS'     extra "gen skeleton" args for signing (-S -k .. -i ..)
+#   --tag TAG         build-log tag, e.g. "test_progs-no_alu32"
+#   --infix STR       intermediate file infix (default: linked). Callers
+#                     generating both a .skel.h and a .lskel.h from the
+#                     same .bpf.o MUST use distinct infixes, or parallel
+#                     builds race on the intermediate files.
+#   --no-determinism-check
+#                     link the inputs once instead of three times.
+#                     By default the object is re-linked twice more and
+#                     the 2nd and 3rd results compared, as a regression
+#                     test for the determinism of "bpftool gen object".
+#
+# The bpftool binary is taken from $BPFTOOL (default: bpftool from PATH).
+# On failure all outputs and intermediates are removed and the script
+# exits non-zero; permissive-mode skipping is the caller's business
+# (see skip_on_fail in Makefile.buildvars).
+
+set -u
+
+bpftool=${BPFTOOL:-bpftool}
+name='' skel='' subskel='' tag='' sign_args='' infix=linked
+lskel=0 det_check=1
+
+while [ $# -gt 0 ]; do
+	case "$1" in
+	--name)		name=$2; shift 2 ;;
+	--skel)		skel=$2; shift 2 ;;
+	--subskel)	subskel=$2; shift 2 ;;
+	--tag)		tag=$2; shift 2 ;;
+	--infix)	infix=$2; shift 2 ;;
+	--sign)		sign_args=$2; shift 2 ;;
+	--lskel)	lskel=1; shift ;;
+	--no-determinism-check) det_check=0; shift ;;
+	--)		shift; break ;;
+	-*)		echo "$0: unknown option: $1" >&2; exit 1 ;;
+	*)		break ;;
+	esac
+done
+
+if [ -z "$name" ] || [ -z "$skel" ] || [ $# -eq 0 ]; then
+	echo "usage: $0 --name NAME --skel OUT [options] OBJ..." >&2
+	exit 1
+fi
+
+base=${skel%.skel.h}
+base=${base%.lskel.h}
+t1=$base.${infix}1.o
+t2=$base.${infix}2.o
+t3=$base.${infix}3.o
+
+fail() {
+	rm -f "$skel" ${subskel:+"$subskel"} "$t1" "$t2" "$t3"
+	exit 1
+}
+
+if [ $# -gt 1 ]; then
+	printf '  %-12s %s\n' 'LINK-BPF' "${tag:+[$tag] }$(basename "$base").bpf.o" >&2
+fi
+printf '  %-12s %s\n' 'GEN-SKEL' "${tag:+[$tag] }$(basename "$skel")" >&2
+
+"$bpftool" gen object "$t1" "$@" || fail
+final=$t1
+if [ "$det_check" -eq 1 ]; then
+	"$bpftool" gen object "$t2" "$t1" || fail
+	"$bpftool" gen object "$t3" "$t2" || fail
+	if ! cmp -s "$t2" "$t3"; then
+		echo "$0: bpftool gen object is not deterministic for $skel" >&2
+		fail
+	fi
+	final=$t3
+fi
+
+flags=
+[ "$lskel" -eq 1 ] && flags=-L
+# $sign_args intentionally unquoted: it is a list of bpftool arguments
+# shellcheck disable=SC2086
+"$bpftool" gen skeleton $sign_args $flags "$final" name "$name" > "$skel" || fail
+
+if [ -n "$subskel" ]; then
+	"$bpftool" gen subskeleton "$final" name "$name" > "$subskel" || fail
+fi
+
+rm -f "$t1" "$t2" "$t3"
+exit 0

3/2 2026/07/22 13:29 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 31368
							output: 64
							thoughts: 265
						

Thoughts:
**My Assessment of This BPF Selftests Patch**

Okay, so I'm looking at a patch here, and it's all about refactoring the build system for the BPF selftests. Specifically, it's messing with the `Makefile` in `tools/testing/selftests/bpf/`. They're splitting it up into `Makefile.buildvars`, `Makefile.runner`, and adding `gen_bpf_skel.sh`. This is a pretty standard approach for complex build systems; it makes things more organized and potentially speeds up parallel builds, which I appreciate.

I've taken a close look, and confirmed that the changes are isolated to this testing infrastructure. It doesn't touch any core kernel code, any drivers, or anything architecture-specific. It's strictly about how the tests themselves are built and run.

Therefore, given that it's just build system changes for selftests, I'm going to flag this as not being relevant for fuzzing.



4/2 2026/07/22 13:29 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch only modifies the build system for BPF selftests (Makefiles and a shell script). It does not make any functional changes to the kernel code.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch only modifies the build system for BPF selftests (Makefiles and a shell script). It does not make any functional changes to the kernel code.",
  "WorthFuzzing": false
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)