Interested Article - Соглашение о вызове

Соглашение о вызове ( англ. calling convention) — описание технических особенностей вызова подпрограмм , определяющее:

  • способы передачи параметров подпрограммам;
  • способы вызова (передачи управления) подпрограмм;
  • способы передачи результатов вычислений, выполненных подпрограммами, в точку вызова;
  • способы возврата (передачи управления) из подпрограмм в точку вызова.

Является частью двоичного интерфейса приложений ( англ. application binary interface , ABI).

Состав

Соглашение о вызове описывает следующее:

  • способ передачи аргументов в функцию . Варианты:
    • аргументы передаются через регистры процессора ;
    • аргументы передаются через стек ;
    • смешанные (соответственно, стандартизируется алгоритм, определяющий, что передаётся через регистры , а что — через стек или другую память ):
      • первые несколько аргументов передаются через регистры ; остальные — через стек (небольшие аргументы ) или другую память (большие аргументы );
      • аргументы небольшого размера передаются через стек, большие аргументы — через другую память ;
  • порядок размещения аргументов в регистрах и/или стеке. Варианты:
    • слева направо или прямой порядок: аргументы размещаются в том же порядке, в котором они перечислены при вызове функции . Достоинство: машинный код соответствует коду на языке высокого уровня ;
    • справа налево или обратный порядок: аргументы передаются в порядке от конца к началу. Достоинство: упрощается реализация функций , принимающих произвольное число аргументов (например, printf() ) (так как на вершине стека оказывается всегда первый аргумент);
  • код, ответственный за очистку стека:
    • код, вызывающий функцию, или вызывающая программа . Достоинство: возможность передачи в функцию произвольного числа аргументов;
    • код самой функции или вызываемая функция . Достоинство: уменьшение количества инструкций, необходимых для вызова функции (инструкция для очистки стека записывается в конце кода функции и только один раз);
  • конкретные инструкции, используемые для вызова и возврата. Для процессора x86 , работающего в защищённом режиме , используются исключительно инструкции call и ret ; при работе в стандартном режиме используются инструкции call near , call far и pushf/call far (для возврата соответственно retn , retf и iret );
  • способ передачи в функцию указателя на текущий объект ( this или self ) в объектно-ориентированных языках . Варианты (для процессора x86 , работающего в защищённом режиме ):
    • как первый аргумент;
    • через регистр ecx или rcx ;
  • код, ответственный за сохранение и восстановление содержимого регистров до и после вызова функции :
    • вызывающая функция;
    • вызываемая функция;
  • список регистров , подлежащих сохранению/восстановлению до/после вызова функции .

Соглашение о вызове может быть описано в документации к ABI архитектуры, в документации к ОС или в документации к компилятору .

Использование

Соглашения о вызовах, используемые на x86 при 32-битной адресации

Список неполный, представлены основные из применяемых по сей день соглашений.

Для перечисленных ниже соглашений (кроме ) перед возвратом значений из функции подпрограмма обязана восстановить значения сегментных регистров , регистров esp и ebp . Значения остальных регистров могут не восстанавливаться.

Если размер возвращаемого значения функции не больше размера регистра eax , возвращаемое значение сохраняется в регистре eax . Иначе возвращаемое значение сохраняется на вершине стека, а указатель на вершину стека сохраняется в регистре eax . Если возвращается объект с автодеструктором (любой объект C++ с ненулевым деструктором , строки произвольной длины в Паскале, BSTR в WinAPI и т. д.), вызывающая программа должна корректно уничтожить его.

cdecl

cdecl (сокращение от англ. c - declaration ) — соглашение о вызовах, используемое компиляторами для языка Си (отсюда название).

Аргументы функций передаются через стек, справа налево. Аргументы, размер которых меньше 4 байт, расширяются до 4 байт. Очистку стека производит . Это основной способ вызова функций с переменным числом аргументов (например, printf() ). Способы получения возвращаемого значения функции приведены в таблице.

Тип Размер возвращаемого значения, байт Способ передачи возвращаемого значения Примечание
Целое число, указатель 1, 2, 4 Через регистр eax Значения, размер которых меньше 4 байт, расширяются до 4 байт
Целое число 8 Через пару регистров edx:eax
Число с плавающей точкой 4, 8 Через регистр st0 (из псевдостека x87 , FPU )
Другие Больше 8 Через регистр eax Указатель на структуру данных сохраняется в регистре eax

Перед вызовом функции вставляется код , называемый прологом ( англ. prolog) и выполняющий следующие действия:

