首页 > 代码库 > Linux Kernel Makefile简析 之 make zImage

Linux Kernel Makefile简析 之 make zImage

    本文将简要分析Linux Kernel编译zImage的过程。读者需具备GNU Make、Bash Shell、Python脚

本、编译器、链接器等方面的基础知识。虽然重点是分析kernel的构建过程,但是也会顺带的分析一些

其他的小的知识点。我们坐车去远行,欣赏沿途的风景,并不会妨碍我们最终抵达我们的目的地,不是

吗?技术分享


    先描述一下具体开发环境:

    . host os: ubuntu 14.04 server LTS

    . cross toolchain: crosstool-ng生成的交叉编译器

    . target: tegra, jeston-tk1

    . kernel: 3.10.40


    在开始分析之前,先做一些必要的说明:

    . 分析过程中,所有文件路径,都是相对于kernel源码根目录的。


    好了,接下来开始分析zImage的编译过程。

    启动终端,在kernel源码根目录下运行命令. envsetup.sh设置编译环境。envsetup.sh的内容如

下:

#/bin/bash
#
# filename: envsetup.sh

export ARCH=arm
export CROSS_COMPILE=~/tegra/tk1/crosstool/crosstool-ng/install/bin/arm-cortex_a9-linux-gnueabi-

    然后运行命令make zImage O=output编译kernel。make读取kernel源码根目录下的Makefile,看到

如下代码片断:

技术分享

图1

    这里会先判断KBUILD_SRC的值是否为空,因为此时 KBUILD_SRC 尚未定义,所以这个判断是成的。

接着将KBUILD_OUTPUT赋为从命令行传过来的变量O的值,即:

KBUILD_OUTPUT := $(O) # KBUILD_OUTPUT := output

附带的变量saved-output也被赋成了同样的值output,然后将KBUILD_OUTPUT的值更新为output目录的完

整路径。KBUILD_OUTPUT是用来存储编译产生的输出的目录。

技术分享

图2

    这里是很重要的片段,是进入构建最终目标的开端。上面图2中的代码片段此时被展开为:

技术分享

图3

请注意,其中/path/to/output/current/path都不是真实的目录名,具体的值依赖于kernel源码根目录的实际路径。/current/path在此时的值为kernel源码根目录所在的路径。

    终于,我们看到了zImage这个我们编译的终极目标,它是依赖于sub-make的。但请记住,make在看

到zImage的产生规则以及其依赖目标的规则后,并不会马上去执行这两条规则,而只是先建立zImage和

sub-make的依赖关系。为什么会这样呢?原来make的执行过程是一个2遍扫描过程,这类似于一些编译器

的编译过程,并不是一次到位,而是分为几个阶段。make在第一遍扫描的时候,只是会去创建变量(并

展开某些类型变量)、建立目标依赖关系树,而只有在第二遍的时候才会去跟根据规则去进行目标的创

建过程。所以在这里zImage以及sub-make规则都不会被马上执行,而只是在make内部作了相应记录,而

后继续往后扫描Makefile剩下的内容。

    这里有一个很重要的变量skip-makefile,被赋值为1,这个变量和我们接下来的分析密切相关。我

们继续看这个Makefile剩下的内容:

技术分享

图4

    这里用...省略了很多内容,中间的内容非常重要且核心,但对我们当前的第1遍是会被跳过的,因

为此时skip-makefile的值为1,所以ifeq ($(skip-makefile),)是不成立的,其间包含的内容自然也被

跳过。 

    FORCE这条特殊的空规则,在kernel所有的Makefile中,随时可以看到它的身影,所以有必要简单的

释一下它的用途。FORCE常见于一条规则依赖列表的末尾,作用是保证依赖它的目标总是被更新。原因

FORCE不对应任何实际文件(.PHONY伪目标),且不依赖任何其标,make会认为这类目标总是要被更新

的,相应依赖它的目标自然也要被更新。

    打起精神,我们的重头戏马上要登场了。make完成对kernel顶层Makefile的第1编扫描后,接下来进

入make对Makefile处理的第2阶段,根据阶段1构建的依赖关系树开始一步步地构建我们的终极目标

zImage。起点是图2所示的位置,我们看图2内容展开后的图3zImage依赖于sub-make,所以sub-make

条规则。我们来看sub-make规则的展开后的内容:

技术分享

图5

这条规则到底做了什么呢?这条规则完成了4个动作:

. make将自己的当前目录切换到了/path/to/output

设置KBUILD_SRC为当前目录(即kernel源码根目录)

. make将重新读取kernel的顶层Makefile(也就是我们当前正在执行的Makefile)

. 构造目标依旧是zImage

    到这里你可能产生了疑问,怎么又回到了我们正在执行的Makefile呢?这不是死循环了,没完没了

了吗?事实并非如此。我们一步步来解释为什么。执行sub-make规则的语句后,make重新从头开始读取

kernel的顶层Makefile,时光流逝,make的脚步又重新走到了图1所示的代码片段处,这里的条件分支判

KBUILD_SRC的值是否为空。记得吗?就在刚刚,我们重新进入Makefile之前,已经设置了KBUILD_SRC

的值为kernel顶层Makefile所在的路径(也是kernel源码根目录所在的路径),所以这个对KBUILD_SRC判空的分支是不成立的,自然分支里面的语句也不会被执行,因此和第1遍执行这个Makefile时有了不同的执行路径。

    make继续前进到了图4所示的代码片段处,要根据skip-makefile的值决定我们接下来要去向哪里。

而这一次,skip-makefile并没有被定义,所以ifeq ($(skip-makefile),)的判定成立,因此make将执行

被该条件分支包含的代码片段,这将我们整个顶层Makefile分析的核心内容。这里面的内容很多,我们

将逐段进行分析,对代码片段截取也是逐段截图。

技术分享

图6

    这段代码工作很简单,完成了几个变量的设置。在我的开发环境下,这些变量分别得到了如下的

值(至于如何得到的,就请看官自己分析了):

srctree = /path/to/kernel/source/base
objtree = /path/to/kernel/source/base/output
src = /path/to/kernel/source/base
obj = /path/to/kernel/source/base/output
VPATH = /path/to/kernel/source/base
SUBARCH = x86

值得注意的是,srctree objtree VPATH被export了。继续往下看:

技术分享

图7(上接图6)

    这段代码定义了一些变量,将得到如下所列的值:

ARCH = arm
CROSS_COMPILE = ~/tegra/tk1/crosstool/crosstool-ng/install/bin/arm-cortex_a9-linux-gnueabi-
UTS_MACHINE = arm
SRCARCH = arm
hdr-arch = arm
KCONFIG_CONFIG = .config
CONFIG_SHELL = /bin/bash

这些变量的重要性,我想也不用我多说了吧。路漫漫其修远兮,革命尚未成功,我们继续赶路吧。

技术分享

技术分享

技术分享

技术分享

技术分享

(未完待续)

本文出自 “JiMoKuangXiangQu” 博客,请务必保留此出处http://4594296.blog.51cto.com/4584296/1953612

Linux Kernel Makefile简析 之 make zImage