文章欢迎转载,保留联系信息,以便交流。
邮箱:eabi010@gmail.com
主页:www.ielife.cn(爱嵌论坛——嵌入式技术学习交流)
博客:blog.csdn.net/ielife
Linux内核如何支持在非源码目录下编译的模块呢?是这篇研究的方向。
一:.查看内核文档Documentation/kbuild/modules.txt
2. How to build external modules
4. Creating a kbuild file for an external module
第2章节说明编译模块所要使用的命令
make -C $KDIR M=`pwd`
make -C $KDIR M=`pwd` modules
第4章节说明如何写一个外部模块的Makefile
215 --> filename: Makefile
216 ifneq ($(KERNELRELEASE),)
217 # kbuild part of makefile
218 obj-m := mydrivers.o
220
221 else
222 # Normal Makefile
224 KERNELDIR := /lib/modules/`uname -r`/build
225 all::
226 $(MAKE) -C $(KERNELDIR) M=`pwd`
232 endif
以上的Makefile与驱动代码mydrivers.c放在同一目录下,执行make即可
其中,make时先执行else中的双冒号目标all,执行命令make -C $(KERNELDIR) M=`pwd`
make -C $(KERNELDIR)表示切换到$(KERNELDIR)目录下执行Makefile文件,因此$(KERNELDIR)需要指定你的源代码树的根目录,根据自己的情况修改224行
而ifneq中的obj-m会在源码树的执行中再次被读取
二:跟踪Makefile的执行流程
由于已经make -C到Linux内核Makefile中,因此分析内核源码目录下的Makefile文件,首先传递进来的M=`pwd`,使得Makefile中会执行
72 ifdef M
73 ifeq ("$(origin M)", "command line")
74 KBUILD_EXTMOD := $(M)
75 endif
76 endif
KBUILD_EXTMOD变量会因为make时传递进来的M而被赋值为外部模块所在的目录
KBUILD_EXTMOD不为空,会自动加上modules选项
137 ifeq ($(KBUILD_EXTMOD),)
138 _all: all
139 else
140 _all: modules
141 endif
因此会执行modules目标
三:modules目标
内核中有两种编译modules的方法,所以有两个地方有modules的目标规则,一个是基于源码树编译的modules,一个是基于外部编译模块使用的modules
区分两者主要是KBUILD_EXTMOD变量,看Makefile中的内容
559 ifeq ($(KBUILD_EXTMOD),)
......
958 PHONY += modules
959 modules: $(vmlinux-dirs) $(if $(KBUILD_BUILTIN),vmlinux)
960 @echo ' Building modules, stage 2.';
961 $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost
......
1165 else
......
1185 KBUILD_MODULES := 1
1186 PHONY += crmodverdir
1187 crmodverdir:
1188 $(Q)mkdir -p $(MODVERDIR)
1189 $(Q)rm -f $(MODVERDIR)/*
1190
1191 PHONY += $(objtree)/Module.symvers
1192 $(objtree)/Module.symvers:
1193 @test -e $(objtree)/Module.symvers || ( \
1194 echo; \
1195 echo " WARNING: Symbol version dump $(objtree)/Module.symvers"; \
1196 echo " is missing; modules will have no dependencies and modversions ."; \
1197 echo )
1198
1199 module-dirs := $(addprefix _module_,$(KBUILD_EXTMOD))
1200 PHONY += $(module-dirs) modules
1201 $(module-dirs): crmodverdir $(objtree)/Module.symvers
1202 $(Q)$(MAKE) $(build)=$(patsubst _module_%,%,$@)
1203
1204 modules: $(module-dirs)
1205 @echo ' Building modules, stage 2.';
1206 $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost
由于KBUILD_EXTMOD不为空,所以执行1165行的else。在1204行会看到外部模块modules的目标规则,会发现modules的建立需要两个stage
stage1主要完成mydrivers.o,stage2通过Makefile.modpost脚本实现.o文件到.ko文件的转化
四:stage1编译过程
crmodverdir 创建$(MODVERDIR)变量所定义的目录,以下为变量定义处
339 export MODVERDIR := $(if $(KBUILD_EXTMOD),$(firstword $(KBUILD_EXTMOD))/).tmp_versions
Module.symvers包含所有被内核构建所导出的符号。
根据以上1185~1202可知道,.o文件是由命令$(Q)$(MAKE) $(build)=$(patsubst _module_%,%,$@)
其中build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj,及build = -f $(srctree)/scripts/Makefile.build obj=$(KBUILD_EXTMOD)
即是传递过来的M=所指定的路径,scripts/Makefile.build文件是Linux内核执行命令的接口文件,而顶层的Makefile一般不执行命令。
由于是make -f scripts/Makefile.build会把后面的文件当作Makefile来解析,因此需要查看Makefile.build文件中的内容
83 __build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
84 $(if $(KBUILD_MODULES),$(obj-m)) \
85 $(subdir-ym) $(always)
86 @:
其中,只有obj-m变量被赋值,因为我们外部写的模块会给出obj-m所对应要编译的选项名字通过script/Makefile.lib的解析,会执行以下部分(静态模式匹配):
200 # Built-in and composite module parts
201 $(obj)/%.o: $(src)/%.c FORCE
202 $(call cmd,force_checksrc)
203 $(call if_changed_rule,cc_o_c)
204
205 # Single-part modules are special since we need to mark them in $(MODVERDIR)
206
207 $(single-used-m): $(obj)/%.o: $(src)/%.c FORCE
208 $(call cmd,force_checksrc)
209 $(call if_changed_rule,cc_o_c)
210 @{ echo $(@:.o=.ko); echo $@; } > $(MODVERDIR)/$(@F:.o=.mod)
这样就生成了.o文件,其中的变量定义都来自于script/Makefile.lib中,例如if_changed_rule就是其中定义的,call函数会调用cmd_cc_o_c生成mydrivers.o文件,当然在210行还会生成相应的mod.c文件
第二个阶段需要Module.symvers,mod.c和stage1阶段的.o文件来生成最终的.ko文件
五:stage2编译过程
模块的第二次编译来自modules规则中的$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost
Makefile.modpost是通过.o生成.ko文件的工具
以上并没有把分析过程的细节,包括Makefile中的语法,及依赖的展开和包含的头文件中定义的相关重要变量和内容做说明,仅是以流程为主,
这些语法在make手册中都会有,其实只要明白了make的语法,顺藤摸瓜就知道外部模块构建的过程了
总结就是,先通过内核的Doc来编写一个外部的Makefile
其次明白内核Makefile的结构,顶层Makefile是确定执行的目标类型
Makefile.build才是真正建立规则的入口,而其中使用到的判断逻辑和值的定义来自Makefile.include和Makefile.lib
最终执行Makefile.modpost编译.o为.ko文件