47

what is the function of NOPL in x86 machine? It feels like it doesn't do anything, but why is it always in the assembly code?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847

3 Answers3

51

NOP is a one-byte "do nothing" operation, quite literally "no operation". NOPW, NOPL, etc.. are the equivalent do-nothings, but take up word and long-sized bytes.

e.g.

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

is equivalent to doing

NOPW // 2byte opcode.

They're very handy for padding things out so a code sequence begins on a particular memory boundary, by taking up a few bytes of instruction space, yet not actually doing anything.

NOP's sole effect on the CPU is to increment IP/EIP by 1. The NOPx equivalents will do so by 2, 4, etc...

Marc B
  • 356,200
  • 43
  • 426
  • 500
  • 4
    I never heard about the operation NOPW and NOPL in the instruction set of an x86.. nor they appear in the Intel Instruction Set Reference :) Maybe you're mixing up different architectures – Jack Sep 24 '12 at 08:20
  • 8
    @Jack smells like AT&T syntax to me – harold Sep 24 '12 at 09:31
  • @harold Don't know.. gcc complains if I use nopw/nopl instead of nop – Jack Sep 24 '12 at 10:00
  • @Jack did you give it an operand? – harold Sep 24 '12 at 10:24
  • @JaggedO'Neill ok that's weird, and unfortunately I don't really have a clue where it's documented (if anywhere..) – harold Sep 25 '12 at 09:39
  • 3
    @Jack: On the X86, NOP is merely an alias for "XCHG AX,AX" since that's a single-cycle instruction with no effect beyond incrementing IP. There are a number of two-byte instructions that could serve that purpose, including those that would move a register to itself (the opcode which represents MOV AX,AX in some contexts would represent MOV EAX,EAX in others, but in either case the sole effect would be to IP by two). – supercat Jul 30 '15 at 18:33
  • 6
    Be aware that you can't "just" pick an apparently idempotent operation. `XCHG AX,AX` and `XCHG BX,BX` are _not_ identical. The first is the official `NOP` and does _not_ cause a data dependency. – MSalters May 17 '16 at 15:08
  • U would need these instructions not only for padding stuff. When u need to implement a timer value that waits EXACTLY 1 second f.e. you will need to consider clockspeed and lenghts of the opearations that form your "shell"(usually a loop of some kind), and then fill the rest with operations that exactly add up to reach to 1 second (that is without interupts ofc). The nop operations comes in handy here – clockw0rk Sep 17 '19 at 20:03
  • @Jack "Intel did not document the NOPL instruction for the i686, yet all their 686 processors happened to implement it" – Knu Jun 15 '21 at 23:26
46

According to John Fremlin's blog: Operands to NOP on AMD64, nopw, nopl etc. are gas syntax, not AT&T syntax.

Below are instruction encodings generated by gas for different nop's from gas source for instruction lengths from 3 to 15 bytes. Note that some are the same as Intel's recommended nop forms (see below), but not all. In particular, in longer nop's gas uses multiple (up to 5) consecutive 0x66 operand prefixes in different nop forms, whereas Intel's recommended nop forms never use more than one 0x66 operand prefix in any single recommended nop instruction.

nop encodings from the source code for gas 2.30 (reformatted for readability):

/* 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
};

Intel uses different syntax, and there are nop's available for all instruction lengths from 1 to 9 bytes. There are several different nop's, as all nop's longer than two bytes accept 1 operand. One-byte nop (0x90) is synonymous with xchg (e)ax,(e)ax.

Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 2 (2A, 2B & 2C): Instruction Set Reference, A-Z, CHAPTER 4: INSTRUCTION SET REFERENCE, M-Z lists recommended nop forms for different instructions lengths:

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

So in addition to these nop's recommended by Intel, there are many other nop's too. In addition to aligning an instruction to a specific memory boundary, as Marc B mentions in his answer, nop's are also very useful in self-modifying code, debugging and reverse-engineering.

Cristian Ciupitu
  • 20,270
  • 7
  • 50
  • 76
nrz
  • 10,435
  • 4
  • 39
  • 71
  • 12
    Notice that on amd64, `nop` is no longer synonymous with `xchg eax,eax`. `nop` does not zero out the top 32 bits of `eax`, but `xchg eax,eax` does. – fuz Jan 04 '15 at 16:40
  • 2
    Indeed, if you assemble `xchg eax,eax` for x86-64, it has to use the 2-byte opcode+modrm encoding ([87 C0](https://www.felixcloutier.com/x86/xchg)), because `0x90` doesn't have the side-effect on RAX. But `xchg eax, ecx` can still assemble to `0x91` - only 0x90 specifically is taken over by `nop` https://www.felixcloutier.com/x86/nop. But `xchg rax,rax` and `xchg ax,ax` can still use REX.W or 66 90 because they have no architectural effect. https://godbolt.org/z/xn5nKnWT6 – Peter Cordes May 17 '21 at 18:27
10

Actually, NOP will be used in the assembly code when the code need to be patched.

Because the size of the new instructions may be different to that of the old ones, padding is needed.

The padding instruction should act as the same as NOP, although it may occupy several bytes.

The reason that we insert a more complex instruction, like 66 90, instead of several NOPs, is one instruction generally executes more quickly than several NOPs.

Adel Khayata
  • 2,717
  • 10
  • 28
  • 46
bfnzd
  • 126
  • 1
  • 2
  • I do not know how many people patch their code in this way, even in 2013... That was really old school, like 1980s or so. Today, it's really just for patching so things are aligned. – Alexis Wilke Mar 05 '23 at 20:17