Linux內核源碼分析--內核啟動之zImage自解壓過程


 

參考:

http://blog.chinaunix.net/uid-20543672-id-3018233.html

Linux內核編譯流程分析

linux2.6內核啟動分析--李枝果(不看是你的損失^_^)

文檔下載地址:

http://files.cnblogs.com/pengdonglin137/Linux%E5%86%85%E6%A0%B8%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90--%E5%86%85%E6%A0%B8%E5%90%AF%E5%8A%A8%E4%B9%8BzImage%E8%87%AA%E8%A7%A3%E5%8E%8B%E8%BF%87%E7%A8%8B.rar

關於內核自解壓完畢后,執行start_kernel的分析,參見:

http://www.cnblogs.com/pengdonglin137/p/3632698.html

 

內核版本:3.0.8

相關文件: 

arch/arm/boot/compressed/head.S

arch/arm/boot/compressed/vmlinux.lds

arch/arm/boot/compressed/piggy.gzip

 

這里僅對內核自解壓進行簡要分析,詳細的分析可以閱讀參考博客文檔。

zImage來歷

 

頂層vmlinux ---->

    arch/arm/boot/Image --->

         arch/arm/boot/compressed/piggy.gz --->

                   arch/arm/boot/compressed/vmlinux --->

                                              arch/arm/boot/zImage

如果要分析zImage的反匯編反匯編文件,可將arch/arm/boot/compressed/vmlinux進行反匯編,

arm-linux-xxx-objdump –d vmlinux > vmlinux.dis

對頂層的vmlinux反匯編得到的是未壓縮的內核的反匯編文件,這個vmlinux才是真正的Linux內核。

 

piggy.gz壓縮文件的特點

 

gzip -f -9 < Image > piggy.gz

 

在piggy.gz的結尾四個字節表示的是 Image 鏡像的大小,並且是以小端格式存放的。下面我們驗證一下:

image

可以看到,Image的大小是6806148B,十六進制值就是67DA84,接下來看看piggy.gz的結尾:

image

image

可以看到,確實是將0x67DA84以小端的格式存放在了piggy.gz的結尾四字節中了。

vmlinux.lds

   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:    . = 0;
  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:    _got_start = .;
  47:    .got            : { *(.got) }
  48:    _got_end = .;
  49:    .got.plt        : { *(.got.plt) }
  50:    _edata = .;
  51:   
  52:    . = ALIGN(8);
  53:    __bss_start = .;
  54:    .bss            : { *(.bss) }
  55:    _end = .;
  56:   
  57:    . = ALIGN(8);        /* the stack must be 64-bit aligned */
  58:    .stack        : { *(.stack) }
  59:   
  60:    .stab 0        : { *(.stab) }
  61:    .stabstr 0        : { *(.stabstr) }
  62:    .stab.excl 0        : { *(.stab.excl) }
  63:    .stab.exclstr 0    : { *(.stab.exclstr) }
  64:    .stab.index 0        : { *(.stab.index) }
  65:    .stab.indexstr 0    : { *(.stab.indexstr) }
  66:    .comment 0        : { *(.comment) }
  67:  }
  68:   

 