После вызова функции вставляется код , называемый эпилогом ( англ. epilog) и выполняющий следующие действия:

pascal

pascal — соглашение о вызовах, используемое компиляторами для языка Паскаль . Также применялось в ОС Windows 3.x .

Аргументы процедур и функций передаются через стек, слева направо. Указатель на вершину стека (значение регистра esp ) на исходную позицию возвращает . Изменяемые параметры передаются только по ссылке . Возвращаемое значение передаётся через изменяемый параметр Result. Параметр Result создаётся неявно и является первым аргументом функции .

stdcall или winapi

stdcall или winapi — соглашение о вызовах, применяемое в ОС Windows для вызова функций WinAPI .

Аргументы функций передаются через стек, справа налево. Очистку стека производит .

fastcall

fastcall — общее название соглашений, передающих параметры через регистры (обычно это самый быстрый для выполнения компьютером способ, отсюда и название (здесь приставка « fast » с англ. переводится как « быстрый », а « call » с англ. переводится как « вызов ». То есть, дословно переводится как « быстрый вызов »)). Если для сохранения всех параметров и промежуточных результатов — регистров недостаточно, то дополнительно используется стек.

Соглашение о вызовах fastcall не стандартизировано, поэтому используется только для вызова процедур и функций, не экспортируемых из исполняемого модуля и не импортируемых извне.

В компиляторах фирмы Borland для соглашения __fastcall , называемого также register , параметры передаются слева направо в регистрах eax , edx и ecx и, если параметров больше трёх, в стеке, также слева направо . Исходное значение указателя на вершину стека (значение регистра esp ) возвращает .

В 32-разрядной версии компилятора фирмы Microsoft , а также в компиляторе GCC , соглашение __fastcall , также называемое __msfastcall , определяет передачу первых двух параметров слева направо в регистрах ecx и edx , а остальные параметры передаются справа налево в стеке. Очистку стека производит .

safecall

safecall — соглашение о вызовах, используемое для вызова методов интерфейсов COM .

Методы интерфейсов COM представляют собой функции , возвращающие тип HRESULT. Код , добавляемый после вызова функции , анализирует возвращаемое значение. При наличии ошибки код записывает код ошибки, сообщение об ошибке и поднимает исключение . Иначе настоящее возвращаемое значение скрывается, вместо него используется параметр, передаваемый в функцию последним по ссылке. Например, можно считать два следующих объявления функции эквивалентными.

// safecall function DoSomething (a : DWORD) : DWORD ; safecall ; // симуляция safecall function DoSomething (a : DWORD ; out Result : DWORD) : HResult ; stdcall ; 

thiscall

thiscall — соглашение о вызовах, используемое компиляторами для языка C++ при вызове методов классов в объектно-ориентированном программировании .

Аргументы функции передаются через стек, справа налево. Очистку стека производит вызываемая функция. Соглашение thiscall отличается от stdcall соглашения только тем, что указатель на объект, для которого вызывается метод ( указатель this ), записывается в регистр ecx .

См. также

  • — библиотека для FFI с набором различных соглашений.

Примечания

  1. ↑ Под небольшими аргументами понимаются значения, размер которых меньше или равен размеру регистра процессора . Например, 1, 2 и 4 байта для процессора x86 , работающего в 32-битном режиме.
  2. ↑ Под большими аргументами понимаются значения, размер которых больше размера регистра процессора . Например, 8 и более байт для процессора x86 , работающего в 32-битном режиме.
  3. Пролог ( англ. prologue) — код , выполняющий сохранение регистров , передачу аргументов в функцию , размещение локальных переменных в стеке функции.
  4. Эпилог ( англ. epilogue) — код , выполняющий возврат управления вызывающей функции, очистку стека, восстановление значений регистров , передачу возвращаемого значения функции.
  5. (неопр.) . docwiki.embarcadero.com (1 июня 2010). Дата обращения: 27 сентября 2010. 20 ноября 2012 года.
  6. (неопр.) . msdn.microsoft.com. Дата обращения: 27 сентября 2010. 20 ноября 2012 года.
  7. Ohse, Uwe (неопр.) . ohse.de. Дата обращения: 27 сентября 2010. 20 ноября 2012 года.
  8. (англ.) . msdn.microsoft.com.

Ссылки

  • на сайте msdn про соглашениях о вызовах, поддерживаемых компилятором Visual Studio .
  • Agner Fog. (рус.) : журнал. — 2014. — 7 августа.

Same as Соглашение о вызове