最开端做个约好:寄存linux代码的根目录咱们叫做:linux_src,代码用linux-3.0.46进行剖析。关于arm体系,$(SRCARCH) = ARM。
或许你会想,终究linux怎么编译生成它的紧缩内核镜像zImage的呢?哈哈,下面就来做个解析。
当咱们在linux_src目录下输入指令:
> make zImage
就会进入linux_src/makfile,然后找zImage方针,这个方针在:
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内核的elf文件。那么vmlinux又是在哪生成的呢?请向下看:
它的生成规矩在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
由于KBUILD_SRC为空,srctree便是当时目录,故
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
所以关于咱们的渠道:
MACHINE
最开端的那一句终究解说为:
zImage:vmlinux
@make –f./scripts/Makefile.build obj=arch/arm/boot MACHINE=arch/arm/mach-evb_armarch/arm/boot/zImage
下面便是进入到linux_src/scripts/Makefile.build这个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文件,公然在里边找到了咱们要的方针:
$(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
到这儿就能够知道了,哦,本来这个当地也是要vmlinux内核elf的啊,这就知道了,不管怎么样,vmlinux文件都要先生成,不然其他的文件都无法成生。下面来解说一下:
$(obj)/Image: vmlinux FORCE
$(call if_changed,objcopy)
@echo
这儿的Image也便是linux_src/arch/arm/boot/Image,它的生成是经过把linux_src/vmlinux这个elf文件用经过objcopy生成bin文件Image。然后:
$(obj)/compressed/vmlinux: $(obj)/Image FORCE
$(Q)$(MAKE) $(build)=$(obj)/compressed $@
这句和上面剖析的相似,便是进入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)
插曲:关于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函数在当发现规矩的依靠有更新,或是目依靠不存在时,再或者是对应方针的指令行参数产生改动时($(strip $(any-prereq) $(arg-check))句子成果不为空),履行后边的句子。
set -e表明假如指令履行有错那么指令中止履行并退出。
接着$(echo-cmd)用来打印出相关的编译指令,接着履行$(cmd_$(1)里的指令。
终究echo cmd_$@ := $(make-cmd) > $(dot-target).cmd将上面履行的指令写入一个叫$(dot-target).cmd的文件中,该文件为躲藏文件,在编译后的内核源码目录及其子目录下随处可见,比如在init/下能够看到.initramfs.o.cmd,.version.o.cmd等等。
那么而一切的指令测存在呢?答案是: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时装备,咱们这儿选gzip,则上面的进程为
运用gzip对arch/arm/boot/Image文件进行紧缩,紧缩成piggy.gzip。然后再生成piggy.gzip.o:
这个是经过piggy.gzip.S文件生成的,内容如下:
input_data:
input_data_end:
能够看到紧缩后的内核文件piggy.gzip是以bin文件的方式编译成piggy.gzip.o的,而且留意这儿的是放在.piggydata段中的,这个在arch/arm/boot/compressed/vmlinux.lds指定的。
内容如下:
是放在.text段的终究的,而且input_data和input_data_end包括里他们的起止地址。
终究是arch/arm/boot/compressed/vmlinux.lds文件的生成,这个是以arch/arm/boot/compressed/vmlinux.lds.in为蓝本,仅仅修改了
这个是在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)
假如内枋是在nor flash中运转的,则选第一个,这时CONFIG_ZBOOT_ROM这个变量会界说,在RAM中运转的话,选第二个。在RAM中运转的代码是被编译成与方位无关的,所以能够加载到任何当地运转。
所以合起来的解说是:
$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.$(suffix_y).o \
这个arch/arm/boot/compressed/vmlinux和前面的根目录下生成的vmlinux都是elf文件,可是却不是同一个东西。它是包令里解压用的头文件head.s,解压程序misc.c,decompress.c的,再参加紧缩内核的elf文件,是能够经过trace32加载运转的。
下面是终究一步:
$(obj)/Image: vmlinux FORCE
$(call if_changed,objcopy)
@echo
这个时分再把arch/arm/boot/compressed/vmlinux经过objcopy生成bin文件zImage,到这儿,zImage文件生成结束。
终究用个图表明一下整个zImage的生成进程:
图1.ARM linux的zImage生成进程