Long multi-byte NOPs: commonly understood macros or other notation
Asked Answered
T

1

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?

Tyrontyrone answered 28/8, 2014 at 9:35 Comment(7)
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.Anastasio
@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 about proposed calign directive to do that, but as far as I can see it never seen the light of the day.Tyrontyrone
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.Anastasio
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).Sciuroid
Intel X86 Encoder/Decoder a.k.a. XED, which is kind of an official Intel tool for x86 encodings, defines long NOPs here: github.com/intelxed/xed/blob/… . Strangely enough, only 1 to 9 bytes long NOPs are supported.Triplex
An analysis of patterns in real binaries: gist.github.com/stevemk14ebr/d117e8d0fd1432fb2a92354a034ce5b9Dominoes
EuroAssembler uses long NOPs in code section for implicit or explicit alignment with mnemonics NOP1, NOP2, .. NOP9, see euroassembler.eu/eadoc/#InsEnhNOPLeoine
H
4

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

Hagan answered 18/11, 2020 at 18:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.