Чтобы поддерживать множество возможностей языка, компилятор Delphi использует несколько различных структур данных и шаблонов генерации машинного кода. Чтобы помочь в понимании того, как реализуются компилятором возможности языка Delphi, чтобы было проще опознавать эти шаблоны и структуры при низкоуровневой отладке ассемблерного кода и чтобы было возможным понимать, что вы делаете при патчинге кода в run-time - я расскажу про некоторые из этих шаблонов в этом посте.
Это обсуждение затрагивает шаблоны, используемые компиляторами D7, D2005, D2006 и D2007 под Win32, но все эти шаблоны должны быть более-менее идентичны для всех других Win32-версий компиляторов Delphi.
Вызов невиртуального метода
Простейший случай - это когда компилятор генерирует код для вызова нормального, не виртуального метода или глобальной подпрограммы. Он использует инструкциюCALL
процессора x86, которая требует немедленного относительного смещения - как части потока команд. Это означает, что нет никаких накладных расходов для расчетов или выборки целевого адреса из внешней памяти. Адрес является частью закодированной инструкции и процессор выполнит идеальный прогноз адреса цели перехода. Например:
type TFoo = class procedure Bar; end; var Foo: TFoo; begin Foo := TFoo.Create; Foo.Bar;Генерируемый код для вызова
Foo.Bar
:
MOV EAX, [Foo] CALL <Относительное смещение TFoo.Bar>Первая инструкция загружает неявный указатель
Self
в регистр EAX
(по умолчанию весь код Delphi использует соглашение вызова register
). Смещение в инструкции CALL
относительно текущего значения регистра IP (Instruction Pointer - указатель на инструкцию кода). Это означает, что закодированный вызов одной и той же подпрограммы (но из разных мест одной функции) будет использовать различные смещения. Основная причина для использования смещений вместо более очевидных абсолютных адресов - уменьшить необходимость правок кода, если модуль (к примеру - DLL) перемещён в памяти (чаще всего - в результате загрузки по адресу, отличному от предпочитаемого базового).Вызов виртуального метода
Виртуальные методы лежат в основе полиморфизма в Delphi. Весь смысл полиморфизма в том, что потомок класса может переопределить метод из базового класса, реализуя конкретное поведение. На уровне реализации это означает, что компилятор не может больше жёстко закодировать конкретный целевой адрес для вызова, а у процессора больше нет такой роскоши, как точное предсказание адреса перехода.На уровне реализации компилятора Win32 виртуальные методы диспетчеризуются с помощью команды
CALL
, которая перенаправляет вызов через таблицу VMT объекта. Каждый виртуальный метод во время компиляции получает уникальный статический индекс (смещение VMT), связанный с ним. Вы не можете получить это смещение непосредственно с помощью кода на Паскале, но BASM теперь имеет директиву VMTOFFSET
, чтобы дать возможность вызова виртуальных методов чистым способом (без хаков - прим.пер.). Вот пример вызова виртуального метода из BASM:
type TMyClass = class procedure Method; virtual; end; procedure TMyClass.Method; begin writeln(ClassName, '.Method'); end; procedure CallMyMethod(Instance: TMyClass); asm MOV ECX, [EAX] CALL [ECX + VMTOFFSET TMyClass.Method] end; var Instance: TMyClass; begin Instance := TMyClass.Create; CallMyMethod(Instance); readln; end.Вот пример машинных кодов, участвующих в вызове виртуального метода:
// EAX содержит экземпляр объекта (Self) MOV ECX, [EAX] // Получить указатель на VMT в ECX CALL [ECX+0x014] // Вызов виртуального метода через слот в VMTВ следующем посте мы углубимся в детали таблицы виртуальных методов, включая рассмотрение хака явного вызова через VMT.
Комментариев нет:
Отправить комментарий
Можно использовать некоторые HTML-теги, например:
<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>
Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку.
Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.
Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.
Примечание. Отправлять комментарии могут только участники этого блога.