除非您已经知道 x86 指令编码的工作原理,否则您链接的文档不是很有用。所以让我们尝试另一个。在this resource 之后,我们得到以下候选编码:
81 /7 iw CMP r/m16,imm16 Compare imm16 with r/m16.
81 /7 id CMP r/m32,imm32 Compare imm32 with r/m32.
83 /7 ib CMP r/m16,imm8 Compare imm8 with r/m16.
83 /7 ib CMP r/m32,imm8 Compare imm8 with r/m32.
这里要注意的一点是,对于 word 和 dword 操作都给出了相同的编码。这不是错误:操作数大小由当前代码段的默认操作数宽度(即我们是在 16、32 还是 64 位模式下运行)以及是否存在 66 或 REX.W 操作数大小覆盖确定字首。规则很简单:
- 在 16 位模式下,默认操作数大小为 16 位
- 在 32 位和 64 位模式下,默认操作数大小为 32 位
-
66 前缀在 16 位和 32 位操作数大小之间切换
- 在 64 位模式下,
REX.W 前缀切换为 64 位操作数大小
所以在 32 位或 64 位模式下编程时,不需要前缀,因为默认操作数大小已经是我们想要的。
现在的问题是使用83 还是81。在这种情况下,两者都可以使用,因为我们的立即数适合 8 位有符号。我们将继续使用83 操作码,因为编码更短。
编码83 /7 ib 告诉我们操作码是83,后跟一个 reg = 7 的 modr/m 字节(其他字段编码 r/m32 操作数),后跟一个 8 位立即数。
r/m32 操作数[ebp-4] 可以在您链接的参考文献中给出的 modr/m 字节表中查找。我们有一个带索引寻址模式的内存操作数;索引ebp 和位移-4。位移适合 8 位有符号,因此我们使用表中的 [ebp+disp8] 条目并以 7d 结束 modr/m 字节。接下来是位移字节,0xfc,表示二进制补码中的 -4。
把它们放在一起,我们得到83 7d fc 02 作为cmp dword ptr [ebp-4], 2 的编码:
83 opcode
7d modr/m byte: reg = 7, r/m = [ebp+disp8]
fc displacement: -4
02 immediate: 2
值得注意的是,32 位和 64 位模式的编码是相同的。对于 16 位模式,需要额外的 66 和 67 前缀来选择 32 位操作数和地址大小,即 66 67 83 7d fc 02。