我们经常能看到内核在编译完成后生产各种映像文件,如:Image 、zImage、bzImage等。
其实最开始出现的是 Image,就一个普通的内核镜像。后来为了节省空间,有了 zImage,进行了压缩可以节省空间。
那么uImage又是什么的?它是uboot专用的映像文件,它是在zImage之前加上一个长度为64字节的“头”,说明这个内核的版本、加载位置、生成时间、大小等信息;其0x40之后与zImage没区别。
几种linux内核文件的区别:
1、vmlinux :编译出来后未压缩最原始的内核文件
2、zImage :vmlinux经过gzip压缩后的文件。
3、bzImage:bz表示“big zImage”,是用bzip2压缩的。两者的不同之处在于,zImage解压缩内核到低端内存(第一个640K),bzImage解压缩内核到高端内存(1M以上)。如果内核比较小,那么采用zImage或bzImage都行,如果比较大应该用bzImage。
4、uImage : U-boot专用的映像文件,它是在zImage之前加上一个长度为0x40的TAG。
5、vmlinuz: bzImage/zImage文件的拷贝或指向bzImage/zImage的符号链接。
6、initrd ,: “initial ramdisk”的简写。一般被用来临时的引导硬件到实际内核vmlinuz能够接管并继续引导的状态
上图为bzImage的组成,其由压缩后的内核镜像vmlinux.bin.gz,加上未压缩部分uncompressed和setup.bin三部分组成,下面分别对这三部分进行详细的说明。
setup.bin
在以前,内核在进行初始化时,需要一些信息,如:显示信息、内存信息等,这些工作就由setup.bin在实模式通过BIOS来获取,然后保存在内核中的变量boot_params中,变量boot_params是结构体boot_params的一个实例。
在完成信息收集之后,setup.bin就会切换成保护模式,并跳转到内核保护模式部分执行。内核将setup.bin收集保存在setup.bin的数据段变量boot_params复制到vmlinux的数据段中。
现在,随着新的BIOS标准的出现,尤其是EFI的出现,为了支持这些新标准,开发者们制定了32位启动协议(32-bit boot protocol)。在32位启动协议下,由Bootloader实现收集这些信息的功能,内核启动时不再需要首先运行实模式部分(即setup.bin),而是直接跳转到内核的保护模式部分。
不过setup.bin也不完全就被淘汰了,其还有一个重要功能就是负责在内核和Bootloader之间传递信息。32位启动协议规定在setup.bin中分配一块空间用来承载这些信息(内核是否可重定位、内核的对齐要求、内核建议的加载地址等),在构建映像时,内核构建系统需要将这些写到setup.bin的这块空间中,所以setup.bin已经失去了过去的作用,不过仍然作为内核和Bootloader之间的桥梁。
uncompressed
内核中的保护模式部分是经过压缩的,因此运行前需要进行解压。Bootloader在加载内核映像后跳转至外围的这段非压缩部分,由这部分非压缩指令,负责解压内核的压缩部分。
除此之外,非压缩部分还需要负责内核的重定位,在编译内核进行链接时,为了方便链接,会指定一个虚假(虚拟)地址,然后以这个虚拟地址为参考,为各个符号分配运行时地址,如果加载的地址和链接时指定的地址不同,就需要对符号的地址进行重新修订,即内核重定位。
vmlinux
在编译时,kbuild分别构建内核各个子目录中的目标文件,然后将它们链接为vmlinux。kbuild会删除vmlinux中一些不必要的信息,并将其命名为vmlinux.bin,最后将其压缩为vmlinux.bin.gz。
不同的镜像可能会有不同的映像组成形式,不过大体上都是相似的,后面我会尝试从源码角度分析uImage映像的组成,会与上面提到的x86的bzImage映像进行对比。
内核映像的格式
setup.bin、vmlinux.bin等都是裸二进制格式的文件,并不是操作系统中程序所使用的ELF格式,原因是操作系统内核本身是工作在"freestanding enviroment"环境下。操作系统不能强制要求Bootloader也提供ELF加载器,而且操作系统镜像也没必要使用ELF格式来组织。
不过在Linux2.6.26版本后,内核压缩部分,使用了ELF格式。而这个解析ELF的工作,也交给了uncompressed部分的代码来实现。
Linux内核 – 内核源码结构 (cnblogs.com)
make menuconfig
Linux内核配置以及Make menuconfig过程分析
linux内核的配置机制及其编译过程
Kbuild系统
参考:Linux内核Makefile分析
在Linux内核里,每个子目录都有一个makefile,它被称作Kbuilt-makefile,它将当前目录的文件编译成built-in.o、库文件和模块文件。然后顶层Makefile里指定这些built-in.o的路径,将它们连接在一起,最后链接成一个vmlinux文件。
内核makefile.txt中将makefile分为 5部分,Kernel Makefile、ARCH Makefile、KBuild Makefile、.config文件以及scripts/Makefile.*
Kernel Makefile 位于Linux 内核源代码的顶层目录,也叫 Top Makefile 。它用于指定编译Linux Kernel 目标文件(vmlinux)和模块(module)路径。它根据.config文件决定了内核根目录下那些文件、子目录被编译进内核。对于内核或驱动开发人员来说,这个文件几乎不用任何修改。
ARCH Makefile
ARCH Makefile位于ARCH/$(ARCH)/Makefile ,是系统对应平台的Makefile 。Kernel Top Makefile会包含这个文件来指定平台相关信息。ARCH Makefile同样根据.config文件,决定了ARCH/$(ARCH) 目录下哪些文件、子目录会被编译进内核,只有平台开发人员会关心这个文件。
Kbuild Makefile
从Linux 内核2.6 开始,Linux 内核的编译采用Kbuild系统 ,这同过去的编译系统有很大的不同,Kbuild系统使用Kbuild Makefile来编译内核或模块。当Kernel Makefile被解析完成后,Kbuild会读取相关的Kbuild Makefile进行内核或模块的编译。Kbuild Makefile有特定的语法指定哪些编译进内核中、哪些编译为模块、及对应的源文件是什么等。内核及驱动开发人员需要编写这个Kbuild Makefile文件。
scripts/Makefile.* 通用规则
Makefile.build
被顶层Makefile所调用,与各级子目录的Makefile合起来构成一个完整的Makefile文件,定义built-in.o、.lib以及目标文件.o的生成规则。这个Makefile文件生成了子目录的.lib、built-in.o以及目标文件.o
Makefile.clean
被顶层Makefile所调用,用来删除目标文件等
Makefile.lib
被Makefile.build所调用,主要是对一些变量的处理,比如说在obj-y前边加上obj目录
Kbuild.include
被Makefile.build所调用,定义了一些函数,如if_changed、if_changed_rule、echo-cmd
.config文件详细参考make menuconfig部分。
在编译嵌入式系统使用的内核时,我们会在内核的根目录下面执行make uImage
来生成内核镜像,或者make后面不接入任何目标。在没有接目标的时候,默认也是生成内核映像uImage。顶层Makefile会通过include指令,将架构相关的Makefile文件引入顶层Makefile中:
include $(srctree)/arch/$(SRCARCH)/Makefile
在如arm架构下的arch/arm/Makefile中:
# Convert bzImage to zImage
bzImage: zImage
zImage Image xipImage bootpImage uImage: vmlinux
$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
从第4行各种的依赖,我们可以看到无论是生成何种image映像,我们都需要依赖vmlinux文件。在前面我们介绍了x86使用的bzImage是带压缩的,那么我们平时使用的uImage是压缩的吗?在arch/arm/boot/Makefile文件里:
$(obj)/uImage: $(obj)/zImage FORCE
@$(check_for_multiple_loadaddr)
$(call if_changed,uimage)
@$(kecho) ' Image $@ is ready'
我们可以看到uImage是依赖于zImage的,zImage是一种带压缩格式的映像,可见uImage也是带压缩的,后面会进行详细分析。
构建vmlinux(如果没有特殊标明,说明代码来源于顶层Makefile文件)
在上面我们看到vmlinux是很关键的依赖,所以我们尝试先对vmlinux进行分析,看它是如何构建的。等分析出了vmlinux后,再往上分析追踪一下uImage是如何构建的。这里我们先往下进行分析,看看vmlinux的如何构建的:
# Final link of vmlinux
cmd_link-vmlinux = $(CONFIG_SHELL) $< $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux)
# Include targets which we want to
# execute if the rest of the kernel build went well.
vmlinux: scripts/link-vmlinux.sh $(vmlinux-deps) FORCE
+$(call if_changed,link-vmlinux)
在顶层Makefile中,我们可以看到vmlinux依赖于scripts/link-vmlinux.sh、$(vmlinux-deps)、FORCE。第7行处通过call引用if_changed函数:
# scripts/Kbuild.include(对于非顶层Makefile的代码片段,会以这种形式进行注明,未注明则为顶层Makefile的片段)
# Execute command if command has changed or prerequisite(s) are updated.
if_changed = $(if $(strip $(any-prereq) $(arg-check)), \
@set -e; \
$(echo-cmd) $(cmd_$(1)); \
printf '%s\n' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd, @:)
if_changed函数定义在scripts/Kbuild.include里面,第6行处有一个$(cmd_$(1))
的东西,我们展开后就成了cmd_link-vmlinux
,就是我们刚刚在顶层Makefile面列出来的,于是我们回到顶层Makefile的cmd_link-vmlinux变量:
cmd_link-vmlinux = $(CONFIG_SHELL) $< $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux)
下面,我们先对 $(CONFIG_SHELL) $< $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux)
进行分析:
CONFIG_SHELL:指定使用的shell解释器类型,这个不重要。
$<:表示第一个依赖,由于cmd_link-vmlinux在vmlinux的“规则体”下面,所以这个$<,翻译一下就是scripts/link-vmlinux.sh
LD:链接器类型,在顶层Makefile的前面,我们一般会配置使用的各种工具链,这个LD是作为参数传给link-vmlinux.sh脚本使用的,可以猜测编译最后会使用这个链接器来链接。
# Make variables (CC, etc...)
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
LDFLAGS、LDFLAGS_vmlinux:链接使用到的一些参数,我们不关心。
变量cmd_link-vmlinux的值$(CONFIG_SHELL) $< $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux)
本质是执行scripts/link-vmlinux.sh脚本,而LD、LDFLAGS、LDFLAGS_vmlinux作为参数传递给link-vmlinux.sh脚本。
从名字我们就可以猜测该脚本用于链接生成vmlinux。下面我们研究scripts/link-vmlinux.sh脚本:
# scripts/link-vmlinux.sh
# Link of vmlinux
# ${1} - optional extra .o files
# ${2} - output file
vmlinux_link()
{
local lds="${objtree}/${KBUILD_LDS}"
if [ "${SRCARCH}" != "um" ]; then
${LD} ${LDFLAGS} ${LDFLAGS_vmlinux} -o ${2} \
-T ${lds} ${KBUILD_VMLINUX_INIT} \
--start-group ${KBUILD_VMLINUX_MAIN} --end-group ${1}
else
...省略...
在第11行处,调用了${LD}
链接器进行链接,输出文件为\${2}
。
${2}:link-vmlinux.sh中会调用vmlinux_link()函数,以vmlinux_link "${kallsymso}" vmlinux
的形式调用,因此生成的文件名就为第二参数vmlinux。
${KBUILD_VMLINUX_INIT}、${KBUILD_VMLINUX_MAIN}:要链接成vmlinux的源文件。
# Objects we will link into vmlinux / subdirs we need to visit
init-y := init/
drivers-y := drivers/ sound/ firmware/
net-y := net/
libs-y := lib/
core-y := usr/
init-y := $(patsubst %/, %/built-in.o, $(init-y))
core-y := $(patsubst %/, %/built-in.o, $(core-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)
# Externally visible symbols (used by link-vmlinux.sh)
export KBUILD_VMLINUX_INIT := $(head-y) $(init-y)
export KBUILD_VMLINUX_MAIN := $(core-y) $(libs-y) $(drivers-y) $(net-y)
export KBUILD_LDS := arch/$(SRCARCH)/kernel/vmlinux.lds
可以看到vmlinux的链接需要各个子目录下的built-in.o和部分目录下的lib.a(也就是lib/目录),以及head-y,其在架构相关Makefile下定义,定义为head-y := arch/arm/kernel/head$(MMUEXT).o
,也就是head.xxx.o。
${lds}:链接时使用的链接脚本,其为顶层Makefile中的KBUILD_LDS变量。
export KBUILD_LDS := arch/$(SRCARCH)/kernel/vmlinux.lds
LDFLAGS、LDFLAGS_vmlinux:顶层Makefile中传进来的一些链接参数,我们不关心。
至此,我们知道了,要链接生成vmlinux,我们需要lib目录下面的lib.a以及子目录下的built-in.o文件,那么它们在如何生成的呢?
回到前面的vmlinux依赖的片段:
vmlinux: scripts/link-vmlinux.sh $(vmlinux-deps) FORCE
+$(call if_changed,link-vmlinux)
我们可以看到vmlinux不仅依赖link-vmlinux.sh脚本,还依赖vmlinux-deps变量,于是我们再对vmlinux-deps进行分析,其依赖如下:
vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \
$(core-y) $(core-m) $(drivers-y) $(drivers-m) \
$(net-y) $(net-m) $(libs-y) $(libs-m)))
vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN)
# The actual objects are generated when descending,
# make sure no implicit rule kicks in
$(sort $(vmlinux-deps)): $(vmlinux-dirs) ;
PHONY += $(vmlinux-dirs)
$(vmlinux-dirs): prepare scripts
$(Q)$(MAKE) $(build)=$@
从第9行来看,目标vmlinux-deps的构建规则,其“规则体”是空的,不过它依赖于vmlinux-dirs,于是我们继续追踪vmlinux-dirs的构建。我们先看一下变量vmlinux-dirs的值,注意该变量的赋值脚本,其中函数filter是make的内置函数,其功能是过滤输入文本中不以“/”结尾的字符串。翻译最终结果等价于:
init: prepare scripts
$(Q)$(MAKE) $(build)=$@
kernel: prepare scripts
$(Q)$(MAKE) $(build)=$@
...
我们将$(Q)$(MAKE) $(build)=$@
展开后为:
make -f script/Makefile.build obj=$@
可见是通过script/Makefile.build中的规则进行编译,下面我们追踪进Makefile.build:
# scripts/Makefile.build
src := $(obj)
PHONY := __build
__build:
__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
$(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
$(subdir-ym) $(always)
目标__build涵盖了内核映像(KBUILD_BUILTIN)和模块(KBUILD_MODULES),这里我们只关注内核映像的构建,不关注模块的构建。对于内核映像来说,目标依赖builtin-target、lib-target、extra-y、subdir-ym和always。我们先看builtin-target和lib-target:
# scripts/Makefile.build
ifneq ($(strip $(lib-y) $(lib-m) $(lib-n) $(lib-)),)
lib-target := $(obj)/lib.a
endif
ifneq ($(strip $(obj-y) $(obj-m) $(obj-n) $(obj-) $(subdir-m) $(lib-target)),)
builtin-target := $(obj)/built-in.o
endif
根据上面的脚本片断可见,builtin-target代表子目录下的built-in.o,lib-target 代表的就是子目录下的lib.a。如果lib-y不为空就构建lib.a,如果obj-y不为空就构建built-in.a。接下来,我们再在看看lib.a和built-in.o是怎么构建的:
# scripts/Makefile.build
# Rule to compile a set of .o files into one .o file
ifdef builtin-target
quiet_cmd_link_o_target = LD $@
# If the list of objects to link is empty, just create an empty built-in.o
cmd_link_o_target = $(if $(strip $(obj-y)),\
$(LD) $(ld_flags) -r -o $@ $(filter $(obj-y), $^) \
$(cmd_secanalysis),\
rm -f $@; $(AR) rcs$(KBUILD_ARFLAGS) $@)
$(builtin-target): $(obj-y) FORCE
$(call if_changed,link_o_target)
targets += $(builtin-target)
endif # builtin-target
#
# Rule to compile a set of .o files into one .a file
#
ifdef lib-target
quiet_cmd_link_l_target = AR $@
cmd_link_l_target = rm -f $@; $(AR) rcs$(KBUILD_ARFLAGS) $@ $(lib-y)
$(lib-target): $(lib-y) FORCE
$(call if_changed,link_l_target)
targets += $(lib-target)
endif
前面我们说明了if_changed的使用,这里我们就可以很容易理解builtin-target就是将变量obj-y中记录的各个目标文件,通过LD链接为built-in.o。对于lib-target,则通过AR是将lib-y中各个目标文件链接为lib.a。
至此,我们就明白了生成vmlinux的lib.a以及built-in.o文件是如何产生的了。
总结一下,我们要生成vmlinux,就需要依赖init/、kernel/、drivers/等目录下的built-in.o文件,同时还需要架构相关的head.xxx.o和lib/lib.a文件。在编译生成vmlinux之前,Makefile的依赖关系会确保后面三者先产生,然后再链接生成vmlinux。
构建vmlinux.bin或Image
接着我们来研究,如何从vmlinux一步一步的生成uImage。在arm架构的Makefile中,我们可以找到目标uImage,它依赖于zImage,而目标zImage又依赖于compressed/vmlinux,而再往下追踪,我们又可以看到zImage依赖于Image:
#arch/arm/boot/Makefile
$(obj)/compressed/vmlinux: $(obj)/Image FORCE
$(Q)$(MAKE) $(build)=$(obj)/compressed $@
$(obj)/zImage: $(obj)/compressed/vmlinux FORCE
$(call if_changed,objcopy)
@$(kecho) ' Kernel: $@ is ready'
$(obj)/uImage: $(obj)/zImage FORCE
@$(check_for_multiple_loadaddr)
$(call if_changed,uimage)
@$(kecho) ' Image $@ is ready'
对比一下x86架构,这个Image就等价于x86架构下的vmlinux.bin,我们可以把它们当做不同架构下是近似。接着我们再对Image追踪下去:
$(obj)/Image: vmlinux FORCE
$(call if_changed,objcopy)
@$(kecho) ' Kernel: $@ is ready'
我们可以看到,其依赖于我们前面构建的vmlinux,在第2行中,我们看到了objcopy,其在scripts/Makefile.lib中(if_changed前面分析过,不再赘述):
#scripts/Makefile.lib
cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $< $@
简言之,这个脚本片段就是将我们构建生成的ELF格式的vmlinux转为裸二进制的vmlinux,在arm架构下称为Image,在x86架构被命名为vmlinux.bin。裸二进制格式去除掉了ELF头文件、Program Header Table、符号表、重定位表等,这些对内核来说是没有意义的,Bootloader加载内核时也不需要这些ELF文件中附加的信息,不过并不会删除保存具体内容的段。
构建压缩后的vmlinux
有了Image之后,我们就可以将焦点转为arch/arm/boot/compressed目录下的vmlinux,其构建的规则如下:
#arch/arm/boot/Makefile
$(obj)/compressed/vmlinux: $(obj)/Image FORCE
$(Q)$(MAKE) $(build)=$(obj)/compressed $@
arch/arm/boot/compressed/vmlinux的构建规则在arch/arm/boot/compressed的Makefile文件中:
#arch/arm/boot/compressed/Makefile
$(obj)/piggy.$(suffix_y): $(obj)/../Image FORCE
$(call if_changed,$(suffix_y))
$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.$(suffix_y).o \
$(addprefix $(obj)/, $(OBJS)) $(lib1funcs) $(ashldi3) FORCE
@$(check_for_multiple_zreladdr)
$(call if_changed,ld)
@$(check_for_bad_syms)
在vmlinux的依赖中有一个piggy.$(suffix_y).o,在编译之前我们使用ls查看arch/arm/boot/compressed目录,会发现没有piggy相关命名的文件,如piggy.c或piggy.S等。
binwatson@binwatson:~/share/kernel/linux-3.7.4/arch/arm/boot/compressed$ ls
Makefile head-shark.S head.S ofw-shark.c sdhi-sh7372.c
atags_to_fdt.c head-sharpsl.S libfdt_env.h piggy.gzip.S sdhi-shmobile.c
big-endian.S head-shmobile.S ll_char_wr.S piggy.lzma.S sdhi-shmobile.h
decompress.c head-vt8500.S misc.c piggy.lzo.S string.c
head-sa1100.S head-xscale.S mmcif-sh7372.c piggy.xzkern.S vmlinux.lds.in
在Makefile中仔细查找,我们会发现一个piggy.$(suffix_y)的文件:
#arch/arm/boot/compressed/Makefile
$(obj)/piggy.$(suffix_y): $(obj)/../Image FORCE
$(call if_changed,$(suffix_y))
$(obj)/piggy.$(suffix_y).o: $(obj)/piggy.$(suffix_y) FORCE
现在我们知道了,piggy.$(suffix_y).o 是由 $(obj)/piggy.$(suffix_y) 生成的。suffix_y的类型由CONFIG_KERNEL_XXX参数来决定,如果我们设置了 CONFIG_KERNEL_GZIP 为 y,那么suffix_y = gzip,即使用gzip的压缩方式,默认就是使用gzip。
#arch/arm/boot/compressed/Makefile
suffix_$(CONFIG_KERNEL_GZIP) = gzip
suffix_$(CONFIG_KERNEL_LZO) = lzo
suffix_$(CONFIG_KERNEL_LZMA) = lzma
suffix_$(CONFIG_KERNEL_XZ) = xzkern
于是就会调用Makefile.lib中的cmd_gzip
对Image进行压缩:
#scripts/Makefile.lib
quiet_cmd_gzip = GZIP $@
cmd_gzip = (cat $(filter-out FORCE,$^) | gzip -n -f -9 > $@) || \
(rm -f $@ ; false)
最后生成piggy.gzip.o(假设我们使用了gzip压缩),再和compressed目录下的其它目标文件一起通过cmd_ld
链接生成新的vmlinux(压缩后的)。
构建zImage
接着我们回到boot目录下的Makefile,再来看看zImage是如何生成的,zImage的构建规则如下:
#arch/arm/boot/Makefile
$(obj)/zImage: $(obj)/compressed/vmlinux FORCE
$(call if_changed,objcopy)
@$(kecho) ' Kernel: $@ is ready'
也是通过调用cmd_objcopy
来生成的,前面链接时又生成了ELF格式的vmlinux,通过objcopy重新将其转为裸二进制文件。
构建uImage
最后是构建uImage,uImage的构建规则如下:
#arch/arm/boot/Makefile
$(obj)/uImage: $(obj)/zImage FORCE
@$(check_for_multiple_loadaddr)
$(call if_changed,uimage)
@$(kecho) ' Image $@ is ready'
#scripts/Makefile.lib
# U-Boot mkimage
# ---------------------------------------------------------------------------
MKIMAGE := $(srctree)/scripts/mkuboot.sh
# SRCARCH just happens to match slightly more than ARCH (on sparc), so reduces
# the number of overrides in arch makefiles
UIMAGE_ARCH ?= $(SRCARCH)
UIMAGE_COMPRESSION ?= $(if $(2),$(2),none)
UIMAGE_OPTS-y ?=
UIMAGE_TYPE ?= kernel
UIMAGE_LOADADDR ?= arch_must_set_this
UIMAGE_ENTRYADDR ?= $(UIMAGE_LOADADDR)
UIMAGE_NAME ?= 'Linux-$(KERNELRELEASE)'
UIMAGE_IN ?= $<
UIMAGE_OUT ?= $@
quiet_cmd_uimage = UIMAGE $(UIMAGE_OUT)
cmd_uimage = $(CONFIG_SHELL) $(MKIMAGE) -A $(UIMAGE_ARCH) -O linux \
-C $(UIMAGE_COMPRESSION) $(UIMAGE_OPTS-y) \
-T $(UIMAGE_TYPE) \
-a $(UIMAGE_LOADADDR) -e $(UIMAGE_ENTRYADDR) \
-n $(UIMAGE_NAME) -d $(UIMAGE_IN) $(UIMAGE_OUT)
通过$(srctree)/scripts
下的mkuboot.sh脚本来生成uImage内核映像。
构建总结
上面就是arm架构的uImage的构建过程,其中head是uImage的64字节的头部,而下图是x86的bzImage的构建过程。
从中我们可以看到,两者构成过程非常相识,对于x86的构建过程分析,可以参考王柏生的《深度探索Linux操作系统:系统构建与原理解析》一书。