MOV Moffs32在64位模式下地址的符号扩展还是零扩展?

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加载到哪个地址?

Pet*_*des 5

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)。moffs64 位模式下的 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 之前就已经存在。