基于linux2.6.38.8内核zImage文件的自解压详解

转载:http://blog.csdn.net/wavemcu/article/details/7270439

 

***************************************************************************************************************************
作者:EasyWave                                                                                 时间:2012.02.18

类别:linux驱动开发                                                                           声明:转载,请保留链接

***************************************************************************************************************************

内核编译完成后会生成zImage内核镜像文件。zImage是如何解压的呢?本文将结合关键代码,讲解zImage的解压过程。还是先来看看zImage的组成吧。在内核编译完成后会在arch/arm/boot/下生成zImage。 

在arch/arm/boot/Makefile中,如下代码:

 

基于linux2.6.38.8内核zImage文件的自解压详解
#
# arch/arm/boot/Makefile
#
# This file is included by the global makefile so that you can add your own
# architecture-specific flags and dependencies.
#
# This file is subject to the terms and conditions of the GNU General Public
# License.  See the file "COPYING" in the main directory of this archive
# for more details.
#
# Copyright (C) 1995-2002 Russell King
#

MKIMAGE         := $(srctree)/scripts/mkuboot.sh

ifneq ($(MACHINE),)
include $(srctree)/$(MACHINE)/Makefile.boot
endif

# Note: the following conditions must always be true:
#   ZRELADDR == virt_to_phys(PAGE_OFFSET + TEXT_OFFSET)
#   PARAMS_PHYS must be within 4MB of ZRELADDR
#   INITRD_PHYS must be in RAM
ZRELADDR    := $(zreladdr-y)
PARAMS_PHYS := $(params_phys-y)
INITRD_PHYS := $(initrd_phys-y)

export ZRELADDR INITRD_PHYS PARAMS_PHYS

targets := Image zImage xipImage bootpImage uImage

ifeq ($(CONFIG_XIP_KERNEL),y)

$(obj)/xipImage: vmlinux FORCE
    $(call if_changed,objcopy)
    @echo   Kernel: $@ is ready (physical address: $(CONFIG_XIP_PHYS_ADDR))

$(obj)/Image $(obj)/zImage: FORCE
    @echo Kernel configured for XIP (CONFIG_XIP_KERNEL=y)
    @echo Only the xipImage target is available in this case
    @false

else

$(obj)/xipImage: FORCE
    @echo Kernel not configured for XIP (CONFIG_XIP_KERNEL!=y)
    @false

$(obj)/Image: vmlinux FORCE
    $(call if_changed,objcopy)
    @echo   Kernel: $@ is ready

$(obj)/compressed/vmlinux: $(obj)/Image FORCE
    $(Q)$(MAKE) $(build)=$(obj)/compressed $@

$(obj)/zImage:    $(obj)/compressed/vmlinux FORCE
    $(call if_changed,objcopy)
    @echo   Kernel: $@ is ready

endif

quiet_cmd_uimage = UIMAGE  $@
      cmd_uimage = $(CONFIG_SHELL) $(MKIMAGE) -A arm -O linux -T kernel            -C none -a $(LOADADDR) -e $(STARTADDR)            -n Linux-$(KERNELRELEASE) -d {1}lt; $@

ifeq ($(CONFIG_ZBOOT_ROM),y)
$(obj)/uImage: LOADADDR=$(CONFIG_ZBOOT_ROM_TEXT)
else
$(obj)/uImage: LOADADDR=$(ZRELADDR)
endif

$(obj)/uImage: STARTADDR=$(LOADADDR)

$(obj)/uImage:    $(obj)/zImage FORCE
    $(call if_changed,uimage)
    @echo   Image $@ is ready

$(obj)/bootp/bootp: $(obj)/zImage initrd FORCE
    $(Q)$(MAKE) $(build)=$(obj)/bootp $@
    @:

$(obj)/bootpImage: $(obj)/bootp/bootp FORCE
    $(call if_changed,objcopy)
    @echo   Kernel: $@ is ready

