¿Qué hace NOPL en el sistema x86?

¿Cuál es la función de NOPL en la máquina x86? Parece que no hace nada, pero ¿por qué siempre está en el código de ensamblaje?

NOP es una operación de un byte “no hacer nada”, literalmente “sin operación”. NOPW, NOPL, etc. son equivalentes a nada, pero toman bytes de palabra y de largo tamaño.

p.ej

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

es equivalente a hacer

 NOPW // 2byte opcode. 

Son muy útiles para rellenar cosas, por lo que una secuencia de código comienza en un límite de memoria particular, ocupando unos pocos bytes de espacio de instrucción, pero sin hacer realmente nada.

El único efecto de NOP en la CPU es incrementar IP / EIP en 1. Los equivalentes NOPx lo harán en 2, 4, etc.

De acuerdo con el blog de John Fremlin: Operandos a NOP en AMD64 , nopw , nopl , etc. son syntax de gas , no de AT & T.

A continuación se muestran las codificaciones de instrucciones generadas por gas para diferentes nop de gas fuente de gas para longitudes de instrucción de 3 a 15 bytes. Tenga en cuenta que algunos son los mismos que los formularios nop recomendados por Intel (ver a continuación), pero no todos. En particular, en gas más largo de nop usa múltiples (hasta 5) prefijos de operando 0x66 consecutivos en diferentes formas de nop , mientras que las formas de nop recomendadas de Intel nunca usan más de un prefijo de operando de 0x66 en ninguna instrucción de nop recomendada.

codificaciones nop del código fuente para gas 2.30 (reformateado para legibilidad):

 /* 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 usa una syntax diferente, y hay nop disponibles para todas las longitudes de instrucción de 1 a 9 bytes. Hay varios nop diferentes, ya que todos los nop ‘s de más de dos bytes aceptan 1 operando. One-byte nop ( 0x90 ) es sinónimo de xchg (e)ax,(e)ax .

Manual del desarrollador de software Intel® 64 e IA-32 Architectures, Volumen 2 (2A, 2B y 2C): Referencia del conjunto de instrucciones, AZ, CAPÍTULO 4: REFERENCIA DEL SETUP DE INSTRUCCIONES, MZ enumera los formularios nop recomendados para diferentes longitudes de instrucciones:

 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 

Por lo tanto, además de estos nop recomendados por Intel, hay muchos otros nop también. Además de alinear una instrucción con un límite de memoria específico, como Marc B menciona en su respuesta, los nop también son muy útiles en el código de automodificación, la depuración y la ingeniería inversa.

En realidad, NOP se usará en el código ensamblador cuando el código necesite ser parchado.

Debido a que el tamaño de las nuevas instrucciones puede ser diferente al de las anteriores, se necesita relleno.

La instrucción de relleno debe actuar igual que NOP, aunque puede ocupar varios bytes.

La razón por la que insertamos una instrucción más compleja, como 66 90, en lugar de varios NOP, es una instrucción que generalmente se ejecuta más rápidamente que varios NOP.