I have stumbled across the "MOV EAX, moffs32" instruction while trying to understand x86-64.
As far as I can tell, this instruction would get encoded (with an moffs32
of 0x12345678
) to just A1 78 56 34 12
.
When I run this through a disassembler, however, I get garbage:
# objdump -D -b binary -mi386 -Mx86-64 -Mintel file.bin
00000000 <.data>:
0: a1 .byte 0xa1
1: 78 56 js 0x59
3: 34 12 xor al,0x12
Same goes for ndisasm
:
# ndisasm -b 64 file.bin
00000000 A1 db 0xa1
00000001 7856 js 0x59
00000003 3412 xor al,0x12
I only get the instruction back if I disassemble it for x86, not x86-64 (ndisasm -b 32
):
00000000 A178563412 mov eax,[0x12345678]
Why is that? The table states that the instruction is valid for 64bit after all.
I feel like I might be missing something fundamental here, but I can't figure out what.
The 32
in moffs32
is only describing the operand-size, not the address-size. The address-size is still 64-bit by default in 64-bit mode.
So it is valid in 64-bit mode, but you need more bytes for the offset, for example:
A1 78 56 34 12 00 00 00 00
That still encodes mov eax, [0x12345678]
, but now in 64-bit mode. This instruction is a rare case of encoding a 64-bit constant address. You can use it with a 32-bit address with an address size override:
67 A1 78 56 34 12
But note that this can be slower due to LCP stalls on Intel CPUs than the 7-byte encoding using the normal 8b 04 25 78 56 34 12
opcode for mov reg, r/m
with the address as a sign-extended disp32
.