红联Linux门户
Linux帮助

分析内核对gzip压缩文件进行解压的方法

发布时间:2006-08-13 09:38:38来源:红联作者:邱建元
  概述
  ----
  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.
*/
文章评论

共有 0 条评论