45

It's not a big secret that x86 (and x86_64) processors have not only the single-byte NOP instruction, but also various types of multi-byte NOP-like instructions.

These are the ones I've managed to find:

Recommended by AMD, ref. AMD Software Optimization Guide for AMD Family 15h Processors, document #47414, section 5.8 "Code Padding with Operand-Size Override and Multibyte NOP", page 94)

90                              NOP1_OVERRIDE_NOP
6690                            NOP2_OVERRIDE_NOP
0f1f00                          NOP3_OVERRIDE_NOP
0f1f4000                        NOP4_OVERRIDE_NOP
0f1f440000                      NOP5_OVERRIDE_NOP
660f1f440000                    NOP6_OVERRIDE_NOP
0f1f8000000000                  NOP7_OVERRIDE_NOP
0f1f840000000000                NOP8_OVERRIDE_NOP
660f1f840000000000              NOP9_OVERRIDE_NOP
66660f1f840000000000            NOP10_OVERRIDE_NOP
6666660f1f840000000000          NOP11_OVERRIDE_NOP

Recommended by Intel, ref. Intel 64 and IA-32 Architectures Software Developer's Manual Volume 2B: Instruction Set Reference, N-Z, section "NOP"

90                              NOP
6690                            66 NOP
0f1f00                          NOP DWORD ptr [EAX]
0f1f4000                        NOP DWORD ptr [EAX + 00H]
0f1f440000                      NOP DWORD ptr [EAX + EAX*1 + 00H]
660f1f440000                    66 NOP DWORD ptr [EAX + EAX*1 + 00H]
0f1f8000000000                  NOP DWORD ptr [EAX + 00000000H]
0f1f840000000000                NOP DWORD ptr [EAX + EAX*1 + 00000000H]
660f1f840000000000              66 NOP DWORD ptr [EAX + EAX*1 + 00000000H]

GNU assembler (in binutils / gas) includes the following patterns:

f32 (older Intel-compatible CPUs up to Pentium):

90                              nop
6690                            xchg %ax,%ax
8d7600                          leal 0(%esi),%esi
8d742600                        leal 0(%esi,1),%esi
908d742600                      nop; leal 0(%esi,1),%esi
8db600000000                    leal 0L(%esi),%esi
8db42600000000                  leal 0L(%esi,1),%esi
908db42600000000                nop; leal 0L(%esi,1),%esi
89f68dbc2700000000              movl %esi,%esi; leal 0L(%edi,1),%edi
8d76008dbc2700000000            leal 0(%esi),%esi; leal 0L(%edi,1),%edi
8d7426008dbc2700000000          leal 0(%esi,1),%esi; leal 0L(%edi,1),%edi
8db6000000008dbf00000000        leal 0L(%esi),%esi; leal 0L(%edi),%edi
8db6000000008dbc2700000000      leal 0L(%esi),%esi; leal 0L(%edi,1),%edi
8db426000000008dbc2700000000    leal 0L(%esi,1),%esi; leal 0L(%edi,1),%edi

alt (for modern CPUs, same for Intel & AMD):

0f1f00                          nopl (%[re]ax)
0f1f4000                        nopl 0(%[re]ax)
0f1f440000                      nopl 0(%[re]ax,%[re]ax,1)
660f1f440000                    nopw 0(%[re]ax,%[re]ax,1)
0f1f8000000000                  nopl 0L(%[re]ax)
0f1f840000000000                nopl 0L(%[re]ax,%[re]ax,1)
660f1f840000000000              nopw 0L(%[re]ax,%[re]ax,1)
662e0f1f840000000000            nopw %cs:0L(%[re]ax,%[re]ax,1)

alt_short (for modern AMD family CPUs):

