【发布时间】:2019-06-28 09:21:04
【问题描述】:
GAS 为以下指令提供以下编码:
push rbp # 0x55
push rbx # 0x53
push r12 # 0x41 0x54
push r13 # 0x41 0x55
来自AMD64 spec(第313页):
PUSH reg64 50 +rq将 64 位寄存器的上下文推入堆栈。
由于 rbp 和 rbx 的偏移量分别为 5 和 3,因此前两个编码是有意义的。不过,我不明白最后两种编码是怎么回事。
我知道0x40-0x4f 是一个 REX 前缀,0x41 设置了REX.B 位(根据此external reference,它是MODRM.rm 或SIB.base 的MSB 的扩展)。规范提到要访问所有 16 个 GPR,您需要使用 REX,但目前还不清楚截止点在哪里。
从查阅 MODRM 和 SIB 的文档来看,我认为没有使用 SIB,因为它的目的是使用基数 + 偏移寄存器进行索引(虽然老实说,我无法真正说出您如何区分 MODRM 和SIB 仅给出编码)。
所以,我怀疑这里使用了 MODRM。目前只考虑push 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 所以这表明那是源寄存器(r12 = 偏移量 12)。如果忽略external (unofficial) reference中的所有表,REX.R + MODRM.mod + MODRM.reg = 0b00101 = 5,这是push指令库0x50的第一个半字节。
所以,我认为我已经倒退了,但我不明白我将如何获得像 0x41 0x54 这样的编码。从AMD reference,图1-10(第54页)有一个脚注,如果MODRM.mod = 01 or 10,那么这个字节“包括一个由指令位移字段指定的偏移量”。这也许会暗示为什么我们有指令偏移REX.R + MODRM.mod + MODRM.reg = 0b00101 = 5。但是,为什么MODRM.mod 是指令偏移量的一部分?如果必须包含它,那么采用这种偏移形式的指令仅限于前缀 0b01 或 0x10。这不可能吧?
tl;博士
- REX 编码对于
push之类的指令实际上是如何工作的? - 需要 REX 前缀的指令偏移截止值是多少? (有文件证明我不能像
push rbp或push rbx那样为push r12执行 0x50 + 12 吗?) - 为什么
MODRM.mod包含在指令库的前缀中? (或者这完全正确吗?) - 这对于像
pop这样的类似指令是否一致? (我怎么知道哪些指令支持这一点?它是否适用于所有具有XX +xx形式的操作码的指令?) - 这在官方手册中的什么地方有记载?
- 如何区分 REX 前缀后跟 MODRM 还是 SIB 字节?
- 是否有更好的文档可以分步列出这些过程,而不是让您在几页之间从一个表跳到另一个表?
【问题讨论】:
-
你不能做
+ 12,因为rm字段只有3位,所以它的最大值是7。REX中的B位是第四位。你可以认为它的意思是“给 rm 加 8”。 -
@RaymondChen 对,我明白为什么对于 MODRM,您需要 REX 中的额外 B 位。我不明白为什么你不能只做
0x50 + 12(推+r12没有REX的偏移量),因为第二个半字节足以存储所有的寄存器偏移量。另外,当规范只谈到MODRM.reg“用于扩展操作编码”时,为什么指令偏移REX.R + MODRM.mod + MODRM.reg(第54页,“ModRM.reg(Bits[5:3])。”)? -
您的所有问题都可以通过仔细阅读手册(如果 AMD 过于含糊,请尝试英特尔)或阅读 Raymond 的评论来回答。 ModRM 对于 16 个寄存器来说太小了,REX.B 扩展了它。有些问题似乎暗示你浏览手册太快了,我觉得你,在页面之间跳转很尴尬;尝试在文本编辑器中记笔记或打印表格(如果可以的话):)
-
@BaileyParker 50+r 只有一个字节长,5 位操作码和 3 位寄存器号,所以没有 REX 就无法编码 r12。编码是从 16 位和 32 位 x86 时代发明的,它们只有 8 个寄存器,不像在过渡到 x86-64 时它们会给你另一个位
-
@phuclv 啊!我错误地理解为操作码为 4,寄存器号为 4。这解释了为什么 R8 以下的所有寄存器都不需要 REX。谢谢!
标签: assembly x86-64 intel gnu-assembler machine-code