问题描述
我现在正在学习汇编,只是想知道是否有人可以澄清尝试将数据移动到无法容纳它的寄存器的行为。
例如,假设我们有(在 x86 上,AT&T 语法):
movl $0xff00abcd,%ax
其中 %ax 是 %eax 的 16 位子寄存器,会发生什么?指令会完全失败,还是 %ax 会包含 $0xff00?
解决方法
这样的指令不可编码:根本就没有机器码字节序列可以告诉 CPU 将值 0xff00abcd
移动到 ax
中。 (为什么架构设计者要指定任何字节序列来告诉 CPU 这样做,而这显然是不可能的?)。所以不可能执行这样的指令,因为它不存在。
那么当你的汇编器被要求汇编一条不存在的指令时,你会怎么做?当然,它应该警告你。它接下来做什么取决于你的汇编器是如何设计的。一些组装者可能只是把它当作一个致命的错误,并在你修复它之前拒绝继续。其他人可能会发出一些不同指令的机器代码,希望它可能是你的意思。
gas 显然做了什么,与在许多其他情况下处理溢出的方式相匹配,是截断高位并发出 movw $0xabcd,%ax
的机器代码。不过,依赖这种行为并不是一个好主意。
首先,这不是 mov
到 16 位寄存器的正确后缀。movl
的意思是“mov
到长字/双字”,因此对于“movw
to a word”,必须更改为 mov
,这是 ax
寄存器的大小。排除这种情况,调试器是检查此类行为的最简单方法。假设您使用 AT&T 语法中的 GAS,可以使用以下简单程序进行检查:
.text
.globl main
main:
movw $0xff00abcd,%ax
# Then exit the program
GAS 可能会抱怨这个:对我来说,它产生了这个警告:
test.S: Assembler messages:
test.S:4: Warning: -16733235 shortened to 43981
如果我在 movw
指令之后立即放置一个断点,我可以看到 %ax
包含 0xabcd
,所以是低 16 位。不过,这对您来说可能也不能保证,也许还有其他一些因素,其中之一是,正如 harold 所指出的,这是汇编程序必须处理的事情,而不是处理器。因此,如果您没有使用 GAS,或者即使您使用了 GAS,您可能会遇到与我所遇到的不同的行为。
指令完全失败,如果你编译你会得到:
warning: word data exceeds bounds [-w+number-overflow]
因此您首先必须将数据移动到 eax
寄存器,然后 ax
将包含 eax
的低 16 位,因此 ax
将等于到0xabcd