Что такое Calling Conventions?
Это стандартизированные методы реализации и вызова функций.
Соглашение о вызовах опредяют как функция вызывается, как функция управляет стеком и стековым кадром, как аргументы передаются в функцию, как функция возвращает значения.
Я разберу несколько наиболее часто используемых
stdcall (Standart Calling Convention)
STDCALL это стандартное соглашение для Win32 API. В данном соглашение, аргументы передаются справа налево и очистка стека ложится на вызываемую функцию. Для передачи аргументов используется стек, т.е. перед вызовом нужно положить аргументы на стек. Возвращаемое значение записывается в регистр eax.
Пример вызова абстрактной функции, которая принимает 3 4-байтовых аргумента.
PUSH 0x5 PUSH 0x4 PUSH 0x8 CALL _func@12
В вызываемой функции:
... mov eax, 0x75 ; Записываем 0x75 в eax, т.е. в регистр, который отвечает за возвращаемое значение ... ret 12 ; Очищаем стек на 12 байт (3 аргумента по 4 байта)
Давайте рассмотрим вызов функции MessageBox из Win32 API, используя NASM.
; Hello World with winapi messagebox using stdcall calling convention extern _MessageBoxA@16 extern _ExitProcess@4 global _main section .data message db "Hello, Habr!", 0 title db "Habr!", 0 section .text _main: push dword 0x00 push dword title push dword message push dword 0 call _MessageBoxA@16 push 0 call _ExitProcess@4
Приведу пример вызова на языке программирования C.
MessageBox(0, message, title, MB_OK);
Разберем то, как мы передаем аргументы:
Первые 4 строки - передача аргументов, согласно STDCALL, то есть справа налево.
Рассмотрим, какие аргументы требуются для вызова функции MessageBox:
int MessageBox( [in, optional] HWND hWnd, [in, optional] LPCTSTR lpText, [in, optional] LPCTSTR lpCaption, [in] UINT uType );
MessageBox принимает 4 аргумента:
HWND - Дескриптор родительского окна. Мы его оставляем 0, так как у нас его нет.
lpText - Строка, которая будет выведена в окне. В нашем случае это "Hello, Habr!"
lpCaption - Заголовок окна. В нашем случае это "Habr!"
uType - Тип MessageBox'a. Мы указываем 0x00, то есть MESSAGEBOX_OK. В окошке будет только одна кнопка - ОК.
С другими типами MessageBox вы можете ознакомиться в официальной документации
Мы видим в каком порядке принимаются параметры при вызове функции, но на стек мы кладем в обратном порядке, то есть первый аргумент, который мы кладем на стек - 0x00 - uType, второй ttl - lpCaption. Это и есть одна из особенностей данного соглашения о вызовах, вторая особенность - eax используется как регистр для возвращаемого значения.
Еще стоит упомянуть имена функций. Они начинаются с символа нижнего подчеркивания и заканчиваются на такую конструкцию "@[Количество байт, которые нужно выделить для аргументов.]"
cdecl (C calling convention)
Стандартное соглашение о вызовах для программ на C/C++.
В данном соглашение аргументы передаются справа налево и кладутся на стек, как и в [[#STDCALL Standart Calling Convention|stdcall]], возвращаемое значение кладется в регистр EAX. Но вот стек уже очищается функцией, которая вызывает. Имена функций начинаются с символа нижнего подчеркивания, без указания количества байт для аргументов к конце.
Пример:
push 18 push 19 call _add add esp, 8
fastcall (Fast calling convention)
Главным отличием от двух соглашениях выше является то, что аргументы кладутся в регистры, если это возможно, что позволяет увеличить скорость вызова функции, потому что обратиться к регистру быстрее, чем к стеку.
Стоит указать, что в x86 только 2 регистра могут быть задействованы для передачи аргументов, остальные будут храниться в стеке.
Аргументы также передаются справа налево. Стек чистится вызываемой функцией.
Пример:
... mov ecx, 0x3 mov edx, 0x1 call @sum@8 ...
К названием функций добавляется @ в начале и следующая конструкция в конце "@[Количество байт, которые нужно выделить для аргументов.]"
thiscall
Это соглашение о вызовах используется для вызова нестатических функций-членов C++.
Так как используется только для нестатических функций-членов, то у нас есть указатель this, который передается в ECX, стек очищается вызываемой функцией, аргументы передаются справа налево на стек, возвращаемое значение помещается в регистр EAX.
Пример:
mov ecx, SomeObj push b push a call _MyMethod
vectorcall
Это соглашение о вызовах, которое было выпущено для увеличения эффективности и скорости обработки, позволяя передавать векторные типы данных в регистры. (RCX/XMM0, RDX/XMM1, R8/XMM2, R9/XMM3 + XMM0-XMM5/YMM0-YMM5).
Стек очищается функцией, которая вызывает, аргументы передаются справа налево.
Итого
Соглашение о вызове - описание правил вызова функций, в которое входит способы передачи аргументов, способы вызова, способы возврата значения, именования функций и очистки памяти после завершения работы функции.
В компиляторе от Microsoft (cl) их можно использовать вот так:
stdcall (/Gz)
fastcall (/Gr)
cdecl (/Gd)
vectorcall (/Gv)