PHONY += initrd FORCE
initrd:
    @test "$(INITRD_PHYS)" != "" ||     (echo This machine does not support INITRD; exit -1)
    @test "$(INITRD)" != "" ||     (echo You must specify INITRD; exit -1)

install: $(obj)/Image
    $(CONFIG_SHELL) $(srctree)/$(src)/install.sh $(KERNELRELEASE)     $(obj)/Image System.map "$(INSTALL_PATH)"

zinstall: $(obj)/zImage
    $(CONFIG_SHELL) $(srctree)/$(src)/install.sh $(KERNELRELEASE)     $(obj)/zImage System.map "$(INSTALL_PATH)"

zi:
    $(CONFIG_SHELL) $(srctree)/$(src)/install.sh $(KERNELRELEASE)     $(obj)/zImage System.map "$(INSTALL_PATH)"

i:
    $(CONFIG_SHELL) $(srctree)/$(src)/install.sh $(KERNELRELEASE)     $(obj)/Image System.map "$(INSTALL_PATH)"

subdir-        := bootp compressed
基于linux2.6.38.8内核zImage文件的自解压详解

我们将上面的部分内容抓取出来,独立来分析,这样就不会被其它的代码干扰到,见下面的代码:

$(obj)/Image: vmlinux FORCE
$(call if_changed,objcopy)
@echo ‘  Kernel: $@ is ready‘

$(obj)/zImage: $(obj)/compressed/vmlinux FORCE
$(call if_changed,objcopy)
@echo ‘  Kernel: $@ is ready‘

由此可见,zImage是由arch/arm/boot/compressed/vmlinux二进制化得到的,与此同时在arch/armboot/compressed/Makefile中:
$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.$(suffix_y).o \
$(addprefix $(obj)/, $(OBJS)) $(lib1funcs) FORCE
$(call if_changed,ld)
@:

$(obj)/piggy.$(suffix_y): $(obj)/../Image FORCE
$(call if_changed,$(suffix_y))

$(obj)/piggy.$(suffix_y).o:  $(obj)/piggy.$(suffix_y) FORCE

其中Image是由内核顶层目录下的vmlinux二进制化后得到的[见上面蓝色加粗部分代码],其中在arch/armboot/compressed/Makefile链接选项中有个 –fpic参数:
EXTRA_CFLAGS  := -fpic -fno-builtin
EXTRA_AFLAGS  := -Wa,-march=all

总结一下zImage的组成,它是由一个压缩后的内核piggy.o,连接上一段初始化及解压功能的代码(head.o misc.o),组成的。见下面的代码:

 

基于linux2.6.38.8内核zImage文件的自解压详解
#
# linux/arch/arm/boot/compressed/Makefile
#
# create a compressed vmlinuz image from the original vmlinux
#
......................
......................
#
# We now have a PIC decompressor implementation.  Decompressors running
# from RAM should not define ZTEXTADDR.  Decompressors running directly
# from ROM or Flash must define ZTEXTADDR (preferably via the config)
# FIXME: Previous assignment to ztextaddr-y is lost here. See SHARK
ifeq ($(CONFIG_ZBOOT_ROM),y)
ZTEXTADDR    := $(CONFIG_ZBOOT_ROM_TEXT)
ZBSSADDR    := $(CONFIG_ZBOOT_ROM_BSS)
else
ZTEXTADDR    := 0
ZBSSADDR    := ALIGN(8)
endif

SEDFLAGS    = s/TEXT_START/$(ZTEXTADDR)/;s/BSS_START/$(ZBSSADDR)/

suffix_$(CONFIG_KERNEL_GZIP) = gzip
suffix_$(CONFIG_KERNEL_LZO)  = lzo
suffix_$(CONFIG_KERNEL_LZMA) = lzma

targets       := vmlinux vmlinux.lds          piggy.$(suffix_y) piggy.$(suffix_y).o          font.o font.c head.o misc.o $(OBJS)

# Make sure files are removed during clean
extra-y       += piggy.gzip piggy.lzo piggy.lzma lib1funcs.S
基于linux2.6.38.8内核zImage文件的自解压详解

