免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2346 | 回复: 9
打印 上一主题 下一主题

[内存管理] 内核采用 in-place 解压缩不会覆盖解压缩部分代码吗? [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-02-24 13:54 |只看该作者 |倒序浏览
内核采用 in-place 解压缩不会覆盖解压缩部分代码吗?就是说看上去 arch/x86/boot/compressed/head_32.S 部分的代码会被解压的内核覆盖。但是似乎代码中并没有进行特殊处理...

论坛徽章:
0
2 [报告]
发表于 2013-02-24 16:01 |只看该作者
回复 1# JackyBsh


不知怎么描述好,再进一步解释一下吧。

内核压缩部分被包围在未压缩部分的中间,由未压缩部分负责解压压缩部分,而解压方式采用的是 in-place 解压,也就是说,内核解压后的部分将覆盖原来的压缩部分。显然,也会覆盖包围在内核压缩影像外围的非压缩部分。但是事实上,解压缩后,非解压缩的代码还有一些工作要做,最后才跳转到解开的压缩部分。因此,这就有一个矛盾,既然被覆盖了,如何还能继续执行呢?相关代码如下:

arch/x86/boot/compressed/head_32.S:

/*
* Do the decompression, and jump to the new kernel..
*/
    leal    z_extract_offset_negative(%ebx), %ebp
                /* push arguments for decompress_kernel: */
    pushl   %ebp        /* output address */
    pushl   $z_input_len    /* input_len */
    ...

    call    decompress_kernel
    addl    $20, %esp

#if CONFIG_RELOCATABLE
/*
* Find the address of the relocations.
*/
    leal    z_output_len(%ebp), %edi

/*
* Calculate the delta between where vmlinux was compiled to run
* and where it was actually loaded.
*/
    movl    %ebp, %ebx
    subl    $LOAD_PHYSICAL_ADDR, %ebx
    jz  2f  /* Nothing to be done if loaded at compiled addr. */
   ...
/*
* Jump to the decompressed kernel.
*/
    xorl    %ebx, %ebx
    jmp *%ebp

也就是说,表面上看,在调用decompress_kernel,本身head_32.S似乎已经被覆盖,那么后面的代码,包括跳转到解压的内核的代码“jmp *%ebp”,怎么还能够依然执行呢?

不知问题描述清楚没有,希望有高手能够解答。




   

论坛徽章:
6
金牛座
日期:2013-10-08 10:19:10技术图书徽章
日期:2013-10-14 16:24:09CU十二周年纪念徽章
日期:2013-10-24 15:41:34狮子座
日期:2013-11-24 19:26:19未羊
日期:2014-01-23 15:50:002015年亚洲杯之阿联酋
日期:2015-05-09 14:36:15
3 [报告]
发表于 2013-02-25 19:08 |只看该作者
回复 2# JackyBsh
在调用decompress_kernel,本身head_32.S似乎已经被覆盖

这个代码是从哪里得出来的?
我怎么记得是,如果解压出来的new kernel可能会覆盖当前内核,那么会先解压出来放到一个地方,然后再mov过来,不会直接覆盖的啊。
   

论坛徽章:
0
4 [报告]
发表于 2013-02-26 14:32 |只看该作者
回复 3# 瀚海书香

似乎从2.6开始就是了,最新的内核都是采用这种压缩方法。网上的文章几乎全部是早期的,误人子弟啊。
   

论坛徽章:
0
5 [报告]
发表于 2013-03-14 20:53 |只看该作者
2.6.11没有变

论坛徽章:
0
6 [报告]
发表于 2013-03-15 09:34 |只看该作者
本帖最后由 aweii 于 2013-03-15 09:41 编辑

2.6.11的还是原来的思路,2.6.24已经是LZ说的那样了。我也不解啊。而且滑动窗口即输出窗口,flush_window()函数中不再有从滑动窗口拷贝到输出的代码了。但是解压输出似乎是原地踏步,32K满后就回到output,那不是先前解压出来的数据又被覆盖了。

------------boot\compressed\Misc_32.c
static void flush_window(void)
{
        /* With my window equal to my output buffer
         * I only need to compute the crc here.
         */
        ulg c = crc;         /* temporary variable */
        unsigned n;
        uch *in, ch;

        in = window;
        for (n = 0; n < outcnt; n++) {
                ch = *in++;   //这里不再有老版本的拷贝解压数据到输出区的代码,而只计算crc
                c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> ;
        }
        crc = c;
        bytes_out += (ulg)outcnt;
        outcnt = 0;
}

简单起见,我只分析了inflate_stored()的部分代码,即未压缩的块。
while (n--)
  {
    NEEDBITS(
    slide[w++] = (uch)b;
    if (w == WSIZE)
    {
      flush_output(w);
      w = 0;
    }
    DUMPBITS(
  }
滑动窗口slide宏定义中是指向window的,而window指针指向decompress_kernel()的参数output,output是指向解压后的内核代码的存放的物理内存位置,即1MB处。这里,滑动窗口数据满32KB后,指针w归零,老版本的在归零前会把32KB数据拷贝到输出,那么归零后原先的部分数据被覆盖是没问题,但是2.6.24版中滑动窗口即输出,没有了拷贝过程,指针归零后继续解压不是覆盖了原来的数据?

论坛徽章:
0
7 [报告]
发表于 2013-03-15 10:26 |只看该作者
我看了下,这块代码是从2.6.20开始变化的

论坛徽章:
0
8 [报告]
发表于 2013-03-15 14:42 |只看该作者
是奇怪啊,arch其它目录下的misc.c中就不一样,i386下的flush_window()着实不可思议

论坛徽章:
0
9 [报告]
发表于 2013-05-08 11:06 |只看该作者
这个问题解决了。奥妙就在编译链接上。我看了下arch/x86/boot/compressed/下的vmlinux_32.lds和vmlinux_32.scr:
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(startup_32)
SECTIONS
{
        /* Be careful parts of head.S assume startup_32 is at
         * address 0.
         */
        . =  0         ;
        .text.head : {
                _head = . ;
                *(.text.head)
                _ehead = . ;
        }
        .data.compressed : {
                *(.data.compressed)
        }
        .text :        {
                _text = .;         /* Text */
                *(.text)
                *(.text.*)
                _etext = . ;
        }
        .rodata : {
                _rodata = . ;
                *(.rodata)         /* read-only data */
                *(.rodata.*)
                _erodata = . ;
        }
        .data :        {
                _data = . ;
                *(.data)
                *(.data.*)
                _edata = . ;
        }
        .bss : {
                _bss = . ;
                *(.bss)
                *(.bss.*)
                *(COMMON)
                _end = . ;
        }
}

关键在于.text段在.data.compressed 段的后面,所以不会被覆盖,而head_32.S的开始部分代码在.text_head段,当开始解压时这部分代码已经运行完了就没用了,所以被覆盖是不要紧的,而后边的代码在.text段,位于压缩代码之后,由于和解压代码之间的output_len-inout_len的空隙的存在,是不会被覆盖的

论坛徽章:
0
10 [报告]
发表于 2013-06-06 21:34 |只看该作者
回复 9# aweii


    谢谢,解决了我一个很大的疑问。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP