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位寄存器的上下文压入堆栈.
由于用于偏移rbp和rbx5个和3个,分别为第一两种编码有意义.但是我不明白最后两个编码是怎么回事.
据我所知,0x40-0x4f是一个REX前缀和0x41具有REX.B位集(其是一个扩展的MSB MODRM.rm或SIB.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指令的一部分偏移?如果必须包含,则采用此偏移形式的指令仅限于前缀0b01或0x10.那可能不对,对吧?
TL;博士
push?push r12像我能做的那样做0x50 + 12 push rbp或push rbx?)MODRM.mod包含在指令库的前缀中?(或者这是正确的吗?)pop?(我怎么知道哪些指令支持这个?它是否适用于所有具有该形式操作码的指令XX +xx?)这里显然没有 ModRM 字节,因为整个指令是一个字节。如果没有操作码字节,就不可能有 ModRM。
\n\npush reg/pop reg缩写形式将 3 位寄存器代码嵌入到操作码 byte 中。就是这个50 + rq意思。(与FF /6 push r/m64使用 ModRM 的编码不同;您可以用它对寄存器操作数进行编码以使指令更长,但通常您只会将其用于push qword [rdi]或其他用途)。
它的格式与 16 / 32 位相同,这就是为什么 x86-64 需要一个额外的位(来自 REX 前缀)来对具有 4 位代码且前导的“新”/高位寄存器之一进行编码位设置。
\n\nOSdev 省略了这种情况,只提到了ModRM.rm和SIB.base。
\n\n\n3.1.1.1 指令汇总表中的操作码列(不带 VEX 前缀的指令)
\n\n\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。
表 3-1 使用与 ModRM 和 SIB 中的寄存器编号相同的编码方案,这并不奇怪,但英特尔全力以赴,拥有所有操作数大小的所有整数寄存器的完整表。包括AH/BH/CH/DH,因为mov ah, 1可以使用2字节的短格式。
我从“四字寄存器(仅限 64 位模式)”列中摘录了相关行:
\n\nFrom 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\nRun Code Online (Sandbox Code Playgroud)\n\n有趣的事实:在英特尔的手册中,他们实际上使用了for ,与 32 位模式下的 for 相同50 + rd。https://www.felixcloutier.com/x86/push。50 + roPUSH r64push r32
\n\n\n这对于像 pop 这样的类似指令是一致的吗?(我如何知道哪些指令支持这一点?它适用于所有具有 XX +xx 形式操作码的指令吗?)
\n
是的。 push/pop reg、mov reg,imm和xchg eax, r32/xchg rax, r64均使用具有 3 个操作码位的相同编码来对寄存器进行编码。
如果我们能够将这 8 个xchg操作码带回一些更有用的东西(例如 64 位模式下更紧凑的 VEX 或 EVEX 前缀),那就太好了,但是当 AMD 对 AMD64 采取保守态度时,这艘船就航行了,主要保持机器代码类似于可能为 32 位模式。不过,他们确实回收了0x4? inc/dec reg操作码以用作 REX 前缀。