使用Makefile通配符跨目录编译内核模块.ko时遇未定义符号问题
解决内核模块自动收集源文件时的符号未定义问题
这个问题我之前也碰到过,本质是内核Kbuild的路径解析逻辑和普通Makefile不一样,我来帮你理清楚:
问题根源
你用$(wildcard src/*.c)自动收集src目录下的源文件时,虽然控制台输出的foo-objs和手动指定的一样,但实际src下的文件并没有被正确编译,导致出现InitFoo1和CleanupFoo1未定义的警告。
核心原因有两个:
- Kbuild的路径上下文差异:你原来的
wildcard是在当前模块目录(foo目录)的shell环境下展开的,当Kbuild切换到内核源码树目录执行构建时,它对相对路径的解析逻辑和当前shell环境不同,无法正确找到src下的源文件。 - 冗余的目标文件:你的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
相关产品推荐
相关产品推荐

