DebugBreak() 中中断指令 int 3 之前的 xchg ax,ax 的目的是什么?

What's the purpose of xchg ax,ax prior to the break instruction int 3 in DebugBreak()?

在 MASM 中,我总是插入一个独立的中断指令

00007ff7`63141120 cc              int     3

但是,用 MSVC DebugBreak 函数替换该指令会生成

KERNELBASE!DebugBreak:
00007ff8`6b159b90 6690            xchg    ax,ax
00007ff8`6b159b92 cc              int     3
00007ff8`6b159b93 c3              ret

我很惊讶地看到 break 指令之前的 xchg 指令

xchg    ax,ax

正如另一位 S.O 所说。文章:

Actually, xchg ax,ax is just how MS disassembles "66 90". 66 is the operand size override, so it supposedly operates on ax instead of eax. However, the CPU still executes it as a nop. The 66 prefix is used here to make the instruction two bytes in size, usually for alignment purposes.

MSVC 与大多数编译器一样,将函数对齐到 16 字节边界。

问题那个xchg指令的目的是什么?

MSVC 在函数开头的任何单字节指令之前生成 2 字节 nop(空函数中的 ret 除外)。我尝试了 __halt_enable_disable 内在函数并看到了相同的效果。

看来是用来打补丁的。 /hotpatch 选项为 x86 提供了相同的更改,而 /hotpatch 选项在 x64 上无法识别。根据 /hotpatch 文档,这是预期的行为(强调我的):

Because instructions are always two bytes or larger on the ARM architecture, and because x64 compilation is always treated as if /hotpatch has been specified, you don't have to specify /hotpatch when you compile for these targets;

因此 x64 的热补丁支持是无条件的,其结果见 DebugBreak 实现。

看这里:https://godbolt.org/z/1G737cErf

请参阅此 post,了解热修补为何需要它:Why do Windows functions all begin with a pointless MOV EDI, EDI instruction?。看起来目前的热补丁已经足够聪明,可以使用任何两个字节或更多的指令,而不仅仅是 MOV EDI, EDI,但它仍然不能使用单字节指令,因为两个字节的向后跳转可能会在指令指针的确切时刻写入指向第二条指令。