linux kernel makefile 分析 - 9 【转载】makefile.build的分析


上一篇:

1 ~ 8 介绍了 顶层的makefile 中的内容。 里面大量用到了 $(build)=xx 的方式。

虽然  $(build)=xx 用法网络已经有很多介绍了。但是,如果不 加入到 这个分析 系列里面,感觉这个分析系列不完整。

所以转载一个   $(build)=xx 用法 介绍,补充作 第 9 篇 。

原文: https://blog.csdn.net/lgjjeff/article/details/90511225

我们如果在kernel的顶层makefile中搜索一下,就会发现有大量如下的命令:
????$(MAKE) $(build)=xxx
build的定义在kbuild.include中,如上图2.7所示,此处再贴一下:
在这里插入图片描述
??????????????????????????????????????????图6.1
????即大量实际目标的编译都是调用scripts/Makefile.build完成的,其调用时传入的参数为obj=xxx,顶层makefile调用该命令时,传入的obj变量都是其包含的文件夹,下图6.2是其部分调用的实例:
在这里插入图片描述
??????????????????????????图6.2
????接下来我们逐段分析该文件的一些关键部分。如图6.3所示,该文件首先定义了一个src变量,并将其赋值为传入obj变量的值,然后定义了一个默认目标__build和一系列的变量定义。之所有将这些变都定义为空,是因为后面包含所在目录的Kbuild或makefile文件时,会为这些变量赋值,而此时先将它们清空,可以保证不会被环境变量中带入的值所干扰。
在这里插入图片描述
??????????????????????????????????????????图6.3
????下图6.4包含了两个文件,其中auto.conf不一定存在,但由于在include前面加了一个-,故即使该语句执行失败,makefile也不会出错退出。Kbuild.include定义了很多通用的变量和定义,我们将在用到的时候再介绍它们。
在这里插入图片描述
??????????????????????图6.4
????图6.5是一个重要的定义,kbuild-dir根据src变量(等于obj变量)是绝对路径还是相对路径来确定当前编译的目录,若为绝对路径则该目录即src变量的值,若为相对路径则该变量就是src变量相对于源码根目录的目录。kbuild-file即在该目录下查找Kbuild文件,若能找到,则使用kbuild作为该目录的编译文件,若找不到则使用该目录下的Makefile作为该目录的编译文件,然后将该文件包含进来。
在这里插入图片描述
??????????????????????????????????????????图6.5
????以drivers目录为例,该目录下不存在Kbuild文件,故kbuild-file为Makefile文件。我们打开该makefile可以看到该文件都是如图6.6所示的obj-xxx相关的定义。它们其实就是实际的编译目标,当然由于Makefile.build是一个通用性的框架,故后续还需要对它们做一定的处理,才能真正编译它们。
在这里插入图片描述
??????????????????????????????????????????图6.6
????接下来它包含了Makefile.lib文件,该文件定义了一些重要的变量,编译相关的flag以及命令,我们先看下其中在Makefile.build中要用到的一些重要变量(如图6.8和6.9所示)
在这里插入图片描述
??????????????????????????????????????????图6.7
????由于有些变量既定义成了obj-y,又定义成了obj-m,因此这里去除掉obj-m中重复的部分。如上所述,这里的obj-y和obj-m以及lib-y等都是在图6.6的makefile中定义的。同样,lib-y就是lib-y和lib-m变量中去除与obj-y重复部分的值。
在这里插入图片描述
??????????????????????????????????????????图6.8
????图6.9定义了另外一些变量,其中modorder为过滤出目录结构的obj-y和obj-m,然后将其依次定义为dir/modules.order的值,即为每个这样的目录定义一个dir/modules.order的文件名。subdir-y是从obj-y中过滤出目录结构的项,并去掉其最后的/构成的,同样subdir-m是从obj-m中过滤出目录结构的项,并去掉最后的/构成的,实际上就是获取它们的所有子目录,subdir-ym是它们的总和。
????obj-y的转换规则为:将原始obj-y中目录结构的目标,修改为dir/built-in.o,非目录结构的目标保持不变,即每个子目录中的目标都会链接成一个built-in.o,然后它们会和本目录下的非目录结构目标再链接成一个总的built-in.o。
????obj-m的转换规则为:将原始obj-m去掉路径部分,即过滤掉了目录结构的目标。其余的变量定义原理与它们差不多,此处不再赘述。
在这里插入图片描述
??????????????????????????????????????????图6.9
????再次回到图6.7的makefile.build中,host-progs相关定义主要用于编译一些运行在主机上工具的,不会影响我们理解编译的总体流程,故不再深入。图6.10是在指定了选项O=xxx之后才会执行的,该部分内容在介绍选项的时候再做详细说明。
在这里插入图片描述
??????????????????????????????????????????图6.10
????下面的代码定义了lib-target和builtin-target,当在该目录下的Makefile或Kbuild文件中定义了lib-y,lib-m或lib-目标,则表明该目录需要编译lib目标。lib目标会被统一定义为lib.a,同时还需要生成lib-sym.o目标。
????若有定义obj-y,obj-m,obj-,subdir-m或lib-target中的一个或多个,则还会编译built-in.o目标。如果编译vmlinux目标,则在需要编译的每个子目录下都会最终链接一个built-in.o目标,然后它们会被最终链接为完整的vmlinux。
????modorder-target即是图6.9中定义的modules.order值。
在这里插入图片描述
??????????????????????????????????????????图6.11
????图6.12非常关键,它定义了默认目标__build的依赖。当存在build in目标时,需要依赖于builtin-target(图6.11中的built-in.o),lib-target(图6.11中的lib.a)和extra-y目标,extra-y在某些目录下的Makefile中会有定义,如arch/arm64/kernel/Makefile中会有如图6.13的定义。
????当存在modules目标时,需要依赖于obj-m(图6.9)和modorder-target(图6.11)。显然,除了依赖本目录下的文件之外,它还需要依赖于本目录包含的子目录相关文件,它们被定义在subdir-ym(图6.9)中。
????arch/arm/kernel/Makefile:
在这里插入图片描述
??????????????????????????????????????????图6.12
在这里插入图片描述
??????????????????????????????????????????图6.13
????仔细观察我们可以发现,这些依赖都不是源文件,而是目标文件,库文件或者其包含的子目录,因此它们除了是依赖以外,其实也是别的依赖的目标。接下来我们逐个看下它们是如何被编译出来的吧。
1.builtin-target目标built-in.o
????它的定义如图6.14,其依赖为obj-y和FORCE。我们先看FORCE目标,它的定义在顶层Makefie中,如图6.15,它既没有依赖,也没有命令,且该目标也不是一个文件。因此若以FORCE作为某个目标的依赖,则总认为它是新的,所以以它作为依赖的命令总会被执行,在这里该命令是$(call if_changed,link_o_target)。
????我们再看依赖obj-y,它定义在图6.9中,即将原始obj-y中目录结构的目标,修改为dir/built-in.o,非目录结构的目标保持不变。call为makefile的内置函数,它会将第一个参数作为一个命令,后面的参数作为该命令的参数来调用命令,此处会调用if_changed命令,参数为link_o_target。
在这里插入图片描述
??????????????????????????????????????????图6.14
在这里插入图片描述
??????????????????????????????????????????图6.15
(1)if_changed命令分析
????它的定义如图6.16,位于kbuild.include中。若any-prereq或arg-check为非空,则执 行下面的shell命令。set -e指的是当其后的shell命令若返回值为非0,则直接错误退出,不再往下执行了。
????$(echo-cmd)用于打印命令cmd_$(1),真正执行的命令即cmd_$(1)和下面的printf命令。printf命令用于将该命令以cmd_xxx := yyyy的形式存入该目录下的.xxx.cmd文件中,其中,yyyy为编译该目标所执行的命令序列。
????将cmd_$(1)扩展后为cmd_link_o_target,它的定义如图6.14,它主要的部分为:
????$(cmd_make_builtin) $@ $(filter $(obj-y), $^)
????将其展开后是:
????$(LD) $(ld_flags) -r -o $@ $(filter $(obj-y), $^)
????再替换其自动化变量后为:
????$(LD) $(ld_flags) -r -o $(builtin-target) $(obj-y)
????它的意思是将$(obj-y)所包含的目标都链接成一个$(builtin-target),也就是built-in.o
在这里插入图片描述
??????????????????????????????????????????图6.16
(2)any-prereq和arg-check的分析
????它们都定义在kbuild.include中,其中any-prereq定义如图6.17,arg-check的定义如图6.18所示。
????any-prereq:其中$?表示比目标新的依赖文件,$^表示所有的依赖文件。故该句的含义是any-prereq为比目标新的依赖中去除PHONY依赖,加上不存在文件的依赖去除PHONY依赖的集合。那么什么是不存在文件的依赖呢?wildcard $^表示存在真实文件的依赖,而$^表示所有的依赖,但有些依赖并没有对应的文件,如前面介绍的FORCE,因此它是一个不存在文件的依赖。
????arg-check:我们先看下cmd_@和cmd_$1到底是什么东西,根据上面的调用规则,我们将它们补齐,cmd_@就是cmd_$(builtin-target),即cmd_$(obj)/built-in.o,假设我们在已经编译过内核的mm目录下打开.built-in.o.cmd,则可看到其值就是如图6.19所示的cmd_mm/built-in.o := xxx。该文件就是图6.16的printf命令产生的,表示前一次编译该模块时所用的命令。
????cmd_$1展开后就是cmd_link_o_target的值,它的定义在图6.14中,即本次调用的命令。所以该依赖实际上是检查前一次编译和本次编译所用的命令是否一样,若不一样,则表示该依赖存在。
在这里插入图片描述
??????????????????????????????????????????图6.17
在这里插入图片描述
??????????????????????????????????????????图6.18
在这里插入图片描述
??????????????????????????????????????????图6.19
(3)综上所述,编译built-in.o时首先会检查以下三个条件:
?????a.依赖是否有更新
?????b.是否有不存在相应文件的依赖
?????c.编译命令是否有变化
?????若其中有一个以上的条件满足,则调用ld命令将$(obj-y)所包含的目标链接成目标built-in.o。
(4)以上其实还有一个问题需要考虑,built-in.o依赖于$(obj-y),而obj-y本身就是目标文件或目录等,因此需要先生成它们才能够执行上面的链接命令。由于它们是比较通用的部分,我们将在第2小节讨论之
2.各种类型目标的生成
(1)由c文件生成.o文件
?????该规则的定义如图6.20,我们先看下其依赖,FORCE我们前面已经介绍过了,但是除了.c文件外,还有两个依赖$(recordmcount_source)和$(objtool_obj),它们又是用来做什么的?
在这里插入图片描述
??????????????????????????????????????????图6.20
????我们可以找下它们的定义分别如图6.21和图6.22所示,从它们的定义可以发现recordmcount_source是用于ftrace的,而objtool_obj则用于stack validation。只有使能了相应配置它们才有定义,因此对于普通的编译来说它们是空的,我们所需要关注的就是.c依赖文件的编译。
在这里插入图片描述
??????????????????????????????????????????图6.21
在这里插入图片描述
??????????????????????????????????????????图6.22
????编译c文件需要调用两条命令,第一条是用于代码静态检查的,默认情况不会被定义,除非通过命令行传入了C=1或C=2参数。因此实际的编译工作是由第二条命令完成的。
$(call if_changed_rule,cc_o_c)命令分析:
????if_changed_rule定义在图6.23中,看到这个条件语句是否似曾相识,不错,它和上面if_changed的定义格式是一样的,即若条件满足则执行rule_cc_o_c命令,否则执行@:命令。其实冒号在makefie中也是一个命令,只不过它什么也不做,相当于一个空命令。
在这里插入图片描述
??????????????????????????????????????????图6.23
????我们在看下rule_cc_o_c命令的定义如图6.24,其中checksrc,objtool和record_mcount都在前面介绍过了。而cmd_modversions_c主要是执行符号表相关的一些操作,我们也不关心。最后剩下cmd_and_fixdep命令,它的参数为cc_o_c。
在这里插入图片描述
??????????????????????????????????????????图6.24
????cmd_and_fixdep的定义如图6.25,它会调用cmd_$(1),即cmd_cc_o_c命令,它的定义如图6.26。看到该命令就是我们平常很熟悉的编译指令了,即将依赖编译成.o目标文件。
在这里插入图片描述
??????????????????????????????????????????图6.25
在这里插入图片描述
??????????????????????????????????????????图6.26
(2)我们再简单看下由.S文件生成.o文件的情形
?????如图6.27所示,它会调用rule_as_o_S函数,该函数定义在图6.28,它会调用cmd_and_fixdep命令,参数为as_o_S。同上面一样,它会调用cmd_as_o_S命令,该命令定义在图6.29中。即通过CC命令将依赖中的.S源文件汇编成.o文件
在这里插入图片描述
??????????????????????????????????????????图6.27
在这里插入图片描述
??????????????????????????????????????????图6.28
在这里插入图片描述
??????????????????????????????????????????图6.29
(3)若为obj-y中的目录结构,则根据图6.9中的定义,起始它被定义为了subdir-y变量,并且和subdir-m一起构成了subdir-ym变量。我们看下该目标是如何生成的,且看图6.30,还是熟悉的$(build),将其展开后如下:
????make -f $(srctree)/scripts/Makefile.build obj=$(subdir-ym)
????即将其subdir-ym做为目标,再次调用本makefile,显然它依次进入每个目录,包含进相应目录的kbuild或makefile文件,然后试图生出子目录的built-in.o目标,若该子目录中还含有子目录,它就又会调用到本命令,持续递归,直到所有定义的目标都被编译完成为止。
在这里插入图片描述
??????????????????????????????????????????图6.30
3.lib-target目标lib.a
????lib-target的规则定义如下图6.31,它依赖于lib-y和FORCE,其中lib-y定义在各个子目录下的Makfile中,如在arch/arm64/lib目录下的makefile定义了如图6.32的lib-y,即其依赖是该目录下的一些.o文件。
在这里插入图片描述
??????????????????????????????????????????图6.31
在这里插入图片描述
??????????????????????????????????????????图6.32
????我们在看下它调用的命令,应该是cmd_link_l_target,该命令也定义在图6.31中。它先将老的lib.a删除掉,然后调用$(AR)将依赖指定的目标文件打包成一个库文件lib.a
4.extra-y目标
????通过图6.12可以看到,它会作为__build的依赖,因此也会被编译,如前面例子中的head.o,会通过cmd_cc_o_S命令由head.S汇编生成。但是由于它不被obj-y或obj-m包含,故其不会被链接到built-in.o会ko模块中。实际上,head.o是整个kernel的入口函数所在的文件,所以它会在链接vmlinux的时候被链接脚本指定放在vmlinux开头的位置。后面有时间的话我们可以在分析kernel的链接脚本时再详细说明它的用处
5.obj-m目标
????obj-m在makefile.lib中被分为了multi-used-m和single-used-m,我们分别看下它们的规则。multi-used-m的定义如图6.33,即调用了cmd_link_multi-m函数,而cmd_link_multi-m函数又会调用cmd_link_multi-y函数。cmd_link_multi-y函数会调用ld命令将link_multi_deps中定义的目标链接起来
在这里插入图片描述
??????????????????????????????????????????图6.33
????single-used-m的定义如图6.34所示,它会调用rule_cc_o_c将源文件编译成目标文件。
在这里插入图片描述
??????????????????????????????????????????图6.34
6.modorder-target目标
????modorder-target规则的定义如图6.35,它实际上执行了一个命令序列modorder-cmds,并将其输出写入以modorder-target的值命令的文件中
在这里插入图片描述
??????????????????????????????????????????图6.35
7.subdir-ym目标
?????该部分在第二小节的第三部分中已经做了说明,此处不再重复