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 之前就已经存在了。
让我们在 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)
另请参阅 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 之前就已经存在了。