在MASM中,我总是插入独立的break指令
00007ff7`63141120 cc int 3
但是,将该指令替换为MSVC DebugBreak函数会生成
KERNELBASE!DebugBreak:
00007ff8`6b159b90 6690 xchg ax,ax
00007ff8`6b159b92 cc int 3
00007ff8`6b159b93 c3 ret
我很惊讶地看到xchg指令在break指令之前
xchg ax,ax
如另一篇S.O.文章所述:
实际上,xchg ax,ax就是MS反汇编“66 90”的方法。66是操作数大小覆盖,所以它应该是在ax而不是eax上操作的。然而,CPU仍然将其作为nop执行。这里使用66前缀使指令的大小为两个字节,通常是为了对齐。
MSVC与大多数编译器一样,将函数与16字节边界对齐。
问题xchg指令的用途是什么?
1条答案
按热度按时间x7rlezfr1#
MSVC在函数开始的任何单字节指令之前生成2字节nop(除了空函数1中的
ret
)。我已经尝试了__halt
、_enable
、_disable
内部函数,并看到了相同的效果。显然,它是用于修补的。
/hotpatch
选项为x86提供了相同的更改,并且/hotpatch
选项在x64上无法识别。根据/hotpatch
文档,这是预期行为(强调我):因为在ARM架构上指令总是两个字节或更大,而且因为x64编译总是被视为指定了/hotpatch,所以在为这些目标编译时不必指定/hotpatch;
因此,热修补支持对于x64是无条件的,其结果可以在
DebugBreak
实现中看到。参见此处:https://godbolt.org/z/1G737cErf
请参阅此帖子了解热修补需要它的原因:Why do Windows functions all begin with a pointless MOV EDI, EDI instruction?。看起来当前的热修补足够聪明,可以使用任何两个字节或更多的指令,而不仅仅是
MOV EDI, EDI
,但它仍然不能使用单字节指令,因为两个字节的向后跳转可以在指令指针指向第二个指令的确切时刻写入。1正如注解中所讨论的,空函数有三个字节的
ret 0
,尽管在MSVC汇编输出中并不明显,因为它在那里仅表示为ret
)