返回列表 回复 发帖

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

概述

1) Linux的初始内核映象以gzip压缩文件的格式存 放在zImage或bzImage之中, 内核的自举

代码将它解压到1M内存开始处. 在内核初始化时, 如 果加载了压缩的initrd映象, 内核会将

它解压到内存盘中, 这两处解压过程都使用了lib/infla te.c文件.



2) inf late.c是从gzip源程序中分离出来的, 包含了一些对全 局数据的直接引用, 在使用时

需要直接嵌入到 代码中. gzip压缩文件时总是在前32K字节的范围内寻找重 复的字符串进行

编码, 在解压时需要一个至少 为32K字节的解压缓冲区, 它定义为window[WSIZE ].

inflate.c使用get_byte ()读取输入文件, 它被定义成宏来提高效率. 输入缓冲区指针 必须

定义为inptr, inflate.c 中对之有减量操作. inflate.c调用flush_win dow()来输出window

缓冲区中的解压 出的字节串, 每次输出长度用outcnt变量表示. 在flu sh_window()中, 还必

须对输出字 节串计算CRC并且刷新crc变量. 在调用gunzip()开 始解压之前, 调用makecrc()

初始化 CRC计算表. 最后gunzip()返回0表示解压成功. < br>


3) zImage或bzIma ge由16位引导代码和32位内核自解压映象两个部分组成. 对 于zImage, 内

核自解压映象被加载到物 理地址0x1000, 内核被解压到1M的部位. 对于bzIm age, 内核自解

压映象被加载到1M开始的 地方, 内核被解压为两个片段, 一个起始于物理地址0x200 0-0x90000,

另一个起始于高端解压映 象之后, 离1M开始处不小于低端片段最大长度的区域. 解压完 成后,

这两个片段被合并到1M的起始位置.



解压根内存盘映象文件的代码

---------------------- ----



;
drivers /block/rd.c

#ifdef BUI LD_CRAMDISK



/*

* gzip declarations < br>
*/



#def ine OF(args) args ;
用于函数原型声明的宏



#ifndef memze ro

#define memzero(s, n) memset ((s), 0, (n))
#endif



typed ef unsigned char uch;
定义inflat e.c所使用的3种数据类型

typedef unsigned short ush;


ty pedef unsigned long ulg;

< br>

#define INBUFSIZ 40 96 用户输入缓冲区尺寸

#define W SIZE 0x8000 /* window size--mu st be a power of two, and */ < br>
/* at least 32K for zip 's deflate method */

< br>
static uch *inbuf;
用户输入 缓冲区,与inflate.c无关

stati c uch *window;
解压窗口


static unsigned insize;
/* valid bytes in inbuf */

static unsigned inptr;
/* index of next byte to be proc essed in inbuf */

stat ic unsigned outcnt;
/* bytes i n output buffer */

sta tic int exit_code;


sta tic long bytes_out;
总解压输出长度,与i nflate.c无关

static stru ct file *crd_infp, *crd_outfp;




#define get_b yte() (inptr <
insize ? inb uf[inptr++] : fill_inbuf()) 读取 输入缓冲区中一个字节



/* Diagnostic functions (stubbed out) */ 一些调试宏

#define Assert(cond,msg)

#defi ne Trace(x)

#define Tr acev(x)

#define Tracev v(x)

#define Tracec(c, x)

#define Tracecv(c,x )



#define STAT IC static



stat ic int fill_inbuf(void);

< br>static void flush_window(vo id);


static void *mall oc(int size);


static v oid free(void *where);


static void error(char *m);
< br>
static void gzip_mark(v oid **);


static void g zip_release(void **);




#include "../../lib/in flate.c"



stati c void __init *malloc(int size )

{

return kma lloc(size, GFP_KERNEL);

}



static voi d __init free(void *where)

{

kfree(where);
< br>
}



stati c void __init gzip_mark(void * *ptr)

{

;
读取用户 一个标记

}



static void __init gzip_relea se(void **ptr)

{
< br>;
归还用户标记

}





/* =========== ============================== ============================== ====

* Fill the input buffer. This is called only wh en the buffer is empty

* and at least one byte is re ally needed.

*/
static int __init fill_inbuf (void) 填充输入缓冲区

{
< br>if (exit_code) return -1;
< br>


insize = crd_in fp->
f_op->
read(crd_infp, inbuf, INBUFSIZ,

& ;
crd_infp->
f_pos);


if (insize == 0) return -1;



inptr = 1;

< br>

return inbuf[0];

}



