Linux 内核顶层Makefile 详解
目錄
- 前602行分析
- make xxx_defconfig 過(guò)程
- Makefile.build 腳本分析
- make 過(guò)程
- built-in.o 文件編譯生成過(guò)程
- make zImage 過(guò)程
前幾章我們重點(diǎn)講解了如何移植uboot 到I.MX6U-ALPHA 開(kāi)發(fā)板上,從本章開(kāi)始我們就開(kāi)始學(xué)習(xí)如何移植Linux 內(nèi)核。同uboot 一樣,在具體移植之前,我們先來(lái)學(xué)習(xí)一下Linux 內(nèi)核的頂層Makefile 文件,因?yàn)轫攲覯akefile 控制著Linux 內(nèi)核的編譯流程。
前602行分析
Linux 的頂層Makefile 和uboot 的頂層Makefile 非常相似,因?yàn)閡boot 參考了Linux,前602行幾乎一樣,所以前面部分我們大致看一下就行了。
1、版本號(hào)
頂層Makefile 一開(kāi)始就是Linux 內(nèi)核的版本號(hào),如下所示:
可以看出,Linux 內(nèi)核版本號(hào)為4.1.15。
2、MAKEFLAGS 變量
MAKEFLAGS 變量設(shè)置如下所示:
3、命令輸出
Linux 編譯的時(shí)候也可以通過(guò)“V=1”來(lái)輸出完整的命令,這個(gè)和uboot 一樣,相關(guān)代碼如下所示:
4、靜默輸出
Linux 編譯的時(shí)候使用“make -s”就可實(shí)現(xiàn)靜默編譯,編譯的時(shí)候就不會(huì)打印任何的信息,同uboot 一樣,相關(guān)代碼如下:
5、設(shè)置編譯結(jié)果輸出目錄
Linux 編譯的時(shí)候使用“O=xxx”即可將編譯產(chǎn)生的過(guò)程文件輸出到指定的目錄中,相關(guān)代碼如下
6、代碼檢查
Linux 也支持代碼檢查,使用命令“make C=1”使能代碼檢查,檢查那些需要重新編譯的文件。“make C=2”用于檢查所有的源碼文件,頂層Makefile 中的代碼如下:
7、模塊編譯
Linux 允許單獨(dú)編譯某個(gè)模塊,使用命令“make M=dir”即可,舊語(yǔ)法“make SUBDIRS=dir”也是支持的。頂層Makefile 中的代碼如下:
外部模塊編譯過(guò)程和uboot 也一樣,最終導(dǎo)出srctree、objtree 和VPATH 這三個(gè)變量的值,其中srctree=.,也就是當(dāng)前目錄,objtree 同樣為“.”。
8、設(shè)置目標(biāo)架構(gòu)和交叉編譯器
同uboot 一樣,Linux 編譯的時(shí)候需要設(shè)置目標(biāo)板架構(gòu)ARCH 和交叉編譯器CROSS_COMPILE,在頂層Makefile 中代碼如下:
252 ARCH ?= $(SUBARCH) 253 CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%)為了方便,一般直接修改頂層Makefile 中的ARCH 和CROSS_COMPILE,直接將其設(shè)置為對(duì)應(yīng)的架構(gòu)和編譯器,比如本教程將ARCH 設(shè)置為為arm,CROSS_COMPILE 設(shè)置為arm-linux-gnueabihf-,如下所示:
252 ARCH ?= arm 253 CROSS_COMPILE ?= arm-linux-gnueabihf-設(shè)置好以后我們就可以使用如下命令編譯Linux 了:
make xxx_defconfig //使用默認(rèn)配置文件配置Linux make menuconfig //啟動(dòng)圖形化配置界面 make -j16 //編譯Linux9、調(diào)用scripts/Kbuild.include 文件
同uboot 一樣,Linux 頂層Makefile 也會(huì)調(diào)用文件scripts/Kbuild.include,頂層Makefile 相應(yīng)代碼如下:
348 # We need some generic definitions (do not try to remake the file). 349 scripts/Kbuild.include: ; 350 include scripts/Kbuild.include10、交叉編譯工具變量設(shè)置
頂層Makefile 中其他和交叉編譯器有關(guān)的變量設(shè)置如下:
LA、LD、CC 等這些都是交叉編譯器所使用的工具。
11、頭文件路徑變量
頂層Makefile 定義了兩個(gè)變量保存頭文件路徑:USERINCLUDE 和LINUXINCLUDE,相關(guān)代碼如下:
381 USERINCLUDE := \ 382 -I$(srctree)/arch/$(hdr-arch)/include/uapi \ 383 -Iarch/$(hdr-arch)/include/generated/uapi \ 384 -I$(srctree)/include/uapi \ 385 -Iinclude/generated/uapi \ 386 -include $(srctree)/include/linux/kconfig.h 387 388 # Use LINUXINCLUDE when you must reference the include/ directory. 389 # Needed to be compatible with the O= option 390 LINUXINCLUDE := \ 391 -I$(srctree)/arch/$(hdr-arch)/include \ 392 -Iarch/$(hdr-arch)/include/generated/uapi \ 393 -Iarch/$(hdr-arch)/include/generated \ 394 $(if $(KBUILD_SRC), -I$(srctree)/include) \ 395 -Iinclude \ 396 $(USERINCLUDE)第381~386 行是USERINCLUDE 是UAPI 相關(guān)的頭文件路徑,第390~396 行是LINUXINCLUDE 是Linux 內(nèi)核源碼的頭文件路徑。重點(diǎn)來(lái)看一下LINUXINCLUDE,其中 srctree=.,hdr-arch=arm,KBUILD_SRC 為空,因此,將USERINCLUDE 和LINUXINCLUDE 展開(kāi)以后為:
USERINCLUDE := \ -I./arch/arm/include/uapi \ -Iarch/arm/include/generated/uapi \ -I./include/uapi \ -Iinclude/generated/uapi \ -include ./include/linux/kconfig.h LINUXINCLUDE := \ -I./arch/arm/include \ -Iarch/arm/include/generated/uapi \ -Iarch/arm/include/generated \ -Iinclude \ -I./arch/arm/include/uapi \ -Iarch/arm/include/generated/uapi \ -I./include/uapi \ -Iinclude/generated/uapi \ -include ./include/linux/kconfig.h12、導(dǎo)出變量
頂層Makefile 會(huì)導(dǎo)出很多變量給子Makefile 使用,導(dǎo)出的這些變量如下:
417 export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION 418 export ARCH SRCARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC 419 export CPP AR NM STRIP OBJCOPY OBJDUMP 420 export MAKE AWK GENKSYMS INSTALLKERNEL PERL PYTHON UTS_MACHINE 421 export HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS 422 423 export KBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS 424 export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE CFLAGS_GCOV CFLAGS_KASAN 425 export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE 426 export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_LDFLAGS_MODULE 427 export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL 428 export KBUILD_ARFLAGSmake xxx_defconfig 過(guò)程
第一次編譯Linux 之前都要使用“make xxx_defconfig”先配置Linux 內(nèi)核,在頂層Makefile中有“%config”這個(gè)目標(biāo),如下所示:
490 config-targets := 0 491 mixed-targets := 0 492 dot-config := 1 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 ifeq ($(KBUILD_EXTMOD),) 501 ifneq ($(filter config %config,$(MAKECMDGOALS)),) 502 config-targets := 1 503 ifneq ($(words $(MAKECMDGOALS)),1) 504 mixed-targets := 1 505 endif 506 endif 507 endif 508 509 ifeq ($(mixed-targets),1) 510 # ================================================================= 511 # We're called with mixed targets (*config and build targets). 512 # Handle them one by one. 513 514 PHONY += $(MAKECMDGOALS) __build_one_by_one 515 516 $(filter-out __build_one_by_one, $(MAKECMDGOALS)): __build_one_by_one 517 @: 518 519 __build_one_by_one: 520 $(Q)set -e; \ 521 for i in $(MAKECMDGOALS); do \ 522 $(MAKE) -f $(srctree)/Makefile $$i; \ 523 done 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 一樣,都是設(shè)置定義變量config-targets、mixed-targets 和dot-config
的值,最終這三個(gè)變量的值為:
因?yàn)閏onfig-targets=1,因此第534 行~541 行成立。第534 行引用arch/arm/Makefile 這個(gè)文件,這個(gè)文件很重要,因?yàn)閦Image、uImage 等這些文件就是由arch/arm/Makefile 來(lái)生成的。
第535 行導(dǎo)出變量KBUILD_DEFCONFIG KBUILD_KCONFIG。
第537 行,沒(méi)有目標(biāo)與之匹配,因此不執(zhí)行。
第540 行,“make xxx_defconfig”與目標(biāo)“%config”匹配,因此執(zhí)行。“%config”依賴scripts_basic、outputmakefile 和FORCE,“%config”真正有意義的依賴就只有scripts_basic,scripts_basic 的規(guī)則如下:
448 scripts_basic: 449 $(Q)$(MAKE) $(build)=scripts/basic 450 $(Q)rm -f .tmp_quiet_recordmcountbuild 定義在文件scripts/Kbuild.include 中,值為build := -f $(srctree)/scripts/Makefile.build
obj,因此將示例代碼35.5.1.2 展開(kāi)就是:
接著回到示例代碼35.5.1.1 的目標(biāo)“%config”處,內(nèi)容如下:
%config: scripts_basic outputmakefile FORCE$(Q)$(MAKE) $(build)=scripts/kconfig $@將命令展開(kāi)就是:
@make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfigMakefile.build 腳本分析
從上一小節(jié)可知,“make xxx_defconfig“配置Linux 的時(shí)候如下兩行命令會(huì)執(zhí)行腳本
scripts/Makefile.build:
我們依次來(lái)分析一下:
1、scripts_basic 目標(biāo)對(duì)應(yīng)的命令
scripts_basic 目標(biāo)對(duì)應(yīng)的命令為:@make -f ./scripts/Makefile.build obj=scripts/basic。打開(kāi)文件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 展開(kāi)后為:
kbuild-dir=./scripts/basic將kbuild-file 展開(kāi)后為:
kbuild-file= ./scripts/basic/Makefile最后將59 行展開(kāi),即:
include ./scripts/basic/Makefile繼續(xù)分析scripts/Makefile.build,如下代碼:
94 __build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \ 95 $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \ 96 $(subdir-ym) $(always) 97 @:__build 是默認(rèn)目標(biāo),因?yàn)槊睢?#64;make -f ./scripts/Makefile.build obj=scripts/basic”沒(méi)有指
定目標(biāo),所以會(huì)使用到默認(rèn)目標(biāo)__build。在頂層Makefile 中,KBUILD_BUILTIN 為1,
KBUILD_MODULES 為空,因此展開(kāi)后目標(biāo)__build 為:
可以看出目標(biāo)__build 有5 個(gè)依賴:builtin-target、lib-target、extra-y、subdir-ym 和always。
這5 個(gè)依賴的具體內(nèi)容如下:
只有always 有效,因此__build 最終為:
__build: scripts/basic/fixdep scripts/basic/bin2c @:__build 依賴于scripts/basic/fixdep 和scripts/basic/bin2c,所以要先將scripts/basic/fixdep 和scripts/basic/bin2c.c 這兩個(gè)文件編譯成fixdep 和bin2c。
綜上所述,scripts_basic 目標(biāo)的作用就是編譯出scripts/basic/fixdep 和scripts/basic/bin2c 這兩個(gè)軟件。
2、%config 目標(biāo)對(duì)應(yīng)的命令
%config 目標(biāo)對(duì)應(yīng)的命令為:@make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig,此命令會(huì)使用到的各個(gè)變量值如下:
可以看出,Makefile.build 會(huì)讀取scripts/kconfig/Makefile 中的內(nèi)容,此文件有如下所示內(nèi)容:
113 %_defconfig: $(obj)/conf 114 $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)目標(biāo)%_defconfig 與xxx_defconfig 匹配,所以會(huì)執(zhí)行這條規(guī)則,將其展開(kāi)就是:
%_defconfig: scripts/kconfig/conf @ scripts/kconfig/conf --defconfig=arch/arm/configs/%_defconfig Kconfig%_defconfig 依賴scripts/kconfig/conf,所以會(huì)編譯scripts/kconfig/conf.c 生成conf 這個(gè)軟件。此軟件就會(huì)將%_defconfig 中的配置輸出到.config 文件中,最終生成Linux kernel 根目錄下的.config 文件。
make 過(guò)程
使用命令“make xxx_defconfig”配置好Linux 內(nèi)核以后就可以使用“make”或者“make all”命令進(jìn)行編譯。頂層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 是默認(rèn)目標(biāo),如果使用命令“make”編譯Linux 的話此目標(biāo)就會(huì)被匹配。
第193 行,如果KBUILD_EXTMOD 為空的話194 行的代碼成立。
第194 行,默認(rèn)目標(biāo)_all 依賴all。
第608 行,目標(biāo)all 依賴vmlinux,所以接下來(lái)的重點(diǎn)就是vmlinux!
頂層Makefile 中有如下代碼:
從第920 行可以看出目標(biāo)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。
重點(diǎn)來(lái)看一下$ (head-y) 、$ (init-y)、$ (core-y) 、$ (libs-y) 、$ (drivers-y) 和$ (net-y)這六個(gè)變量的值。
1、head-y
head-y 定義在文件arch/arm/Makefile 中,內(nèi)容如下:
當(dāng)不使能MMU 的話MMUEXT=-nommu,如果使能MMU 的話為空,因此head-y 最終的值為:
head-y = arch/arm/kernel/head.o2、init-y、drivers-y 和net-y
在頂層Makefile 中有如下代碼:
從示例代碼35.5.3.4 可知,init-y、libs-y、drivers-y 和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.o3、libs-y
libs-y 基本和init-y 一樣,在頂層Makefile 中存在如下代碼: 561 libs-y := lib/ ...... 900 libs-y1 := $(patsubst %/, %/lib.a, $(libs-y)) 901 libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y)) 902 libs-y := $(libs-y1) $(libs-y2)根據(jù)示例代碼35.5.3.5 可知,libs-y 應(yīng)該等于“l(fā)ib.a built-in.o”,這個(gè)只正確了一部分!因?yàn)?br /> 在arch/arm/Makefile 中會(huì)向libs-y 中追加一些值,代碼如下:
286 libs-y := arch/arm/lib/ $(libs-y)arch/arm/Makefile 將libs-y 的值改為了:arch/arm/lib $(libs-y),展開(kāi)以后為:
libs-y = arch/arm/lib lib/因此根據(jù)示例代碼35.5.3.5 的第900~902 行可知,libs-y 最終應(yīng)該為:
libs-y = arch/arm/lib/lib.a lib/lib.a arch/arm/lib/built-in.o lib/built-in.o4、core-y
core-y 和init-y 也一樣,在頂層Makefile 中有如下代碼:
但是在arch/arm/Makefile 中會(huì)對(duì)core-y 進(jìn)行追加,代碼如下:
269 core-$(CONFIG_FPE_NWFPE) += arch/arm/nwfpe/ 270 core-$(CONFIG_FPE_FASTFPE) += $(FASTFPE_OBJ) 271 core-$(CONFIG_VFP) += arch/arm/vfp/ 272 core-$(CONFIG_XEN) += arch/arm/xen/ 273 core-$(CONFIG_KVM_ARM_HOST) += arch/arm/kvm/ 274 core-$(CONFIG_VDSO) += arch/arm/vdso/ 275 276 # If we have a machine-specific directory, then include it in the build. 277 core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/ 278 core-y += arch/arm/probes/ 279 core-y += arch/arm/net/ 280 core-y += arch/arm/crypto/ 281 core-y += arch/arm/firmware/ 282 core-y += $(machdirs) $(platdirs)第269~274 行根據(jù)不同的配置向core-y 追加不同的值,比如使能VFP 的話就會(huì)在.config中有CONFIG_VFP=y 這一行,那么core-y 就會(huì)追加“arch/arm/vfp/”。
第277~282 行就是對(duì)core-y 直接追加的值。
在頂層Makefile 中有如下一行:
經(jīng)過(guò)上述代碼的轉(zhuǎn)換,最終core-y 的值為:
core-y = usr/built-in.o arch/arm/vfp/built-in.o \arch/arm/vdso/built-in.o arch/arm/kernel/built-in.o \arch/arm/mm/built-in.o arch/arm/common/built-in.o \arch/arm/probes/built-in.o arch/arm/net/built-in.o \arch/arm/crypto/built-in.o arch/arm/firmware/built-in.o \arch/arm/mach-imx/built-in.o kernel/built-in.o\mm/built-in.o fs/built-in.o \ipc/built-in.o security/built-in.o \crypto/built-in.o block/built-in.o關(guān)于head-y 、init-y、core-y 、libs-y 、drivers-y 和net-y 這6 個(gè)變量就講解到這里。這些變量都是一些built-in.o 或.a 等文件,這個(gè)和uboot 一樣,都是將相應(yīng)目錄中的源碼文件進(jìn)行編譯,然后在各自目錄下生成built-in.o 文件,有些生成了.a 庫(kù)文件。最終將這些built-in.o 和.a 文件進(jìn)行鏈接即可形成ELF 格式的可執(zhí)行文件,也就是vmlinux!但是鏈接是需要鏈接腳本的,vmlinux 的依賴arch/arm/kernel/vmlinux.lds 就是整個(gè)Linux 的鏈接腳本。
示例代碼35.5.3.2 第933 行的命令“+(callifchanged,link?vmlinux)”表示將(call if_changed,link-vmlinux) ”表示將(callifc?hanged,link?vmlinux)”表示將(call if_changed,link-vmlinux)的結(jié)果作為最終生成vmlinux 的命令,前面的“+”表示該命令結(jié)果不可忽略。$(call if_changed,link-vmlinux)是調(diào)用函數(shù)if_changed,link-vmlinux 是函數(shù)if_changed 的參數(shù),函數(shù)if_changed 定義在文件scripts/Kbuild.include 中,如下所示:
247 if_changed = $(if $(strip $(any-prereq) $(arg-check)), \ 248 @set -e; \ 249 $(echo-cmd) $(cmd_$(1)); \ 250 printf '%s\n' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd)any-prereq 用于檢查依賴文件是否有變化,如果依賴文件有變化那么any-prereq 就不為空,否則就為空。arg-check 用于檢查參數(shù)是否有變化,如果沒(méi)有變化那么arg-check 就為空。
第248 行,“@set -e”告訴bash,如果任何語(yǔ)句的執(zhí)行結(jié)果不為true(也就是執(zhí)行出錯(cuò))的話就直接退出。
第249 行,$ (echo-cmd)用于打印命令執(zhí)行過(guò)程,比如在鏈接vmlinux 的時(shí)候就會(huì)輸出“LINK vmlinux”。$ (cmd_$ (1))中的$ (1)表示參數(shù),也就是link-vmlinux,因此$ (cmd_$(1))表示執(zhí)行cmd_link-vmlinux 的內(nèi)容。cmd_link-vmlinux 在頂層Makefile 中有如下所示定義:
第915 行就是cmd_link-vmlinux 的值,其中CONFIG_SHELL=/bin/bash,$<表示目標(biāo)vmlinux的第一個(gè)依賴文件,根據(jù)示例代碼35.5.3.2 可知,這個(gè)文件為scripts/link-vmlinux.sh。LD= arm-linux-gnueabihf-ld -EL,LDFLAGS 為空。LDFLAGS_vmlinux 的值由頂層Makefile 和arch/arm/Makefile 這兩個(gè)文件共同決定,最終LDFLAGS_vmlinux=-p --no-undefined -X --pic-veneer --build-id。因此cmd_link-vmlinux 最終的值為:
cmd_link-vmlinux = /bin/bash scripts/link-vmlinux.sh arm-linux-gnueabihf-ld -EL -p --no-undefined -X --pic-veneer --build-idcmd_link-vmlinux 會(huì)調(diào)用scripts/link-vmlinux.sh 這個(gè)腳本來(lái)鏈接出vmlinux!在link-vmlinux.sh 中有如下所示代碼:
51 vmlinux_link() 52 { 53 local lds="${objtree}/${KBUILD_LDS}" 54 55 if [ "${SRCARCH}" != "um" ]; then 56 ${LD} ${LDFLAGS} ${LDFLAGS_vmlinux} -o ${2} \ 57 -T ${lds} ${KBUILD_VMLINUX_INIT} \ 58 --start-group ${KBUILD_VMLINUX_MAIN} --end-group ${1} 59 else 60 ${CC} ${CFLAGS_vmlinux} -o ${2} \ 61 -Wl,-T,${lds} ${KBUILD_VMLINUX_INIT} \ 62 -Wl,--start-group \ 63 ${KBUILD_VMLINUX_MAIN} \ 64 -Wl,--end-group \ 65 -lutil ${1} 66 rm -f linux 67 fi 68 } ...... 216 info LD vmlinux 217 vmlinux_link "${kallsymso}" vmlinuxvmliux_link 就是最終鏈接出vmlinux 的函數(shù),第55 行判斷SRCARCH 是否等于“um”,如果不相等的話就執(zhí)行56~58 行的代碼。因?yàn)镾RCARCH=arm,因此條件成立,執(zhí)行56~58 行的代碼。這三行代碼就應(yīng)該很熟悉了!就是普通的鏈接操作,連接腳本為lds= ./arch/arm/kernel/vmlinux.lds ,需要鏈接的文件由變量KBUILD_VMLINUX_INIT 和KBUILD_VMLINUX_MAIN 來(lái)決定,這兩個(gè)變量在示例代碼35.5.3.2 中已經(jīng)講解過(guò)了。
第217 行調(diào)用vmlinux_link 函數(shù)來(lái)鏈接出vmlinux。
使用命令“make V=1”編譯Linux,會(huì)有如圖35.5.3.1 所示的編譯信息:
至此我們基本理清了make 的過(guò)程,重點(diǎn)就是將各個(gè)子目錄下的built-in.o、.a 等文件鏈接在一起,最終生成vmlinux 這個(gè)ELF 格式的可執(zhí)行文件。鏈接腳本為arch/arm/kernel/vmlinux.lds,鏈接過(guò)程是由shell 腳本scripts/link-vmlinux.s 來(lái)完成的。接下來(lái)的問(wèn)題就是這些子目錄下的built-in.o、.a 等文件又是如何編譯出來(lái)的呢?
built-in.o 文件編譯生成過(guò)程
根據(jù)示例代碼35.5.3.2 第920 行可知,vmliux 依賴vmlinux-deps,而vmlinux-deps= $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN),KBUILD_LDS是鏈接腳本,這里不考慮,剩下的KBUILD_VMLINUX_INIT 和KBUILD_VMLINUX_MAIN 就是各個(gè)子目錄下的built-in.o、.a 等文件。最終vmlinux-deps 的值如下:
除了arch/arm/kernel/vmlinux.lds 以外,其他都是要編譯鏈接生成的。在頂層Makefile 中有如下代碼:
sort 是排序函數(shù),用于對(duì)vmlinux-deps 的字符串列表進(jìn)行排序,并且去掉重復(fù)的單詞。可以看出vmlinux-deps 依賴vmlinux-dirs,vmlinux-dirs 也定義在頂層Makefile 中,定義如下:
889 vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \ 890 $(core-y) $(core-m) $(drivers-y) $(drivers-m) \ 891 $(net-y) $(net-m) $(libs-y) $(libs-m)))vmlinux-dirs 看名字就知道和目錄有關(guān),此變量保存著生成vmlinux 所需源碼文件的目錄,值如下:
在頂層Makefile 中有如下代碼:
目標(biāo)vmlinux-dirs 依賴prepare 和scripts,這兩個(gè)依賴不去浪費(fèi)時(shí)間了,重點(diǎn)看一下第947行的命令。build 前面已經(jīng)說(shuō)了,值為“-f ./scripts/Makefile.build obj”,因此將947 行的命令展開(kāi)就是:
@ make -f ./scripts/Makefile.build obj=$@$@表示目標(biāo)文件,也就是vmlinux-dirs 的值,將vmlinux-dirs 中的這些目錄全部帶入到命令中,結(jié)果如下:
@ 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這些命令運(yùn)行過(guò)程其實(shí)都是一樣的,我們就以“@ make -f ./scripts/Makefile.build obj=init”這個(gè)命令為例,講解一下詳細(xì)的運(yùn)行過(guò)程。這里又要用到Makefile.build 這個(gè)腳本了,此腳本默認(rèn)目標(biāo)為_(kāi)_build,這個(gè)在35.5.2 小節(jié)已經(jīng)講過(guò)了,我們?cè)賮?lái)看一下,__build 目標(biāo)對(duì)應(yīng)的規(guī)則如下:
94 __build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \ 95 $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \ 96 $(subdir-ym) $(always) 97 @:當(dāng)只編譯Linux 內(nèi)核鏡像文件,也就是使用“make zImage ”編譯的時(shí)候,
KBUILD_BUILTIN=1,KBUILD_MODULES 為空。“make”命令是會(huì)編譯所有的東西,包括Linux內(nèi)核鏡像文件和一些模塊文件。如果只編譯Linux 內(nèi)核鏡像的話,__build 目標(biāo)簡(jiǎn)化為:
重點(diǎn)來(lái)看一下builtin-target 這個(gè)依賴,builtin-target 同樣定義在文件scripts/Makefile.build中,定義如下:
86 ifneq ($(strip $(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)),) 87 builtin-target := $(obj)/built-in.o 88 endif第87 行就是builtin-target 變量的值,為“$(obj)/built-in.o”,這就是這些built-in.o 的來(lái)源了。
要生成built-in.o,要求obj-y、obj-m、obj-、subdir-m 和lib-target 這些變量不能全部為空。最后一個(gè)問(wèn)題:built-in.o 是怎么生成的?在文件scripts/Makefile.build 中有如下代碼:
第336 行的目標(biāo)就是builtin-target,依賴為obj-y,命令為“KaTeX parse error: Double subscript at position 24: …_changed,link_o_?target)”,也就是調(diào)用函…(1)所對(duì)應(yīng)的命令($(1)就是函數(shù)的第1 個(gè)參數(shù)),在這里就是調(diào)用
cmd_link_o_target 所對(duì)應(yīng)的命令,也就是第331~334 行的命令。cmd_link_o_target 就是使用LD將某個(gè)目錄下的所有.o 文件鏈接在一起,最終形成built-in.o。
make zImage 過(guò)程
1、vmlinux、Image,zImage、uImage 的區(qū)別
前面幾小節(jié)重點(diǎn)是講vmlinux 是如何編譯出來(lái)的,vmlinux 是ELF 格式的文件,但是在實(shí)際中我們不會(huì)使用vmlinux,而是使用zImage 或uImage 這樣的Linux 內(nèi)核鏡像文件。那么vmlinux、zImage、uImage 他們之間有什么區(qū)別呢?
①、vmlinux 是編譯出來(lái)的最原始的內(nèi)核文件,是未壓縮的,比如正點(diǎn)原子提供的Linux 源碼編譯出來(lái)的vmlinux 差不多有16MB,如圖35.5.5.1 所示:
②、Image 是Linux 內(nèi)核鏡像文件,但是Image 僅包含可執(zhí)行的二進(jìn)制數(shù)據(jù)。Image 就是使用objcopy 取消掉vmlinux 中的一些其他信息,比如符號(hào)表什么的。但是Image 是沒(méi)有壓縮過(guò)的,Image 保存在arch/arm/boot 目錄下,其大小大概在12MB 左右如圖35.5.5.2 所示:
相比vmlinux 的16MB,Image 縮小到了12MB。
③、zImage 是經(jīng)過(guò)gzip 壓縮后的Image,經(jīng)過(guò)壓縮以后其大小大概在6MB 左右,如圖35.5.5.3 所示:
④、uImage 是老版本uboot 專用的鏡像文件,uImag 是在zImage 前面加了一個(gè)長(zhǎng)度為64字節(jié)的“頭”,這個(gè)頭信息描述了該鏡像文件的類型、加載位置、生成時(shí)間、大小等信息。但是新的uboot 已經(jīng)支持了zImage 啟動(dòng)!所以已經(jīng)很少用到uImage 了,除非你用的很古老的uboot。
使用“make”、“make all”、“make zImage”這些命令就可以編譯出zImage 鏡像,在arch/arm/Makefile 中有如下代碼:
310 BOOT_TARGETS = zImage Image xipImage bootpImage uImage ...... 315 $(BOOT_TARGETS): vmlinux 316 $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@第310 行,變量BOOT_TARGETS 包含zImage,Image,xipImage 等鏡像文件。
第315 行,BOOT_TARGETS 依賴vmlinux,因此如果使用“make zImage”編譯的Linux 內(nèi)
核的話,首先肯定要先編譯出vmlinux。
第316 行,具體的命令,比如要編譯zImage,那么命令展開(kāi)以后如下所示:
看來(lái)又是使用scripts/Makefile.build 文件來(lái)完成vmlinux 到zImage 的轉(zhuǎn)換。
關(guān)于Linux 頂層Makefile 就講解到這里,基本和uboot 的頂層Makefile 一樣,重點(diǎn)在于vmlinux 的生成。最后將vmlinux 壓縮成我們最常用的zImage 或uImage 等文件。
總結(jié)
以上是生活随笔為你收集整理的Linux 内核顶层Makefile 详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: U-Boot 移植
- 下一篇: linux环境配置与使用合集