You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Makefile通配符跨目录编译内核模块.ko时遇未定义符号问题

解决内核模块自动收集源文件时的符号未定义问题

这个问题我之前也碰到过,本质是内核Kbuild的路径解析逻辑和普通Makefile不一样,我来帮你理清楚:

问题根源

你用$(wildcard src/*.c)自动收集src目录下的源文件时,虽然控制台输出的foo-objs和手动指定的一样,但实际src下的文件并没有被正确编译,导致出现InitFoo1和CleanupFoo1未定义的警告。

核心原因有两个:

  1. Kbuild的路径上下文差异:你原来的wildcard是在当前模块目录(foo目录)的shell环境下展开的,当Kbuild切换到内核源码树目录执行构建时,它对相对路径的解析逻辑和当前shell环境不同,无法正确找到src下的源文件。
  2. 冗余的目标文件:你的Makefile里$(MODULE_NAME)-objs += main.o $(SRCS:.c=.o)这一行,因为SRCS包含了main.c,所以$(SRCS:.c=.o)会生成main.o,最终foo-objs里会重复出现main.o——虽然Kbuild会忽略重复项,但这是不必要的冗余。

另外还要注意你代码里的一个笔误:src/foo1.c里的清理函数写的是CleanupTest,但头文件foo1.h里声明的是CleanupFoo1,这即使编译了src/foo1.c也会导致CleanupFoo1符号未定义,记得修正这个函数名!

正确的解决方案

要让wildcard在内核Kbuild的上下文里执行,利用Kbuild提供的$(src)变量(它指向你的模块源代码目录)来正确查找文件。修改后的Makefile如下:

MODULE_NAME = foo
obj-m += $(MODULE_NAME).o

# 自动收集src目录下的所有.c文件
# 先通过$(src)指定模块目录,确保Kbuild能找到src子目录下的文件
SRCS := main.c $(wildcard $(src)/src/*.c)
# 去掉路径中的$(src)/前缀,转换为相对于模块目录的路径
SRCS := $(patsubst $(src)/%,%,$(SRCS))

# 直接生成目标文件列表,避免重复添加main.o
$(MODULE_NAME)-objs := $(SRCS:.c=.o)

all:
	# 查看最终的目标文件列表
	echo $($(MODULE_NAME)-objs)
	KCFLAGS="-I$(PWD)/include" \
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

代码解释

  • $(wildcard $(src)/src/*.c):让Kbuild在模块目录($(src))下的src子目录中查找所有.c文件,无论Kbuild在哪个工作目录执行,都能准确定位到源文件。
  • $(patsubst $(src)/%,%,$(SRCS)):将路径中的$(src)/前缀移除,得到main.c、src/foo1.c这类相对路径,确保Kbuild能正确关联目标文件和对应的源文件。
  • $(MODULE_NAME)-objs := $(SRCS:.c=.o):直接将源文件列表转换为目标文件列表,避免了重复添加main.o的问题。

效果验证

修改后运行make,你会发现src目录下的所有.c文件都会被自动编译。以后新增子组件时,只需要将对应的.c文件放到src目录,无需手动修改Makefile,完全满足你的需求。

内容的提问来源于stack exchange,提问作者cyng93

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:02:22