/* === ============================== ============================== ============

* Write t he output window window[0..out cnt-1] and update crc and byte s_out.

* (Used for the decompressed data only.)

*/

static void __i nit flush_window(void) 输出windo w缓冲区中outcnt个字节串

{

ulg c = crc;
/* temporary variable */

unsigned n ;


uch *in, ch;




crd_outfp->
f_op-&g t;
write(crd_outfp, window, out cnt, &
crd_outfp->
f_pos) ;


in = window;


for (n = 0;
n <
outcnt;
n+ +) {

ch = *in++;

< br>c = crc_32_tab[((int)c ^ ch ) &
0xff] ^ (c >
>
8) ;
计算输出串的CRC

}

crc = c;


bytes_out += (ulg)outcnt;
刷新总字节数

ou tcnt = 0;


}


static void __init error (char *x) 解压出错调用的函数

{

printk(KERN_ERR "%s", x);


exit_code = 1;


}



static int __init

crd_load(st ruct file * fp, struct file *o utfp)

{

int re sult;




insize = 0;
/* valid bytes in inbuf */

inptr = 0;
/* index o f next byte to be processed in inbuf */

outcnt = 0;
/* bytes in output buffer */ < br>
exit_code = 0;


bytes_out = 0;


crc = ( ulg)0xffffffffL;
/* shift regi ster contents */


< br>crd_infp = fp;


crd_ outfp = outfp;


inbuf = kmalloc(INBUFSIZ, GFP_KERNEL) ;


if (inbuf == 0) {
printk(KERN_ERR "RAMDISK : Couldn't allocate gzip buffe r\n");


return -1;


}

window = kmalloc (WSIZE, GFP_KERNEL);


i f (window == 0) {

prin tk(KERN_ERR "RAMDISK: Couldn't allocate gzip window\n");


kfree(inbuf);


ret urn -1;


}

make crc();


result = gunzip ();


kfree(inbuf);


kfree(window);


ret urn result;


}



#endif /* BUILD_CRAMDI SK */





32位内核自解压代码

----------- -------



;
arch /i386/boot/compressed/head.S < br>
.text



# include

#include
< br>

.globl startup_32 对 于zImage该入口地址为0x1000;
对于bzImage 为0x101000



star tup_32:

cld

cl i

movl $(__KERNEL_DS), %eax

movl %eax,%ds

movl %eax,%es

mov l %eax,%fs

movl %eax,% gs



lss SYMBOL_ NAME(stack_start),%esp # 自解压代码 的堆栈为misc.c中定义的16K字节的数组

xorl %eax,%eax

1: inc l %eax # check that A20 really IS enabled

movl %eax, 0x000000 # loop forever if it isn't

cmpl %eax,0x1000 00

je 1b


< br>/*

* Initialize efl ags. Some BIOS's leave bits li ke NT set. This would

* confuse the debugger if this code is traced.

* XXX - best to initialize before s witching to protected mode.
*/

pushl $0

popfl

/*

* Clear BSS 清除解压程序的BSS段

*/

xorl %eax,%eax

movl $ SYMBOL_NAME(_edata ),%edi

movl $ SYMBOL_N AME(_end),%ecx

subl %e di,%ecx

cld

re p

stosb

/*

* Do the decompression, a nd jump to the new kernel..
*/

subl $16,%esp # place for structure on the stack

movl %esp,%eax < br>
pushl %esi # real mode pointer as second arg

pushl %eax # address of struct ure as first arg

call SYMBOL_NAME(decompress_kernel)

orl %eax,%eax # 如果返回非 零,则表示为内核解压为低端和高端的两个片断

jnz 3f

popl %esi # dis card address

popl %esi # real mode pointer

x orl %ebx,%ebx

ljmp $(_ _KERNEL_CS), $0x100000 # 运行sta rt_kernel



/* < br>
* We come here, if we w ere loaded high.

* We need to move the move-in-place routine down to 0x1000
* and then start it with the buffer addresses in registers ,

* which we got from the stack.

*/

3:

movl $move_routine_ start,%esi

movl $0x100 0,%edi

movl $move_rout ine_end,%ecx

subl %esi ,%ecx

addl $3,%ecx

shrl $2,%ecx # 按字取整
< br>cld

rep

mov sl # 将内核片断合并代码复制到0x1000区域, 内核的 片段起始为0x2000



po pl %esi # discard the address

