当我们只知道操作码的十六进制时如何编码指令

How to encode an instruction when we just know the hex for opcode

this source, they have given that hex for cmp r/m16/32 imm8 is 0x837. Somewhere i got that, hex for ebp is 0b0101. With this information, how can i encode the instruction cmp dword [ebp-4] 2? I have been searching for this from a couple of hours and got no clues except for this(第61页)类似的指令cmpb [=11=]xf,(%rdi)编码为80 3f 0f。但我无法理解这一点,因为我提到的前一个来源说 0x803 是给 sbb 的。另外 rdi 是 0b0111 而不是 0b1111(实际上是 r15)。我很困惑...如果可能的话,我想要 x86-32 和 x86-64 的编码指令。

查阅官方 Intel® 64 和 IA-32 架构软件开发人员手册第 2 卷:指令集参考,A-Z,在 Intel's website 上提供多种格式。翻到关于CMP的页面,找到CMP r/m32, imm8那一行。这将操作码列作为 83 /7 ib。转向 Table 2-2。具有 ModR/M 字节 的 32 位寻址形式。选择 [ebp]+disp8 行和 /digit 7 列。该单元格告诉您 ModR/M 字节是 7d。您需要附加 -4 的位移,即 8 位有符号的 fc 和您的直接操作数。因此,完整的指令是 83 7d fc 02。在 64 位模式下,如果您使用 rbp,编码是相同的。如果要保留 ebp,则需要使用 67.

的地址大小覆盖前缀

除非您已经知道 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 位模式下决定)和 66REX.W 操作数大小覆盖前缀。规则很简单:

  • 在16位模式下,默认操作数大小为16位
  • 在 32 位和 64 位模式下,默认操作数大小为 32 位
  • 66 前缀在 16 位和 32 位操作数大小之间切换
  • 在 64 位模式下,REX.W 前缀切换到 64 位操作数大小

因此在 32 位或 64 位模式下编程时,不需要前缀,因为默认操作数大小已经是我们想要的。

现在的问题是使用83还是81。在这种情况下,两者都可以使用,因为我们的立即数适合带符号的 8 位。我们将继续使用 83 操作码,因为编码更短。

编码83 /7 ib告诉我们操作码是83后跟一个modr/m字节reg = 7(其他字段编码r/m32操作数)后跟通过 8 位立即数。

r/m32 操作数 [ebp-4] 可以在您链接的参考文献中给出的 modr/m 字节 table 中查找。我们有一个带索引寻址模式的内存操作数;索引 ebp 和位移 -4。位移适合 8 位有符号位,因此我们使用 table 中的 [ebp+disp8] 条目并在 modr/m 字节的 7d 处结束。接下来是位移字节,0xfc for −4 in two's complement.

将它们放在一起我们得到 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 位模式,需要额外的 6667 前缀来 select 32 位操作数和地址大小,给出 66 67 83 7d fc 02.