当前位置:Linux教程 - Linux资讯 - 分析内核对gzip压缩文件进行解压的方法

分析内核对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.
[1] [2] 下一页 

(出处:http://www.sheup.com)


上一页 [1] [2]