x86机器中NOPL的作用是什么?感觉好像什么都没做,但为什么总是在汇编代码里呢?
3 回答
NOP
是一个单字节的“无操作”操作,字面意思是“无操作”。NOPW、NOPL 等是等效的无所事事,但占用 word 和 long-sized 字节。
例如
NOP // 1byte opcode
NOP // 1byte opcode
相当于做
NOPW // 2byte opcode.
它们对于填充内容非常方便,因此代码序列从特定的内存边界开始,占用了几个字节的指令空间,但实际上并没有做任何事情。
NOP 对 CPU 的唯一影响是将IP
/增加EIP
1。NOPx 等效项将增加 2、4 等...
根据John Fremlin 的博客:AMD64 上 NOP 的操作数,nopw
等nopl
是gas
语法,而不是 AT&T 语法。
下面是gas
为不同nop
的gas
源代码生成的指令编码,指令长度为 3 到 15 个字节。请注意,有些与英特尔推荐的nop
形式相同(见下文),但不是全部。特别是,在long 中,以不同nop
的形式gas
使用多个(最多 5 个)连续0x66
的操作数前缀nop
,而 Intel 推荐的形式从不在任何一条推荐的指令中nop
使用多个操作数前缀。0x66
nop
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 在自修改代码、调试和逆向工程中也非常有用。
实际上,在需要修补代码时,汇编代码中会使用 NOP。
因为新指令的大小可能与旧指令的大小不同,所以需要填充。
填充指令的作用应该与 NOP 相同,尽管它可能占用几个字节。
我们插入更复杂的指令(如 66 90)而不是几个 NOP 的原因是,一条指令通常比几个 NOP 执行得更快。