在 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
?
最佳答案
如果代码逐字节清除 BSS 部分 movw $_end, %cx
就足够了。但是,此代码不会将 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 的数量将设置为零。
关于Linux kernel header.S源码,为什么清零BSS时需要_end+3?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13065207/