欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ARM架构生成uImage过程

程序员文章站 2022-06-20 21:02:28
...

内核编译完成之后首先在顶层目录中生成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来启动。