Что делает следующая инструкция ассемблера x86?В чем смысл инструкции x86 «call dword ptr ds: [00923030h]»?
call dword ptr ds:[00923030h]
Это косвенный вызов, который я подозреваю, но точно как он вычисляет адрес для вызова?
Что делает следующая инструкция ассемблера x86?В чем смысл инструкции x86 «call dword ptr ds: [00923030h]»?
call dword ptr ds:[00923030h]
Это косвенный вызов, который я подозреваю, но точно как он вычисляет адрес для вызова?
[EDIT] Обновлен
Всякий раз, когда вы видите операнд памяти, который выглядит как ds:0x00923030
, это режим адресации сегмента относительной. Фактический адрес, на который ссылается tp, находится по линейному адресу 0x00923030 относительно базового адреса регистра сегмента ds
.
Сегментация памяти в архитектуре x86 несколько сбивает с толку, и я думаю, что Wikipedia неплохо объясняет это.
В основном, x86, имеет ряд специальных сегмента регистров: cs
(код сегмент), ds
(данные сегмент), es
, fs
, gs
и ss
(стек сегмент). Каждый доступ к памяти связан с определенным регистром сегмента. Как правило, вы не указываете регистр сегмента, и в зависимости от того, как обращается к памяти, используется регистр сегмента по умолчанию. Например, для чтения инструкций используется регистр cs
.
Каждый регистр сегментов имеет определенный базовый адрес и предел. Базовый адрес определяет физический адрес, которому соответствует линейный адрес 0x00000000, а предел определяет максимально допустимый линейный адрес для этого сегмента. Например, если базовый адрес был 0x00040000, а предел был 0x0000FFFF, то единственными действительными линейными адресами были бы 0x00000000 - 0x0000FFFF, а соответствующие физические адреса были бы 0x00040000 - 0x0004FFFF.
Таким образом, физический адрес, на котором вызывается подпрограмма, задается базовым адресом, хранящимся в регистре сегментов ds
, плюс 0x00923030. Но мы еще не закончили - инструкция содержит слово ptr
. Это добавляет дополнительный уровень косвенности, поэтому фактической целью подпрограммы является адрес , хранящийся на месте ds:0x00923030
.
В AT & T синтаксиса (принят ассемблером GNU), инструкция будет записана следующим образом:
lcall *ds:0x00923030
Для полных окровавленных деталей того, что команда делает, увидеть 80386 reference manual. Этот конкретный вариант команды: "CALL r/m16"
(косвенный косвенный косвенный косвенный косвенный космос).
Не вполне, я думаю, есть косвенная связь. Таким образом, это должно быть: Физический адрес, по которому вызывается подпрограмма, задается значением на базовом адресе, хранящимся в регистре сегмента ds плюс 0x00923030. –
Селектор сегментов не указывает на физический адрес, как вы говорите, а на линейный адрес. Вместо этого физический адрес получается на последнем шаге, когда логический адрес уже разрешен для линейного адреса. – newgre
Спасибо. Замечательно, что люди готовы поделиться своим опытом в определенной области с другими. Ты спас мне много времени. – 2009-02-06 14:19:24
IIRC, он принимает значение регистра DS (и сдвигает его влево на 4 бита), добавляет к этому немедленному заданному значению, извлекает значение dword из результирующей ячейки памяти, которая становится адресом для вызова. (EDIT: это верно для 16-битного реального режима, для защищенного режима см. Другие ответы.)
это неверно, смещение не добавляется к значению регистра ds. Это фактически логический адрес. – newgre
Вы правы, я думал о реальном режиме. –
Этот конкретный код операции совершает вызов через виртуальный адрес (32bit здесь), находящийся в местоположении, на которое указывает логический адрес ds:[00923030h]
.
Логический адрес состоит из двух компонентов:
Пожалуйста, обратите внимание, что приведенное выше вычисление обозначает линейный адрес, не физической (см Intel руководства volume 3a, рисунок 2.2), который затем транслируется через стандартный механизм 4KB пейджинга, т.е. адрес состоит из индекса на страницу каталога, таблицу страниц и смещение на выбранную страницу. Имейте в виду, что все операционные системы основного потока используют так называемый модуль памяти с плоской памятью, что означает, что все сегментные селектора указывают на адрес 0x00000000 с пределом, установленным на 0xFFFFFFFF, что является причиной того, что вы можете использовать между всеми сегментами и в конечном итоге привести (легкой) эксплуатации переполнения буфера.
Ассемблер, который вы указали, скорее всего, будет вызван через таблицу адресов импорта (см. this отличная статья) для исполняемого файла, то есть маловероятно, что это вызов подпрограммы ординал.
Код, подобный этому, испускается компиляторами, потому что окончательный виртуальный адрес импортированной функции из внешней DLL не может быть известен вообще во время компиляции (из-за перезагрузки dll). Используя такую вызывающую конструкцию, загрузчик ОС может вставить правильный виртуальный адрес по указателю адреса по логическому адресу, а компилятору не нужно заботиться о том, какой адрес конечная функция имеет в любом случае.
Спасибо. Это действительно код, созданный компилятором для вызова виртуальной функции. Ваш ответ очень помог и спас мне много времени. – 2009-02-06 14:21:41
Возможный дубликат [Что делает «DS: \ [40207A \]» означает в сборе?] (Http://stackoverflow.com/questions/3819699/what-does-ds40207a-mean-in-assembly) –