Как мне получить байт `ModeR / M`, соответствующий инструкции` call dword ptr` в x86, используя документы Intel? - PullRequest
4 голосов
/ 01 ноября 2019

Я отладил следующий код в VS2017 (обратите внимание на точку останова ниже):

enter image description here

После этого вы найдете разборку для точки останова, упомянутой выше:

enter image description here

Как вы можете видеть на рисунке выше, машинный код, сгенерированный компилятором для инструкции call dword ptr [fp], был FF 55 F8, гдеFF - это код операции для инструкции вызова, 55 - это значение для байта ModeR/M, а F8 - это 8-разрядное смещение со значением -8, которое я объясню ниже.

Теперь, если вы посмотрите на приведенный ниже байт «Таблица 2-2: 32-битные адресации с байтом ModeR / M», полученный из тома 2А «Руководства разработчика программного обеспечения для 64 архитектур IA-32», вы заметите, что я выделилномер 55, упомянутый выше, который соответствует действующему адресу [EBP]+disp8. Таким образом, инструкция CALL в ассемблере перейдет к инструкции, адрес которой получен из адреса в регистре EBP плюс 8-разрядное смещение, упомянутое выше, со значением -8. И этот адрес правильный. Он соответствует адресу инструкции JMP, которая в итоге передает код функции f.

enter image description here

Таким образом, все выглядит хорошо. Но есть один важный момент, который я упускаю: я должен был получить байт ModeR/M, используя ссылку на инструкцию CALL в руководстве Intel и приведенную выше таблицу 2.2. Но я до сих пор не знаю, как это сделать. Любая подсказка будет высоко оценена, так как я работаю над этим уже несколько дней, и я до сих пор ничего не понимаю об этом.

1 Ответ

6 голосов
/ 01 ноября 2019

Что вам не хватает, так это то, что часть кода операции закодирована в байте ModR / M. Обычно байт ModR / M кодирует два операнда. Первый операнд является регистром или операндом памяти, как указано в метках строк слева от таблицы, в то время как второй операнд является регистром, заданным заголовками столбцов в верхней части таблицы. Для инструкций только с одним операндом, таких как инструкция CALL, второй операнд вместо этого используется для предоставления дополнительных битов кода операции.

Если вы посмотрите документацию для инструкции CALL, то увидите, что код операции для инструкции "Call Near, абсолютно косвенный адрес, указанный в r / m32" указан как FF /2. /2 указывает, что дополнительные биты кода операции в байте ModR / M имеют значение 2 в этой инструкции. Если вы затем посмотрите на заголовки столбцов «(в десятичном формате) / цифра (код операции)», то увидите, что число 2 появляется в начале столбца. Если вы посмотрите вниз на этот столбец, вы увидите 55 в строке, помеченной «[EBP] + disp8».

Это описано в Руководстве Intel для разработчиков ПО, том 2, в разделе 3.1.1.1 Столбец кода операции вСводная таблица инструкций (инструкции без префикса VEX):

  • / цифра - цифра от 0 до 7 указывает, что байт ModR / M инструкции использует только r / m (регистр)или память) операнд. Поле reg содержит цифру, которая обеспечивает расширение кода операции инструкции.

Единственное, что вам не хватает, это то, что call dword ptr [fp] - это текст, сгенерированный дизассемблером. Он никогда не был собран, и как будет собран, зависит от того, как определено fp. Дизассемблер знает из отладочной информации, что компилятор сгенерировал, где локальная переменная fp живет в стеке, и знает, что [ebp - 8] может использоваться для доступа к ней. Он отображал fp вместо [ebp - 8], потому что первое было бы более значимым в большинстве случаев. Вы сможете увидеть это позже, сняв флажок «Показать имена символов».

Обратите внимание, что строка call std::operator<<std:char_traits<char> > не может быть собрана независимо от того, как определены std, operator и т. Д., И т. Д. показывает, что разборка, на которую вы смотрите, на самом деле не предназначена для сборки. Это часто имеет место с дизассемблерами. Вывод должен быть информативным, лучшей альтернативой, чем просмотр последовательности шестнадцатеричных байтов. Это не должен быть обратимый процесс.

...