当前位置: 首页 > news >正文

【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
    指定目标架构(如 armx86_64mips 等)

  • 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. 典型工作流程

  1. 编写驱动代码
    保存为 demo_driver.c

  2. 编写 Makefile
    参考上述模板

  3. 编译模块

    
    make        # 默认编译
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-  # 交叉编译

  4. 加载测试

    
    sudo insmod demo_driver.ko
    dmesg | tail  # 查看内核日志

  5. 清理构建

    
    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 应包含以下核心部分:

  1. 模块目标定义 (obj-m)

  2. 内核源码路径 (KDIR)

  3. 编译规则 (调用 make -C)

  4. 清理规则

通过灵活组合参数和选项,可满足从单模块开发到复杂跨平台编译的各种需求。

相关文章:

  • 方案精读:华为数字化转型实践案例合集【附全文阅读】
  • 关于Diamond机械手的运动学与动力学的推导
  • Java 开发玩转 MCP:从 Claude 自动化到 Spring AI Alibaba 生态整合
  • list的使用
  • 基于C++(MFC)实现的文件管理系统
  • Visual Studio C++引入第三方库
  • Spring HTTP Interface 入门案例介绍
  • sentinel安装部署及测试--实践
  • 在Vmware15(虚拟机免费) 中安装纯净win10详细过程
  • RK3588 实现音视频对讲
  • Oracle 12.1.0.2补丁安装全流程
  • 如何使用3DMAX插件PFSpliner将3D对象转化为艺术样条线?
  • AOP的基本应用案例---统计每个函数的执行时间
  • IntelliJ IDEA 项目导入后 Java 文件图标显示为红色小写 j 的解决方法
  • LVS+keepalived搭建高可用架构
  • 资源直方图与资源平衡技术在资源约束下的作用是什么?
  • Paramiko 使用教程
  • [特殊字符] UnionFS(联合文件系统)原理解析:容器背后的存储技术
  • css button 点击效果
  • Github 2025-04-17 Go开源项目日报 Top9
  • 自强!助残!全国200个集体和260名个人受到表彰
  • 深圳南澳码头工程环评将再次举行听证会,项目与珊瑚最近距离仅80米
  • 证券日报:降准今日正式落地,年内或还有降准空间
  • 马上评|“为偶像正名”的正确做法是什么
  • 人民日报评外卖平台被约谈:摒弃恶性竞争,实现行业健康发展
  • 福建厦门市副市长、市公安局局长陈育煌出任吉林省公安厅厅长