0x48
是一个 REX 前缀,W 字段设置为 1,表示 64-bit操作数大小。
(不是 64 字节)。
许多用于立即版本指令的操作码,包括83
,使用3位/r
ModR/M 字节中的字段作为 3 个额外的操作码位。英特尔的第 2 卷手册对此进行了记录,我认为附录中的操作码表也包含了它。
这就是为什么大多数原始 8086 立即指令,例如and r/m, imm
仍然只允许 2 个操作数,与shrd eax, edx, 4
or imul edx, [rdi], 12345
其中两个 ModRM 字段都用于对操作数以及操作码隐含的立即操作数进行编码。 SHRD/SHLD 并添加了 386 和 imul-immediate新增186个 https://ulukai.org/ecm/insref.htm#insIMUL。复制与与 (and eax, edx, 0xf
) 不可编码,但至少 x86 可以使用 LEA 进行复制和添加/子操作。
每条指令都有自己的文档,例如add(第 2 卷手册的 html 摘录) https://www.felixcloutier.com/x86/add,显示编码如
REX.W + 83 /0 ib
for ADD r/m64, imm8
,这就是你所拥有的。
来自 wiki.osdev.org 的 ModRM 位字段图 https://wiki.osdev.org/X86-64_Instruction_Encoding#ModR.2FM_and_SIB_bytes
7 0
+---+---+---+---+---+---+---+---+
| mod | reg | rm |
+---+---+---+---+---+---+---+---+
0xc4 = 0b11000100,因此 reg 字段 = 0。因此我们的操作码是83 /0
,采用英特尔的表示法。
其余的 ModRM 字段是:
- mode = 0b11,因此 rm 字段编码寄存器操作数,而不是寻址模式的基址寄存器。
- rm = 0b100。寄存器 #4 = SPL/SP/ESP/RSP。 (在本例中为 RSP,因为它是 64 位操作数大小)。请参阅英特尔手册,或https://wiki.osdev.org/X86-64_Instruction_Encoding#Registers https://wiki.osdev.org/X86-64_Instruction_Encoding#Registers对于桌子。
所以指令是add rsp, 0x38
ndisasm -b64
agrees:
$ cat > foo.asm
db 0x48, 0x83, 0xC4, 0x38
$ nasm foo.asm # create a flat binary with those bytes, not an object file
$ ndisasm -b64 foo
00000000 4883C438 add rsp,byte +0x38