感谢支持
我们一直在努力

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

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

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


[plain]


  1. #  

  2. # arch/arm/boot/Makefile  

  3. #  

  4. # This file is included by the global makefile so that you can add your own  

  5. # architecture-specific flags and dependencies.  

  6. #  

  7. # This file is subject to the terms and conditions of the GNU General Public  

  8. # License.  See the file “COPYING” in the main directory of this archive  

  9. # for more details.  

  10. #  

  11. # Copyright (C) 1995-2002 Russell King  

  12. #  

  13.   

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

  15.   

  16. ifneq ($(MACHINE),)  

  17. include $(srctree)/$(MACHINE)/Makefile.boot  

  18. endif  

  19.   

  20. # Note: the following conditions must always be true:  

  21. #   ZRELADDR == virt_to_phys(PAGE_OFFSET + TEXT_OFFSET)  

  22. #   PARAMS_PHYS must be within 4MB of ZRELADDR  

  23. #   INITRD_PHYS must be in RAM  

  24. ZRELADDR    := $(zreladdr-y)  

  25. PARAMS_PHYS := $(params_phys-y)  

  26. INITRD_PHYS := $(initrd_phys-y)  

  27.   

  28. export ZRELADDR INITRD_PHYS PARAMS_PHYS  

  29.   

  30. targets := Image zImage xipImage bootpImage uImage  

  31.   

  32. ifeq ($(CONFIG_XIP_KERNEL),y)  

  33.   

  34. $(obj)/xipImage: vmlinux FORCE  

  35.     $(call if_changed,objcopy)  

  36.     @echo ‘  Kernel: $@ is ready (physical address: $(CONFIG_XIP_PHYS_ADDR))’  

  37.   

  38. $(obj)/Image $(obj)/zImage: FORCE  

  39.     @echo ‘Kernel configured for XIP (CONFIG_XIP_KERNEL=y)’  

  40.     @echo ‘Only the xipImage target is available in this case’  

  41.     @false  

  42.   

  43. else  

  44.   

  45. $(obj)/xipImage: FORCE  

  46.     @echo ‘Kernel not configured for XIP (CONFIG_XIP_KERNEL!=y)’  

  47.     @false  

  48.   

  49. $(obj)/Image: vmlinux FORCE  

  50.     $(call if_changed,objcopy)  

  51.     @echo ‘  Kernel: $@ is ready’  

  52.   

  53. $(obj)/compressed/vmlinux: $(obj)/Image FORCE  

  54.     $(Q)$(MAKE) $(build)=$(obj)/compressed $@  

  55.   

  56. $(obj)/zImage:  $(obj)/compressed/vmlinux FORCE  

  57.     $(call if_changed,objcopy)  

  58.     @echo ‘  Kernel: $@ is ready’  

  59.   

  60. endif  

  61.   

  62. quiet_cmd_uimage = UIMAGE  $@  

  63.       cmd_uimage = $(CONFIG_SHELL) $(MKIMAGE) -A arm -O linux -T kernel \  

  64.            -C none -a $(LOADADDR) -e $(STARTADDR) \  

  65.            -n ‘Linux-$(KERNELRELEASE)’ -d {1}lt; $@  

  66.   

  67. ifeq ($(CONFIG_ZBOOT_ROM),y)  

  68. $(obj)/uImage: LOADADDR=$(CONFIG_ZBOOT_ROM_TEXT)  

  69. else  

  70. $(obj)/uImage: LOADADDR=$(ZRELADDR)  

  71. endif  

  72.   

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

  74.   

  75. $(obj)/uImage:  $(obj)/zImage FORCE  

  76.     $(call if_changed,uimage)  

  77.     @echo ‘  Image $@ is ready’  

  78.   

  79. $(obj)/bootp/bootp: $(obj)/zImage initrd FORCE  

  80.     $(Q)$(MAKE) $(build)=$(obj)/bootp $@  

  81.     @:  

  82.   

  83. $(obj)/bootpImage: $(obj)/bootp/bootp FORCE  

  84.     $(call if_changed,objcopy)  

  85.     @echo ‘  Kernel: $@ is ready’  

  86.   

  87. PHONY += initrd FORCE  

  88. initrd:  

  89.     @test “$(INITRD_PHYS)” != “” || \  

  90.     (echo This machine does not support INITRD; exit -1)  

  91.     @test “$(INITRD)” != “” || \  

  92.     (echo You must specify INITRD; exit -1)  

  93.   

  94. install: $(obj)/Image  

  95.     $(CONFIG_SHELL) $(srctree)/$(src)/install.sh $(KERNELRELEASE) \  

  96.     $(obj)/Image System.map “$(INSTALL_PATH)”  

  97.   

  98. zinstall: $(obj)/zImage  

  99.     $(CONFIG_SHELL) $(srctree)/$(src)/install.sh $(KERNELRELEASE) \  

  100.     $(obj)/zImage System.map “$(INSTALL_PATH)”  

  101.   

  102. zi:  

  103.     $(CONFIG_SHELL) $(srctree)/$(src)/install.sh $(KERNELRELEASE) \  

  104.     $(obj)/zImage System.map “$(INSTALL_PATH)”  

  105.   

  106. i:  

  107.     $(CONFIG_SHELL) $(srctree)/$(src)/install.sh $(KERNELRELEASE) \  

  108.     $(obj)/Image System.map “$(INSTALL_PATH)”  

  109.   

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


