ADD 指令形式,英特尔手册没有描述操作数大小覆盖,如 "byte" 或 "word"

ADD instruction forms, Intel manual doesn't describe operand size overrides like "byte" or "word"

我正在查看由 NASM 组装的英特尔汇编示例。它有指令:

add byte [ebx], 32

我如何从文档中知道“byte”的作用?

我正在阅读的这本书在文本中解释了“字节”如何告诉汇编程序我们只将一个字节写入 ebx。我不清楚我是如何通过查看文档知道这一点的。

从本书和其他地方的示例来看,ADD 指令似乎有两种形式:

  1. ADD <dest> <src>
  2. ADD <size> <dest> <src>

但是,当我查看 Intel 文档[1]时,我没有看到任何与我的表格相似的内容。 table 中给出的每条指令只有一个逗号,对我来说,这使得所有相应的操作码似乎只接受两个输入。有一个 table 给出了“Instruction Operand Encoding”。操作数 3 和 4 不适用。环顾网络,大多数网站都没有提及任何关于大小参数的信息(更不用说它是否适用于我的处理器了)。

我正在 Intel(R) Core(TM) i7-6700HQ CPU 386 模式下组装:

nasm -f elf -g -F stabs -o $OBJECTFILE 
ld -m elf_i386 -o $BUILDNAME $OBJECTFILE

也许该指令需要一个额外的 386 操作数但不是较新的架构?


[1]“英特尔® 64 位和 IA-32 架构软件开发人员手册合卷:1、2A、2B、2C、2D、3A、3B、3C、3D 和 4”,第 1 卷。 pdf 中的 2A 3-31 第 605 页。

https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html

asm源码中的byte关键字设置指令的operand-size属性。在机器代码中,这将由数字操作码或 16/32/64 位 operand-size,由当前 CPU 模式和 non-byte 操作码的前缀暗示。 Intel 的手册记录了 machine-code 形式,而不是 asm 源语法。

请参阅以下回复:如何将其编码为机器代码。

  • Is there a default operand size in the x86-64 (AMD64) architecture?

这就是为什么 assemblers 也有独立于 ISA 手册的手册。

例如NASM的手册,Chapter 3: The NASM Language 3.1 NASM源代码行的布局描述了助记符和操作数的语法布局。不幸的是,该部分忽略了您可以放在操作数前面的覆盖,只有 o16 之类的前缀可以放在助记符前面! (作为指定 operand-size 的笨拙的手动方式。)

手册中确实有在多个地方使用 operand-size 覆盖的示例,例如在 2.2 Quick Start for MASM Users it points out that NASM needs mov word [var], 2 even if var is var dw 0 which in MASM would magically imply an operand-size for that instruction. And mention of the same specifiers when used with strict to force the encoding of the immediate, not just the operand-size. e.g. add ecx, strict dword 123 forces the add r/m32, imm32 form, while add ecx, dword 123 still allows the add r/m32, imm8 form. (https://www.felixcloutier.com/x86/add)


其他一些 x86 assemblers,例如 GAS 和 clang/LLVM,默认情况下使用 AT&T syntax,这与 Intel 手册用来谈论指令的内容非常不同,其中 operand-size 由指令助记符上的后缀指定(如果需要),例如 movb $'a', (%rdi) 而不是 MASM mov byte ptr [rdi], 'a' (注意额外的 ptr 关键字)或 NASM mov byte [rdi], 'a'

汇编语法取决于工具,而不是 ISA。 Intel 的手册,尤其是第 2 卷,列出每个可用指令的部分, 而不是 详细说明如何在 asm 源代码中指定 operand-size 时会产生歧义。


在 asm 源代码中,寄存器可以表示 operand-size

对于两个操作数必须大小相同的指令,寄存器操作数在 asm 源语法中暗示 operand-size,因此您无需指定它。例如add eax, [rdi] 不需要 add eax, dword [rdi].

但是 mov-immediate 到内存(或任何其他 op mem,imm 指令)是不明确的,one-operand 内存指令如 inc [mem],以及操作数不存在的罕见指令不必像 shl [rdi], cl(目标大小可以是 b/w/d/q)或 movzx eax, [rdi](源大小可以是字节或字)

一样大小

好的 assemble 喜欢 NASM 的人会在这种歧义上出错。 Less-good assemblers 有时会选择默认值。例如GAS 为 MOV 以外的指令选择双字,例如add , (%rdi),直到最近才添加了一个警告!

同样,[rdi + rax] 指定 64 位 address-size,而 [edi + eax] 将是 32 位 address-size。 [1234] 之类的默认 address-size (在 asm 源代码中)是当前模式的位数,即不在机器代码中使用 67 address-size 前缀。

同样,这是关于 asm source-level 语法的 100%。将指令编码为某种模式的机器代码必然意味着 operand-size.

这就是为什么你需要告诉 assembler CPU 将解码的模式。例如使用 NASM bits 32 如果您正在制作平面二进制文件或在引导加载程序中切换模式。或者更正常地通过使用 nasm -felf64 进行汇编来制作 64 位目标文件。在那种情况下,bits 32 会让您将不匹配的机器代码放入错误的目标文件中,而不是在 assemble 时因 push ebx 无法针对 64 位模式进行编码而导致错误。