|

- 帖子
- 51
- 精华
- 4
- 积分
- 233
- 阅读权限
- 30
- 在线时间
- 24 小时
|
1楼
发表于 2007-4-28 10:06
| 只看该作者
分析内核对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> |
|