8
votes

While solving a bug I came across a difference between import jump tables of two Win64 DLLs. 64bit version of kernel32.dll uses plain FF25 jmp instruction in its import jump tables. On the other hand 64bit version of advapi32.dll uses 48FF25 which indicates REX.w=1 prefix before the jmp opcode. However, both seem to have 32bit operand specifying a RIP+offset address.

Is there any meaning for REX.w prefix on this specific opcode?

I'm not working with machine code often, so please excuse any factual mistakes.

1
I think you'll find it documented in the Intel docs.David Heffernan
It's not listed in the intel manual and the disassemblers I have also can't make any sense of it. I think rex.w is just ignored.Jester

1 Answers

11
votes

The REX.W prefix is ignored. In 64-bit mode the FF /4 opcode is always has a 64-bit operand (JMP r/m64), so operand size changing prefixes (REX.W, 66) have no effect.

The reason why this REX.W prefix is present is probably to conform with the Microsoft's x64 calling convention's rules regarding unwinding. The jump import stub is effectively a one instruction function, and since exceptions on Windows are asynchronous, they can happen at any time, it's possible for an exception to be generated while executing this function. Microsoft places a number of restrictions on instructions used at the start and end of functions. In particular the function must end with an epilogue that contains only certain instructions. According Kevin Frei's blog on MSDN if the last instruction is a indirect jump it must use the REX.W prefix:

One other note: if the final jmp isn’t an ip-relative jmp, but an indirect jmp, it must be preceded by the REX prefix, to indicate to the OS unwind routines that the jump is headed outside of the function, otherwise, the OS assumes it’s a jump to a different location inside the same function.

The inconsistency between using REX.W may have come about because this rule described above is not entirely consistent with what Microsoft official documentation requires of a final JMP instruction:

Only a subset of jmp statements are allowable in the epilog. These are exclusively of the class of jmps with ModRM memory references where ModRM mod field value 00. The use of jmps in the epilog with ModRM mod field value 01 or 10 is prohibited.

Note that since this would exclude relative JMP instructions which don't use a ModR/M encoding, the most common kind of JMP to end a function with, so I'm inclined to believe it's the official documentation that's in error here.

Other possible reasons for the inconsistency are the Microsoft's unwinder handles import jump stubs specially or that jump stubs without a REX.W prefix are a bug and would cause the program to be terminated in the very unlikely case when an exception happens to occur while they're being executed.