[PATCH] tools/resolve_btfids: fix OUTPUT leakage from kselftest causing corrupted build paths

From: Jiangshan Yi

Date: Thu Jul 16 2026 - 04:58:06 EST


When building kselftest-all, the kselftest Makefile passes OUTPUT as a
command-line variable to each test directory:

tools/testing/selftests/Makefile:
$(MAKE) OUTPUT=$$BUILD/$$TARGET -C $$TARGET

GNU Make automatically exports command-line variables to all sub-makes.
This OUTPUT propagates through the entire build chain:

kselftest/mm (OUTPUT=.../selftests/mm)
-> gen_mods_dir -> page_frag (kernel module build)
-> tools/bpf/resolve_btfids (host tool dependency)

The resolve_btfids Makefile uses a conditional assignment:

OUTPUT ?= $(srctree)/tools/bpf/resolve_btfids/

However, ?= cannot override a variable inherited from a parent build's
command line or environment. The inherited OUTPUT points to the wrong
directory (the mm selftest directory) and lacks a trailing slash.

The tools/build Makefile.build system concatenates $(OUTPUT) with
filenames without a separating slash in its pattern rules:

$(OUTPUT)%.o: %.c FORCE

When OUTPUT lacks a trailing '/', this produces corrupted paths such
as ".../selftests/mmmain.o" instead of ".../selftests/mm/main.o".
Meanwhile, the resolve_btfids Makefile itself uses $(OUTPUT)/filename
(with an explicit slash), so the HOSTLD step produces the file at a
different path than where the LINK step looks for it:

HOSTLD produces: .../selftests/mmresolve_btfids-in.o (no slash)
LINK looks for: .../selftests/mm/resolve_btfids-in.o (with slash)

This results in a linker error:

ld: cannot find .../resolve_btfids-in.o: No such file or directory

Fix this by checking the origin of OUTPUT. When it is "command line"
or "environment" (indicating leakage from a parent build such as
kselftest), use 'override' to reset it to the correct directory. When
built normally via the kernel build system, OUTPUT is set by
scripts/Makefile.include with origin "file" from the O= parameter,
which is correct and remains unaffected.

The fix can be reproduced and verified with:

make -C tools/bpf/resolve_btfids clean
# Before fix: fails with corrupted paths
make OUTPUT=/path/to/selftests/mm -C tools/bpf/resolve_btfids
# After fix: succeeds, artifacts in resolve_btfids directory

# Also test environment variable leakage:
OUTPUT=/path/to/selftests/mm make -C tools/bpf/resolve_btfids

Signed-off-by: Jiangshan Yi <yijiangshan@xxxxxxxxxx>
---
tools/bpf/resolve_btfids/Makefile | 16 ++++++++++++++++
1 file changed, 16 insertions(+)

diff --git a/tools/bpf/resolve_btfids/Makefile b/tools/bpf/resolve_btfids/Makefile
index 7672208f65e4..eee88c73fd7a 100644
--- a/tools/bpf/resolve_btfids/Makefile
+++ b/tools/bpf/resolve_btfids/Makefile
@@ -26,6 +26,22 @@ HOSTAR ?= ar
HOSTPKG_CONFIG ?= pkg-config
CROSS_COMPILE =

+# If OUTPUT was inherited from a parent build's command line (e.g. from
+# kselftest), it points to the wrong directory and may lack a trailing
+# slash. The tools/build Makefile.build concatenates $(OUTPUT) with
+# filenames without a separating slash ($(OUTPUT)%.o), so a missing
+# trailing '/' produces corrupted paths such as "mmmain.o" instead of
+# "mm/main.o", and the linker cannot find resolve_btfids-in.o.
+#
+# When built via the kernel build system, OUTPUT is set by
+# scripts/Makefile.include (origin "file") from the O= parameter, which
+# is correct. Only a command-line origin indicates leakage from a
+# parent build and needs to be reset. Both "command line" and
+# "environment" origins indicate leakage; "file" (set by Makefile.include
+# from O=) and "undefined" are legitimate and must not be overridden.
+ifneq ($(filter command line environment,$(origin OUTPUT)),)
+override OUTPUT := $(srctree)/tools/bpf/resolve_btfids/
+endif
OUTPUT ?= $(srctree)/tools/bpf/resolve_btfids/

LIBBPF_SRC := $(srctree)/tools/lib/bpf/
--
2.25.1