对于MOV moffs32,在64位模式下对地址进行符号或零扩展?

时间:2016-06-06 19:55:24

标签: assembly x86 64-bit x86-64 memory-address

让我们在 67A1FFFFFFFF 中以 {{1}} 编码 {{1}} 指令 {{1}} (切换有效地址大小)通过67前缀从默认64到32位)。

英特尔的enter image description here(doc编号:325383-057US,自2015年12月起) 2A 2-11说:

  

2.2.1.3位移
  在64位模式下寻址使用现有的32位ModR / M和SIB编码。 ModR / M和SIB大小不会更改。他们   保持8位或32位,并符号扩展为64位。

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

  

2.2.1.6 RIP相对寻址

     

RIP相对寻址由64位模式启用,而不是由64位地址大小启用。使用了   地址大小前缀不会禁用RIP相对寻址。该   地址大小前缀的效果是截断和零扩展   计算有效地址为32位。

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

从EAX加载的地址是什么,A)FFFFFFFFFFFFFFFF或B)00000000FFFFFFFF?

1 个答案:

答案 0 :(得分:2)

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

英特尔的x86手册vol.1说:

  

在IA-32e模式下,所有16位和 32位地址计算都是零扩展,以形成64位地址。首先将地址计算截断为当前模式的有效地址大小(64位模式或兼容模式),由任何地址大小前缀覆盖。然后将结果零扩展到完整的64位地址宽度。 [...]在64位模式下生成的32位地址只能访问64位模式有效地址的低4 GB。

这适用于特殊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 的长度(与ModR不同) 64位模式下的/ M寻址模式,始终为disp0 / 8/32)。这实际上是causes LCP stalls on Skylake, according to my testing,对于a32 mov eax, [abs buf](NASM选择对该情况使用moffs编码,因为指定了a32覆盖,它比ModR / M + disp32短)

无论如何,这意味着将其解析为mov eax, [0xFFFFFFFF]是错误的(至少在NASM语法中),因为它会组合成一个执行不同操作的指令。

将汇总回该机器代码的正确YASM / NASM syntax

a32 mov eax, [0xFFFFFFFF]

NASM也接受mov eax, [a32 0xFFFFFFFF],但YASM不接受。

GNU as也提供了表达方式(不使用.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编码,拒绝使用AL / AX / EAX / RAX以外的寄存器进行汇编。 a32 mov [0xfffffff], cl汇编为67 88 0c 25 ff ff ff 0f addr32 mov BYTE PTR ds:0xfffffff,clmov r/m8, r8的ModR / M + disp32编码)。

您可以编写mov eax, [qword 0xffff...]来获取moffs64编码,但无法进行32位moffs编码。

Agner Fog的objconv反汇编程序弄错了(从上面的块中拆解了使用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也会错误地反汇编,代码甚至不能以相同的方式工作

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

如果它不会使用mov eax, [qword 0xffffffff]关键字,我会期望像[{1}}这样的反汇编。这将组装成64位moff,引用与原始地址相同的地址,但更长。在向AMD64之前已经存在的ndisasm添加AMD64支持时,可能会忽略这一点。