0

在 x86 汇编中有些东西让我困惑了一段时间,这是 NASM 如何/何时可以推断操作的大小,这是一个示例:

mov ebx, [eax]

在这里,我们将存储在 eax 中保存的地址的 4 个字节移动到 ebx 中。由于寄存器是 32 位,因此操作的大小被推断为 4 个字节。

但是,此操作不会被推断并引发编译错误:

mov [eax], 123456

当然解决方案是这样的:

mov dword [eax], 123456

这会将数字 123456 的 32 表示移动到存储在 eax 中保存的地址的字节中。

但这让我很困惑,它肯定可以看到 eax 是 32 位的,所以不应该假设我想将它存储为 32 位值而无需在 mov 之后指定 dword 吗?

当然,如果我想将 12345 的 16 位表示(适合 16 位的较小数字)放入 eax 我会这样做:

mov ax, 12345
4

3 回答 3

4

对于具有内存目标和直接源的任何指令,操作数大小将是不明确的(因此必须指定)。(两个操作数实际上都不是寄存器,即使在寻址模式中使用一个或多个。)

地址大小和操作数大小是指令的独立属性。


引用您在对另一个答案的评论中所说的话,因为我认为这是您困惑的核心:

我希望将mov [eax], 1内存地址 eax 中保存的 4 个字节设置为 1 的 32 位表示

BYTE/WORD/DWORD [PTR] 注解与内存地址的大小无关;它大约是该地址处内存中变量的大小。假设平面 32 位寻址,地址总是四个字节长,因此必须进入 Exx 寄存器。因此,当源操作数是立即数时,目标操作数上的 dword(或其他)注释是汇编器知道它是否应该修改 1、2 或 4 字节 RAM 的唯一方法。

如果我演示这些注释对机器代码的影响,也许会有所帮助:

$ objdump -d -Mintel test.o
...
   0:      c6 00  01             mov    BYTE PTR  [eax], 0x1
   3:   66 c7 00  01 00          mov    WORD PTR  [eax], 0x1
   8:      c7 00  01 00 00 00    mov    DWORD PTR [eax], 0x1

objdump(与实际打印的方式相比,我稍微调整了间距。)

注意两件事:(1)三个不同的操作数前缀产生三个不同的机器指令,(2)使用不同的前缀会改变源操作数在机器代码中的长度。

于 2015-06-20T22:30:22.583 回答
3

mov [eax], 123456

该指令将对源操作数使用立即寻址,对目标操作数使用间接寻址,即将十进制 123456 放入存储在寄存器 eax 中的内存地址中,正如您所指出的,但 eax 指向的内存地址本身不必是 32位大小。NASM 无法推断目标操作数的大小。寄存器 eax 中的指针大小为 32 位。

地址大小和操作数大小是指令的完全独立的属性。

当然,如果我想将 12345 的 16 位表示放入 eax 我会这样做: mov ax, 12345

是的,但是在这里您对源操作数使用立即寻址,对目标操作数使用寄存器寻址。汇编器可以从目标寄存器的大小(在 AX 寄存器的情况下为 16 位,保留完整 EAX 的高 2 字节未修改,因此您实际上并未设置 32 位)推断出您希望移动的数据量EAX 到那个值)。

编译错误

我认为您的意思是组装错误:)

于 2015-06-20T22:15:59.287 回答
2

在您的第一种情况下,它可以毫无问题地确定它,因为 EBX 是一个 32 位寄存器。但是在第二个中,您将 EAX 用作地址,而不是目标寄存器,因此 nasm 开发人员采取了安全路线并让开发人员选择大小。

如果你这样做mov [eax], 1了,nasm 可以从中确定什么?是否要将字节、16 位或 32 位内存块设置为 1?这是完全未知的。这就是为什么最好强制开发人员说明尺寸的原因。

如果你说mov eax, 123456从那时起目的地是一个寄存器,那将是完全不同的。

于 2015-06-20T22:09:18.697 回答