vit*_*oft 5 64-bit x86 assembly x86-64 memory-address
让我们MOV EAX,[0xFFFFFFFF]
以64位模式将指令编码为67A1FFFFFFFF
(有效地址大小由67前缀从默认的64位切换为32位)。
英特尔指令参考手册(从2015年12月起,文档订购号:325383-057US)在第Vol。2A 2-11说:
2.2.1.3
64位模式下的位移寻址使用现有的32位ModR / M和SIB编码。ModR / M和SIB大小不变。它们保留为8位或32位,并被符号扩展为64位。
这表明应该对32bit位移进行符号扩展,但是我不确定这是否也涉及特殊的moffs寻址模式。英特尔在下一页上说:
2.2.1.6 RIP相对寻址
RIP相对寻址是通过64位模式而不是64位地址大小启用的。地址大小前缀的使用不会禁用RIP相对寻址。地址大小前缀的作用是将计算的有效地址截断并将其零扩展为32位。
这表明在相对寻址模式下,将disp32符号扩展到64位,再添加到RIP,然后截断并进行零扩展。但是,我不确定同一规则是否适用于绝对寻址模式,MOV moffs操作就是这种情况。
从A)FFFFFFFFFFFFFFFF或B)00000000FFFFFFFF将EAX加载到哪个地址?
67 A1 FFFFFFFF
未使用disp32
寻址模式,因此文档的 Mod/RM 部分不适用。
英特尔的 x86 手册 vol.1 说:
所有 16 位和32 位地址计算都在 IA-32e 模式下进行零扩展以形成 64 位地址。地址计算首先被截断为当前模式(64 位模式或兼容模式)的有效地址大小,由任何地址大小前缀覆盖。然后将结果零扩展到完整的 64 位地址宽度。[...] 在 64 位模式下生成的 32 位地址只能访问 64 位模式有效地址的低 4 GB。
这适用于 的特殊moffs
绝对寻址形式mov
以及常规 ModR/M 寻址模式,例如mov eax, [edi]
代替mov eax, [rdi]
。
请注意,moffs8/16/32/64
命名显示的是操作数大小,而不是地址大小(例如mov al, moffs8
)。moffs
64 位模式下的 32 位地址大小没有不同的术语。
address-size 前缀将A1
操作码从 64 位立即地址更改为 32 位,即它更改指令其余部分的长度(与 64 位模式下的 ModR/M 寻址模式不同,后者始终为 disp0/ 8/32)。根据我的测试,这实际上会导致 Skylake 上的 LCP 停顿,因为a32 mov eax, [abs buf]
(NASM 选择在这种情况下使用 moffs 编码,因为a32
指定了覆盖,它比 ModR/M + disp32 短)
另请参阅长度更改前缀 (LCP) 在简单的 x86_64 指令上是否会导致停顿?有关 LCP 停顿的更多详细信息,包括67h
地址大小前缀。
无论如何,这意味着将其反汇编mov eax, [0xFFFFFFFF]
是错误的(至少在 NASM 语法中),因为它会重新组合成一条执行不同操作的指令。
将组装回该机器代码的正确 YASM/ NASM 语法是
a32 mov eax, [0xFFFFFFFF]
NASM 也接受mov eax, [a32 0xFFFFFFFF]
,但 YASM 不接受。
GNUas
还提供了一种表达方式(不使用.byte
):
addr32 mov 0xffffffff,%eax
movl 0x7FFFFFFF, %eax # 8B mod/rm disp32
movl 0xFFFFFFFF, %eax # A1 64bit-moffs32: Older GAS versions may have required the movabs mnemonic to force a moffs encoding
movabs 0x7FFFFF, %eax # A1 64b-moffs32: movabs forces MOFFS
movabs 0xFFFFFFFF, %rax # REX A1 64b-moffs64
movabs 0xFFFF, %ax # 66 A1 64b-moffs64: operand-size prefix
.byte 0x67, 0xa1, 0xff, 0xff, 0xff, 0xff # disassembles to addr32 mov 0xffffffff,%eax
# and that syntax works as assembler input:
addr32 mov 0xffffffff,%eax # 67 A1 FF FF FF FF: 32b-moffs32
Run Code Online (Sandbox Code Playgroud)
使用 NASM/YASM,无法以拒绝与 AL/AX/EAX/RAX 以外的寄存器组装的方式强制32 位 MOFFS 编码。 a32 mov [0xfffffff], cl
组装成67 88 0c 25 ff ff ff 0f addr32 mov BYTE PTR ds:0xfffffff,cl
(ModR/M + disp32 编码mov r/m8, r8
)。
您可以编写mov eax, [qword 0xffff...]
以获取moffs64
编码,但无法要求 32 位 moffs 编码。
Agner Fog 的objconv
反汇编器弄错了(as
从上面的块中反汇编 GNU 生成的机器代码)。 objconv
似乎假定符号扩展。(它将机器代码放在注释中prefixes: opcode, operands
)
; Note: Absolute memory address without relocation
mov eax, dword [abs qword 7FFFFFH] ; 0033 _ A1, 00000000007FFFFF
...
; Note: Absolute memory address without relocation
mov eax, dword [0FFFFFFFFFFFFFFFFH] ; 0056 _ 67: A1, FFFFFFFF
Run Code Online (Sandbox Code Playgroud)
ndisasm -b64
也会错误地反汇编,以编写甚至无法以相同方式工作的代码:
00000073 A1FFFF7F00000000 mov eax,[qword 0x7fffff]
-00
...
00000090 67A1FFFFFFFF mov eax,[0xffffffff]
Run Code Online (Sandbox Code Playgroud)
mov eax, [qword 0xffffffff]
如果不使用a32
关键字,我会期望像 反汇编。这将组装成一个 64 位 moff,它引用与原始地址相同的地址,但更长。可能在向 ndisasm 添加 AMD64 支持时忽略了这一点,该支持在 AMD64 之前就已经存在。