41

x86机器中NOPL的作用是什么?感觉好像什么都没做,但为什么总是在汇编代码里呢?

4

3 回答 3

48

NOP是一个单字节的“无操作”操作,字面意思是“无操作”。NOPW、NOPL 等是等效的无所事事,但占用 word 和 long-sized 字节。

例如

NOP // 1byte opcode
NOP // 1byte opcode

相当于做

NOPW // 2byte opcode.

它们对于填充内容非常方便,因此代码序列从特定的内存边界开始,占用了几个字节的指令空间,但实际上并没有做任何事情。

NOP 对 CPU 的唯一影响是将IP/增加EIP1。NOPx 等效项将增加 2、4 等...

于 2012-09-24T05:49:54.063 回答
44

根据John Fremlin 的博客:AMD64 上 NOP 的操作数nopwnoplgas语法,而不是 AT&T 语法。

下面是gas为不同nopgas源代码生成的指令编码,指令长度为 3 到 15 个字节。请注意,有些与英特尔推荐的nop形式相同(见下文),但不是全部。特别是,在long 中,以不同nop的形式gas使用多个(最多 5 个)连续0x66的操作数前缀nop,而 Intel 推荐的形式从不在任何一条推荐的指令中nop使用多个操作数前缀。0x66nop

nop来自gas 2.30源代码的编码(重新格式化以提高可读性):

/* nopl (%[re]ax) */
static const unsigned char alt_3[] = {0x0f,0x1f,0x00};
/* nopl 0(%[re]ax) */
static const unsigned char alt_4[] = {0x0f,0x1f,0x40,0x00};
/* nopl 0(%[re]ax,%[re]ax,1) */
static const unsigned char alt_5[] = {0x0f,0x1f,0x44,0x00,0x00};
/* nopw 0(%[re]ax,%[re]ax,1) */
static const unsigned char alt_6[] = {0x66,0x0f,0x1f,0x44,0x00,0x00};
/* nopl 0L(%[re]ax) */
static const unsigned char alt_7[] = {0x0f,0x1f,0x80,0x00,0x00,0x00,0x00};
/* nopl 0L(%[re]ax,%[re]ax,1) */
static const unsigned char alt_8[] = {0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
/* nopw 0L(%[re]ax,%[re]ax,1) */
static const unsigned char alt_9[] =
  {0x66,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
/* nopw %cs:0L(%[re]ax,%[re]ax,1) */
static const unsigned char alt_10[] =
  {0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
static const unsigned char *const alt_patt[] = {
  f32_1, f32_2, alt_3, alt_4, alt_5, alt_6, alt_7, alt_8,
  alt_9, alt_10
};

英特尔使用不同的语法,并且nop' 可用于从 1 到 9 字节的所有指令长度。有几个不同nop的 ',因为所有nop' 都长于两个字节,都接受 1 个操作数。一字节nop( 0x90) 与 同义xchg (e)ax,(e)ax

英特尔® 64 和 IA-32 架构软件开发人员手册,第 2 卷(2A、2B 和 2C):指令集参考,AZ,第 4 章:指令集参考,MZ列出nop了针对不同指令长度的推荐形式:

Table 4-12. Recommended Multi-Byte Sequence of NOP Instruction

Length   Assembly                                   Byte Sequence
2 bytes  66 NOP                                     66 90H
3 bytes  NOP DWORD ptr [EAX]                        0F 1F 00H
4 bytes  NOP DWORD ptr [EAX + 00H]                  0F 1F 40 00H
5 bytes  NOP DWORD ptr [EAX + EAX*1 + 00H]          0F 1F 44 00 00H
6 bytes  66 NOP DWORD ptr [EAX + EAX*1 + 00H]       66 0F 1F 44 00 00H
7 bytes  NOP DWORD ptr [EAX + 00000000H]            0F 1F 80 00 00 00 00H
8 bytes  NOP DWORD ptr [EAX + EAX*1 + 00000000H]    0F 1F 84 00 00 00 00 00H
9 bytes  66 NOP DWORD ptr [EAX + EAX*1 + 00000000H] 66 0F 1F 84 00 00 00 00 00H

所以除了nop英特尔推荐的这些,还有很多其他nop的。正如 Marc B 在他的回答中提到的那样,除了将指令与特定的内存边界对齐之外,nop's 在自修改代码、调试和逆向工程中也非常有用。

于 2012-09-24T11:23:34.737 回答
10

实际上,在需要修补代码时,汇编代码中会使用 NOP。

因为新指令的大小可能与旧指令的大小不同,所以需要填充。

填充指令的作用应该与 NOP 相同,尽管它可能占用几个字节。

我们插入更复杂的指令(如 66 90)而不是几个 NOP 的原因是,一条指令通常比几个 NOP 执行得更快。

于 2013-10-13T05:08:38.507 回答