arch/arm/boot/compressed/head.S

   1:  .section ".start", #alloc, #execinstr
   2:  /*
   3:  * 清理不同的調用約定
   4:  */
   5:  .align
   6:  .arm @ 啟動總是進入ARM狀態
   7:  start:
   8:  .type start,#function
   9:  .rept 7
  10:  mov r0, r0
  11:  .endr
  12:  ARM( mov r0, r0 )
  13:  ARM( b 1f )
  14:  THUMB( adr r12, BSYM(1f) )
  15:  THUMB( bx r12 )
  16:  .word 0x016f2818 @ 用於boot loader的魔數
  17:  .word start @ 加載/運行zImage的絕對地址(編譯時確定), 在vmlinux.lds中可以看到,zImage的鏈接起始地址是0
  18:  .word _edata @ zImage結束地址,分析vmlinux.lds可以看到,_edata是 .got 段的結束地址,后面緊接的就是.bss段和.stack段
  19:  THUMB( .thumb )
  20:  1: mov r7, r1 @ 保存構架ID到r7(此前由bootloader放入r1)
  21:     mov r8, r2 @ 保存內核啟動參數地址到r8(此前由bootloader放入r2)
  22:  #ifndef __ARM_ARCH_2__
  23:  /*
  24:  * 通過Angel調試器啟動 - 必須進入 SVC模式且關閉FIQs/IRQs
  25:  * (numeric definitions from angel arm.h source).
  26:  * 如果進入時在user模式下,我們只需要做這些
  27:  */
  28:  mrs r2, cpsr @ 獲取當前模式
  29:  tst r2, #3 @ 判斷是否是user模式
  30:  bne not_angel
  31:  mov r0, #0x17 @ angel_SWIreason_EnterSVC
  32:  ARM( swi 0x123456 ) @ angel_SWI_ARM  swi會產生軟中斷,會跳入中斷向量表,這個向量表用的是bootloader的,因為在head.S中並沒有建立新的向量表
  33:  THUMB( svc 0xab ) @ angel_SWI_THUMB
  34:  not_angel:
  35:  mrs r2, cpsr @ 關閉中斷
  36:  orr r2, r2, #0xc0 @ 以保護調試器的運作 關閉IRQ和FIQ
  37:  msr cpsr_c, r2
  38:  #else
  39:  teqp pc, #0x0c000003@ 關閉中斷(此外bootloader已設置模式為SVC)
  40:  #endif

 

GOT表是什么?

GOT(Global Offset Table)表中每一項都是本運行模塊要引用的一個全局變量或函數的地址。可以用GOT表來間接引用全局變量、函數,也可以把GOT表的首地址作為一個基准,用相對於該基准的偏移量來引用靜態變量、靜態函數。

更多介紹請參考:

http://blog.sina.com.cn/s/blog_54f82cc201011oqy.html

http://blog.sina.com.cn/s/blog_54f82cc201011oqv.html

接着分析head.S

   1:  /*
   2:  * 注意一些緩存的刷新和其他事務可能需要在這里完成
   3:  * - is there an Angel SWI call for this?
   4:  */
   5:  /*
   6:  * 一些構架的特定代碼可以在這里被連接器插入,
   7:  * 但是不應使用 r7(保存構架ID), r8(保存內核啟動參數地址), and r9.
   8:  */
   9:  .text
  10:  /*
  11:  * 此處確定解壓后的內核映像的絕對地址(物理地址),保存於r4
  12:  * 由於配置的不同可能有的結果
  13:  * (1)定義了CONFIG_AUTO_ZRELADDR
  14:  *      ZRELADDR是已解壓內核最終存放的物理地址
  15:  *      如果AUTO_ZRELADDR被選擇了, 這個地址將會在運行是確定:
  16:  *      將當pc值和0xf8000000做與操作,
  17:  *      並加上TEXT_OFFSET(內核最終存放的物理地址與內存起始的偏移)
  18:  *      這里假定zImage被放在內存開始的128MB內
  19:  * (2)沒有定義CONFIG_AUTO_ZRELADDR
  20:  *      直接使用zreladdr(此值位於arch/arm/mach-xxx/Makefile.boot文件確定)
  21:  */
  22:  #ifdef CONFIG_AUTO_ZRELADDR
  23:  @ 確定內核映像地址
  24:  mov r4, pc
  25:  and r4, r4, #0xf8000000
  26:  add r4, r4, #TEXT_OFFSET
  27:  #else
  28:  ldr r4, =zreladdr                             以我的板子為例,它的值是0x80008000,即它的物理內存起始地址是0x80000000
  29:  #endif
  30:  bl cache_on /* 開啟緩存(以及MMU) */         關於cache_on這部分,我在這里就不分析了
  31:  restart: adr r0, LC0   獲取LC0的運行地址,而不是鏈接地址
  32:  ldmia r0, {r1, r2, r3, r6, r10, r11, r12}
       /*
            依次將LC0的鏈接地址放入r1中,bss段的鏈接起始和結束地址__bss_start和_end分別放入r2和r3中
            _edata的鏈接地址放入r6中,piggy.gz的倒數第四個字節的地址放入r10中, got表的起始和結束鏈接地址分別放入r11和r12中
      */