[plain]


  1. #  

  2. # linux/arch/arm/boot/compressed/Makefile  

  3. #  

  4. # create a compressed vmlinuz image from the original vmlinux  

  5. #  

  6. ………………….  

  7. ………………….  

  8. #  

  9. # We now have a PIC decompressor implementation.  Decompressors running  

  10. # from RAM should not define ZTEXTADDR.  Decompressors running directly  

  11. # from ROM or Flash must define ZTEXTADDR (preferably via the config)  

  12. # FIXME: Previous assignment to ztextaddr-y is lost here. See SHARK  

  13. ifeq ($(CONFIG_ZBOOT_ROM),y)  

  14. ZTEXTADDR   := $(CONFIG_ZBOOT_ROM_TEXT)  

  15. ZBSSADDR    := $(CONFIG_ZBOOT_ROM_BSS)  

  16. else  

  17. ZTEXTADDR   := 0  

  18. ZBSSADDR    := ALIGN(8)  

  19. endif  

  20.   

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

  22.   

  23. suffix_$(CONFIG_KERNEL_GZIP) = gzip  

  24. suffix_$(CONFIG_KERNEL_LZO)  = lzo  

  25. suffix_$(CONFIG_KERNEL_LZMA) = lzma  

  26.   

  27. targets       := vmlinux vmlinux.lds \  

  28.          piggy.$(suffix_y) piggy.$(suffix_y).o \  

  29.          font.o font.c head.o misc.o $(OBJS)  

  30.   

  31. # Make sure files are removed during clean  

  32. 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’


[plain]


  1. /*  

  2.  *  linux/arch/arm/boot/compressed/vmlinux.lds.in  

  3.  *  

  4.  *  Copyright (C) 2000 Russell King  

  5.  *  

  6.  * This program is free software; you can Redistribute it and/or modify  

  7.  * it under the terms of the GNU General Public License version 2 as  

  8.  * published by the Free Software Foundation.  

  9.  */  

  10. OUTPUT_ARCH(arm)  

  11. ENTRY(_start)  

  12. SECTIONS  

  13. {  

  14.   /DISCARD/ : {  

  15.     *(.ARM.exidx*)  

  16.     *(.ARM.extab*)  

  17.     /*  

  18.      * Discard any r/w data – this produces a link error if we have any,  

  19.      * which is required for PIC decompression.  Local data generates  

  20.      * GOTOFF relocations, which prevents it being relocated independently  

  21.      * of the text/got segments.  

  22.      */  

  23.     *(.data)  

  24.   }  

  25.   

  26.   . = TEXT_START;  

  27.   _text = .;  

  28.   

  29.   .text : {  

  30.     _start = .;  

  31.     *(.start)  

  32.     *(.text)  

  33.     *(.text.*)  

  34.     *(.fixup)  

  35.     *(.gnu.warning)  

  36.     *(.rodata)  

  37.     *(.rodata.*)  

  38.     *(.glue_7)  

  39.     *(.glue_7t)  

  40.     *(.piggydata)  

  41.     . = ALIGN(4);  

  42.   }  

  43.   

  44.   _etext = .;  

  45.   

  46.   /* Assume size of decompressed image is 4x the compressed image */  

  47.   _image_size = (_etext – _text) * 4;  

  48.   

  49.   _got_start = .;  

  50.   .got          : { *(.got) }  

  51.   _got_end = .;  

  52.   .got.plt      : { *(.got.plt) }  

  53.   _edata = .;  

  54.   

  55.   . = BSS_START;  

  56.   __bss_start = .;  

  57.   .bss          : { *(.bss) }  

  58.   _end = .;  

  59.   

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

  61.   .stack        : { *(.stack) }  

  62.   

  63.   .stab 0       : { *(.stab) }  

  64.   .stabstr 0        : { *(.stabstr) }  

  65.   .stab.excl 0      : { *(.stab.excl) }  

  66.   .stab.exclstr 0   : { *(.stab.exclstr) }  

  67.   .stab.index 0     : { *(.stab.index) }  

  68.   .stab.indexstr 0  : { *(.stab.indexstr) }  

  69.   .comment 0        : { *(.comment) }  

  70. }  

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

赞(0) 打赏
转载请注明出处:服务器评测 » 基于Linux2.6.38.8内核zImage文件的自解压详解
分享到: 更多 (0)

听说打赏我的人,都进福布斯排行榜啦!

支付宝扫一扫打赏

微信扫一扫打赏