Linux内核header.S来源,为什么_end + 3在归零BSS时需要?

时间:2012-10-25 09:08:36

标签: linux assembly linux-kernel x86 boot

在Linux源代码树中,文件arch/x86/boot/header.S具有与此类似的x86代码,以便在调用main之前清除 BSS 部分:

...
# Zero the bss
    movw    $__bss_start, %di
    movw    $_end+3, %cx
    xorl    %eax, %eax
    subw    %di, %cx
    shrw    $2, %cx
    rep; stosl
...

为什么_end地址添加了3个?为什么不movw $_end, %cx而不是movw $_end+3, %cx

1 个答案:

答案 0 :(得分:6)

如果代码一直按字节movw $_end, %cx清除 BSS 部分就足够了。但是,此代码并未将 BSS STOSB 归零,而是使用 STOSL 。一次存储32位而不是8位通常更有效。

STOSL EAX (使用xorl %eax, %eax设置为零)足够多次将 BSS 的整个范围清除为0。 +3确保如果 BSS 部分($ _end - $ __ bss_start)的长度不能被4整除,则计算清除所需的 DWORD 的数量将被围捕。如果这种四舍五入没有发生,那么在大小不能被4整除的情况下,最后的字节可能不会被清除。

此处进行的计算假设__bss_start是指向BSS段开头的指针,_end是指向 BSS 末尾的指针。计算要清除的32位 DWORD 的数量的等式是有效的:

NUMDWORDS=(_end+3-__bss_start) >> 2

shrw $2, %cx(计算中的>>2)是整数除以4,其结果始终向下舍入。我们在字节数上加上+3,这样当除以4时,它会有效地向上舍入到最接近的 DWORD 数。然后使用该值作为 DWORD 的数量 STOSL 将设置为零。