提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
目录
[一、make xxx_defconfig 过程](#一、make xxx_defconfig 过程)
[二、Makefile.build 脚本分析](#二、Makefile.build 脚本分析)
[2.1、scripts_basic 目标对应的命令](#2.1、scripts_basic 目标对应的命令)
[2.2、 %config 目标对应的命令](#2.2、 %config 目标对应的命令)
[三、make 过程](#三、make 过程)
[3.2、init-y、drivers-y 和 net-y](#3.2、init-y、drivers-y 和 net-y)
[四、built-in.o 文件编译生成过程](#四、built-in.o 文件编译生成过程)
[五、 make zImage 过程](#五、 make zImage 过程)
前言
上两期主要介绍了Linux 内核的获取,编译以及顶层 Makefile 的简单介绍,这一期开始会对顶层 Makefile进行更加全面细致的研究。
一、make xxx_defconfig 过程
第一次编译 Linux 之前都要使用"make xxx_defconfig"先配置 Linux 内核,在顶层 Makefile 中有"%config"这个目标,如下所示:
490 config-targets := 0
491 mixed-targets := 0
492 dot-config := 0
493
494 ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)
495 ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)
496 dot-config := 0
497 endif
498 endif
499
500 ifneq ($(KBUILD_EXTMOD),)
501 ifneq ($(filter config %config,$(MAKECMDGOALS)),)
502 config-targets := 1
503 endif
504 endif
---
# 第二部分:混合目标处理
505 ifneq ($(words $(MAKECMDGOALS)),1)
506 mixed-targets := 1
507 endif
508 endif
509 endif
510
511 ifeq ($(mixed-targets),1)
512 # We're called with mixed targets (*config and build targets).
513 # Handle them one by one.
514 PHONY += $(MAKECMDGOALS) __build_one_by_one
515 $(filter-out __build_one_by_one, $(MAKECMDGOALS)): __build_one_by_one
516 __build_one_by_one:
517 @:
518
519 _build_one_by_one:
520 for i in $(MAKECMDGOALS); do \
521 $(MAKE) -f $(srctree)/Makefile $$i; \
522 done
523
524
525 else
526 ifeq ($(config-targets),1)
527 # ================================================================
528 # *config targets only - make sure prerequisites are updated, and
529 # descend in scripts/kconfig to make the *config target
530
531 # Read arch specific Makefile to set KBUILD_DEFCONFIG as needed.
532 # KBUILD_DEFCONFIG may point out an alternative default
533 # configuration used for 'make defconfig'
534 include arch/$(SRCARCH)/Makefile
535 export KBUILD_DEFCONFIG KBUILD_KCONFIG
536
537 config: scripts_basic outputmakefile FORCE
538 $(Q)$(MAKE) $(build)=scripts/kconfig $@
539
540 %config: scripts_basic outputmakefile FORCE
541 $(Q)$(MAKE) $(build)=scripts/kconfig $@
542
543 else
......
563 endif # KBUILD_EXTMOD
第 490~507 行和 uboot 一样,都是设置定义变量 config-targets、mixed-targets 和 dot-config
的值,最终这三个变量的值为:
config-targets=1
mixed-targets=0
dot-config=1
因为 config-targets=1,因此第 534 行~541 行成立。第 534 行引用 arch/arm/Makefile 这个文
件,这个文件很重要,因为 zImage、uImage 等这些文件就是由 arch/arm/Makefile 来生成的。
第 535 行导出变量 KBUILD_DEFCONFIG KBUILD_KCONFIG。
第 537 行,没有目标与之匹配,因此不执行。
第 540 行,"make xxx_defconfig"与目标"%config"匹配,因此执行。"%config"依赖scripts_basic、outputmakefile 和 FORCE,"%config"真正有意义的依赖就只有 scripts_basic, scripts_basic 的规则如下:
scripts_basic:
$(Q)$(MAKE) $(build)=scripts/basic
$(Q)rm -f .tmp_quiet_recordmcount
build 定义在文件 scripts/Kbuild.include 中,值为 build := -f $(srctree)/scripts/Makefile.build obj,因此将上述代码展开就是:
scripts_basic:
@make -f ./scripts/Makefile.build obj=scripts/basic // 也可以没有@,视配置而定
@rm -f .tmp_quiet_recordmcount // 也可以没有@
其实
%config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@
展开就是:
@make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig
二、Makefile.build 脚本分析
从make xxx_defconfig的过程中可知,"make xxx_defconfig"配置 Linux 的时候如下两行命令会执行脚本 scripts/Makefile.build:
@make -f ./scripts/Makefile.build obj=scripts/basic
@make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig
依次分析一下这两行代码
2.1、 scripts_basic 目标对应的命令
scripts_basic 目标对应的命令为:@make -f ./scripts/Makefile.build obj=scripts/basic。打开文件 scripts/Makefile.build,有如下代码:
41 # The filename Kbuild has precedence over Makefile
42 kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
43 kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
44 include $(kbuild-file)
将 kbuild-dir 展开后为:
kbuild-dir=./scripts/basic
将 kbuild-file 展开后为:
kbuild-file=./scripts/basic/Makefile
最后将44行展开如下:
include ./scripts/basic/Makefile
继续分析scripts/Makefile.build 如下代码:
__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
$(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
$(subdir-ym) $(always)
@:
build 是默认目标,因为命令"@make -f ./scripts/Makefile.build obj=scripts/basic"没有指定目标,所以会使用到默认目标__build。在顶层 Makefile 中,KBUILD_BUILTIN 为 1,KBUILD_MODULES 为空,因此展开后目标__build 为:
__build:$(builtin-target) $(lib-target) $(extra-y) $(subdir-ym) $(always)
@:
可以看出目标__build 有 5 个依赖:builtin-target、lib-target、extra-y、subdir-ym 和 always。 这 5 个依赖的具体内容如下:
builtin-target =
lib-target =
extra-y =
subdir-ym =
always = scripts/basic/fixdep scripts/basic/bin2c
只有 always 有效,因此__build 最终为:
__build: scripts/basic/fixdep scripts/basic/bin2c
@:
__build 依赖于 scripts/basic/fixdep 和 scripts/basic/bin2c,所以要先将 scripts/basic/fixdep 和
scripts/basic/bin2c.c 这两个文件编译成 fixdep 和 bin2c。
所以总结一下:make -f ./scripts/Makefile.build obj= scripts/basic ->生成fixdep和bin2c
2.2 、 %config 目标对应的命令
%config 目标对应的命令为:
@make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig
其实他的作用是编译scripts/kconfig/conf.c生成conf 这个软件
此软件就会将%_defconfig 中的配置输出到.config 文件中,最终生成 Linux kernel 根目录下
的.config 文件。
即:
make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig ->
scripts/kconfig/conf --defconfig=arch/arm/configs/%_defconfig Kconfig ->.config
三、make 过程
使用命令"make xxx_defconfig"配置好 Linux 内核以后就可以使用"make"或者"make all" 命令进行编译。顶层Makefile 有如下代码:
125 PHONY := _all
126 _all:
......
192 PHONY += all
193 ifeq ($(KBUILD_EXTMOD),)
194 _all: all
195 else
196 _all: modules
197 endif
......
608 all: vmlinux
第 126 行,_all 是默认目标,如果使用命令"make"编译 Linux 的话此目标就会被匹配。
第 193 行,如果 KBUILD_EXTMOD 为空的话 194 行的代码成立。
第 194 行,默认目标_all 依赖 all。
第 608 行,目标 all 依赖 vmlinux,所以接下来的重点就是 vmlinux!
vmlinux相关代码也在顶层 Makefile 代码段,如下:
# 第一部分:vmlinux 链接准备
901 # Externally visible symbols (used by link-vmlinux.sh)
902 export KBUILD_VMLINUX_INIT := $(head-y) $(init-y)
903 export KBUILD_VMLINUX_MAIN := $(core-y) $(libs-y) $(drivers-y) $(net-y)
904 export KBUILD_LDS := arch/$(SRCARCH)/kernel/vmlinux.lds
905 export LDFLAGS_vmlinux
906 # used by scripts/package/Makefile
907 export KBUILD_IMAGE := $(if $(filter-out arch/%,$(vmlinux-
all-y)),arch/$(SRCARCH)/boot/$(KBUILD_IMAGE),$(KBUILD_IMAGE))
908 vmlinux-dirs := arch Documentation include samples scripts tools virt
909
910 vmlinux: $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN)
$(KBUILD_VMLINUX_LIBS)
911
# 第二部分:vmlinux 链接及后续处理
913 # Final link of vmlinux
914 cmd_link-vmlinux = $(CONFIG_SHELL) $< $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux)
915 vmlinux: scripts/link-vmlinux.sh FORCE
916 $(call if_changed,link-vmlinux)
917 @:
918
919 # Include targets which we want to
920 # execute if the rest of the kernel build went well.
921 vmlinux: scripts/link-vmlinux.sh FORCE
922 ifdef CONFIG_HEADERS_CHECK
923 $(Q)$(MAKE) -f $(srctree)/Makefile headers_check
924 endif
925 ifdef CONFIG_SAMPLES
926 $(Q)$(MAKE) $(build)=samples
927 endif
928 ifdef CONFIG_BUILD_DOCSRC
929 $(Q)$(MAKE) $(build)=Documentation
930 endif
931 ifdef CONFIG_GDB_SCRIPTS
932 $(Q)ln -fsn $(srctree) $@.gdb /bin/pwd/scripts/gdb/vmlinux-gdb.py
933 endif
934 $(call if_changed,link-vmlinux)
第 920 行可以看出目标 vmlinux 依赖 scripts/link-vmlinux.sh $(vmlinux-deps) FORCE。
912 行定义了 vmlinux-deps,值为:
vmlinux-deps=$(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN)
第 905 行,KBUILD_VMLINUX_INIT= (head-y) (init-y)。
第 906 行,KBUILD_VMLINUX_MAIN = (core-y) (libs-y) (drivers-y) (net-y)。
第 907 行,KBUILD_LDS= arch/$(SRCARCH)/kernel/vmlinux.lds,其中 SRCARCH=arm,因
此 KBUILD_LDS= arch/arm/kernel/vmlinux.lds。
综上所述,vmlinux 的依赖为:scripts/link-vmlinux.sh、(head-y) 、(init-y)、$(core-y) 、
(libs-y) 、(drivers-y) 、$(net-y)、arch/arm/kernel/vmlinux.lds 和 FORCE。
第 933 行的命令用于链接生成 vmlinux。
所以重点来看一下这6个依赖:下(head-y) 、(init-y)、(core-y) 、(libs-y) 、(drivers-y) 和(net-y)
3.1 、 head-y
head-y 定义在文件 arch/arm/Makefile 中,内容如下:
head-y := arch/arm/kernel/head$(MMUEXT).o
当不使能 MMU 的话 MMUEXT=-nommu,如果使能 MMU 的话为空,因此 head-y 最终的值为:
head-y = arch/arm/kernel/head.o
3.2 、 init-y 、 drivers-y 和 net-y
在顶层 Makefile 中有如下代码:
init-y := init/
drivers-y := drivers/ sound/ firmware/
net-y := net/
......
init-y := $(patsubst %/, %/built-in.o, $(init-y))
drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y))
net-y := $(patsubst %/, %/built-in.o, $(net-y))
这样便知道这三个变量的值应该为:
init-y = init/built-in.o
drivers-y = drivers/built-in.o sound/built-in.o firmware/built-in.o
net-y = net/built-in.o
3.3 、 libs-y
其过程和 init-y 也一样
libs-y其实为如下:
libs-y := lib/lib.a arch/arm/lib/lib.a lib/built-in.o arch/arm/lib/built-in.o
3.4、 core-y
core-y 和 init-y 也一样,在顶层 Makefile 中有如下代码:
core-y := usr/
......
core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/
但是在 arch/arm/Makefile 中会对 core-y 进行追加,代码如下:
core-$(CONFIG_FPE_NWFPE) += arch/arm/nwfpe/
core-$(CONFIG_FPE_FASTFPE) += $(FASTFPE_OBJ)
core-$(CONFIG_VFP) += arch/arm/vfp/
core-$(CONFIG_XEN) += arch/arm/xen/
core-$(CONFIG_KVM_ARM_HOST) += arch/arm/kvm/
core-$(CONFIG_VDSO) += arch/arm/vdso/
# If we have a machine-specific directory, then include it in the build.
core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/
core-y += arch/arm/probes/
core-y += arch/arm/net/
core-y += arch/arm/crypto/
core-y += arch/arm/firmware/
core-y += $(machdirs) $(platdirs)
最后那几行就是被追加的值
在顶层 Makefile 中有如下一行:
core-y := $(patsubst %/, %/built-in.o, $(core-y))
经过上述代码的转换,最终 core-y 的值为:
core-y = usr/built-in.o \
arch/arm/vdso/built-in.o \
arch/arm/mm/built-in.o \
arch/arm/probes/built-in.o \
arch/arm/crypto/built-in.o \
arch/arm/mach-imx/built-in.o \
mm/built-in.o \
ipc/built-in.o \
crypto/built-in.o \
arch/arm/vfp/built-in.o \
arch/arm/kernel/built-in.o \
arch/arm/common/built-in.o \
arch/arm/net/built-in.o \
arch/arm/firmware/built-in.o \
kernel/built-in.o \
fs/built-in.o \
security/built-in.o \
block/built-in.o
这些变量都是将相应目录中的源码文件进行编译,然后在各自目录下生成 built-in.o 文件,有些生成了.a 库文件。最终将这些 built-in.o 和.a 文件进行链接即可形成 ELF 格式的可执行文件,也就是 vmlinux。
链接脚本为 arch/arm/kernel/vmlinux.lds, 链接过程是由shell脚本scripts/link-vmlinux.s来完成的。
各自目录下生成 built-in.o 文件,有些生成了.a 库文件,这些又是怎么编译出来的?
四、built-in.o 文件编译生成过程
由前几小节可知vmliux 依赖 vmlinux-deps,而 vmlinux-deps=
(KBUILD_LDS) (KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN)
KBUILD_LDS 是链接脚本
剩下的 KBUILD_VMLINUX_INIT 和 KBUILD_VMLINUX_MAIN 就是各个子目录下的 built-in.o、.a 等文件。最终 vmlinux-deps 的值如下:
vmlinux-deps = arch/arm/kernel/vmlinux.lds \
init/built-in.o \
arch/arm/vfp/built-in.o \
arch/arm/kernel/built-in.o \
arch/arm/common/built-in.o \
arch/arm/net/built-in.o \
arch/arm/firmware/built-in.o \
kernel/built-in.o \
fs/built-in.o \
security/built-in.o \
block/built-in.o \
lib/lib.a \
lib/built-in.o \
sound/built-in.o \
net/built-in.o \
arch/arm/kernel/head.o \
usr/built-in.o \
arch/arm/vdso/built-in.o \
arch/arm/mm/built-in.o \
arch/arm/probes/built-in.o \
arch/arm/crypto/built-in.o \
arch/arm/mach-imx/built-in.o \
mm/built-in.o \
ipc/built-in.o \
crypto/built-in.o \
arch/arm/lib/lib.a \
arch/arm/lib/built-in.o \
drivers/built-in.o \
firmware/built-in.o
除了 arch/arm/kernel/vmlinux.lds 以外,其他都是要编译链接生成的
出vmlinux-deps 依赖 vmlinux-dirs,vmlinux-dirs此变量保存着生成 vmlinux 所需源码文件的目录,值如下:
vmlinux-dirs = init \
usr \
arch/arm/vfp \
arch/arm/vdso \
arch/arm/kernel \
arch/arm/mm \
arch/arm/common \
arch/arm/probes \
arch/arm/net \
arch/arm/crypto \
arch/arm/firmware \
arch/arm/mach-imx \
kernel \
mm \
fs \
ipc \
security \
crypto \
block \
drivers \
sound \
firmware \
net \
arch/arm/lib \
lib
顶层Makefile文件有以下代码:
$(vmlinux-dirs): prepare scripts
$(Q)$(MAKE) $(build)=$@
build=-f ./scripts/Makefile.build obj
所以展开就是:
@ make -f ./scripts/Makefile.build obj=$@
$@表示目标文件,也就是 vmlinux-dirs 的值,将 vmlinux-dirs 中的这些目录全部带入到命
令中,结果如下:
@make -f ./scripts/Makefile.build obj=init
@make -f ./scripts/Makefile.build obj=usr
@make -f ./scripts/Makefile.build obj=arch/arm/vfp
@make -f ./scripts/Makefile.build obj=arch/arm/vdso
@make -f ./scripts/Makefile.build obj=arch/arm/kernel
@make -f ./scripts/Makefile.build obj=arch/arm/mm
@make -f ./scripts/Makefile.build obj=arch/arm/common
@make -f ./scripts/Makefile.build obj=arch/arm/probes
@make -f ./scripts/Makefile.build obj=arch/arm/net
@make -f ./scripts/Makefile.build obj=arch/arm/crypto
@make -f ./scripts/Makefile.build obj=arch/arm/firmware
@make -f ./scripts/Makefile.build obj=arch/arm/mach-imx
@make -f ./scripts/Makefile.build obj=kernel
@make -f ./scripts/Makefile.build obj=mm
@make -f ./scripts/Makefile.build obj=fs
@make -f ./scripts/Makefile.build obj=ipc
@make -f ./scripts/Makefile.build obj=security
@make -f ./scripts/Makefile.build obj=crypto
@make -f ./scripts/Makefile.build obj=block
@make -f ./scripts/Makefile.build obj=drivers
@make -f ./scripts/Makefile.build obj=sound
@make -f ./scripts/Makefile.build obj=firmware
@make -f ./scripts/Makefile.build obj=net
@make -f ./scripts/Makefile.build obj=arch/arm/lib
@make -f ./scripts/Makefile.build obj=lib
五、 make zImage 过程
前面几小节重点是讲 vmlinux 是如何编译出来的,vmlinux 是 ELF 格式的文件,但是在实际中我们不会使用 vmlinux,而是使用 zImage 或 uImage 这样的 Linux 内核镜像文件。那么 vmlinux、zImage、uImage 他们之间有什么区别呢?
vmlinux 是编译出来的最原始的内核文件,如下图:

Image 是 Linux 内核镜像文件,Image 保存在 arch/arm/boot 目录下,如下图:

zImage 是经过 gzip 压缩后的 Image
uImage 是老版本 uboot 专用的镜像文件
使用"make"、"make all"、"make zImage"这些命令就可以编译出 zImage 镜像
总结
对Linux 顶层 Makefile进行了详谈。