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



在这个来源中,他们给出了cmp r/m16/32 imm8的十六进制是0x837。我在某处得到了,ebp 的十六进制是 0b0101。有了这些信息,我该如何对指令cmp dword [ebp-4] 2进行编码?我一直在几个小时内寻找这个,除了这个(第 61 页)类似的指令cmpb $0xf,(%rdi)编码为 80 3f 0f 之外,没有任何线索。但我无法理解这一点,因为我提到的前一个消息来源说0x803是针对 sbb 的。此外,rdi 是 0b0111 而不是 0b1111(实际上是 r15)。我越来越困惑了...如果可能,我想要 x86-32 和 x86-64 的编码指令。

您链接的文档不是很有用,除非您已经知道 x86 指令编码的工作原理。 因此,让我们尝试另一个。 在此资源(摘自英特尔官方 PDF 手册1)之后,我们得到以下候选编码:

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.

这里要注意的一件事是,对于单词和 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后跟一个 reg = 7 的 modr/m 字节(其他字段编码 r/m32 操作数),后跟一个 8 位立即。

r/m32 操作数[ebp-4]可以在您链接的参考中给出的 modr/m 字节表中查找。 我们有一个具有索引寻址模式的内存操作数;指数ebp和位移-4. 位移适合 8 位有符号,因此我们使用表中的[ebp+disp8]条目,最终为 modr/m 字节的7d。 接下来是位移字节,在 2 的补码中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 位模式,需要额外的6667前缀来选择 32 位操作数和地址大小,从而66 67 83 7d fc 02.


脚注 1:在英特尔的软件开发人员手册 (SDM) 中,指令的操作数大小属性在第 1 卷第 3.6 章中描述,为该模式选择非默认模式的66h前缀在第 2 卷第 2.1.1 组第 3 组中描述。

其他编码细节大多在 vol.2 手册中。 像 https://www.felixcloutier.com/x86/和 https://c9x.me/x86/这样的网站是从英特尔的vol.2 PDF中抓取的,但只包括每个指令的条目,而不是告诉您适用于每个指令的基础知识或如何阅读条目详细信息的介绍内容。 另请参阅如何读取英特尔操作码表示法

请参阅英特尔® 64 和 IA-32 架构软件开发人员手册第 2 卷:指令集参考,A-Z,可在英特尔网站上以多种格式获得。转到关于CMP的页面,找到带有CMP r/m32, imm8的行。这将操作码列83 /7 ib.转向表 2-2. 带有 ModR/M 字节的 32 位寻址形式。选择[ebp]+disp8行和/digit 7列。单元格告诉您 ModR/M 字节是7d。您需要附加以 8 位签名fc-4位移和您的直接操作数。因此,完整的指令83 7d fc 02。在 64 位模式下,如果使用rbp则编码相同。如果要保留ebp则需要使用地址大小覆盖前缀67

最新更新