popl %ebx # real mode pointer

popl %esi # lo w_buffer_start 内核低端片段的起始地址

popl %ecx # lcount 内核低端片段 的字节数量

popl %edx # high _buffer_start 内核高端片段的起始地址

popl %eax # hcount 内核高端片段的 字节数量

movl $0x100000,%e di 内核合并的起始地址

cli # mak e sure we don't get interrupte d

ljmp $(__KERNEL_CS), $0x1000 # and jump to the mov e routine



/* < br>
* Routine (template) fo r moving the decompressed kern el in place,

* if we w ere high loaded. This _must_ P IC-code !

*/

m ove_routine_start:

mov l %ecx,%ebp

shrl $2,%e cx

rep

movsl # 按字拷贝第1个片段

movl %ebp,% ecx

andl $3,%ecx
< br>rep

movsb # 传送不完全字

movl %edx,%esi

movl %eax,%ecx # NOTE: rep mo vsb won't move if %ecx == 0
addl $3,%ecx

shr l $2,%ecx # 按字对齐

rep < br>
movsl # 按字拷贝第2个片段
< br>movl %ebx,%esi # Restore se tup pointer

xorl %ebx, %ebx

ljmp $(__KERNEL_C S), $0x100000 # 运行start_kernel

move_routine_end:



;
arch/i386/boot/ compressed/misc.c



/*

* gzip declarat ions

*/


#define OF(args) args
#define STATIC static


#undef memset
#undef memcpy

#defin e memzero(s, n) memset ((s), 0 , (n))



typedef unsigned char uch;


ty pedef unsigned short ush;


typedef unsigned long ulg;




#define WSIZE 0x8000 /* Window size must be at least 32k, */

/* a nd a power of two */

< br>
static uch *inbuf;
/* i nput buffer */

static uch window[WSIZE];
/* Sliding window buffer */


< br>static unsigned insize = 0;
/* valid bytes in inbuf */
static unsigned inptr = 0;
/* index of next byte to be processed in inbuf */

static unsigned outcnt = 0;
/ * bytes in output buffer */


/* gzip flag byt e */

#define ASCII_FLA G 0x01 /* bit 0 set: file prob ably ASCII text */

#de fine CONTINUATION 0x02 /* bit 1 set: continuation of multi-p art gzip file */

#defi ne EXTRA_FIELD 0x04 /* bit 2 s et: extra field present */

#define ORIG_NAME 0x08 /* bit 3 set: original file name present */

#define CO MMENT 0x10 /* bit 4 set: file comment present */

#de fine ENCRYPTED 0x20 /* bit 5 s et: file is encrypted */
< br>#define RESERVED 0xC0 /* bi t 6,7: reserved */



#define get_byte() (inptr <
insize ? inbuf[inptr++] : fill_inbuf())


/* Diagnostic functions */ < br>
#ifdef DEBUG

# define Assert(cond,msg) {if(!( cond)) error(msg);
}

# define Trace(x) fprintf x

# define Tracev(x) {if (ve rbose) fprintf x ;
}

# define Tracevv(x) {if (verbose >
1) fprintf x ;
}

# define Tracec(c,x) {if (verbos e &
&
(c)) fprintf x ;
}

# define Tracecv(c,x) {if (verbose>
1 &
&
(c)) fprintf x ;
}

#els e

# define Assert(cond ,msg)

# define Trace(x )

# define Tracev(x) < br>
# define Tracevv(x)

# define Tracec(c,x)

# define Tracecv(c,x)

#endif



sta tic int fill_inbuf(void);


static void flush_window(v oid);


static void erro r(char *m);


static voi d gzip_mark(void **);


static void gzip_release(void **);




/*
* This is set up by the setu p-routine at boot-time

*/

static unsigned ch ar *real_mode;
/* Pointer to r eal-mode data */


< br>#define EXT_MEM_K (*(unsign ed short *)(real_mode + 0x2))

#ifndef STANDARD_MEMOR Y_BIOS_CALL

#define AL T_MEM_K (*(unsigned long *)(re al_mode + 0x1e0))

#end if

#define SCREEN_INFO (*(struct screen_info *)(real _mode+0))



exte rn char input_data[];


extern int input_len;




static long bytes_out = 0;


static uch *outpu t_data;


static unsigne d long output_ptr = 0;






static void * malloc(int size);


stat ic void free(void *where);


static void error(char *m );


static void gzip_ma rk(void **);