targets       := vmlinux vmlinux.lds \
piggy.$(suffix_y) piggy.$(suffix_y).o \
font.o font.c head.o misc.o $(OBJS)

那么内核是从什么地方开始运行的呢?这个要看lds文件。其实zImage的生成经历了两次链接过程:一是顶层vmlinux的生成,在arch/arm /boot/vmlinux.lds(这个lds文件是由arch/arm/kernel/vmlinux.lds.S生成的)中;另一次是arch /arm/boot/compressed/vmlinux的生成,在arch/arm/boot/compressed/vmlinux.lds.in 中。zImage的入口点应该由arch/arm/boot/compressed/vmlinux.lds.in决定。从中可以看出入口点为‘_start’

基于linux2.6.38.8内核zImage文件的自解压详解
/*
 *  linux/arch/arm/boot/compressed/vmlinux.lds.in
 *
 *  Copyright (C) 2000 Russell King
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
  /DISCARD/ : {
    *(.ARM.exidx*)
    *(.ARM.extab*)
    /*
     * Discard any r/w data - this produces a link error if we have any,
     * which is required for PIC decompression.  Local data generates
     * GOTOFF relocations, which prevents it being relocated independently
     * of the text/got segments.
     */
    *(.data)
  }

  . = TEXT_START;
  _text = .;

  .text : {
    _start = .;
    *(.start)
    *(.text)
    *(.text.*)
    *(.fixup)
    *(.gnu.warning)
    *(.rodata)
    *(.rodata.*)
    *(.glue_7)
    *(.glue_7t)
    *(.piggydata)
    . = ALIGN(4);
  }

  _etext = .;

  /* Assume size of decompressed image is 4x the compressed image */
  _image_size = (_etext - _text) * 4;

  _got_start = .;
  .got            : { *(.got) }
  _got_end = .;
  .got.plt        : { *(.got.plt) }
  _edata = .;

  . = BSS_START;
  __bss_start = .;
  .bss            : { *(.bss) }
  _end = .;

  . = ALIGN(8);        /* the stack must be 64-bit aligned */
  .stack        : { *(.stack) }

  .stab 0        : { *(.stab) }
  .stabstr 0        : { *(.stabstr) }
  .stab.excl 0        : { *(.stab.excl) }
  .stab.exclstr 0    : { *(.stab.exclstr) }
  .stab.index 0        : { *(.stab.index) }
  .stab.indexstr 0    : { *(.stab.indexstr) }
  .comment 0        : { *(.comment) }
}
基于linux2.6.38.8内核zImage文件的自解压详解

在arch/arm/boot/compressed/head.S中找到入口点。
看看head.S会做些什么样的工作:[下面是从网络上摘录]
1: 对于各种Arm CPU的DEBUG输出设定,通过定义宏来统一操作;
2: 设置kernel开始和结束地址,保存architecture ID;
3: 如果在ARM2以上的CPU中,用的是普通用户模式,则升到超级用户模式,然后关中断
4: 分析LC0结构delta offset,判断是否需要重载内核地址(r0存入偏移量,判断r0是否为零)。
5: 需要重载内核地址,将r0的偏移量加到BSS region和GOT table中的每一项。
   对于位置无关的代码,程序是通过GOT表访问全局数据目标的,也就是说GOT表中中记录的是全局数据目标的绝对地址,所以其中的每一项也需要重载。
6: 清空bss堆栈空间r2-r3
7: 建立C程序运行需要的缓存
8: 这时r2是缓存的结束地址,r4是kernel的最后执行地址,r5是kernel境象文件的开始地址
9: 用文件misc.c的函数decompress_kernel(),解压内核于缓存结束的地方(r2地址之后)。

基于linux2.6.38.8内核zImage文件的自解压详解,布布扣,bubuko.com

基于linux2.6.38.8内核zImage文件的自解压详解

上一篇:修复/lib/ld-linux.so.2: bad ELF interpreter: No such file or directory问题


下一篇:linux操作系统简介