zoukankan      html  css  js  c++  java
  • 基于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中,如下代码:

    #
    # 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

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

    $(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),组成的。见下面的代码:

    #
    # 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

    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’

    /*
     *  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) }
    }

    在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地址之后)。

  • 相关阅读:
    leetcode Convert Sorted List to Binary Search Tree
    leetcode Convert Sorted Array to Binary Search Tree
    leetcode Binary Tree Level Order Traversal II
    leetcode Construct Binary Tree from Preorder and Inorder Traversal
    leetcode[105] Construct Binary Tree from Inorder and Postorder Traversal
    证明中序遍历O(n)
    leetcode Maximum Depth of Binary Tree
    限制 button 在 3 秒内不可重复点击
    HTML 和 CSS 画三角形和画多边行基本原理及实践
    在线前端 JS 或 HTML 或 CSS 编写 Demo 处 JSbin 与 jsFiddle 比较
  • 原文地址:https://www.cnblogs.com/pengdonglin137/p/3631348.html
Copyright © 2011-2022 走看看