指令编码中的 REX 前缀是什么?
What is REX prefix in Instruction Encoding?
在指令编码中,我们有一个可选字段 REX 前缀,它是这样拆分的:
Field contains
0100 – fixed constant value
W – 1 when using 64-bit data
R – expands the Reg field to 4 bit
X – expands Index field to 4 bit
B – expands the R/M field or Base to 4 bit
我问我的教授“当使用 64 位数据时”的真正含义,每次我得到不同的答案:
当使用像 r9、r10、r9d 等添加到 x64 的新寄存器时...
当使用 64 位大小的寄存器时,如 rax、rbx、r9 等...
当指令使用(read/write)内存中的64位数据时。
我真的很困惑,正确答案是什么(可能是 none 个,因为我不再相信我的教授了)。
所有三种情况都需要一个 REX 前缀:
- #2 和 #3 需要在 REX 中设置 W 位,因为它们“使用 64 位数据”(操作数大小)
- #1 需要设置其他位之一,具体取决于哪些寄存器是“高”寄存器
例如,addq %rax, %rbx
、addq %rax, long_var
和 addq 3, long_var
都需要设置 W 位的 REX 前缀,因为它们都有 64 位操作数大小。 addq %rax, %r9
因为使用了“新”寄存器,所以同时需要 W 和 B。
然而,addl %eax, %r9d
不需要 W 位,因为它的操作数大小是 32 位。它确实仍然需要B位来使用新寄存器r9d
,因此仍然需要前缀字节,但不是因为“使用64位数据”。 (案例一)
可以同时处理多个案例。 addq (%r8, %r9, 8), %r10
需要在 REX 前缀中设置所有四个位,包括 W
.
纯案例 #3 的示例是 movq 3, 0x1000
将 8 字节整数 123(从 32 位符号扩展)存储到绝对地址 0x1000
的内存中,没有寄存器全部涉及,但 64 位操作数大小只能使用 W=1 的 REX 前缀设置。更通常情况下,您 movq 3, symbol(%rip)
引用静态存储,但仍然不包括任何 通用 整数寄存器,或 movq 3, 8(%rsp)
涉及寻址模式的地方是寄存器,但实际上源和目标都不是 寄存器。
另见 https://wiki.osdev.org/X86-64_Instruction_Encoding#REX_prefix
一些操作码默认为 64 位操作数大小并且不需要 REX 前缀;例如 pushq
汇编为 6a 01
。 (并且 pushl
不可编码;REX.W=0 无法将其覆盖回 32 位。只有 66
操作数大小前缀可以将其更改为 16 位,因此pushw
是可编码的。)这主要是堆栈操作,包括 call / ret 和间接调用 / 跳转,例如 ff 17
callq *(%rdi)
从内存加载新的 64 位 RIP,以及推送 64 位 return 地址。
在指令编码中,我们有一个可选字段 REX 前缀,它是这样拆分的:
Field contains
0100 – fixed constant value
W – 1 when using 64-bit data
R – expands the Reg field to 4 bit
X – expands Index field to 4 bit
B – expands the R/M field or Base to 4 bit
我问我的教授“当使用 64 位数据时”的真正含义,每次我得到不同的答案:
当使用像 r9、r10、r9d 等添加到 x64 的新寄存器时...
当使用 64 位大小的寄存器时,如 rax、rbx、r9 等...
当指令使用(read/write)内存中的64位数据时。
我真的很困惑,正确答案是什么(可能是 none 个,因为我不再相信我的教授了)。
所有三种情况都需要一个 REX 前缀:
- #2 和 #3 需要在 REX 中设置 W 位,因为它们“使用 64 位数据”(操作数大小)
- #1 需要设置其他位之一,具体取决于哪些寄存器是“高”寄存器
例如,addq %rax, %rbx
、addq %rax, long_var
和 addq 3, long_var
都需要设置 W 位的 REX 前缀,因为它们都有 64 位操作数大小。 addq %rax, %r9
因为使用了“新”寄存器,所以同时需要 W 和 B。
然而,addl %eax, %r9d
不需要 W 位,因为它的操作数大小是 32 位。它确实仍然需要B位来使用新寄存器r9d
,因此仍然需要前缀字节,但不是因为“使用64位数据”。 (案例一)
可以同时处理多个案例。 addq (%r8, %r9, 8), %r10
需要在 REX 前缀中设置所有四个位,包括 W
.
纯案例 #3 的示例是 movq 3, 0x1000
将 8 字节整数 123(从 32 位符号扩展)存储到绝对地址 0x1000
的内存中,没有寄存器全部涉及,但 64 位操作数大小只能使用 W=1 的 REX 前缀设置。更通常情况下,您 movq 3, symbol(%rip)
引用静态存储,但仍然不包括任何 通用 整数寄存器,或 movq 3, 8(%rsp)
涉及寻址模式的地方是寄存器,但实际上源和目标都不是 寄存器。
另见 https://wiki.osdev.org/X86-64_Instruction_Encoding#REX_prefix
一些操作码默认为 64 位操作数大小并且不需要 REX 前缀;例如 pushq
汇编为 6a 01
。 (并且 pushl
不可编码;REX.W=0 无法将其覆盖回 32 位。只有 66
操作数大小前缀可以将其更改为 16 位,因此pushw
是可编码的。)这主要是堆栈操作,包括 call / ret 和间接调用 / 跳转,例如 ff 17
callq *(%rdi)
从内存加载新的 64 位 RIP,以及推送 64 位 return 地址。