ARM linux解析之zImage鏡像文件的生成
可能你會(huì)想,究竟linux如何編譯生成它的壓縮內(nèi)核鏡像zImage的呢?哈哈,下面就來做個(gè)解析。
本文引用地址:http://m.butianyuan.cn/article/201611/316874.htm當(dāng)我們?cè)趌inux_src目錄下輸入命令:
> make zImage
就會(huì)進(jìn)入linux_src/makfile,然后找zImage目標(biāo),這個(gè)目標(biāo)在:
linux_src/makfile包含的:include $(srctree)/arch/$(SRCARCH)/Makefile里面,
位于:linux_src/arch/arm/makfile里:
zImage Image xipImage bootpImage uImage: vmlinux
可以看到zImage依賴于vmlinux,這里的vmlinux指的是linux_src/vmlinux,這是編譯生成的linux內(nèi)核的elf文件。那么vmlinux又是在哪生成的呢?請(qǐng)向下看:
它的生成規(guī)則在linux_src/makefile文件中,如下:
#vmlinux image - including updated kernel symbols
vmlinux:$(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o $(kallsyms.o) FORCE
ifdef CONFIG_HEADERS_CHECK
endif
ifdef CONFIG_SAMPLES
endif
ifdef CONFIG_BUILD_DOCSRC
endif
vmlinux的生成依賴于:$(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o $(kallsyms.o),同樣在linux_src/makefile文件中:
vmlinux-init:= $(head-y) $(init-y)
vmlinux-main:= $(core-y) $(libs-y) $(drivers-y) $(net-y)
vmlinux-all
vmlinux-lds
modpost-init := $(filter-out init/built-in.o, $(vmlinux-init))
vmlinux.o: $(modpost-init) $(vmlinux-main) FORCE
kallsyms.o:= .tmp_kallsyms$(last_kallsyms).o
這里可以看到vmlinux就是由這些依賴文件通過arch/$(SRCARCH)/kernel/vmlinux.lds鏈接生成的,我就不再每往下解釋了。
生成了linux_src/vmlinux之后,再回頭看arch/arm/boot/compressed/Makefile文件中的:
zImage Image xipImage bootpImage uImage: vmlinux
其中$(build)在linux_src/scripts/kbuild.include:
###
# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj=
# Usage:
# $(Q)$(MAKE) $(build)=dir
build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj
因?yàn)镵BUILD_SRC為空,srctree就是當(dāng)前目錄,故
build := -f ./scripts/Makefile.build obj
boot := arch/arm/boot
ifneq ($(machine-y),)
MACHINE
else
MACHINE
endif
machine-$(CONFIG_ARCH_EVB_ARM)
Kconfig中:CONFIG_ARCH_EVB_ARM = y
所以對(duì)于我們的平臺(tái):
MACHINE
最開始的那一句最終解釋為:
zImage:vmlinux
@make –f./scripts/Makefile.build obj=arch/arm/boot MACHINE=arch/arm/mach-evb_armarch/arm/boot/zImage
下面就是進(jìn)入到linux_src/scripts/Makefile.build這個(gè)makefile文件中
src := $(obj)
kbuild-dir:= $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
kbuild-file:= $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
include$(kbuild-file)
在這里:
src:= $(obj) :=arch/arm/boot
kbuild-dir:= $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
//這句的意思是,如果src是以/開頭的,則kbuild-dir :=$(src),
如果不是的就等于kbuild-dir :=$(srctree)/$(src)
這里kbuild-dir:= ./arm/arm/boot
kbuild-file :=./arch/arm/boot/Kbuild ./arch/arm/boot/Makefile
最后一句:
include ./arch/arm/boot/Kbuild ./arch/arm/boot/Makefile
可以看到這里是要包含linux_src/arch/arm/boot/Makefile文件,果然在里面找到了我們要的目標(biāo):
$(obj)/zImage:$(obj)/compressed/vmlinux FORCE
$(call if_changed,objcopy)
@echo
再找$(obj)/compressed/vmlinux:
$(obj)/compressed/vmlinux: $(obj)/Image FORCE
$(Q)$(MAKE) $(build)=$(obj)/compressed $@
再接著找$(obj)/Image:
$(obj)/Image: vmlinux FORCE
$(call if_changed,objcopy)
@echo
到這里就可以知道了,哦,原來這個(gè)地方也是要vmlinux內(nèi)核elf的啊,這就知道了,無論怎么樣,vmlinux文件都要先生成,否則其他的文件都無法成生。下面來解釋一下:
$(obj)/Image: vmlinux FORCE
$(call if_changed,objcopy)
@echo
這里的Image也就是linux_src/arch/arm/boot/Image,它的生成是通過把linux_src/vmlinux這個(gè)elf文件用通過objcopy生成bin文件Image。然后:
$(obj)/compressed/vmlinux: $(obj)/Image FORCE
$(Q)$(MAKE) $(build)=$(obj)/compressed $@
這句和上面分析的類似,就是進(jìn)入linux_src/arch/arm/boot/compress/mafile中生成:
arch/arm/boot/compress/vmlinux文件,如下:
$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.$(suffix_y).o
HEAD
OBJS
$(obj)/piggy.$(suffix_y): $(obj)/../Image FORCE
$(obj)/piggy.$(suffix_y).o:
$(obj)/vmlinux.lds: $(obj)/vmlinux.lds.in arch/arm/boot/Makefile $(KCONFIG_CONFIG)
插曲:關(guān)于if_changed的解釋:
Kbuild.include:
# >< substitution is for echo to work,
# >$< substitution to preserve $ when reloading .cmd file
# note: when using inline perl scripts [perl -e ...$$t=1;...]
# in $(cmd_xxx) double $$ your perl vars
make-cmd= $(subst \,\\,$(subst #,\#,$(subst $$,$$$$,$(call escsq,$(cmd_$(1))))))
# Find any prerequisites that is newer than target or that does not exist.
# PHONY targets skipped in both cases.
any-prereq= $(filter-out $(PHONY),$?) $(filter-out $(PHONY) $(wildcard $^),$^)
# Execute command if command has changed or prerequisite(s) are updated.
#
if_changed= $(if $(strip $(any-prereq) $(arg-check)),
if_changed函數(shù)在當(dāng)發(fā)現(xiàn)規(guī)則的依賴有更新,或是目依賴不存在時(shí),再或者是對(duì)應(yīng)目標(biāo)的命令行參數(shù)發(fā)生改變時(shí)($(strip $(any-prereq) $(arg-check))語句結(jié)果不為空),執(zhí)行后面的語句。
set -e表示如果命令執(zhí)行有錯(cuò)那么命令停止執(zhí)行并退出。
接著$(echo-cmd)用來打印出相關(guān)的編譯命令,接著執(zhí)行$(cmd_$(1)里的命令。
最后echo cmd_$@ := $(make-cmd) > $(dot-target).cmd將上面執(zhí)行的命令寫入一個(gè)叫$(dot-target).cmd的文件中,該文件為隱藏文件,在編譯后的內(nèi)核源碼目錄及其子目錄下隨處可見,比如在init/下可以看到.initramfs.o.cmd,.version.o.cmd等等。
那么而所有的命令測(cè)存在呢?答案是:scripts/Makefile.lib:
scripts/Makefile.build里面:include scripts/Makefile.lib
scripts/Makefile.lib:
# Objcopy
# ---------------------------------------------------------------------------
quiet_cmd_objcopy= OBJCOPY
cmd_objcopy= $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $< $@
# Gzip
# ---------------------------------------------------------------------------
quiet_cmd_gzip= GZIP
cmd_gzip= (cat $(filter-out FORCE,$^) | gzip -n -f -9 > $@) || (rm -f $@ ; false)
言歸正傳,回到上面:
$(obj)/piggy.$(suffix_y): $(obj)/../Image FORCE
$(obj)/piggy.$(suffix_y).o:
這里的suffix_y在,arch/arm/boot/compressed:
suffix_$(CONFIG_KERNEL_GZIP) = gzip
suffix_$(CONFIG_KERNEL_LZO)
suffix_$(CONFIG_KERNEL_LZMA) = lzma
CONFIG_KERNEL_GZIP=y是在init/Kconfig文件里選擇的,menuconfig時(shí)配置,我們這里選gzip,則上面的過程為
使用gzip對(duì)arch/arm/boot/Image文件進(jìn)行壓縮,壓縮成piggy.gzip。然后再生成piggy.gzip.o:
這個(gè)是通過piggy.gzip.S文件生成的,內(nèi)容如下:
input_data:
input_data_end:
可以看到壓縮后的內(nèi)核文件piggy.gzip是以bin文件的形式編譯成piggy.gzip.o的,并且注意這里的是放在.piggydata段中的,這個(gè)在arch/arm/boot/compressed/vmlinux.lds指定的。
內(nèi)容如下:
是放在.text段的最后的,并且input_data和input_data_end包含里他們的起止地址。
最后是arch/arm/boot/compressed/vmlinux.lds文件的生成,這個(gè)是以arch/arm/boot/compressed/vmlinux.lds.in為藍(lán)本,只是修改了
這個(gè)是在arch/arm/boot/compressed/Makefile中
SEDFLAGS = s/TEXT_START/$(ZTEXTADDR)/;s/BSS_START/$(ZBSSADDR)/
$(obj)/vmlinux.lds: $(obj)/vmlinux.lds.in arch/arm/boot/Makefile $(KCONFIG_CONFIG)
ifeq ($(CONFIG_ZBOOT_ROM),y)
ZTEXTADDR
ZBSSADDR:= $(CONFIG_ZBOOT_ROM_BSS)
else
ZTEXTADDR
ZBSSADDR:= ALIGN(8)
endif
就是:TEXT_START = $(ZTEXTADDR)
如果內(nèi)枋是在nor flash中運(yùn)行的,則選第一個(gè),這時(shí)CONFIG_ZBOOT_ROM這個(gè)變量會(huì)定義,在RAM中運(yùn)行的話,選第二個(gè)。在RAM中運(yùn)行的代碼是被編譯成與位置無關(guān)的,所以可以加載到任何地方運(yùn)行。
所以合起來的解釋是:
$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.$(suffix_y).o
這個(gè)arch/arm/boot/compressed/vmlinux和前面的根目錄下生成的vmlinux都是elf文件,但是卻不是同一個(gè)東西。它是包令里解壓用的頭文件head.s,解壓程序misc.c,decompress.c的,再加入壓縮內(nèi)核的elf文件,是可以通過trace32加載運(yùn)行的。
下面是最后一步:
$(obj)/Image: vmlinux FORCE
$(call if_changed,objcopy)
@echo
這個(gè)時(shí)候再把a(bǔ)rch/arm/boot/compressed/vmlinux通過objcopy生成bin文件zImage,到這里,zImage文件生成完畢。
最后用個(gè)圖表示一下整個(gè)zImage的生成過程:
圖1.ARM linux的zImage生成過程
評(píng)論