Что делает NOPL в системе x86?

Какова функция NOPL на машине x86? Кажется, что он ничего не делает, но почему он всегда находится в коде сборки?

Ответ 1

NOP - однобайтная операция "ничего не делать", буквально "без операции". NOPW, NOPL и т.д. Являются эквивалентными do-nothings, но берут слова и большие байты.

например.

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

эквивалентно выполнению

NOPW // 2byte opcode.

Они очень удобны для заполнения вещей, поэтому последовательность кода начинается с определенной границы памяти, занимая несколько байтов пространства инструкций, но фактически ничего не делая.

Единственный эффект NOP для ЦП состоит в том, чтобы увеличивать IP/EIP на 1. Эквиваленты NOPx будут делать это на 2, 4 и т.д....

Ответ 2

Изменить: Добавлены ссылки на документацию gas и gas кодировки для разных форм nop:

Согласно Блог Джона Фремлина: Операнды для NOP на AMD64, nopw, nopl и т.д. являются синтаксисом gas, а не синтаксисом AT & T.

Ниже приведены кодировки команд, генерируемые gas для разных nop из gas источника для длин команд от 3 до 15 байтов. Обратите внимание, что некоторые из них такие же, как рекомендованные Intel формы nop (см. Ниже), но не все. В частности, более длинный nop gas использует несколько (до 5) последовательных префиксов операндов 0x66 в разных формах nop, тогда как Intel рекомендует nop формы никогда не использовать более одного префикса операнда 0x66 в любом единственная рекомендуемая инструкция nop.

nop encodings из gas источника (переформатирован для удобства чтения):

/* nopl (%[re]ax) */
static const char alt_3[] =  {0x0f,0x1f,0x00};
/* nopl 0(%[re]ax) */
static const char alt_4[] =  {0x0f,0x1f,0x40,0x00};
/* nopl 0(%[re]ax,%[re]ax,1) */
static const char alt_5[] =  {0x0f,0x1f,0x44,0x00,0x00};
/* nopw 0(%[re]ax,%[re]ax,1) */
static const char alt_6[] =  {0x66,0x0f,0x1f,0x44,0x00,0x00};
/* nopl 0L(%[re]ax) */
static const char alt_7[] =  {0x0f,0x1f,0x80,0x00,0x00,0x00,0x00};
/* nopl 0L(%[re]ax,%[re]ax,1) */
static const char alt_8[] =  {0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
/* nopw 0L(%[re]ax,%[re]ax,1) */
static const char alt_9[] =  {0x66,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
/* nopw %cs:0L(%[re]ax,%[re]ax,1) */
static const char alt_10[] = {0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
/* data16
nopw %cs:0L(%[re]ax,%[re]ax,1) */
static const char alt_long_11[] = {0x66,0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
/* data16
data16
nopw %cs:0L(%[re]ax,%[re]ax,1) */
static const char alt_long_12[] = {0x66,0x66,0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
/* data16
data16
data16
nopw %cs:0L(%[re]ax,%[re]ax,1) */
static const char alt_long_13[] = {0x66,0x66,0x66,0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
/* data16
data16
data16
data16
nopw %cs:0L(%[re]ax,%[re]ax,1) */
static const char alt_long_14[] = {0x66,0x66,0x66,0x66,0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
/* data16
data16
data16
data16
data16
nopw %cs:0L(%[re]ax,%[re]ax,1) */
static const char alt_long_15[] = {0x66,0x66,0x66,0x66,0x66,0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
/* nopl 0(%[re]ax,%[re]ax,1)
nopw 0(%[re]ax,%[re]ax,1) */
static const char alt_short_11[] = {0x0f,0x1f,0x44,0x00,0x00,0x66,0x0f,0x1f,0x44,0x00,0x00};
/* nopw 0(%[re]ax,%[re]ax,1)
nopw 0(%[re]ax,%[re]ax,1) */
static const char alt_short_12[] = {0x66,0x0f,0x1f,0x44,0x00,0x00,0x66,0x0f,0x1f,0x44,0x00,0x00};
/* nopw 0(%[re]ax,%[re]ax,1)
nopl 0L(%[re]ax) */
static const char alt_short_13[] = {0x66,0x0f,0x1f,0x44,0x00,0x00,0x0f,0x1f,0x80,0x00,0x00,0x00,0x00};
/* nopl 0L(%[re]ax)
nopl 0L(%[re]ax) */
static const char alt_short_14[] = {0x0f,0x1f,0x80,0x00,0x00,0x00,0x00,0x0f,0x1f,0x80,0x00,0x00,0x00,0x00};
/* nopl 0L(%[re]ax)
nopl 0L(%[re]ax,%[re]ax,1) */
static const char alt_short_15[] = {0x0f,0x1f,0x80,0x00,0x00,0x00,0x00,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};

Intel использует различный синтаксис, а nop доступен для всех длин команд от 1 до 9 байтов. Существует несколько разных nop, так как все nop длиной более двух байтов принимают 1 операнд. Однобайтный nop (0x90) является синонимом xchg (e)ax,(e)ax.

Руководство разработчика программного обеспечения Intel® 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, рекомендованным Intel, есть и много других nop. В дополнение к выравниванию инструкции к определенной границе памяти, как отмечает Марк B в своем ответе, nop также очень полезны в самомодифицируемом коде, отладке и обратном проектировании.

Ответ 3

Фактически, NOP будет использоваться в коде сборки, когда код должен быть исправлен.

Поскольку размер новых инструкций может отличаться от размера старых, требуется дополнение.

Команда заполнения должна действовать так же, как и NOP, хотя она может занимать несколько байтов.

Причина, по которой мы вставляем более сложную команду, например 66 90, вместо нескольких NOP, - это одна команда, как правило, выполняется быстрее, чем несколько NOP.