在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
?
答案 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 将设置为零。