【linux模块化编译驱动Makefile异常引发的惨案】
因内核模块名字与.c名字重合导致多文件编译“undefined!”异常,信息如下:
[liuyang:sdram]$./build.sh all
make -C /home/liuyang/tsingmicro/git/IPC/kernel M=/home/liuyang/tsingmicro/git/IPC/libs/drv/sdram modules
make[1]: 进入目录“/home/liuyang/tsingmicro/git/IPC/kernel”
CC [M] /home/liuyang/tsingmicro/git/IPC/libs/drv/sdram/./ts_ddr.o
CC [M] /home/liuyang/tsingmicro/git/IPC/libs/drv/sdram/./ddr_proc.o
LD [M] /home/liuyang/tsingmicro/git/IPC/libs/drv/sdram/ts_ddr.o
Building modules, stage 2.
MODPOST 1 modules
WARNING: "ddr_get_training_window" [/home/liuyang/tsingmicro/git/IPC/libs/drv/sdram/ts_ddr.ko] undefined!
WARNING: "g_ddr" [/home/liuyang/tsingmicro/git/IPC/libs/drv/sdram/ts_ddr.ko] undefined!
WARNING: "get_port_timeout" [/home/liuyang/tsingmicro/git/IPC/libs/drv/sdram/ts_ddr.ko] undefined!
WARNING: "debug_level" [/home/liuyang/tsingmicro/git/IPC/libs/drv/sdram/ts_ddr.ko] undefined!
CC /home/liuyang/tsingmicro/git/IPC/libs/drv/sdram/ts_ddr.mod.o
LD [M] /home/liuyang/tsingmicro/git/IPC/libs/drv/sdram/ts_ddr.ko
make[1]: 离开目录“/home/liuyang/tsingmicro/git/IPC/kernel”
cat build.sh >build
chmod a+x build
#错误的Makefile示例KCFLAGS += -DDEBUG
# Add user supplied CPPFLAGS, AFLAGS and CFLAGS as the last assignments
KBUILD_CPPFLAGS += $(KCPPFLAGS)
KBUILD_AFLAGS += $(KAFLAGS)
KBUILD_CFLAGS += $(KCFLAGS)TARGET := ts_ddr
CURRENT_PATH := $(shell pwd)
C_SRC := ./ts_ddr.c ./ddr_proc.c #此处错误导致编译和运行异常
#C_SRC := ./ts_ddr2.c
C_SRC_OBJ := $(C_SRC:.c=.o)
obj-m += $(TARGET).o
$(TARGET)-objs += $(C_SRC_OBJ)
build: kernel_modules
kernel_modules:$(MAKE) -C $(TARGET_KERNEL_DIR) M=$(CURRENT_PATH) modules
clean:$(MAKE) -C $(TARGET_KERNEL_DIR) M=$(CURRENT_PATH) clean
.PHONY: modules clean
后来又改成单文件编译,insmod后又出现“ module license "unspecified' taints kernel”异常信息,如下所示:
最后发现是ko名字不能与.c文件一致,否则生成.o的时候回产生异常。 最后整理一下编写 Linux 内核模块驱动 Makefile 的详细指南,包含各部分的解释和实际示例:
1. 最简版 Makefile
obj-m += hello.o # 将 hello.c 编译为内核模块 hello.ko
KDIR ?= /lib/modules/$(shell uname -r)/build # 内核源码路径all:make -C $(KDIR) M=$(PWD) modules # 调用内核构建系统clean:make -C $(KDIR) M=$(PWD) clean # 清理编译产物
2. 各组成部分详解
2.1 目标定义
-
obj-m
指定要编译为可加载模块的目标文件,后缀.o
会自动对应到.c
文件
示例:obj-m += module1.o # 编译 module1.c -> module1.ko obj-m += module2.o # 编译 module2.c -> module2.ko
-
多文件模块
若模块由多个源文件组成,需定义<module_name>-objs
:obj-m += complex_mod.o complex_mod-objs := file1.o file2.o # 合并 file1.c 和 file2.c
2.2 内核源码路径
-
KDIR
指定内核源码目录,通常为以下两种情况:# 方式1:使用当前运行中的内核源码 KDIR ?= /lib/modules/$(shell uname -r)/build# 方式2:自定义内核源码路径(适用于交叉编译) KDIR ?= /path/to/custom/kernel
2.3 编译指令
-
核心编译命令
make -C $(KDIR) M=$(PWD) modules -C $(KDIR):切换到内核源码目录M=$(PWD):告知内核构建系统模块源码位于当前目录modules:执行编译模块的目标
2.4 清理指令
make -C $(KDIR) M=$(PWD) clean
清除生成的 .ko
, .o
, .mod.*
等文件
3. 完整功能 Makefile 示例
# 模块定义
obj-m += demo_driver.o
demo_driver-objs := main.o helper.o # 多文件模块# 内核源码路径配置
ARCH ?= arm # 交叉编译架构(可选)
CROSS_COMPILE ?= arm-linux-gnueabihf- # 交叉编译工具链(可选)
KDIR ?= /lib/modules/$(shell uname -r)/build# 编译参数
EXTRA_CFLAGS += -DDEBUG # 添加调试宏
EXTRA_CFLAGS += -I$(PWD)/include # 添加自定义头文件路径# 编译目标
all:make ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KDIR) M=$(PWD) modulesclean:make -C $(KDIR) M=$(PWD) clean# 安装模块到系统(可选)
install:sudo cp demo_driver.ko /lib/modules/$(shell uname -r)/extra/sudo depmod -a
4. 关键参数说明
4.1 跨平台编译
-
ARCH
指定目标架构(如arm
,x86_64
,mips
等) -
CROSS_COMPILE
设置交叉编译工具链前缀(如arm-linux-gnueabihf-
)
4.2 编译选项
-
EXTRA_CFLAGS
添加自定义编译选项:EXTRA_CFLAGS += -O2 # 优化级别 EXTRA_CFLAGS += -Werror # 视警告为错误 EXTRA_CFLAGS += -DCUSTOM_DEBUG # 定义宏
4.3 模块签名(安全启动需配置)
CONFIG_MODULE_SIG=y # 启用模块签名
CONFIG_MODULE_SIG_ALL=y # 签名所有模块
5. 典型工作流程
-
编写驱动代码
保存为demo_driver.c
-
编写 Makefile
参考上述模板 -
编译模块
make # 默认编译 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- # 交叉编译
-
加载测试
sudo insmod demo_driver.ko dmesg | tail # 查看内核日志
-
清理构建
make clean
6. 常见问题解决
6.1 内核版本不匹配
-
现象:
make
报错提示内核源码版本不一致 -
解决:
# 确认当前运行内核版本 uname -r# 安装对应内核头文件(Debian/Ubuntu) sudo apt install linux-headers-$(uname -r)
6.2 交叉编译失败
-
检查项:
-
确认
ARCH
和CROSS_COMPILE
设置正确 -
确保交叉工具链已安装且路径正确
-
6.3 模块依赖问题
-
手动加载依赖:
sudo modprobe dependency_module
7. 高级技巧
7.1 条件编译
ifeq ($(DEBUG),1)EXTRA_CFLAGS += -DDEBUG
endif
使用方式:
make DEBUG=1
7.2 外部传参
# Makefile 中定义
param ?= default_value
EXTRA_CFLAGS += -DPARAM_VALUE=$(param)
调用方式:
make param=42
7.3 生成版本信息
demo_driver-objs := main.o version.o
version.o: generate_version.sh./generate_version.sh # 生成版本信息
总结
一个标准的内核模块 Makefile 应包含以下核心部分:
-
模块目标定义 (
obj-m
) -
内核源码路径 (
KDIR
) -
编译规则 (调用
make -C
) -
清理规则
通过灵活组合参数和选项,可满足从单模块开发到复杂跨平台编译的各种需求。