вторник, 9 декабря 2008 г.

История соглашений вызова, часть 3

Это перевод The history of calling conventions, part 3. Автор: Реймонд Чен.
Вторая часть.
Окей, поехали: 32-х битные модели вызова x86.

(Кстати, на случай, если вы не поняли: я говорю только о тех моделях вызова, которые вы можете встретить в Windows-программировании или в компиляторах от Microsoft. Я не буду рассматривать соглашения вызова других операционных систем или модели вызова, специфичные для конкретного языка или производителя компилятора).

Запомните: если какая-либо модель вызова используется для члена класса C++, тогда будет существовать скрытый параметр "this", который будет неявно передан первым аргументом в функцию (*).

Все модели

Все 32-х разрядные соглашения вызова x86 сохраняют регистры EDI, ESI, EBP и EBX и используют пару EDX:EAX для возврата результата (**).

C (cdecl)

В 32-х битном мире работают те же правила, что и в 16-ти битном. Параметры передаются справа налево (поэтому первый параметр будет ближе к вершине стека), а чистит стек вызывающий. Имена функций декорируются ведущим знаком подчёркивания.

stdcall

Это стандартное соглашение, используемое во всём Win32, за исключением функций с переменным числом параметров (которые используют cdecl) и очень небольшого числа функций, использующих fastcall. Параметры передаются справа налево, а стек чистит вызываемый. Имена функций декорируются ведущим знаком подчёркивания и в конце ставится знак @ и размер принимаемых функцией параметров в байтах.

fastcall

Первые два параметра передаются в ECX и EDX, а остаток передаётся через стек так же, как и при stdcall. И снова стек чистит вызываемый. Имена функций декорируются ведущим знаком @, в конце также ставится @ и после него указывается размер принимаемых функцией параметров в байтах (включая параметры в регистрах).

thiscall

Первый параметр (который "this") передаётся в ECX, а все остальные параметры передаются как в stdcall - через стек. И здесь стек чистит вызываемый. Имена функций декорируются весьма сложным образом компилятором C++, включая и типы параметров (среди всех прочих вещей). Это необходимо, потому что C++ допускает перегрузку функций, поэтому необходимо использовать сложные правила декорирования для того, чтобы два варианта перегруженных функций имели бы различные имена.

На MSDN есть несколько неплохих диаграм, демонстрирующих эти соглашения вызова.

(***)

Помните: модель вызова - это контракт между вызываемой и вызывающей стороной. Если вы один из тех психов, которые ещё пишут на ассемблере, то для вас это означает, что ваша callback-функция обязана сохранять регистры, как того требует соглашение вызова, потому что вызывающая сторона (операционная система) рассчитывает на это. Если, к примеру, вы во время вызова испортите регистр EBX, то не удивляйтесь, когда начнут происходить всякие плохие вещи. Подробнее об этом - в следующий раз.

Читать далее.

Примечания переводчика:
(*) Для Delphi справедливо аналогичное утверждение: у любого метода класса есть неявный параметр Self, который является первым параметром функции этого метода. Поэтому у метода, объявленного с двумя параметрами, на самом деле будет три параметра, причём первым будет Self, а вторым и третьим - первый и второй параметры в объявлении метода.
(**) Вообще, пара EDX:EAX используется скорее как исключение из правила. Т.е. все возвращаемые значения возвращаются в EAX, а если они там не помещаются, то в функцию передаётся указатель, по которому и записывается результат. Однако, (только) для 8-ми байтовых записей есть спец. правило: они возвращаются в паре EDX:EAX. В Delphi даже есть баг: компилятор генерирует неверный код для функции, возвращающей 8-ми байтовую запись.
(***) Моделью вызова по-умолчанию в Delphi является модель register. В ней для передачи параметров используются регистры EAX, EDX, ECX и стек. Обычно первые три параметра идут в регистры, а остальные - в стек как в stdcall, но это необязательно. В языке есть свои правила для распределения параметров между регистрами и стеком - эти правила довольно сложны (так, например, действительные числа передаются через стек сопроцессора). Параметры передаются слева-направо и стек чистит вызываемый.

Комментариев нет:

Отправить комментарий

Можно использовать некоторые HTML-теги, например:

<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>

Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку.

Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.

Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.

Примечание. Отправлять комментарии могут только участники этого блога.