static vo id gzip_release(void **);




static void puts(c onst char *);




extern int end;


static long free_mem_ptr = (long)&am p;
end;


static long fre e_mem_end_ptr;




#define INPLACE_MOVE_ROUTINE 0x1000 内核片段合并代码的运行地址

# define LOW_BUFFER_START 0x2000 内核低端解压片段的起始地址

#define LOW_BUFFER_MAX 0x90000 内核低端解压 片段的终止地址

#define HEAP_S IZE 0x3000 为解压低码保留的堆的尺寸,堆起始于BS S的结束

static unsigned i nt low_buffer_end, low_buffer_ size;


static int high_ loaded =0;


static uch *high_buffer_start /* = (uch * )(((ulg)&
end) + HEAP_SIZE) */;




static cha r *vidmem = (char *)0xb8000;
< br>
static int vidport;


static int lines, cols;
< br>


#include "../.. /../../lib/inflate.c"



static void *malloc(in t size)

{

void *p;




if (size <
0) error("Malloc error\n") ;


if (free_mem_ptr < ;
= 0) error("Memory error\n");




free_mem_ptr = (free_mem_ptr + 3) &
~3;
/* Align */



p = (void *)free_mem_ptr;

< br>free_mem_ptr += size;

< br>

if (free_mem_ptr &g t;
= free_mem_end_ptr)

error("\nOut of memory\n");



return p;

}



static voi d free(void *where)

{ /* Don't care */

}



static void gzip_ mark(void **ptr)

{

*ptr = (void *) free_mem_ ptr;


}



static void gzip_release(void **ptr)

{

free _mem_ptr = (long) *ptr;

}



static voi d scroll(void)

{
< br>int i;




memc py ( vidmem, vidmem + cols * 2 , ( lines - 1 ) * cols * 2 );


for ( i = ( lines - 1 ) * cols * 2;
i <
lines * c ols * 2;
i += 2 )

vidm em[ i ] = ' ';


}
< br>

static void puts(co nst char *s)

{

int x,y,pos;


char c;




x = SCREEN_INF O.orig_x;


y = SCREEN_I NFO.orig_y;




wh ile ( ( c = *s++ ) != '\0' ) {

if ( c == '\n' ) {
x = 0;


if ( ++y >
= lines ) {

scroll ();


y--;


}

} else {

vidmem [ ( x + cols * y ) * 2 ] = c;
< br>
if ( ++x >
= cols ) {

x = 0;


if ( + +y >
= lines ) {

scr oll();


y--;


}

}

}

}



SCREEN_INFO.or ig_x = x;


SCREEN_INFO. orig_y = y;




po s = (x + cols * y) * 2;
/* Upd ate cursor position */

outb_p(14, vidport);


outb_p(0xff &
(pos >
> ;
9), vidport+1);


outb _p(15, vidport);


outb_ p(0xff &
(pos >
>
1), vidport+1);


}



void* memset(void* s, int c, size_t n)

