MOV moffs32 的 64 位模式下地址的符号或零扩展?

Sign or Zero Extension of address in 64bit mode for MOV moffs32?

让我们在 64 位模式 中将指令 MOV EAX,[0xFFFFFFFF] 编码为 67A1FFFFFFFF(有效地址大小由 67 前缀从默认的 64 位切换为 32 位)。

英特尔的 instruction reference manual(文档订单号:325383-057US,自 2015 年 12 月起)第 Vol. 2A 2-11 说:

2.2.1.3 Displacement
Addressing in 64-bit mode uses existing 32-bit ModR/M and SIB encodings. The ModR/M and SIB sizes do not change. They remain 8 bits or 32 bits and are sign-extended to 64 bits.

这表明 32 位位移应该 符号扩展 但我不确定这是否也涉及特殊的 moffs 寻址模式。 在下一页英特尔说:

2.2.1.6 RIP-Relative Addressing

RIP-relative addressing is enabled by 64-bit mode, not by a 64-bit address-size. The use of the address-size prefix does not disable RIP-relative addressing. The effect of the address-size prefix is to truncate and zero-extend the computed effective address to 32 bits.

这表明在相对寻址模式下,disp32 被符号扩展为 64 位,添加到 RIP,然后被截断并零扩展。 Hover 我不确定相同的规则是否适用于绝对寻址模式,这是 MOV moffs 操作的情况。

将从哪个地址加载 EAX,A) FFFFFFFFFFFFFFFF 或 B) 00000000FFFFFFFF ?

67 A1 FFFFFFFF 没有使用 disp32 寻址模式,因此文档的 Mod/RM 部分不适用。

Intel 的 x86 手册 vol.1 说:

All 16-bit and 32-bit address calculations are zero-extended in IA-32e mode to form 64-bit addresses. Address calculations are first truncated to the effective address size of the current mode (64-bit mode or compatibility mode), as overridden by any address-size prefix. The result is then zero-extended to the full 64-bit address width. [...] A 32-bit address generated in 64-bit mode can access only the low 4 GBytes of the 64-bit mode effective addresses.

这适用于特殊 moffs absolute addressing forms of mov 以及常规 ModR/M 寻址模式,例如 mov eax, [edi] 而不是 mov eax, [rdi]

请注意,moffs8/16/32/64 命名显示的是操作数大小,而不是地址大小(例如 mov al, moffs8)。在 64 位模式下,32 位地址大小 moffs 没有不同的术语。

地址大小前缀将 A1 操作码从 64 位立即地址更改为 32 位,即它更改了 rest 的长度指令(与 64 位模式中的 ModR/M 寻址模式不同,后者始终为 disp0/8/32)。这实际上是 causes LCP stalls on Skylake, according to my testing,对于 a32 mov eax, [abs buf](NASM 选择在这种情况下使用 moffs 编码,因为指定了 a32 覆盖,它比 ModR/M 短 + disp32)

另请参阅 ,了解有关 LCP 停顿的更多详细信息,包括 67h 地址大小前缀。


无论如何,这意味着将其反汇编为 mov eax, [0xFFFFFFFF] 是错误的(至少在 NASM 语法中),因为 assemble 返回到执行不同操作的指令。

将 assemble 返回到该机器代码的正确 YASM/NASM syntax

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

使用NASM/YASM,无法强制 32 位MOFFS 编码以拒绝assemble 以外的寄存器[=] 102=]。 a32 mov [0xfffffff], cl assembles 到 67 88 0c 25 ff ff ff 0f addr32 mov BYTE PTR ds:0xfffffff,cl(ModR/M + mov r/m8, r8 的 disp32 编码)。

你可以写 mov eax, [qword 0xffff...] 来获得 moffs64 编码,但是没有办法要求 32 位 moffs 编码。


Agner Fog 的 objconv disassembler 弄错了(从上面的块中反汇编使用 GNU as 生成的机器代码)。 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

ndisasm -b64 也发现 assemble 不正确,代码甚至无法以相同的方式工作:

00000073  A1FFFF7F00000000  mov eax,[qword 0x7fffff]
         -00
...
00000090  67A1FFFFFFFF      mov eax,[0xffffffff]

如果不使用 a32 关键字,我会期待像 mov eax, [qword 0xffffffff] 这样的反汇编。这将 assemble 到一个 64 位 moffs,它引用与原始地址相同的地址,但更长。可能是在为 ndisasm 添加 AMD64 支持时忽略了这一点,ndisasm 在 AMD64 之前就已经存在了。