我正在编写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
组装:
;add.s
add al, 1
add ax, 1
add eax, 1
add rax, 1
反汇编:
.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
所以第一个是正确的,就像手册中所说的一样,但是汇编器在ADD参考表的下方使用了指令,例如REX前缀,为什么使用那些而不是我之前列出的指令?
现在第二个ADD ax, 1
;搜索之后,我发现66
是操作数大小的覆盖前缀,但未在ADD参考表中列出,因此,当我选择添加此前缀时,似乎找不到更多的信息。英特尔手册中的旧前缀?
我试图按照手册中的说明拆卸05 01,但是它不能将其识别为只是数字的操作码。英特尔手册是一个很好的资源,我认为它只是缺少一些额外的说明和结构,仍然试图把我的头都围绕在ModRM上。
答案 0 :(得分:2)
在列出的说明中注意立即数的大小。立即数与寄存器的大小相同。无论寄存器的大小如何,您测试的汇编程序使用的指令均使用一字节立即数。这使指令更短。您可以通过提供适当大小的立即数来使用列出的说明,例如: 05 01 00 00 00
有关前缀的说明,请参见2.1.1节。 操作数大小覆盖前缀允许程序在16位和32位操作数大小之间切换。两种尺寸都可以 为默认值;使用前缀将选择非默认大小。 在64位模式下,默认始终为32位,因此66h前缀选择16位操作数的大小。
答案 1 :(得分:1)
有多种操作码可将立即数添加到64位寄存器中
REX.W + 05 id ADD RAX, imm32 Add imm32 sign-extended to 64-bits to RAX.
REX.W + 81 /0 id ADD r/m64, imm32 Add imm32 sign-extended to 64-bits to r/m64.
REX.W + 83 /0 ib ADD r/m64, imm8 Add sign-extended imm8 to r/m64.
https://www.felixcloutier.com/x86/ADD.html
因为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
Why NASM on Linux changes registers in x86_64 assembly
现在,如果您仔细查看桌子,您可能会看到一些奇怪的东西
05 iw ADD AX, imm16 Add imm16 to AX.
05 id ADD EAX, imm32 Add imm32 to EAX.
81 /0 iw ADD r/m16, imm16 Add imm16 to r/m16.
81 /0 id ADD r/m32, imm32 Add imm32 to r/m32.
01 /r ADD r/m16, r16 Add r16 to r/m16.
01 /r ADD r/m32, r32 Add r32 to r/m32.
03 /r ADD r16, r/m16 Add r/m16 to r16.
03 /r ADD r32, r/m32 Add r/m32 to r32.
为什么同一条指令的所有16位和32位版本都具有相同的操作码?
答案是当前模式将定义指令类型。如果您以16位模式运行,则默认情况下将使用16位寄存器;如果您处于32位或64位模式,则默认大小为32位。如果要使用其他尺寸,则必须使用66h (Operand-size override) prefix。这意味着在16位模式下,您将获得以下输出,而不是上面看到的
83 c0 01 add ax, 1
66 83 c0 01 add eax, 1
我试图按照手册中的说明拆卸05 01,但是它不能将其识别为只是数字的操作码
因为05
之后必须是4字节的立即数(如手册中所示id/imm32
)或2字节的立即数(iw/imm16
),具体取决于默认的操作数大小。只有带有imm8/ib
的指令才能立即具有单个字节。例如online disassembler给我下面的输出
0: 05 01 02 03 04 add eax,0x4030201
5: 66 05 01 02 add ax,0x201
由于与上述相同的原因,选择操作码83h是因为0x01可以容纳在一个字节中,从而使长度相同,并且汇编程序可以选择自己喜欢的任何内容
0: 66 83 c0 01 add ax,0x1
4: 66 05 01 00 add ax,0x1
您可能想阅读