PUSH的英特尔REX编码

时间:2019-02-04 15:36:00

标签: assembly x86-64 intel gas machine-code

GAS为以下指令提供了以下编码:

push rbp    # 0x55
push rbx    # 0x53
push r12    # 0x41 0x54
push r13    # 0x41 0x55

来自AMD64 spec(第313页):

  

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

由于rbprbx的偏移量分别为5和3,因此前两种编码是有意义的。不过,我不明白最后两种编码的含义。

我知道0x40-0x4f是REX前缀,并且0x41设置了REX.B位(它是MODRM.rm或{{1}的MSB的扩展名) },根据此external reference)。该规范提到要访问所有16个GPR,您都需要使用REX,但不清楚截止位置。

从咨询MODRM和SIB的文档开始,我不认为会使用SIB,因为它的目的是使用基址+偏移量寄存器进行索引(尽管老实说,我无法真正告诉您如何区分MODRM和SIB SIB仅给出了编码)。

所以,我怀疑这里正在使用MODRM。目前仅考虑SIB.basepush r12)(并注意到0x41 0x54已偏移r12),我们有:

12

+----------------+--------------------+ | 0x41 | 0x54 | +----------------+--------------------+ | REX | MODRM | +--------+-------+-----+--------+-----+ | Prefix | WRXB | mod | reg | rm | | 0100 | 0001 | 01 | 01 0 | 100 | +--------+-------+-----+--------+-----+ ,因此这将表明它是源寄存器(REX.B + MODRM.rm = 0b1100 = 12 =偏移量12)。如果您忽略external (unofficial) referencer12中的所有表,这是推送指令库REX.R + MODRM.mod + MODRM.reg = 0b00101 = 5的第一个半字节。

因此,我想我已经将其倒退了,但是我不明白如何获得像0x50这样的编码。在AMD reference中,图1-10(第54页)的脚注是,如果0x41 0x54,则字节“包括由指令位移字段指定的偏移量”。这也许暗示了为什么我们有指令偏移量MODRM.mod = 01 or 10。但是,为什么指令的REX.R + MODRM.mod + MODRM.reg = 0b00101 = 5部分偏移了?如果必须包含它,则采用此偏移量格式的指令仅限于前缀MODRM.mod0b01。那不对吧?

tl; dr

  • REX编码实际上如何用于0x10之类的指令?
  • 需要REX前缀的指令偏移量截止值是多少? (是否已证明我无法像pushpush r12那样为push rbp做0x50 + 12?)
  • 为什么在指令库的前缀中包含push rbx? (或者这完全正确吗?)
  • 这与MODRM.mod之类的类似指令是否一致? (而且我怎么知道哪些指令支持此操作?它对所有具有pop形式的操作码的指令都起作用吗?)
  • 正式手册在哪里记录了?
  • 如何区分REX前缀后面是MODRM字节还是SIB字节?
  • 是否有更好的文档来逐步安排这些过程,而不是使您在一个表到另一个表之间跳转?

1 个答案:

答案 0 :(得分:4)

这里显然没有ModRM字节,因为 entire 指令是一个字节。没有操作码字节就无法拥有ModRM。

push reg / pop reg简短形式将 3位寄存器代码嵌入到操作码字节中。这就是50 + rq的意思。 (与使用ModRM的FF /6 push r/m64编码不同;您可以 对该寄存器进行编码,以使指令更长,但通常仅将其用于push qword [rdi]等等。

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

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


英特尔第二卷手册PDF记录了编码:

  

3.1.1.1 Opcode Column in the Instruction Summary Table (Instructions without VEX Prefix)

     
      
  • ...

  •   
  • + rb + rw + rd + ro -表示为操作码的低3位字节用于对寄存器操作数进行编码,而无需   modR / M字节。该指令列出了相应的十六进制值   低3位的操作码字节的字节为000b。在非64位模式下,   寄存器代码(从0到7)加到的十六进制值   操作码字节。 在64位模式下,指示REX.b的四位字段   并且opcode [2:0]字段对指令的寄存器操作数进行编码。   “ + ro”仅适用于64位模式。有关代码,请参见表3-1。

  •   

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

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

From Intel's Table 3-1. Register Codes Associated With +rb, +rw, +rd, +ro
        reg    REX.B  Reg Field
        RBX    None    3

        RBP    None    5

        R12    Yes     4
        R13    Yes     5

有趣的事实:在50 + rd中,他们实际上使用50 + ro而不是PUSH r64,与32位模式下的push r32相同。 https://www.felixcloutier.com/x86/push


  

对于类似pop这样的说明,这是否一致? (而且我怎么知道哪些指令支持此功能?它对所有操作码形式为XX + xx的指令都起作用吗?)

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

如果我们可以让这8个xchg操作码返回更有用的东西(例如64位模式下的更紧凑的VEX或EVEX前缀),那会很好,但是当AMD保守地将它与AMD64一起播放时,它就大受欢迎了,大多数情况下使机器代码尽可能与32位模式相似。不过,他们确实收回了0x4? inc/dec reg操作码以用作REX前缀。