0f1f440000660f1f440000          nopl 0(%[re]ax,%[re]ax,1); nopw 0(%[re]ax,%[re]ax,1)
660f1f440000660f1f440000        nopw 0(%[re]ax,%[re]ax,1); nopw 0(%[re]ax,%[re]ax,1)
660f1f4400000f1f8000000000      nopw 0(%[re]ax,%[re]ax,1); nopl 0L(%[re]ax)
0f1f80000000000f1f8000000000    nopl 0L(%[re]ax); nopl 0L(%[re]ax)
0f1f80000000000f1f840000000000  nopl 0L(%[re]ax); nopl 0L(%[re]ax,%[re]ax,1)

alt_long (for modern Intel family CPUs):

66662e0f1f840000000000          data16; nopw %cs:0L(%[re]ax,%[re]ax,1)
6666662e0f1f840000000000        data16; data16; nopw %cs:0L(%[re]ax,%[re]ax,1)
666666662e0f1f840000000000      data16; data16; data16; nopw %cs:0L(%[re]ax,%[re]ax,1)
66666666662e0f1f840000000000    data16; data16; data16; data16; nopw %cs:0L(%[re]ax,%[re]ax,1)
6666666666662e0f1f840000000000  data16; data16; data16; data16; data16; nopw %cs:0L(%[re]ax,%[re]ax,1)

My question is the following: is there any widespread / common naming for all these byte sequences, as macros, pseudo-mnemonics, or anything like that? So far I've only found that:

  • AMD recommends to name them NOPx_OVERRIDE_NOP (x = byte length).
  • gas understands nopw (as 2 byte nop) and nopl (as 4 byte nop)

How modern disassemblers tend to output these values?

Related, but not duplicate question: What does NOPL do in x86 system?

phuclv
  • 37,963
  • 15
  • 156
  • 475
GreyCat
  • 16,622
  • 18
  • 74
  • 112
  • 4
    They are usually emitted by alignment directives, because most of the time the programmer is interested in getting to a particular aligned address and doesn't care or know how many bytes that will be from the current location. – Jester Aug 28 '14 at 09:43
  • @Jester, could you name some alignment directives / assemblers that reliably give these NOP paddings? So far I've found out that normal `align` pads with 0s (and that's perfectly understandable - you won't want to pad with anything else in non-code section, for example). So far I've found only [nasm feature request](http://sourceforge.net/p/nasm/feature-requests/69/) about proposed `calign` directive to do that, but as far as I can see it never seen the light of the day. – GreyCat Aug 28 '14 at 10:47
  • 4
    In `GAS` all the alignment directives (`.align`, `.p2align`, `.balign`) work like this when used in code sections. As you said, `nasm` seems to be missing this feature. – Jester Aug 28 '14 at 11:02
  • 2
    This is an old question, but I just encountered one of these long nop instructions generated by Visual Studio, for C / C++ code that flows into a loop. The nop is used to pad the code so that the loop branch target is on a 16 byte boundary (in the case I saw, it happened to be a 32 byte boundary). – rcgldr Aug 03 '17 at 07:48
  • Intel X86 Encoder/Decoder a.k.a. XED, which is kind of an official Intel tool for x86 encodings, defines long NOPs here: https://github.com/intelxed/xed/blob/b53f33e44dd2e5fb2e63f9c0e35c65b72c933dae/src/enc/xed-encode.c#L460 . Strangely enough, only 1 to 9 bytes long NOPs are supported. – Grigory Rechistov Feb 22 '18 at 15:47
  • 1
    An analysis of patterns in real binaries: https://gist.github.com/stevemk14ebr/d117e8d0fd1432fb2a92354a034ce5b9 – Stephen Eckels Aug 23 '20 at 17:05
  • EuroAssembler uses long NOPs in code section for implicit or explicit alignment with mnemonics `NOP1`, `NOP2`, .. `NOP9`, see https://euroassembler.eu/eadoc/#InsEnhNOP – vitsoft Feb 10 '21 at 15:41

1 Answers1

4

Recent GAS in binutils has a .nops N pseudo-instruction that expands to the requested number of bytes for the target:

Florian Weimer
  • 32,022
  • 3
  • 48
  • 92