PUSH的Intel REX编码

Bai*_*ker 5 assembly x86-64 intel gnu-assembler machine-code

GAS对以下说明进行了以下编码:

push rbp    # 0x55
push rbx    # 0x53
push r12    # 0x41 0x54
push r13    # 0x41 0x55
Run Code Online (Sandbox Code Playgroud)

AMD64规范(页313):

PUSH reg64 50 +rq  将64位寄存器的上下文压入堆栈.

由于用于偏移rbprbx5个和3个,分别为第一两种编码有意义.但是我不明白最后两个编码是怎么回事.

据我所知,0x40-0x4f是一个REX前缀和0x41具有REX.B位集(其是一个扩展的MSB MODRM.rmSIB.base,根据该外部参考).规范提到要访问所有16个GPR,你需要使用REX,但目前还不清楚截止点在哪里.

通过查阅MODRM和SIB的文档,我不认为使用了SIB,因为它的目的是使用base + offset寄存器进行索引(虽然说实话,我不能真正告诉你如何区分MODRM和SIB只是给出了编码).

所以,我怀疑这里使用的是MODRM.考虑到当前的push r12(0x41 0x54)(注意到r12有偏移12),我们有:

+----------------+--------------------+
| 0x41           | 0x54               |
+----------------+--------------------+
| REX            | MODRM              |
+--------+-------+-----+--------+-----+
| Prefix | WRXB  | mod | reg    | rm  |
| 0100   | 0001  | 01  | 01   0 | 100 |
+--------+-------+-----+--------+-----+
Run Code Online (Sandbox Code Playgroud)

REX.B + MODRM.rm = 0b1100 = 12所以这表明这是源寄存器(r12=偏移12).如果忽略所有在表的外部(非官方)参考,REX.R + MODRM.mod + MODRM.reg = 0b00101 = 5,这是推指令底座的第一半字节0x50.

所以,我认为我已经向后工作,但我不明白我将如何得到像这样的编码0x41 0x54.从AMD参考,图1-10(页54)有一个脚注,如果MODRM.mod = 01 or 10,则字节"包括由指令位移字段指定的偏移量".这或许暗示了为什么我们有指令偏移REX.R + MODRM.mod + MODRM.reg = 0b00101 = 5.但是,为什么MODRM.mod指令的一部分偏移?如果必须包含,则采用此偏移形式的指令仅限于前缀0b010x10.那可能不对,对吧?

TL;博士

  • REX编码如何实际用于指令push
  • 需要REX前缀的指令偏移截止是多少?(是否记录我不能push r12像我能做的那样做0x50 + 12 push rbppush rbx?)
  • 为什么MODRM.mod包含在指令库的前缀中?(或者这是正确的吗?)
  • 对于类似的指令,这是否一致pop?(我怎么知道哪些指令支持这个?它是否适用于所有具有该形式操作码的指令XX +xx?)
  • 官方手册中记录了哪些内容?
  • 如何区分REX前缀后跟MODRM或SIB字节?
  • 是否有更好的文档,可能会逐步将这些过程放在一起,而不是让你在一个表之间跳转几页?

Pet*_*des 4

这里显然没有 ModRM 字节,因为整个指令是一个字节。如果没有操作码字节,就不可能有 ModRM。

\n\n

push reg/pop reg缩写形式将 3 位寄存器代码嵌入到操作码 byte 中。就是这个50 + rq意思。(与FF /6 push r/m64使用 ModRM 的编码不同;您可以用它对寄存器操作数进行编码以使指令更长,但通常您只会将其用于push qword [rdi]或其他用途)。

\n\n

它的格式与 16 / 32 位相同,这就是为什么 x86-64 需要一个额外的位(来自 REX 前缀)来对具有 4 位代码且前导的“新”/高位寄存器之一进行编码位设置。

\n\n

OSdev 省略了这种情况,只提到了ModRM.rmSIB.base

\n\n
\n\n

Intel 的 vol.2 手册 PDF 记录了编码:

\n\n
\n

3.1.1.1 指令汇总表中的操作码列(不带 VEX 前缀的指令)

\n\n
    \n
  • ...

  • \n
  • +rb , +rw , +rd , +ro \xe2\x80\x94 表示操作码字节的低 3 位用于对寄存器操作数进行编码,不带 \n modR/M 字节。该指令列出了操作码字节对应的十六进制值,低3位为000b。在非 64 位模式下,将从 0 到 7 的寄存器代码添加到操作码字节的十六进制值中。在64位模式下,表示REX.b\n的四位字段,opcode[2:0]字段编码指令的寄存器操作数。\n \xe2\x80\x9c+ro\xe2\x80\x9d 仅适用于 64 位模式。代码见表3-1。

  • \n
\n
\n\n

表 3-1 使用与 ModRM 和 SIB 中的寄存器编号相同的编码方案,这并不奇怪,但英特尔全力以赴,拥有所有操作数大小的所有整数寄存器的完整表。包括AH/BH/CH/DH,因为mov ah, 1可以使用2字节的短格式。

\n\n

我从“四字寄存器(仅限 64 位模式)”列中摘录了相关行:

\n\n
From Intel\'s Table 3-1. Register Codes Associated With +rb, +rw, +rd, +ro\n        reg    REX.B  Reg Field\n        RBX    None    3\n\n        RBP    None    5\n\n        R12    Yes     4\n        R13    Yes     5\n
Run Code Online (Sandbox Code Playgroud)\n\n

有趣的事实:在英特尔的手册中,他们实际上使用了for ,与 32 位模式下的 for 相同50 + rdhttps://www.felixcloutier.com/x86/push50 + roPUSH r64push r32

\n\n
\n\n
\n

这对于像 pop 这样的类似指令是一致的吗?(我如何知道哪些指令支持这一点?它适用于所有具有 XX +xx 形式操作码的指令吗?)

\n
\n\n

是的。 push/pop regmov reg,immxchg eax, r32/xchg rax, r64均使用具有 3 个操作码位的相同编码来对寄存器进行编码。

\n\n

如果我们能够将这 8 个xchg操作码带回一些更有用的东西(例如 64 位模式下更紧凑的 VEX 或 EVEX 前缀),那就太好了,但是当 AMD 对 AMD64 采取保守态度时,这艘船就航行了,主要保持机器代码类似于可能为 32 位模式。不过,他们确实回收了0x4? inc/dec reg操作码以用作 REX 前缀。

\n