Какова функция 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.