我对x86-64二进制编码非常陌生。我正在尝试修复一些旧的“汇编程序”代码。
无论如何,我正在尝试执行以下操作(Intel语法):
mov [rbp+rcx], al
汇编器当前正在生成此:
88 04 0D
但这似乎不是有效的指令。如果我将SIB字节的基数从rbp
更改为其他寄存器,则可以正常工作。另一种使其工作的方法是添加一个零字节的字节位移(88 44 0D 00
)。其他类似的操作码似乎也会发生这种情况。
为什么我不能在rbp
中使用mod=00
?
答案 0 :(得分:5)
这意味着rbp
的编码是没有基址寄存器的转义码(SIB中只有disp32或ModRM中只有RIP相对rel32)。大多数汇编程序将[rbp]
组装成[rbp + disp8=0]
。
由于不需要缩放,请使用[rcx + rbp]
来避免需要disp8 = 0,因为rbp
可以作为索引。
(在长模式下,SS和DS总是等效的,因此base = RBP表示SS而base = RCX表示使用DS段无关紧要。)
(根据我在Why are rbp and rsp called general purpose registers?上写的答案)。这个问题看起来像是复制或移植此部分的理想位置。
rbp
/ r13
不能是没有位移的基址寄存器:该编码的意思是:(在ModRM中)rel32
(相对于RIP ),或(在SIB中)disp32
,没有基址寄存器。 (r13
在ModRM / SIB中使用相同的3位,因此此选择通过不使指令长度解码器查看the REX.B bit来获得第4个基寄存器位来简化解码)。 [r13]
组合为[r13 + disp8=0]
。 [r13+rdx]
会与[rdx+r13]
组合在一起(可以选择通过交换基数/索引来避免此问题)。
rsp
/ r12
作为基址寄存器始终需要一个SIB字节。 (base = RSP的ModR / M编码是转义码,用于向SIB字节发送信号,如果r12
的处理方式不同,则更多的解码器将不得不关心REX前缀。)
rsp
不能是索引寄存器。这样就可以对[rsp]
进行编码,该编码比[rsp + rsp]
更有用。 (英特尔本可以为32位寻址模式(386中的新增功能)设计ModRM / SIB编码,因此只有在base = ESP时才可以使用没有索引的SIB。这将使[eax + esp*4]
成为可能,并且仅排除{ {1}},但这没有用,因此他们通过使index = ESP成为不带索引的代码来简化硬件,而不考虑基础,这允许两种冗余方式来编码任何基础或base + disp寻址模式:有或没有SIB。)
[esp + esp*1/2/4/8]
可以是索引寄存器。与其他情况不同,这不会影响指令长度解码。而且,不能像其他情况一样使用更长的编码来解决。 AMD希望AMD64的寄存器组尽可能正交,因此在索引/无索引解码中,他们花了一些额外的晶体管来检查REX.X才有意义。例如,r12
需要index = r12,因此[rsp + r12*4]
的用途不完全会导致AMD64成为较差的编译器目标。
r12
这些都同样适用于32位寻址模式。除了没有EIP相对编码外,其他编码都是相同的,只有两种冗余的方法可以对disp32进行编码,而无需任何基础。
另请参阅https://wiki.osdev.org/X86-64_Instruction_Encoding#32.2F64-bit_addressing_2,以获取类似于英特尔第2卷手册中的表格。
这似乎在其他类似的操作码中发生。
r / m操作数的ModRM编码始终相同。有些操作码需要一个寄存器操作数,有些需要内存,但是实际的ModRM +可选的SIB +可选的位移是固定的,因此无论指令如何,相同的硬件都可以对其进行解码。
有一些罕见的操作码,例如 0: 41 8b 03 mov eax,DWORD PTR [r11]
3: 41 8b 04 24 mov eax,DWORD PTR [r12] # needs a SIB like RSP
7: 41 8b 45 00 mov eax,DWORD PTR [r13+0x0] # needs a disp8 like RBP
b: 41 8b 06 mov eax,DWORD PTR [r14]
e: 41 8b 07 mov eax,DWORD PTR [r15]
11: 43 8b 04 e3 mov eax,DWORD PTR [r11+r12*8] # *can* be an index
,它们的操作数完全不使用ModRM编码,但是任何使用相同格式的操作码。