NOPL在x86系统中做了什么?

29 x86 assembly

在x86机器中NOPL的功能是什么?感觉它没有做任何事情,但为什么总是在汇编代码中?

Mar*_*c B 35

NOP是一个单字节的"无所事事"操作,完全是"无操作".NOPW,NOPL等等等等,但是占用字和长字节.

例如

NOP // 1byte opcode
NOP // 1byte opcode
Run Code Online (Sandbox Code Playgroud)

相当于做

NOPW // 2byte opcode.
Run Code Online (Sandbox Code Playgroud)

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

NOP对CPU的唯一影响是增加IP/ EIP减1.NOPx等价物将增加2,4等...

  • @Jack闻起来像AT&T语法给我 (5认同)
  • 请注意,您不能“仅仅”选择一个明显的幂等操作。`XCHG AX,AX` 和 `XCHG BX,BX` _不_相同。第一个是官方的“NOP”并且_不_导致数据依赖。 (5认同)
  • 我从来没有听说过x86指令集中的操作NOPW和NOPL ..它们也没有出现在英特尔指令集参考中:)也许你混淆了不同的架构 (2认同)
  • @Jack:在X86上,NOP只是"XCHG AX,AX"的别名,因为这是一个单周期指令,除了递增IP之外没有任何影响.有许多双字节指令可以用于此目的,包括那些将寄存器移动到自身的指令(表示MOV AX的操作码,某些上下文中的AX表示MOV EAX,其他情况下表示EAX,但无论如何都是唯一的影响将是两个IP). (2认同)

nrz*_*nrz 34

约翰Fremlin的博客:操作数对AMD64 NOP,nopw,nopl等都是gas语法,而不是AT&T语法.

下面是由生成的指令编码gas为不同的nop从的gas从3到15个字节为指令的长度.请注意,有些与英特尔推荐的nop表格相同(见下文),但并非全部.特别是,在较长nopgas使用多个(最多5个)连续的0x66在不同的操作数前缀nop的形式,而英特尔的推荐nop形式从来没有使用一个以上的0x66在任何单一的推荐操作数前缀nop指令.

nop从编码的源代码用于气体 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
};
Run Code Online (Sandbox Code Playgroud)

英特尔使用不同的语法,并且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
Run Code Online (Sandbox Code Playgroud)

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

  • 请注意,在amd64上,`nop`不再是`xchg eax,eax`的同义词.`nop`不会将`eax`的前32位清零,但是`xchg eax,eax`会. (7认同)
  • 事实上,如果你为 x86-64 汇编 `xchg eax,eax`,它必须使用 2 字节操作码+modrm 编码([87 C0](https://www.felixcloutier.com/x86/xchg)),因为“0x90”对 RAX 没有副作用。但是 `xchg eax, ecx` 仍然可以汇编为 `0x91` - 只有 0x90 具体被 `nop` https://www.felixcloutier.com/x86/nop 接管。但是`xchg rax,rax`和`xchg ax,ax`仍然可以使用REX.W或66 90,因为它们没有架构效果。https://godbolt.org/z/xn5nKnWT6 (2认同)

小智 6

实际上,当代码需要修补时,NOP将用在汇编代码中.

由于新指令的大小可能与旧指令的大小不同,因此需要填充.

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

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