Почему к адресу вызова сборки добавлено 0xFBFFFFFF?
Итак, я пишу ассемблер для ОС, которую я разрабатываю. Все идет хорошо, я имею в виду все инструкции mov, и теперь я хочу реализовать такие инструкции, как call и jmp. У меня действительно нет хорошей документации, поэтому я просматриваю машинный код, сгенерированный NASM, чтобы выяснить коды операций и тому подобное. Я хотел посмотреть, что такое код операции для вызова, поэтому я скомпилировал код, который начинался с метки в начале. Я ожидал, что адрес после кода операции вызова будет 00 00 00 00, но это был FB FF FF FF. Я думал, что это связано с символами, поэтому я скомпилировал код с вызовом 0x000000, чтобы увидеть, что произошло, и адрес был точно таким же (0xFBFFFFFF). Может кто-нибудь объяснить это мне, я в замешательстве.
3 ответа
Было бы полезно показать фактический код, который вы разбираете. Скорее всего, это число с отрицательным смещением с прямым порядком байтов. 0xFFFFFFFB = -5 в дополнении 2 с. Вы написали:
Label: call Label
Если call - это 1-байтовый код операции с 4-байтовым относительным смещением, это имеет смысл.
Наиболее распространенной формой CALL в 32-битном пользовательском режиме кода x86 является CALL rel32
, который "вызывает" точку в операнде плюс адрес следующей инструкции. Это близкий родственник.
Для справки, можно было бы использовать абсолютный вызов, но кодировка составляет 6 байтов вместо 5; кодировка будет FF 14 XX XX XX XX
, Лишний байт (14
) говорит инструкции прочитать немедленное смещение, которое будет использовано как немедленное. Однако, это должно быть переписано в зависимости от базы модуля, на которой загружена ваша программа; относительный вызов не требует внимания при переезде.
Чтобы визуализировать, как это работает при выполнении инструкции, это происходит:
EIP
(указатель инструкции) увеличивается, чтобы указывать на следующую инструкцию,- Этот адрес помещается в стек (для предоставления адреса возврата),
- Немедленно (например, значение rel32) добавляется к
EIP
, а также - Следующая инструкция читается из (новой)
[EIP]
как обычно.
Когда эта инструкция закодирована, она выглядит так: E8 XX XX XX XX
, Отсюда видно, что длина инструкции составляет 5 байт.
Так как EIP
увеличивается на длину инструкции, вызов будет относительным к точке 5 байтов после начала инструкции. Таким образом, если получится, что относительный адрес вашей инструкции CALL 0x00000000 равен 0x00000000, необходимо вычесть 5 из EIP
; ваш ассемблер преобразовал абсолютный адрес в относительный.
Смещение может быть отрицательным. Также напомним, что x86-адреса имеют младший порядок. Таким образом, инструкция E8 FB FF FF FF
,
Следствием этой конкретной инструкции, что интересно, является то, что EIP+5
будет постоянно помещаться в стек до исключения (#SS(0)
) генерируется.
call
часто встречается в различных формах, например, тот, который использует абсолютный адрес для перехода, другой, который прыгает относительно текущего. Это может быть относительный, хотя четыре байта, вероятно, не являются смещением напрямую.
Если вы сомневаетесь, особенно при реализации ассемблера, вы можете взглянуть на руководство или таблицу данных.