ARM架构生成uImage过程
内核编译完成之后首先在顶层目录中生成vmlinux名称的原始ELF文件,接着在arch/arm/boot/Makefile文件中,使用objcopy工具,将vmlinux转化为Image镜像,用到的参数由变量OBJCOPYFLAGS和$(OBJCOPYFLAGS_$(@F)指定,makefile编译目标位于scripts/Makefile.lib文件中。
arch/arm/boot/Makefile
OBJCOPYFLAGS :=-O binary -R .comment -S
$(obj)/Image: vmlinux FORCE
$(call if_changed,objcopy)
arm64架构与arm架构不同,其指定了OBJCOPYFLAGS_Image变量,来规定objcopy使用的参数。
arch/arm64/boot/Makefile
OBJCOPYFLAGS_Image :=-O binary -R .note -R .note.gnu.build-id -R .comment -S
$(obj)/Image: vmlinux FORCE
$(call if_changed,objcopy)
文件scripts/Makefile.lib中定义的objcopy命令。
# Objcopy
# ---------------------------------------------------------------------------
quiet_cmd_objcopy = OBJCOPY [email protected]
cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $< [email protected]
压缩版vmlinux
其次,在得到Image文件后,内核对其进行压缩处理生成名称为vmlinux的文件,与最初的ELF格式的vmlinux不同,并且结果保存在arch/arm/boot/compressed目录下。生成过程由compressed下的代码完成。
arch/arm/boot/Makefile
$(obj)/compressed/vmlinux: $(obj)/Image FORCE
$(Q)$(MAKE) $(build)=$(obj)/compressed [email protected]
参见文件arch/arm/boot/compressed/Makefile,此目录下的编译文件除了本目录下的head.S、misc.c和decompress.c等文件,还需要用到arch/arm/boot/lib目录下的以下基础库函数文件:bswapsdi2.S、ashldi3.S和lib1funcs.S等。
HEAD = head.o
OBJS += misc.o decompress.o
targets := vmlinux vmlinux.lds piggy_data piggy.o lib1funcs.o ashldi3.o bswapsdi2.o head.o $(OBJS)
$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.o \
$(addprefix $(obj)/, $(OBJS)) $(lib1funcs) $(ashldi3) $(bswapsdi2) $(efi-obj-y) FORCE
@$(check_for_multiple_zreladdr)
$(call if_changed,ld)
@$(check_for_bad_syms)
以上所有的文件编译完成之后,由ld命令连接成压缩版的vmlinux文件。ld命令参见scripts/Makefile.lib文件中的LD目标。
scripts/Makefile.lib
# Linking
# ---------------------------------------------------------------------------
quiet_cmd_ld = LD [email protected]
cmd_ld = $(LD) $(LDFLAGS) $(ldflags-y) $(LDFLAGS_$(@F)) $(filter-out FORCE,$^) -o [email protected]
内核支持对Image目标文件进行压缩处理,支持的压缩算法有gzip、lzo、lzma、xzkern和lz4,假设选择的为gzip压缩。如下arch/arm/boot/compressed/Makefile所示,将Image应用gzip压缩之后,生成piggy_data文件,压缩命令参见文件scripts/Makefile.lib。
$(obj)/piggy_data: $(obj)/../Image FORCE
$(call if_changed,$(compress-y))
$(obj)/piggy.o: $(obj)/piggy_data
scripts/Makefile.lib:
# Gzip
# ---------------------------------------------------------------------------
quiet_cmd_gzip = GZIP [email protected]
cmd_gzip = (cat $(filter-out FORCE,$^) | gzip -n -f -9 > [email protected]) || (rm -f [email protected] ; false)
需要注意的是piggy.S文件,其全部代码如下,功能为将以上压缩的piggy_data使用incbin命令打包进piggy.o目标文件中。以便Image镜像文件与其它compressed目录下的文件最终打包到一起。Image存放在最终镜像的.piggydata段,参见连接文件vmlinux.lds.S。
.section .piggydata,#alloc
.globl input_data
input_data:
.incbin "arch/arm/boot/compressed/piggy_data"
.globl input_data_end
input_data_end:
arch/arm/boot/compressed/vmlinux.lds.S
.piggydata : {
*(.piggydata)
__piggy_size_addr = . - 4;
}
关于解压缩程序decompress.c,其中根据内核配置,包含了具体的压缩文件。比如如果配置了lzma压缩算法,此处会包含对应的解压文件lib/decompress_unlzma.c,或者如果定义了gzip压缩,此处包含的为lib/decompress_inflate.c解压算法。在uboot将启动内核之后,内核将首先解压自身的代码,如下函数decompress_kernel,其在head.S中被调用,屏幕上打印Uncompressing Linux...。
void decompress_kernel(unsigned long output_start, unsigned long free_mem_ptr_p, unsigned long free_mem_ptr_end_p, int arch_id)
{
putstr("Uncompressing Linux...");
}
zImage文件生成
参见arch/arm/boot/Makefile文件,zImage的生成比较简单,使用objcopy命令拷贝而成,命令参数参见变量OBJCOPYFLAGS。
OBJCOPYFLAGS :=-O binary -R .comment -S
$(obj)/zImage: $(obj)/compressed/vmlinux FORCE
$(call if_changed,objcopy)
uImage文件生成
参见文件arch/arm/boot/Makefile,uImage根据scripts/Makefile.lib中的uimage目标生成。
ifneq ($(LOADADDR),)
UIMAGE_LOADADDR=$(LOADADDR)
else
ifeq ($(CONFIG_ZBOOT_ROM),y)
UIMAGE_LOADADDR=$(CONFIG_ZBOOT_ROM_TEXT)
else
UIMAGE_LOADADDR=$(ZRELADDR)
endif
endif
$(obj)/uImage: $(obj)/zImage FORCE
@$(check_for_multiple_loadaddr)
$(call if_changed,uimage)
在文件scripts/Makefile.lib中,由shell脚本mkuboot.sh生成uImage,其中的参数UIMAGE_LOADADDR在以上arch/arm/boot/Makefile文件中指定,输入文件变量UIMAGE_IN为zImage,输出文件UIMAGE_OUT为最终的uImage。
# 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 ?= [email protected]
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)
文件scripts/mkuboot.sh比较简单,找到交叉编译系统中的mkimage工具,如果没有,使用自身系统中的mkimage工具,运行以生成uImage镜像。
#
# Build U-Boot image when `mkimage' tool is available.
#
MKIMAGE=$(type -path "${CROSS_COMPILE}mkimage")
if [ -z "${MKIMAGE}" ]; then
MKIMAGE=$(type -path mkimage)
if [ -z "${MKIMAGE}" ]; then
# Doesn't exist
echo '"mkimage" command not found - U-Boot images will not be built' >&2
exit 1;
fi
fi
# Call "mkimage" to create U-Boot image
${MKIMAGE} "[email protected]"
最后,当前的arm64体系架构已经不支持zImage和uImage的编译目标,可使用mkimage工具给不经压缩的Image镜像加上uboot头部信息,生成uImage启动镜像,由u-boot来启动。