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

use*_*636 7 linux x86 assembly linux-kernel bootloader

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

...
# Zero the bss
    movw    $__bss_start, %di
    movw    $_end+3, %cx
    xorl    %eax, %eax
    subw    %di, %cx
    shrw    $2, %cx
    rep; stosl
...
Run Code Online (Sandbox Code Playgroud)

为什么_end地址中添加了3个?为什么不movw $_end, %cx代替movw $_end+3, %cx

Mic*_*tch 7

如果代码逐字节清除BSS部分movw $_end, %cx就足够了.但是,此代码不零出BSSSTOSB他们使用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
Run Code Online (Sandbox Code Playgroud)

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