{
int i;


char *ss = (char*)s;




fo r (i=0;
i return s;


} < br>


void* memcpy(vo id* __dest, __const void* __sr c,

size_t __n)

{

int i;


char *d = (char *)__dest, *s = (ch ar *)__src;




fo r (i=0;
i<
__n;
i++) d[ i ] = s[ i ];


return __dest;


}



/* ============================== ============================== ===============

* Fill the input buffer. This is cal led only when the buffer is em pty

* and at least one byte is really needed.
*/

static int fill_i nbuf(void)

{

i f (insize != 0) {

erro r("ran out of input data\n");


}



inbu f = input_data;


insize = input_len;


inptr = 1;


return inbuf[0];

}



/* === ============================== ============================== ============

* Write t he output window window[0..out cnt-1] and update crc and byte s_out.

* (Used for the decompressed data only.)

*/

static void flu sh_window_low(void)

{

ulg c = crc;
/* tempor ary variable */

unsign ed n;


uch *in, *out, c h;




in = window ;


out = &
output_da ta[output_ptr];


for (n = 0;
n <
outcnt;
n++) {
ch = *out++ = *in++;


c = crc_32_tab[((int)c ^ ch) &
0xff] ^ (c >
>
8);


}

crc = c;


bytes_out += (ulg)out cnt;


output_ptr += (ul g)outcnt;


outcnt = 0;


}



stat ic void flush_window_high(void )

{

ulg c = cr c;
/* temporary variable */
unsigned n;


uch *in, ch;


in = window;


for (n = 0;
n <
out cnt;
n++) {

ch = *outp ut_data++ = *in++;


if ((ulg)output_data == low_buffe r_end) output_data=high_buffer _start;


c = crc_32_tab [((int)c ^ ch) &
0xff] ^ ( c >
>
8);


}
< br>crc = c;


bytes_out += (ulg)outcnt;


outcnt = 0;


}


static void flush_window(voi d)

{

if (high_ loaded) flush_window_high();
< br>
else flush_window_low() ;


}



st atic void error(char *x)
< br>{

puts("\n\n");


puts(x);


puts("\n \n -- System halted");




while(1);
/* Halt */

}



#def ine STACK_SIZE (4096)



long user_stack [STACK _SIZE];




struct {

long * a;


s hort b;


} stack_start = { &
user_stack [STACK_SI ZE] , __KERNEL_DS };


< br>
void setup_normal_outpu t_buffer(void) 对于zImage, 直接解压到 1M

{

#ifdef ST ANDARD_MEMORY_BIOS_CALL
if (EXT_MEM_K <
1024) err or("Less than 2MB of memory.\n ");


#else

if ( (ALT_MEM_K >
EXT_MEM_K ? AL T_MEM_K : EXT_MEM_K) <
1024 ) error("Less than 2MB of memo ry.\n");


#endif
output_data = (char *)0x1000 00;
/* Points to 1M */

free_mem_end_ptr = (long)real _mode;


}


< br>struct moveparams {

uch *low_buffer_start;
int lc ount;


uch *high_buffer _start;
int hcount;


};




void setup_ou tput_buffer_if_we_run_high(str uct moveparams *mv)

{

high_buffer_start = (u ch *)(((ulg)&
end) + HEAP_S IZE);
内核高端片段的最小起始地址

#i fdef STANDARD_MEMORY_BIOS_CALL

if (EXT_MEM_K <
(3 *1024)) error("Less than 4MB o f memory.\n");


#else < br>
if ((ALT_MEM_K >
EXT _MEM_K ? ALT_MEM_K : EXT_MEM_K ) <
(3*1024)) error("Less t han 4MB of memory.\n");

#endif

mv->
low_bu ffer_start = output_data = (ch ar *)LOW_BUFFER_START;


low_buffer_end = ((unsigned i nt)real_mode >
LOW_BUFFER_M AX

? LOW_BUFFER_MAX : (unsigned int)real_mode) &
~0xfff;


low_buffer_si ze = low_buffer_end - LOW_BUFF ER_START;


high_loaded = 1;


free_mem_end_ptr = (long)high_buffer_start;


if ( (0x100000 + low_buff er_size) >
((ulg)high_buffe r_start)) {

;
如果高端片段的最 小起始地址小于它实际应加载的地址,则将它置为实际地址,
;
这样高端片段就无需再次移动了,否则它要向前移 动

high_buffer_start = (uch *)(0x100000 + low_buffer_ size);


mv->
hcount = 0;
/* say: we need not to mov e high_buffer */

}

else mv->
hcount = -1;
待定

mv->
high_buffer_ start = high_buffer_start;


}



void cl ose_output_buffer_if_we_run_hi gh(struct moveparams *mv)

{

if (bytes_out &g t;
low_buffer_size) {

mv->
lcount = low_buffer_siz e;


if (mv->
hcount)

mv->
hcount = bytes_ out - low_buffer_size;
求出高端片段的 字节数

} else { 如果解压后内核只有 低端的一个片段

mv->
lcount = bytes_out;


mv->
hc ount = 0;


}

}



int decompress _kernel(struct moveparams *mv, void *rmode)

{
real_mode = rmode;


< br>
if (SCREEN_INFO.orig_vi deo_mode == 7) {

vidme m = (char *) 0xb0000;


vidport = 0x3b4;


} els e {

vidmem = (char *) 0xb8000;


vidport = 0x3 d4;


}



lines = SCREEN_INFO.orig_video _lines;


cols = SCREEN_ INFO.orig_video_cols;




if (free_mem_ptr <
0x100000) setup_normal_output_ buffer();


else setup_o utput_buffer_if_we_run_high(mv );




makecrc();


puts("Uncompressing Li nux... ");


gunzip();
< br>
puts("Ok, booting the k ernel.\n");


if (high_l oaded) close_output_buffer_if_ we_run_high(mv);


retur n high_loaded;


}
< br>
返回列表