您似乎在这里有两个问题:
- 返回值的高位在返回之前是否需要清零?(在调用之前,是否需要将参数的高位清零?)
- 与该决定相关的成本/收益是什么?
第一个问题的答案是 否定的, 高位 可能 有垃圾,并且PeterCordes已经就该主题写了一个非常好的答案。
至于第二个问题,我怀疑未定义高位总体上对性能更好。一方面,使用32位运算时,零扩展值无需付出任何额外费用。但是另一方面,并非总是需要事先将高位清零。如果允许高位垃圾,则可以将其留给接收值的代码,以便仅在实际需要时才执行零扩展(或符号扩展)。
但我想强调另一个考虑因素: 安全性
信息泄漏
当未清除结果的高位时,它们可能会在堆栈/堆中保留其他信息的片段,例如函数指针或地址。如果存在一种机制,可以执行更高特权的功能并在之后检索
rax(或
eax)的全部值,则可能会导致
信息泄漏
。例如,系统调用可能会将指针从内核泄漏到用户空间,从而导致内核ASLR失败。否则IPC机制可能会泄漏有关另一个进程的地址空间的信息,这可能有助于开发沙箱突破。
当然,也许有人会认为,防止信息泄漏不是ABI的责任;程序员应正确执行其代码。虽然我确实同意,但要求编译器将高位清零,仍然可以消除这种特殊形式的信息泄漏。
你不应该相信你的输入
另一方面,更重要的是,编译器不应盲目地相信任何接收到的值的高位都清零,否则函数可能无法按预期运行,这也可能导致可利用的条件。例如,考虑以下内容:
unsigned char buf[256];...__fastcall void write_index(unsigned char index, unsigned char value) { buf[index] = value;}如果允许我们假设
index其高位清零,那么我们可以将上面的代码编译为:
write_index: ;; sil = index, dil = value mov rax, offset buf mov [rax+rsi], dil ret
但是,如果我们能够从我们自己的代码中调用这个函数,我们可以值提供
rsi出的
[0,255]范围,并写入到内存超出缓冲区的范围。
当然,编译器实际上不会生成这样的代码,因为如上所述, 被调用方 有责任对其参数进行零扩展或符号扩展,而不是 调用方的
参数扩展。我认为,这是一个非常实际的原因,要让接收值的代码始终假定高位有垃圾并明确将其删除。



