34

我正在查看我的程序的分解(因为它崩溃了),并注意到很多

xchg    ax, ax

我用谷歌搜索了它,发现它本质上是一个 nop,但是为什么 Visual Studio 做一个 xchg 而不是一个 noop?

该应用程序是一个 C# .NET3.5 64 位应用程序,由 Visual Studio 编译

4

6 回答 6

42

在 x86 上,NOP指令 XCHG AX, AX

2 条助记指令汇编成相同的二进制操作码。(实际上,我想汇编程序可以使用任何xchg一个寄存器,但是据我所知,AX或者EAX是通常用于的寄存器)。nop

xchg ax, ax具有不更改寄存器值和不更改标志的属性(嘿 - 这是一个无操作!)。


编辑(回应 Anon 的评论。):

xchg哦,对了 - 现在我记得指令有几种编码。有些采用一组 mod/r/m 位(如许多 Intel x86 架构指令)来指定源和目标。这些编码占用超过一个字节。还有一种特殊编码,它使用单个字节并与通用寄存器交换(E)AX. 如果指定的寄存器也是(E)AX,那么你有一个单字节 NOP 指令。您还可以使用指令的较大变体指定(E)AX与自身交换xchg

我猜想 MSVC 使用xchgwith的多字节版本(E)AX作为源和目标,当它想要咀嚼超过一个字节而不进行任何操作时 - 它需要与单字节相同的周期数xchg,但使用更多空间。在反汇编中,您不会看到xchg解码为 a的多字节NOP,即使结果相同。

具体来说xchg eax, eax,或者nop可以编码为操作码0x90,或者0x87 0xc0取决于您是否希望它用完 1 个或 2 个字节。Visual Studio 反汇编程序(可能还有其他反汇编程序)会将操作码解码0x90NOP指令,并将操作码解码0x87 0xc0xchg eax, eax.

自从我完成详细的汇编语言工作以来已经有一段时间了,所以我很可能在这里至少有一个错误......

于 2010-01-25T22:13:49.653 回答
10

xchg ax,ax并且nop实际上是相同的指令,它们映射到相同的操作码(0x90 iirc)。没关系,xchg ax,ax No-Op。为什么要使用不执行任何操作的指令浪费额外的操作码编码?

有问题的是为什么你看到两个助记符都被打印出来了。我想这只是你反汇编的一个缺陷,没有二进制差异。

于 2010-01-25T22:14:09.320 回答
7

其实,xchg ax,ax这就是MS拆机“66 90”的方式。66 是操作数大小覆盖,因此它应该在ax而不是eax. 但是,CPU 仍将其作为 nop 执行。此处使用 66 前缀使指令大小为两个字节,通常用于对齐目的。

于 2012-01-09T12:13:07.077 回答
4

MSVC 通常将 NOP 放入已编译的代码中以进行调试构建。这允许“编辑并继续”工作。

于 2010-01-26T02:58:24.443 回答
2

我不知道这是否与问题有关,但许多 Windows 功能都以 MOV EDI、EDI 开头。这也是一个 2 字节的 NOP。两个字节的 NOP 对热补丁代码很有用,因为您可以安全地用短 JMP 替换它。

参考:http: //blogs.msdn.com/b/oldnewthing/archive/2011/09/21/10214405.aspx

于 2015-05-12T11:31:46.770 回答
1

这里真正的问题是;为什么反汇编程序选择将其显示为xchg ax,ax而不是nop

我怀疑这是来自 32 位或 64 位代码,并且(鉴于反汇编程序显示xchg ax,ax而不是xchg eax,eax),有一个操作数大小覆盖前缀旨在使nop稍大(以使用更少的指令实现一定数量的填充);并且前缀的存在混淆了反汇编程序,导致xchg ax,ax(而不是nopor o16 nop)。

于 2019-02-08T00:32:07.273 回答