确定何时 NASM 可以推断出 mov 操作的大小

Determining when NASM can infer the size of the mov operation

有些东西让我在 x86 汇编中困惑了一段时间,它 how/when 可以 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

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

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

如果你说mov eax, 123456那就完全不同了,因为那时目的地是寄存器。

mov [eax], 123456

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

地址大小和操作数大小是一条指令的完全不同的属性。

Surely if I wanted to put the 16 bit representation of 12345 into eax I would do this: mov ax, 12345

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

compile error

我想你的意思是汇编错误:)

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

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


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

I would expect mov [eax], 1 to set the 4 bytes held in memory address eax to the 32 bit representation of 1

BYTE/WORD/DWORD[PTR]注解不是关于内存地址的大小;它大约是该地址处内存中变量的大小。假设平面 32 位寻址, 地址 总是四个字节长,因此必须放在 Exx 寄存器中。因此,当源操作数是立即数时,目标操作数上的双字(或其他)注释是汇编程序知道它是否应该修改 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) 使用不同的前缀会改变发送到机器代码中的源操作数的长度。