Почему инструкция nopl
в исполняемом файле x86 принимает операнд? Не нужно делать ничего, ну, ничего?
nopl 0x0(%rax)
Почему инструкция nopl
в исполняемом файле x86 принимает операнд? Не нужно делать ничего, ну, ничего?
nopl 0x0(%rax)
Множественные наборы команд многих процессоров имеют несколько способов представления функционально-идентичных инструкций. Например, исходный набор команд ARM содержит инструкции для загрузки R0 с любым значением формы b << n
, где b
- это значение от 0 до 255, а n
- четное число от 0 до 24. Если нужно загрузите R0 со значением 256, можно загрузить инструкцию, которая загружает ее с помощью 1<<8
, или можно использовать инструкцию для 4<<6
, 16<<4
или 64<<2
. Инструкции по загрузке этих разных значений имеют разные бинарные кодировки, хотя все четыре команды имеют одинаковый эффект.
Ассемблеры для некоторых компиляторов выходят из своего пути, чтобы предоставить средства для запроса какой из кажущихся одинаковых инструкций фрагмент кода должен использовать. Хотя это, как правило, не важно, бывают моменты, когда может быть желательно избежать использования определенных значений байтов внутри куска кода, или могут быть моменты, когда изменения в определенных байтах внутри фрагмента кода должны иметь особый эффект. Например, восемь бит в вышеприведенных командах ARM используются для указания значения b
. Если код должен был перезаписать часть b
одной из приведенных выше инструкций значением 12, значение, загруженное в R0, будет зависеть от того, какая из исходных четырех команд была использована; это может быть 0x0C00, 0x0300, 0x00C0 или 0x0030.
Хотя ассемблеры для 8x86 обычно не позволяют явно различать все возможные кодировки команд, могут быть некоторые контексты, где может быть полезно указать, какие байтовые значения должны быть включены в инструкцию. Например, один подход к обработке исключений заключается в том, чтобы иметь рутинную проверку, когда возникает исключение, является ли команда на обратном адресе определенной формой NOP и, если она есть, интерпретирует ее операнд как адрес структуры данных содержащие информацию об исключении. На практике большинство языков 8х86, которые поддерживают исключения, используют другие способы их обработки, но вышеупомянутый метод будет замедлять нормальные возвраты функций на время, необходимое для извлечения и выполнения длинного NOP, но будет иметь возможность обрабатывать исключительные выходы относительно эффективно (большинство языки используют более медленный подход для обработки прерываний, чтобы избежать затрат на выполнение NOP в случае без исключения, но другие языки могли бы делать что-то по-другому).
Иногда я использую nops при отладке. Если я знаю, как что-то пойдет не так, но требует тысяч разрывов точки прерывания, чтобы обнаружить, что я пишу код, который проверяет его. Он может выглядеть примерно так (код стиля C):
if (condition_occurred)
{
asm("nop");
}
Когда я устанавливаю точку останова на линии "asm", отладчик настроит регистр DRx с линейным (физическим) адресом (соответствующим виртуальному адресу) nop. Когда это местоположение достигнуто, прерывание прерывания происходит, и вы вводите отладчик. Если вы выполняете без отладчика, nop будет обработан (ничего не происходит). Поэтому здесь я хочу инструкцию, которая ничего не делает, и имеет смысл, что она делает (не делает).
Вот пример того, где инструкция "ничего не делать" фактически ничего не делает... хотя и косвенно.
См. стр. 8 в в этой статье и обратите внимание на первую (верхнюю) инструкцию цикла в примере 3 (которая является примером примера 2). Также сноска в правом нижнем углу страницы.
Автор намекает, что дополнительные nops могут ускорить процесс далее.
Итак, у nops определенно есть свои возможности.