这篇文章侧重点在M=`pwd`的个人理解,不足之处希望大神斧正,谢谢。
一直以为M=`pwd`是make的参数,可是这个参数也写的够简单。最近重看<跟我写makefile>时,发现常有这样的makefile内容:
# Use 'make V=1' to see the full commands
ifdef V
ifeq ("$(origin V)", "command line")
KBUILD_VERBOSE = $(V)
endif
endif
出于好奇,make时顺手输入make V=1,接下来就是被整屛的编译信息包围。在makefile中,ifdef/endif用于判断变量是否定义,而origin函数则告诉make,这个变量是来自命令行输入还是文件中定义。那么,整句话被理解为:如果定义了变量V,并且V来自于命令行,那么编译时将输出详细信息。
由此,想到编译模块时,也会传入M=`pwd`,虽然网上清一色的解释,这是指定模块路径的参数,但总觉得有失严谨(当然,这篇文章也未必严谨)。百度了诺干页,终于整理出了我想要的答案:
在内核makefile中有如下一段(网摘,暂未考证,不过可信度很高)
# Use make M=dir to specify directory of external module to build
# Old syntax make ... SUBDIRS=$PWD is still supported
# Setting the environment variable KBUILD_EXTMOD take precedence
ifdef SUBDIRS
KBUILD_EXTMOD ?= $(SUBDIRS)
endif
ifeq ("$(origin M)", "command line")
KBUILD_EXTMOD := $(M)
endif
从这段内容理解如果make传入的命令行变量存在且是M,那么,变量KBUILD_EXTMOD变为变量M的值,即`pwd`。
从变量名可推知,这极有可能是编译外部模块相关的变量,以此为字眼继续搜索。网上有人列举编译内核时涉及的环境变量时提到:
KBUILD_EXTMOD:当编译外部模块时设置内核源码查找路径,目录可以用以下几种方式指定
1、在命令行用M=path
2、环境变量KBUILD_EXTMOD
3、环境变量SUBDIRS
用M=path会覆盖其它两种情况
从这至少可以确定,我的猜测还算靠谱,但是还是没有找到如何将代码编译成模块。那就继续搜索,最终在一篇名为<Linux Kbuild工作原理详细分析(以DVSDK生成PowerVR显卡模块为例)>(http://www.csdn123.com/html/blogs/20130709/33930.htm)文章中找到相关线索。
按作者原文,编译模块分2个阶段(其实这个在ldd上也提到,反正我一直不理解):step 1:编译生成mod.o文件;step 2:好像涉及到模块链接和生成sym信息。2.6以后的内核编译完模块后都会为模块链接生成符号信息,其中涉及到Makefile.modpost,关于这个的作用可以参考存在依赖关系的内核模块的编译问题(http://blog.chinaunix.net/uid-23769728-id-3792911.html)。看过这篇文章,再结合kbuild在根目录下寻找目标modules的代码:
PHONY += modules
modules: $(vmlinux-dirs) $(if $(KBUILD_BUILTIN),vmlinux)
$(Q)$(AWK) '!x[$$0]++' $(vmlinux-dirs:%=$(objtree)/%/modules.order) >
$(objtree)/modules.order
@$(kecho) ' Building modules, stage 2.';
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.fwinst obj=firmware __fw_modbuild
最后第二句是make -f Makefile.modpost,用这种非标准名字的makefile文件执行代码编译,而scripts/Makefile.modpost中这句:
include $(if $(wildcard $(KBUILD_EXTMOD)/Kbuild), \
$(KBUILD_EXTMOD)/Kbuild, $(KBUILD_EXTMOD)/Makefile)
意思是如果在KBUILD_EXTMOD路径下找到Kbuild就include $(KBUILD_EXTMOD)/Kbuild,否则include $(KBUILD_EXTMOD)/Makefile。这个路径正好包括我们自己的代码路径,因此,模块顺利的被编译链接。