Как определить длину кода операции инструкции x86-64, используя сам процессор?

Я знаю, что есть библиотеки, которые могут "анализировать" двоичный машинный код / ​​код операции, чтобы определить длину инструкции процессора x86-64.

Но мне интересно, так как процессор имеет внутреннюю схему, чтобы определить это, есть ли способ использовать сам процессор, чтобы определить размер команды из двоичного кода? (Может быть, даже взломать?)

1 ответ

Флаг Trap (TF) в EFLAGS/RFLAGS делает CPU одношаговым, т.е. делает исключение после выполнения одной инструкции.

Поэтому, если вы пишете отладчик, вы можете использовать возможность одноступенчатого ЦП, чтобы найти границы команд в блоке кода. Но только запустив его, и если он выйдет из строя (например, загрузка с не отображенного адреса), вы получите это исключение вместо одношагового исключения TF.

(Большинство ОС имеют средства для подключения и пошагового выполнения другого процесса, например, Linux ptrace Таким образом, вы можете создать непривилегированный процесс песочницы, в котором вы можете пройти через некоторые неизвестные байты машинного кода...)

Или, как указывает @Rbmn, вы можете использовать средства отладки, поддерживаемые ОС, для самостоятельного выполнения действий.


@Harold и @MargaretBloom также указывают на то, что вы можете поместить байты в конец страницы (за которой следует не отображенная страница) и запустить их. Посмотрите, получите ли вы #UD, ошибку страницы или исключение #GP.

  • #UD: декодеры увидели полную, но неверную инструкцию.
  • ошибка страницы на не отображенной странице: декодеры нажимают на не отображенную страницу, прежде чем решить, что это недопустимая инструкция.
  • #GP: инструкция была привилегированной или ошибочной по другим причинам.

Чтобы исключить декодирование + выполнение в качестве полной инструкции и затем ошибку на не отображенной странице, начните только с 1 байта перед не отображенной страницей и продолжайте добавлять дополнительные байты, пока не прекратите получать ошибки страницы.

Взлом x86 ISA Кристофером Домасом более подробно описывает эту технику, включая ее использование для поиска недокументированных нелегальных инструкций, например: 9a13065b8000d7 7-байтовая недопустимая инструкция; вот когда это останавливает сбой страницы. (objdump -d просто говорит 0x9a (bad) и декодирует остальные байты, но, по-видимому, реальное оборудование Intel не удовлетворено тем, что оно плохо, пока не получит еще 6 байтов).


Счетчики производительности HW, такие как instructions_retired.any также показывать количество команд, но, ничего не зная о конце инструкции, вы не знаете, куда поместить rdpmc инструкция. Обивка с 0x90 NOP и просмотр количества выполненных команд, вероятно, не сработают, потому что вам нужно знать, где вырезать и начинать заполнение.


Мне интересно, почему бы Intel и AMD не ввести инструкцию для этого

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

Не было бы смысла помещать микрокодированный дизассемблер за каким-то новым кодом операции.

Кроме того, аппаратные декодеры подключены только для работы как часть внешнего интерфейса в пути выборки кода, а не для передачи им произвольных данных. Они уже заняты декодированием инструкций в большинстве циклов и не готовы работать с данными. Добавление инструкций, которые декодируют байты машинного кода x86, почти наверняка будет выполнено путем репликации этого оборудования в исполнительный блок ALU, а не путем запроса кэш-памяти decoded-uop или L1i (в схемах, где границы команд отмечены в L1i) или отправки данных через фактические предварительные декодеры внешнего интерфейса и захват результата вместо постановки его в очередь для остальной части внешнего интерфейса.

Единственный реальный высокопроизводительный сценарий использования, о котором я могу подумать, - это эмуляция или поддержка новых инструкций, таких как Эмулятор разработки программного обеспечения Intel (SDE). Но если вы хотите запускать новые инструкции на старых процессорах, суть в том, что старые процессоры не знают об этих новых инструкциях.

Количество процессорного времени, затрачиваемого на разборку машинного кода, довольно мало по сравнению с количеством времени, которое процессоры затрачивают на выполнение математических операций с плавающей запятой или обработку изображений. Есть причина, по которой у нас есть такие вещи, как SIMD FMA и AVX2 vpsadbw в наборе инструкций для ускорения тех специальных задач, которые процессоры проводят много времени, но не для вещей, которые мы можем легко сделать с помощью программного обеспечения.

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

На верхнем уровне сложности специального назначения в Nehalem были введены строковые инструкции SSE4.2. Они могут делать классные вещи, но их сложно использовать. https://www.strchr.com/strcmp_and_strlen_using_sse_4.2 (также включает в себя strstr, который является реальным вариантом использования, где pcmpistri can be faster than SSE2 or AVX2, unlike for strlen / strcmp where plain old pcmpeqb / pminub` работает очень хорошо, если используется эффективно (см. рукописный asm glibc). В любом случае, эти новые инструкции все еще являются многопользовательскими даже в Skylake и не используются широко. Я думаю, что компиляторам трудно выполнять автоматическое векторизацию с ними, и большая часть обработки строк выполняется на языках, где не так просто тесно интегрировать несколько встроенных функций с низкими издержками.


установка батута (для горячей установки двоичной функции.)

Даже это требует расшифровки инструкций, а не просто определения их длины.

Если первые несколько байтов инструкции функции использовали режим адресации RIP (или jcc rel8/rel32 или даже jmp или же call ), перемещение его в другое место нарушит код. (Спасибо @Rbmn за указание этого углового случая.)

Другие вопросы по тегам