33: ldr sp, [r0, #28]   此時r0中存放的還是LC0的運行地址,所以加28后正好是LC0數組中的.L_user_stack_end的值(棧的結束地址),他在head.S的結尾定義

reloc_code_end:

        .align
        .section ".stack", "aw", %nobits
.L_user_stack:    .space    4096
.L_user_stack_end:

  34:  /*
  35:  * 我們可能運行在一個與編譯時定義的不同地址上,
  36:  * 所以我們必須修正變量指針
  37:  */
  38:  sub r0, r0, r1 @ 計算偏移量                              編譯地址與運行地址的差值,以此來修正其他符號的地址
  39:  add r6, r6, r0 @ 重新計算_edata                          獲得_edata的實際運行地址
  40:  add r10, r10, r0 @ 重新獲得壓縮后的內核大小數據位置      獲取Image大小存放的實際物理地址    
  41:  /*
  42:  * 內核編譯系統將解壓后的內核大小數據
  43:  * 以小端格式
  44:  * 附加在壓縮數據的后面(其實是“gzip -f -9”命令的結果)
  45:  * 下面代碼的作用是將解壓后的內核大小數據正確地放入r9中(避免了大小端問題)
  46:  */
  47:  ldrb r9, [r10, #0]      以我們的例子,此時r9為0x84
  48:  ldrb lr, [r10, #1]                    lr為 0xDA
  49:  orr r9, r9, lr, lsl #8                r9為0xDA84
  50:  ldrb lr, [r10, #2]                    lr為0x67
  51:  ldrb r10, [r10, #3]                   r10是0x00
  52:  orr r9, r9, lr, lsl #16               r9為0x67DA84
  53:  orr r9, r9, r10, lsl #24              r9是0x0067DA84
  54:  /*
  55:  * 下面代碼的作用是將正確的當前執行映像的結束地址放入r10
  56:  */
  57:  #ifndef CONFIG_ZBOOT_ROM
  58:  /* malloc 獲取的內存空間位於重定向的棧指針之上 (64k max) */
  59:  add sp, sp, r0           使用r0修正sp,得到堆棧的實際結束物理地址,為什么是結束地址?因為棧是向下生長的
  60:  add r10, sp, #0x10000    執行這句之前sp中存放的是棧的結束地址,執行后,r10中存放的是堆空間的結束物理地址
  61:  #else
  62:  /*
  63:  * 如果定義了 ZBOOT_ROM, bss/stack 是非可重定位的,
  64:  * 但有些人依然可以將其放在RAM中運行,
  65:  * 這時我們可以參考 _edata.
  66:  */
  67:  mov r10, r6
  68:  #endif
  69:  /*
  70:  * 檢測我們是否會發生自我覆蓋的問題
  71:  * r4 = 解壓后的內核起始地址(最終執行位置)   在我們的例子中r4就是0x80008000
  72:  * r9 = 解壓后內核的大小                       即arch/arm/boot/Image的大小,0x67DA84
  73:  * r10 = 當前執行映像的結束地址, 包含了 bss/stack/malloc 空間(假設是非XIP執行的), 上面已經分析了r10存放的是堆空間的結束地址
  74:  * 我們的基本需求是:
  75:  * (若最終執行位置r4在當前映像之后)r4 - 16k 頁目錄 >= r10 -> OK
  76:  * (若最終執行位置r4在當前映像之前)r4 + 解壓后的內核大小 <= 當前位置 (pc) -> OK
  77:  *  如果上面的條件不滿足,就會自我覆蓋,必須先搬運當前映像
  78:  */
  79:  add r10, r10, #16384
  80:  cmp r4, r10         @ 假設最終執行位置r4在當前映像之后
  81:  bhs wont_overwrite
  82:  add r10, r4, r9     @ 假設最終執行位置r4在當前映像之前
  83:  ARM( cmp r10, pc )  @ r10 = 解壓后的內核結束地址注:這里的r4+r9計算出的大小並不包含棧和堆空間
  84:  THUMB( mov lr, pc )
  85:  THUMB( cmp r10, lr )
  86:  bls wont_overwrite

 

下面是LC0區域的數據內容:

   1:          .align    2
   2:          .type    LC0, #object
   3:  LC0:        .word    LC0            @ r1
   4:          .word    __bss_start        @ r2
   5:          .word    _end     @ r3
   6:          .word    _edata             @ r6
   7:          .word    input_data_end - 4    @ r10 (inflated size location)
   8:          .word    _got_start        @ r11
   9:          .word    _got_end    @ ip
  10:          .word    .L_user_stack_end    @ sp
  11:          .size    LC0, . - LC0

還記得piggy.gz的結尾四個字節的含義嗎?他表示的是arch/arm/boot/Image的大小,在LC0區域中:

.word    input_data_end - 4    @ r10 (inflated size location)

的意思:

input_data_end 在piggy.gzip.S中定義:

   1:      .section .piggydata,#alloc
   2:      .globl    input_data
   3:  input_data:
   4:      .incbin    "arch/arm/boot/compressed/piggy.gzip"
   5:      .globl    input_data_end
   6:  input_data_end:

所以,"input_data_end – 4" 表示的是piggy.gz的倒數第四個字節的地址,也是為了便於獲得Image的大小。

接着分析head.S:

   1:  /*
   2:  * 將當前的映像重定向到解壓后的內核之后(會發生自我覆蓋時才執行,否則就被跳過)
   3:  * r6 = _edata(已校正)
   4:  * r10 = 解壓后的內核結束地址   對於我的例子: r10就是  0x80008000 + 0x67DA84(arch/arm/boot/Image的大小)
          這樣可以保證一次重定位就可以達到預期目標
   5:  * 因為我們要把當前映像向后移動, 所以我們必須由后往前復制代碼,
   6:  * 以防原數據和目標數據的重疊
   7:  */
   8:  /*
   9:  * 將解壓后的內核結束地址r10擴展(reloc_code_end - restart),
  10:  * 並對齊到下一個256B邊界。
  11:  * 這樣避免了當搬運的偏移較小時的自我覆蓋
  12:  */
  13:      add r10, r10, #((reloc_code_end - restart + 256) & ~255) 
  14:      bic r10, r10, #255
  15:  /* 獲取需要搬運的當前映像的起始位置r5,並向下做32B對齊. */
  16:      adr r5, restart
  17:      bic r5, r5, #31
  18:      sub r9, r6, r5  @ _edata - restart(已向下對齊)= 需要搬運的大小
  19:      add r9, r9, #31
  20:      bic r9, r9, #31 @ 做32B對齊 ,r9 = 需要搬運的大小
  21:      add r6, r9, r5  @ r6 = 當前映像需要搬運的結束地址,由於r5和r9都已經對齊,所以r6也對齊了。
          /* 上面對齊以后,有利於后面的代碼拷貝,而且對齊的同時又保證了拷貝代碼的完整性,至少沒有少拷貝代碼*/
  22:      add r9, r9, r10 @ r9 = 當前映像搬運的目的地的結束地址
  23:  /* 搬運當前執行映像,不包含 bss/stack/malloc 空間*/
  24:      1: ldmdb r6!, {r0 - r3, r10 - r12, lr}
  25:      cmp r6, r5
  26:      stmdb r9!, {r0 - r3, r10 - r12, lr}
  27:      bhi 1b
  28:  /* 保存偏移量,用來修改sp和實現代碼跳轉 */
  29:      sub r6, r9, r6            求要搬移的代碼的原始地址與目的地址的偏移量
  30:      #ifndef CONFIG_ZBOOT_ROM
  31:  /* cache_clean_flush 可能會使用棧,所以重定向sp指針 */
  32:      add sp, sp, r6            修正sp地址,修正后sp指向重定向后的代碼的棧區的結束地址(棧向下生長),棧區后面緊跟的就是堆空間
  33:      #endif
  34:      bl cache_clean_flush @ 刷新緩存
  35:  /* 通過搬運的偏移和當前的實際 restart 地址來實現代碼跳轉*/
  36:      adr r0, BSYM(restart)    獲得restart的運行地址
  37:      add r0, r0, r6           獲得重定向后的代碼的restart的物理地址
  38:      mov pc, r0               跳到重定向后的代碼的restart處開始執行
  39:  /* 在上面的跳轉之后,程序又從restart開始。
  40:  * 但這次在檢查自我覆蓋的時候,新的執行位置必然滿足
  41:  * 最終執行位置r4在當前映像之前,r4 + 壓縮后的內核大小 <= 當前位置 (pc)
  42:  * 所以必然直接跳到了下面的wont_overwrite執行
  43:  */
  44:  wont_overwrite:
  45:  /*
  46:  * 如果delta(當前映像地址與編譯時的地址偏移)為0, 我們運行的地址就是編譯時確定的地址.
  47:  * r0 = delta
  48:  * r2 = BSS start(編譯值)
  49:  * r3 = BSS end(編譯值)
  50:  * r4 = 內核最終運行的物理地址
  51:  * r7 = 構架ID(bootlodaer傳遞值)
  52:  * r8 = 內核啟動參數指針(bootlodaer傳遞值)
  53:  * r11 = GOT start(編譯值)
  54:  * r12 = GOT end(編譯值)
  55:  * sp = stack pointer(修正值)
  56:  */
  57:      teq r0, #0 @測試delta值
  58:      beq not_relocated @如果delta為0,無須對GOT表項和BSS進行重定位
  59:      add r11, r11, r0 @重定位GOT start
  60:      add r12, r12, r0 @重定位GOT end
  61:      #ifndef CONFIG_ZBOOT_ROM
  62:  /*
  63:  * 如果內核配置 CONFIG_ZBOOT_ROM = n,
  64:  * 我們必須修正BSS段的指針
  65:  * 注意:sp已經被修正
  66:  */
  67:      add r2, r2, r0 @重定位BSS start
  68:      add r3, r3, r0 @重定位BSS end
  69:  /*    
  70:  * 重定位所有GOT表的入口項
  71:  */
  72:      1: ldr r1, [r11, #0] @ 重定位GOT表的入口項
  73:      add r1, r1, r0 @ 這個修正了 C 引用
  74:      str r1, [r11], #4
  75:      cmp r11, r12
  76:      blo 1b
  77:      #else
  78:  /*
  79:  * 重定位所有GOT表的入口項.
  80:  * 我們只重定向在(已重定向后)BSS段外的入口
  81:  */
  82:      1: ldr r1, [r11, #0] @ 重定位GOT表的入口項
  83:      cmp r1, r2 @ entry < bss_start ||
  84:      cmphs r3, r1 @ _end < entry table
  85:      addlo r1, r1, r0 @ 這個修正了 C 引用
  86:      str r1, [r11], #4
  87:      cmp r11, r12
  88:      blo 1b
  89:      #endif
  90:  /*
  91:  * 至此當前映像的搬運和調整已經完成
  92:  * 可以開始真正的工作的
  93:  */
  94:      not_relocated: mov r0, #0
  95:      1: str r0, [r2], #4 @ 清零 bss(初始化BSS段)
  96:      str r0, [r2], #4
  97:      str r0, [r2], #4
  98:      str r0, [r2], #4
  99:      cmp r2, r3
 100:      blo 1b
 101:  /*
 102:  * C運行時環境已經充分建立.
 103:  * 設置一些指針就可以解壓內核了.
 104:  * r4 = 內核最終運行的物理地址
 105:  * r7 = 構架ID
 106:  * r8 = 內核啟動參數指針
 107:  *
 108:  * 下面對r0~r3的配置是decompress_kernel函數對應參數
 109:  * r0 = 解壓后的輸出位置首地址
 110:  * r1 = 可用RAM空間首地址  即堆空間的起始地址
 111:  * r2 = 可用RAM空間結束地址  即堆空間的結束地址
 112:  * r3 = 構架ID
 113:  * 就是這個decompress_kernel(C函數)輸出了"Uncompressing Linux..."
 114:  * 以及" done, booting the kernel.\n"
 115:  */
 116:      mov r0, r4
 117:      mov r1, sp @ malloc 獲取的內存空間位於棧指針之上
 118:      add r2, sp, #0x10000 @ 64k max
 119:      mov r3, r7
 120:      bl decompress_kernel
 121:  /*
 122:  * decompress_kernel(misc.c)--調用-->
 123:  * do_decompress(decompress.c)--調用-->
 124:  * decompress(../../../../lib/decompress_xxxx.c根據壓縮方式的配置而不同)
 125:  */
 126:  /*
 127:  * 以下是為跳入解壓后的內核,再次做准備(恢復解壓前的狀態)
 128:  */
 129:      bl cache_clean_flush
 130:      bl cache_off@ 數據緩存必須關閉(內核的要求)
 131:      mov r0, #0 @ r0必須為0
 132:      mov r1, r7@ 恢復構架ID到r1
 133:      mov r2, r8 @ 恢復內核啟動參數指針到r2
 134:      mov pc, r4 @ 跳入解壓后的內核映像(Image)入口(arch/arm/kernel/head.S)

 

下面簡要看一下解壓縮程序的調用過程:

arch/arm/boot/compressed/misc.c

   1:  void
   2:  decompress_kernel(unsigned long output_start, unsigned long free_mem_ptr_p,
   3:          unsigned long free_mem_ptr_end_p,
   4:          int arch_id)
   5:  {
   6:      int ret;
   7:   
   8:      output_data        = (unsigned char *)output_start;    /*解壓地址*/
   9:      free_mem_ptr        = free_mem_ptr_p;               /*堆棧起始地址*/
  10:      free_mem_end_ptr    = free_mem_ptr_end_p;           /*堆棧結束地址*/
  11:      __machine_arch_type    = arch_id;                      /*uboot傳入的machid*/
  12:   
  13:      arch_decomp_setup();
  14:   
  15:      putstr("Uncompressing Linux...");
  16:      ret = do_decompress(input_data, input_data_end - input_data,
  17:                  output_data, error);
  18:      if (ret)
  19:          error("decompressor returned an error");
  20:      else
  21:          putstr(" done, booting the kernel.\n");
  22:  }

 

arch/arm/boot/compressed/decompressed.c

   1:  #  define Tracec(c,x)
   2:  #  define Tracecv(c,x)
   3:  #endif
   4:   
   5:  #ifdef CONFIG_KERNEL_GZIP
   6:  #include "../../../../lib/decompress_inflate.c"
   7:  #endif
   8:   
   9:  #ifdef CONFIG_KERNEL_LZO
  10:  #include "../../../../lib/decompress_unlzo.c"
  11:  #endif
  12:   
  13:  #ifdef CONFIG_KERNEL_LZMA
  14:  #include "../../../../lib/decompress_unlzma.c"
  15:  #endif
  16:   
  17:  int 
do_decompress
(u8 *input, int len, u8 *output, void (*error)(char *x))
  18:  {
  19:      return 
decompress
(input, len, NULL, NULL, output, NULL, error);
  20:  }

 

lib/decompress_inflate.c

   1:  #ifdef STATIC
   2:  /* Pre-boot environment: included */
   3:   
   4:  /* prevent inclusion of _LINUX_KERNEL_H in pre-boot environment: lots
   5:   * errors about console_printk etc... on ARM */
   6:  #define _LINUX_KERNEL_H
   7:   
   8:  #include "zlib_inflate/inftrees.c"
   9:  #include "zlib_inflate/inffast.c"
  10:  #include "zlib_inflate/inflate.c"
  11:   
  12:  #else /* STATIC */
  13:  /* initramfs et al: linked */
  14:   
  15:  #include <linux/zutil.h>
  16:   
  17:  #include "zlib_inflate/inftrees.h"
  18:  #include "zlib_inflate/inffast.h"
  19:  #include "zlib_inflate/inflate.h"
  20:   
  21:  #include "zlib_inflate/infutil.h"
  22:   
  23:  #endif /* STATIC */
  24:   
  25:  #include <linux/decompress/mm.h>
  26:   
  27:  #define GZIP_IOBUF_SIZE (16*1024)
  28:   
  29:  static int INIT nofill(void *buffer, unsigned int len)
  30:  {
  31:      return -1;
  32:  }
  33:   
  34:  /* Included from initramfs et al code */
  35:  STATIC int INIT gunzip(unsigned char *buf, int len,
  36:                 int(*fill)(void*, unsigned int),
  37:                 int(*flush)(void*, unsigned int),
  38:                 unsigned char *out_buf,
  39:                 int *pos,
  40:                 void(*error)(char *x)) {
  41:      u8 *zbuf;
  42:      struct z_stream_s *strm;
  43:      int rc;
  44:      size_t out_len;
  45:   
  46:      rc = -1;
  47:      if (flush) {
  48:          out_len = 0x8000; /* 32 K */
  49:          out_buf = malloc(out_len);
  50:      } else {
  51:          out_len = 0x7fffffff; /* no limit */
  52:      }
  53:      if (!out_buf) {
  54:          error("Out of memory while allocating output buffer");
  55:          goto gunzip_nomem1;
  56:      }
  57:   
  58:      if (buf)
  59:          zbuf = buf;
  60:      else {
  61:          zbuf = malloc(GZIP_IOBUF_SIZE);
  62:          len = 0;
  63:      }
  64:      if (!zbuf) {
  65:          error("Out of memory while allocating input buffer");
  66:          goto gunzip_nomem2;
  67:      }
  68:   
  69:      strm = malloc(sizeof(*strm));
  70:      if (strm == NULL) {
  71:          error("Out of memory while allocating z_stream");
  72:          goto gunzip_nomem3;
  73:      }
  74:   
  75:      strm->workspace = malloc(flush ? zlib_inflate_workspacesize() :
  76:                   sizeof(struct inflate_state));
  77:      if (strm->workspace == NULL) {
  78:          error("Out of memory while allocating workspace");
  79:          goto gunzip_nomem4;
  80:      }
  81:   
  82:      if (!fill)
  83:          fill = nofill;
  84:   
  85:      if (len == 0)
  86:          len = fill(zbuf, GZIP_IOBUF_SIZE);
  87:   
  88:      /* verify the gzip header */
  89:      if (len < 10 ||
  90:         zbuf[0] != 0x1f || zbuf[1] != 0x8b || zbuf[2] != 0x08) {
  91:          if (pos)
  92:              *pos = 0;
  93:          error("Not a gzip file");
  94:          goto gunzip_5;
  95:      }
  96:   
  97:      /* skip over gzip header (1f,8b,08... 10 bytes total +
  98:       * possible asciz filename)
  99:       */
 100:      strm->next_in = zbuf + 10;
 101:      strm->avail_in = len - 10;
 102:      /* skip over asciz filename */
 103:      if (zbuf[3] & 0x8) {
 104:          do {
 105:              /*
 106:               * If the filename doesn't fit into the buffer,
 107:               * the file is very probably corrupt. Don't try
 108:               * to read more data.
 109:               */
 110:              if (strm->avail_in == 0) {
 111:                  error("header error");
 112:                  goto gunzip_5;
 113:              }
 114:              --strm->avail_in;
 115:          } while (*strm->next_in++);
 116:      }
 117:   
 118:      strm->next_out = out_buf;
 119:      strm->avail_out = out_len;
 120:   
 121:      rc = zlib_inflateInit2(strm, -MAX_WBITS);
 122:   
 123:      if (!flush) {
 124:          WS(strm)->inflate_state.wsize = 0;
 125:          WS(strm)->inflate_state.window = NULL;
 126:      }
 127:   
 128:      while (rc == Z_OK) {
 129:          if (strm->avail_in == 0) {
 130:              /* TODO: handle case where both pos and fill are set */
 131:              len = fill(zbuf, GZIP_IOBUF_SIZE);
 132:              if (len < 0) {
 133:                  rc = -1;
 134:                  error("read error");
 135:                  break;
 136:              }
 137:              strm->next_in = zbuf;
 138:              strm->avail_in = len;
 139:          }
 140:          rc = zlib_inflate(strm, 0);
 141:   
 142:          /* Write any data generated */
 143:          if (flush && strm->next_out > out_buf) {
 144:              int l = strm->next_out - out_buf;
 145:              if (l != flush(out_buf, l)) {
 146:                  rc = -1;
 147:                  error("write error");
 148:                  break;
 149:              }
 150:              strm->next_out = out_buf;
 151:              strm->avail_out = out_len;
 152:          }
 153:   
 154:          /* after Z_FINISH, only Z_STREAM_END is "we unpacked it all" */
 155:          if (rc == Z_STREAM_END) {
 156:              rc = 0;
 157:              break;
 158:          } else if (rc != Z_OK) {
 159:              error("uncompression error");
 160:              rc = -1;
 161:          }
 162:      }
 163:   
 164:      zlib_inflateEnd(strm);
 165:      if (pos)
 166:          /* add + 8 to skip over trailer */
 167:          *pos = strm->next_in - zbuf+8;
 168:   
 169:  gunzip_5:
 170:      free(strm->workspace);
 171:  gunzip_nomem4:
 172:      free(strm);
 173:  gunzip_nomem3:
 174:      if (!buf)
 175:          free(zbuf);
 176:  gunzip_nomem2:
 177:      if (flush)
 178:          free(out_buf);
 179:  gunzip_nomem1:
 180:      return rc; /* returns Z_OK (0) if successful */
 181:  }
 182:   
 183:  #define decompress gunzip

 

為了便於理解,可以參考下面一張圖:

內核自解壓的全過程

 

完!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM