为什么我的汇编器不使用 ADD EAX,1 手册文档中的 05 操作码 (add eax,imm32) 简写形式,但对 04 ADD AL, 1 却使用它?

1 x86 assembly machine-code opcode instruction-encoding

我正在编写一个 x86-64 汇编程序。我正在浏览 Intel x86 手册第 2 卷,试图了解如何从程序集中生成正确的指令。我主要了解它是如何工作的,但一直在组装和拆卸说明以检查我是否正确。

在 ADD 参考表(第 2A 卷,3.31)中:

opcode        | Instruction  
04 ib         | ADD AL, imm8  
05 iw         | ADD AX, imm16  
05 id         | ADD EAX, imm32  
REX.W + 05 id | ADD RAX, imm32  
Run Code Online (Sandbox Code Playgroud)

集合:

;add.s   
add al, 1
add ax, 1
add eax, 1
add rax, 1
Run Code Online (Sandbox Code Playgroud)

拆卸:

.text:
   0:   04 01           add al, 1
   2:   66 83 c0 01     add ax, 1
   6:   83 c0 01        add eax, 1
   9:   48 83 c0 01     add rax, 1
Run Code Online (Sandbox Code Playgroud)

所以第一个是正确的,就像手册所说的那样,但是汇编器使用 ADD 参考表下面的指令,比如 REX 前缀,为什么要使用这些指令而不是我之前列出的指令呢?

现在是第二个ADD ax, 1;搜索后,我发现这66是操作数大小覆盖前缀,但未在 ADD 参考表中列出,因此我何时选择添加此前缀,我似乎无法找到有关它或 Intel 中其他旧前缀的太多信息手动的?

我尝试按照手册中所示反汇编 05 01,但它没有将其识别为操作码,而只是数字。英特尔手册是一个很好的资源,我认为它只是缺少一些额外的解释和结构,仍然试图让我了解 ModRM 的内容。

phu*_*clv 5

有多种操作码可用于向 64 位寄存器添加立即数

操作码 操作说明 描述
REX.W + 05 id ADD RAX, imm32 将 imm32 符号扩展为 64 位添加到 RAX。
REX.W + 81 /0 id ADD r/m64, imm32 将 imm32 符号扩展为 64 位添加到 r/m64。
REX.W + 83 /0 ib ADD r/m64, imm8 将符号扩展 imm8 添加到 r/m64。

因为01适合一个字节,所以汇编器使用操作码83来节省指令长度。如果您尝试add rax, 100000000或类似的操作,您将获得操作码05

现在,要强制进行另一种解码而不是更有效的解码,您需要在汇编器中定义一些语法。例如 nasm 使用strict关键字

mov    eax, 1                ; 5 bytes to encode (B8 imm32)
mov    rax, strict dword 1   ; 7 bytes: REX mov r/m64, sign-extended-imm32.    NASM optimizes mov rax,1 to the 5B version, but dword or strict dword stops it for some reason
mov    rax, strict qword 1   ; 10 bytes
Run Code Online (Sandbox Code Playgroud)

现在,如果你仔细观察表格,你可能会发现一些“奇怪”的东西

操作码 操作说明 描述
05 iw ADD AX, imm16 将 imm16 添加到 AX。
05 id ADD EAX, imm32 将 imm32 添加到 EAX。
81 /0 iw ADD r/m16, imm16 将 imm16 添加到 r/m16。
81 /0 id ADD r/m32, imm32 将 imm32 添加到 r/m32。
01 /r ADD r/m16, r16 将 r16 添加到 r/m16。
01 /r ADD r/m32, r32 将 r32 添加到 r/m32。
03 /r ADD r16, r/m16 将 r/m16 添加到 r16。
03 /r ADD r32, r/m32 将 r/m32 添加到 r32。

为什么同一条指令的所有 16 位和 32 位版本都具有相同的操作码?

答案是当前模式将定义指令类型。如果您在 16 位模式下运行,则默认情况下将使用 16 位寄存器,如果您在 32 或 64 位模式下运行,则默认大小将为 32 位。如果您想使用其他大小,则必须使用66h (操作数大小覆盖)前缀。这意味着在 16 位模式下,您将得到以下输出,而不是上面看到的输出

83 c0 01           add ax, 1
66 83 c0 01        add eax, 1
Run Code Online (Sandbox Code Playgroud)

我尝试按照手册中所示反汇编 05 01,但它没有将其识别为操作码,只是数字

因为05后面必须跟一个 4 字节立即数(id/imm32如手册中所示)或 2 字节立即数 ( iw/imm16),具体取决于默认操作数大小。只有 的指令imm8/ib才能有单字节立即数。例如,在线反汇编程序给出以下输出:

0:  05 01 02 03 04          add    eax,0x4030201
5:  66 05 01 02             add    ax,0x201
Run Code Online (Sandbox Code Playgroud)

出于与上面相同的原因,选择操作码 83h 是因为 0x01 适合一个字节,使得长度相同,并且汇编器可以选择它喜欢的任何内容

0:  66 83 c0 01             add    ax,0x1
4:  66 05 01 00             add    ax,0x1
Run Code Online (Sandbox Code Playgroud)

您可能想阅读此内容