歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> Linux資訊 >> 更多Linux

分析內核對gzip壓縮文件進行解壓的方法

  作者:opera   概述   ----   1) Linux的初始內核映象以gzip壓縮文件的格式存放在zImage或bzImage之中, 內核的自舉   代碼將它解壓到1M內存開始處. 在內核初始化時, 如果加載了壓縮的initrd映象, 內核會將解壓到內存盤中, 這兩處解壓過程都使用了lib/inflate.c文件.   2) inflate.c是從gzip源程序中分離出來的, 包含了一些對全局數據的直接引用, 在使用時   需要直接嵌入到代碼中. gzip壓縮文件時總是在前32K字節的范圍內尋找重復的字符串進行   編碼, 在解壓時需要一個至少為32K字節的解壓緩沖區, 它定義為window[WSIZE].   inflate.c使用get_byte()讀取輸入文件, 它被定義成宏來提高效率. 輸入緩沖區指針必須   定義為inptr, inflate.c中對之有減量操作. inflate.c調用flush_window()來輸出window   緩沖區中的解壓出的字節串, 每次輸出長度用outcnt變量表示. 在flush_window()中, 還必   須對輸出字節串計算CRC並且刷新crc變量. 在調用gunzip()開始解壓之前, 調用makecrc()   初始化CRC計算表. 最後gunzip()返回0表示解壓成功.   3) zImage或bzImage由16位引導代碼和32位內核自解壓映象兩個部分組成. 對於zImage, 內   核自解壓映象被加載到物理地址0x1000, 內核被解壓到1M的部位. 對於bzImage, 內核自解   壓映象被加載到1M開始的地方, 內核被解壓為兩個片段, 一個起始於物理地址0x2000-0x90000,   另一個起始於高端解壓映象之後, 離1M開始處不小於低端片段最大長度的區域. 解壓完成後,   這兩個片段被合並到1M的起始位置.   解壓根內存盤映象文件的代碼   --------------------------   代碼:   ; drivers/block/rd.c   #ifdef BUILD_CRAMDISK   /*   * gzip declarations   */   #define OF(args) args ; 用於函數原型聲明的宏   #ifndef memzero   #define memzero(s, n) memset ((s), 0, (n))   #endif   typedef unsigned char UCh; 定義inflate.c所使用的3種數據類型   typedef unsigned short ush;   typedef unsigned long ulg;   #define INBUFSIZ 4096 用戶輸入緩沖區尺寸   #define WSIZE 0x8000 /* window size--must be a power of two, and */    /* at least 32K for zip's deflate method */     static uch *inbuf; 用戶輸入緩沖區,與inflate.c無關   static uch *window; 解壓窗口   static unsigned insize; /* valid bytes in inbuf */   static unsigned inptr; /* index of next byte to be processed in inbuf */   static unsigned outcnt; /* bytes in output buffer */   static int exit_code;   static long bytes_out; 總解壓輸出長度,與inflate.c無關   static struct file *crd_infp, *crd_outfp;   #define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf()) 讀取輸入緩沖區中一個字節  /* Diagnostic functions (stubbed out) */ 一些調試宏   #define Assert(cond,msg)   #define Trace(x)   #define Tracev(x)   #define Tracevv(x)   #define Tracec(c,x)   #define Tracecv(c,x)   #define STATIC static   static int fill_inbuf(void);   static void flush_window(void);   static void *malloc(int size);   static void free(void *where);   static void error(char *m);   static void gzip_mark(void **);   static void gzip_release(void **);   #include "../../lib/inflate.c"  static void __init *malloc(int size)   {    return kmalloc(size, GFP_KERNEL);   }   static void __init free(void *where)   {    kfree(where);   }   static void __init gzip_mark(void **ptr)   {    ; 讀取用戶一個標記   }   static void __init gzip_release(void **ptr)   {    ; 歸還用戶標記   }   /* ===========================================================================   * Fill the input buffer. This is called only when the buffer is empty   * and at least one byte is really needed.   */   static int __init fill_inbuf(void) 填充輸入緩沖區   {    if (exit_code) return -1;    insize = crd_infp->f_op->read(crd_infp, inbuf, INBUFSIZ,    &crd_infp->f_pos);    if (insize == 0) return -1;    inptr = 1;    return inbuf[0];   }     /* ===========================================================================   * Write the output window window[0..outcnt-1] and update crc and bytes_out.   * (Used for the decompressed data only.)   */   static void __init flush_window(void) 輸出window緩沖區中outcnt個字節串   {    ulg c = crc; /* temporary variable */    unsigned n;    uch *in, ch;    crd_outfp->f_op->write(crd_outfp, window, outcnt, &crd_outfp->f_pos);    in = window;    for (n = 0; n < outcnt; n++) {    ch = *in++;    c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); 計算輸出串的CRC    }    crc = c;    bytes_out += (ulg)outcnt; 刷新總字節數    outcnt = 0;   }   static void __init error(char *x) 解壓出錯調用的函數   {    printk(KERN_ERR "%s", x);    exit_code = 1;   }  static int __init   crd_load(struct file * fp, struct file *outfp)   {    int result;    insize = 0; /* valid bytes in inbuf */    inptr = 0; /* index of next byte to be processed in inbuf */    outcnt = 0; /* bytes in output buffer */    exit_code = 0;    bytes_out = 0;    crc = (ulg)0xffffffffL; /* shift register contents */    crd_infp = fp;    crd_outfp = outfp;    inbuf = kmalloc(INBUFSIZ, GFP_KERNEL);    if (inbuf == 0) {    printk(KERN_ERR "RAMDISK: Couldn't allocate gzip buffer\n");    return -1;    }    window = kmalloc(WSIZE, GFP_KERNEL);    if (window == 0) {    printk(KERN_ERR "RAMDISK: Couldn't allocate gzip window\n");    kfree(inbuf);    return -1;    }    makecrc();    result = gunzip();    kfree(inbuf);    kfree(window);    return result;   }   #endif /* BUILD_CRAMDISK */   32位內核自解壓代碼   ------------------   ; arch/i386/boot/compressed/head.S   .text   #include   #include   .globl startup_32 對於zImage該入口地址為0x1000; 對於bzImage為0x101000   startup_32:    cld    cli    movl $(__KERNEL_DS),%eax    movl %eax,%ds    movl %eax,%es    movl %eax,%fs    movl %eax,%gs    lss SYMBOL_NAME(stack_start),%esp # 自解壓代碼的堆棧為misc.c中定義的16K字節的數組    xorl %eax,%eax   1: incl %eax # check that A20 really IS enabled    movl %eax,0x000000 # loop forever if it isn't    cmpl %eax,0x100000    je 1b   /*   * Initialize eflags. Some BIOS's leave bits like NT set. This would   * confuse the debugger if this code is traced.   * XXX - best to initialize before switching to protected mode.   */    pushl $0    popfl   /*   * Clear BSS 清除解壓程序的BSS段   */    xorl %eax,%eax    movl $ SYMBOL_NAME(_edata),%edi    movl $ SYMBOL_NAME(_end),%ecx    subl %edi,%ecx    cld    rep    stosb   /*   * Do the decompression, and jump to the new kernel..   */    subl $16,%esp # place for structure on the stack    movl %esp,%eax    pushl %esi # real mode pointer as second arg    pushl %eax # address of structure as first arg    call SYMBOL_NAME(decompress_kernel)    orl %eax,%eax # 如果返回非零,則表示為內核解壓為低端和高端的兩個片斷    jnz 3f    popl %esi # discard address    popl %esi # real mode pointer    xorl %ebx,%ebx    ljmp $(__KERNEL_CS), $0x100000 # 運行start_kernel  /*   * We come here, if we were loaded high.




Copyright © Linux教程網 All Rights Reserved