一、概述 本文基于Linux Kernel 4.10版本講解。 Linux內(nèi)核采用類似于GNU Make的kbuild構(gòu)建而成,關(guān)于內(nèi)核的構(gòu)建系統(tǒng)kbuild,可以先看本公眾號(hào)內(nèi)以前的文章: Kbuild結(jié)構(gòu)簡介 arm linux 內(nèi)核的構(gòu)建分為三次編譯鏈接,一次組合。三次鏈接的中間結(jié)果分別是: 1.arch/arm/boot/compressed/vmlinux 2.arch/arm/boot/vmlinux.bin 3.arch/arm/boot/setup.bin 最后的組合就是將vmlinux.bin和setup.bin組合成arch/arm/boot/zImage: 二、vmlinux的構(gòu)建 vmlinux的構(gòu)建在頂層的Makefile中: cmd_link-vmlinux = \ $(CONFIG_SHELL) $<$(LD) $(LDFLAGS) $(LDFLAGS_vmlinux) ; \ $(if $(ARCH_POSTLINK), $(MAKE) -f$(ARCH_POSTLINK) $@, true) vmlinux: scripts/link-vmlinux.sh vmlinux_prereq$(vmlinux-deps) FORCE +$(callif_changed,link-vmlinux) 其中,call是make的內(nèi)置函數(shù),用于調(diào)用用戶自己定義的帶有參數(shù)的函數(shù),這里調(diào)用的是if_changed,參數(shù)是link-vmlinux。 if_changed是scripts/Kbuild.include里定義的一個(gè)函數(shù),定義如下: if_changed = $(if $(strip $(any-prereq) $(arg-check)), \ @set -e; \ $(echo-cmd) $(cmd_$(1)); \ echo 'cmd_$@ :=$(make-cmd)' > $(dot-target).cmd) any-prereq 檢查是否有依賴比目標(biāo)新,或者依賴還沒有創(chuàng)建;arg-check 檢查編譯目標(biāo)的命令相對(duì)上次是否發(fā)生變化。set –e 命令表示make 出錯(cuò)時(shí)直接退出,加個(gè)@ 符號(hào)表示不顯示該set 命令。cmd_$(1) 中的1 表示傳給if_changed 的第一個(gè)參數(shù)。嵌入式物聯(lián)網(wǎng)智能硬件企鵝意義氣嗚嗚吧久零就易,在這里傳給if_changed 的實(shí)參是link-vmlinux ,所以cmd_$(1) 展開后為cmd_link-vmlinux 。 注意cmd_link-vmlinux中的$<表示規(guī)則中的第一個(gè)依賴,即scripts/link-vmlinux.sh。這個(gè)腳本用于vmlinux的鏈接,內(nèi)容如下: # Link of vmlinux # ${1} - optionalextra .o files # ${2} - output file vmlinux_link() { locallds="${objtree}/${KBUILD_LDS}" local objects if [ "${SRCARCH}" !="um" ]; then if [ -n"${CONFIG_THIN_ARCHIVES}" ]; then objects="--whole-archivebuilt-in.o ${1}" else objects="${KBUILD_VMLINUX_INIT} \ --start-group \ ${KBUILD_VMLINUX_MAIN} \ --end-group \ ${1}" fi ${LD} ${LDFLAGS}${LDFLAGS_vmlinux} -o ${2} \ -T ${lds} ${objects} else ... ... fi } 如果平臺(tái)不是“um”,就將變量KBUILD_VMLINUX_INIT和KBUILD_VMLINUX_MAIN中的目標(biāo)文件鏈接為vmlinux;否則就直接編譯為vmlinux,也就是式中的${2}。 接下來以core-y來分析變量KBUILD_VMLINUX_MAIN: Linux-4.10/Makefile: exportKBUILD_VMLINUX_INIT := $(head-y) $(init-y) exportKBUILD_VMLINUX_MAIN := $(core-y) $(libs-y) $(drivers-y) $(net-y) $(virt-y) exportKBUILD_LDS :=arch/$(SRCARCH)/kernel/vmlinux.lds … … core-y := usr/ … … core-y += kernel/ certs/ mm/ fs/ ipc/security/ crypto/ block/ … … core-y := $(patsubst %/, %/built-in.o,$(core-y)) make 的內(nèi)置函數(shù)patsubst用于查找模式匹配的字符串,并進(jìn)行替換。在上面這句語句里,就是將所有‘/’替換成‘/built-in.o’。因此core-y最終變?yōu)椋?/font> core-y :=user/built-in.o kernel/built-in.o mm/built-in.o fs/built-in.o ipc/built-in.osecurity/ built-in.o crypto/ built-in.o block/ built-in.o 再看其他幾個(gè)類似的賦值語句: init-y := $(patsubst %/,%/built-in.o, $(init-y)) drivers-y :=$(patsubst %/, %/built-in.o, $(drivers-y)) net-y := $(patsubst %/,%/built-in.o, $(net-y)) libs-y1 := $(patsubst %/, %/lib.a,$(libs-y)) libs-y2 := $(patsubst %/, %/built-in.o,$(libs-y)) libs-y := $(libs-y1) $(libs-y2) virt-y := $(patsubst %/,%/built-in.o, $(virt-y)) 不難看出,vmlinux就是由這些目錄下的built-in.o和lib.a鏈接而成。 vmlinux的另一個(gè)依賴是vmlinux-deps,其構(gòu)建規(guī)則也在頂層Makefile中定義: Linux-4.10/Makefile: vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y)$(init-m) \ $(core-y) $(core-m) $(drivers-y)$(drivers-m) \ $(net-y) $(net-m) $(libs-y) $(libs-m)$(virt-y))) vmlinux-deps :=$(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN) … … # The actual objectsare generated when descending, # make sure noimplicit rule kicks in $(sort$(vmlinux-deps)): $(vmlinux-dirs) ; … … $(vmlinux-dirs):prepare scripts $(Q)$(MAKE) $(build)=$@ 目標(biāo)vmlinux-deps的構(gòu)建規(guī)則下沒有命令可執(zhí)行,只依賴于另外一個(gè)目標(biāo)vmlinux-dirs, 該變量的賦值語句里的filter表示過濾掉不以‘/’結(jié)尾的字符串。而filter的這些輸入變量,如core-y,其子目錄都是以‘/’結(jié)尾。因此vmlinux-dirs是一個(gè)多目標(biāo)規(guī)則,相當(dāng)于: init: prepare scripts $(Q) $(MAKE) $(build) =$@ kernel: preparescripts $(Q) $(MAKE) $(build) =$@ … … 規(guī)則中的命令展開為: Make –f script/Makefile.buildobj=$@ Make的自動(dòng)變量$@表示規(guī)則的目標(biāo),這里就是要構(gòu)建的子目錄init,kernel等。 總結(jié)一下,kbuild依次構(gòu)建Makefile中指定的子目錄,生成built-in.o、lib.a等文件,然后鏈接為vmlinux。
|