Из Википедии, бесплатной энциклопедии
  (Перенаправлено со стандартного вызова )
Перейти к навигации Перейти к поиску

В этой статье описываются соглашения о вызовах, используемые при программировании микропроцессоров архитектуры x86 .

Соглашения о вызовах описывают интерфейс вызываемого кода:

  • Порядок, в котором размещаются атомарные (скалярные) параметры или отдельные части сложного параметра.
  • Как параметры передаются (помещаются в стек, помещаются в регистры или и то, и другое)
  • Какие регистры вызываемая функция должна сохранять для вызывающего абонента (также известные как регистры, сохраненные вызываемым пользователем или энергонезависимые регистры)
  • Как задача подготовки стека и восстановления после вызова функции распределяется между вызывающим и вызываемым

Это тесно связано с назначением размеров и форматов типам языка программирования. Другой тесно связанной темой является изменение имен , которое определяет, как имена символов в коде отображаются в имена символов, используемые компоновщиком. Соглашения о вызовах, представления типов и изменение имен - все это часть того, что известно как двоичный интерфейс приложения (ABI).

Часто существуют тонкие различия в том, как разные компиляторы реализуют эти соглашения, поэтому часто бывает сложно связать код, который компилируется разными компиляторами. С другой стороны, соглашения, которые используются в качестве стандарта API (например, stdcall), реализованы очень единообразно.

Историческая справка [ править ]

До микрокомпьютеров производитель машин обычно предоставлял операционную систему и компиляторы для нескольких языков программирования . Соглашения о вызовах для каждой платформы были определены инструментами программирования производителя.

Ранние микрокомпьютеры до Commodore Pet и Apple II обычно поставлялись без ОС или компиляторов. IBM PC пришел с предвестником от Microsoft в Windows, операционная систему диска ( DOS ), но он не пришел с компилятором. Единственный аппаратный стандарт для IBM PC-совместимых машин был определен процессорами Intel (8086, 80386) и буквальным оборудованием, поставляемым IBM. Расширения оборудования и все стандарты программного обеспечения (за исключением соглашения о вызовах BIOS ) были брошены на рынок конкуренции.

Множество независимых фирм по разработке программного обеспечения предлагали операционные системы, компиляторы для многих языков программирования и приложения. Фирмы реализовали множество различных схем вызова, часто взаимоисключающих, основанных на различных требованиях, исторической практике и творческом подходе программистов.

После потрясения рынка, связанного с IBM, преобладали операционные системы и инструменты программирования Microsoft (с разными соглашениями), в то время как фирмы второго уровня, такие как Borland и Novell , и проекты с открытым исходным кодом, такие как GCC , по-прежнему поддерживали свои собственные стандарты. В конечном итоге были приняты положения о взаимодействии между поставщиками и продуктами, что упростило проблему выбора жизнеспособного соглашения. [1]

Очистка вызывающего абонента [ править ]

В этих соглашениях вызывающий объект очищает аргументы из стека.

cdecl [ править ]

Cdecl (что означает C декларацию ) является соглашением о вызовах , которые берут свое начало от компилятора Microsoft, для языка программирования Си и используются многими компиляторами для архитектуры x86 . [1] В cdecl аргументы подпрограммы передаются в стек . Целочисленные значения и адреса памяти возвращаются в регистре EAX , значения с плавающей запятой - в регистре ST0 x87 . Регистры EAX, ECX и EDX сохраняются для вызывающего абонента, а остальные - для вызываемого абонента. x87Регистры с плавающей запятой от ST0 до ST7 должны быть пустыми (извлечены или освобождены) при вызове новой функции, а регистры от ST1 до ST7 должны быть пустыми при выходе из функции. ST0 также должен быть пустым, если не используется для возврата значения.

В контексте языка программирования C аргументы функции помещаются в стек в порядке справа налево, т. Е. Последний аргумент помещается первым.

Рассмотрим следующий фрагмент исходного кода C:

int  вызываемый ( интервал ,  интервал ,  интервал );int  caller ( void ) { вернуть  вызываемый ( 1 ,  2 ,  3 )  +  5 ; }

На x86 он может создать следующий код сборки ( синтаксис Intel ):

Вызывающий:  ; создать новый кадр вызова  ; (некоторые компиляторы могут вместо этого выдавать инструкцию 'enter')  push  ebp  ; сохранить старый кадр вызова  mov  ebp ,  esp  ; инициализировать новый кадр вызова  ; аргументы push-вызова в обратном порядке  ; (некоторые компиляторы могут вычитать необходимое пространство из указателя стека  ; затем записывать каждый аргумент напрямую, см. ниже  ; инструкция 'enter' также может делать что-то подобное)  ; sub esp, 12: инструкция 'enter' может сделать это за нас  ; mov [ebp-4], 3: или mov [esp + 8], 3  ; mov [ebp-8], 2: или mov [esp + 4], 2  ; mov [ebp-12], 1: или mov [esp], 1  нажатие  3  нажатие  2 нажмите  1  вызов  вызываемого абонента  ; вызов подпрограммы 'callee'  add  esp ,  12  ; удалить аргументы вызова из кадра  add  eax ,  5  ; изменить результат подпрограммы  ; (eax - это возвращаемое значение нашего вызываемого  ; поэтому нам не нужно перемещать его в локальную переменную)  ; восстановить старую рамку вызова  ; (некоторые компиляторы могут вместо этого выдавать инструкцию выхода)  mov  esp ,  ebp  ; большинство соглашения о вызовах диктует EBP быть вызываемыми сохранено,  ; т.е. сохраняется после вызова вызываемого абонента.  ; поэтому он по-прежнему указывает на начало нашего кадра стека.  ; нам нужно убедиться ; вызываемый абонент не изменяет (и не восстанавливает) ebp, однако  ; поэтому нам нужно убедиться  ; он использует соглашение о вызовах, которое делает этот  всплывающий  ebp  ; восстановить старый кадр вызова  ret  ; возвращаться

Вызывающий очищает стек после возврата из вызова функции.

Соглашение о вызовах cdecl обычно является соглашением о вызовах по умолчанию для компиляторов C x86 , хотя многие компиляторы предоставляют параметры для автоматического изменения используемых соглашений о вызовах. Чтобы вручную определить функцию как cdecl, некоторые поддерживают следующий синтаксис:

возвращаемый_тип  __cdecl  FUNC_NAME ();

Варианты [ править ]

Есть несколько вариантов интерпретации cdecl. В результате программы x86, скомпилированные для разных платформ операционных систем и / или разными компиляторами, могут быть несовместимы, даже если они оба используют соглашение «cdecl» и не обращаются к базовой среде.

Что касается того, как возвращать значения, некоторые компиляторы возвращают простые структуры данных с длиной 2 или менее регистров в паре регистров EAX: EDX, а также более крупные структуры и объекты классов, требующие специальной обработки обработчиком исключений (например, определенный конструктор, деструктор или присваивание) возвращаются в память. Для передачи «в память» вызывающий объект выделяет память и передает на нее указатель в качестве скрытого первого параметра; вызываемый объект заполняет память и возвращает указатель, выдвигая скрытый указатель при возврате. [2]

В Linux , GCC устанавливает де - факто стандартом для соглашения о вызовах. Начиная с версии 4.5 GCC, стек должен быть выровнен по 16-байтовой границе при вызове функции (предыдущие версии требовали только 4-байтового выравнивания). [1] [3]

Версия cdecl описана в System V ABI для систем i386. [4]

системный вызов [ править ]

Это похоже на cdecl в том, что аргументы сдвигаются справа налево. EAX, ECX и EDX не сохраняются. Размер списка параметров в двойных словах передается в AL.

Системный вызов - это стандартное соглашение о вызовах для 32-битного OS / 2 API.

optlink [ редактировать ]

Аргументы сдвигаются справа налево. Три первых (крайних левых) аргумента передаются в EAX, EDX и ECX, и до четырех аргументов с плавающей запятой передаются в ST0 по ST3, хотя место для них зарезервировано в списке аргументов в стеке. Результаты возвращаются в EAX или ST0. Регистры EBP, EBX, ESI и EDI сохраняются.

Optlink используется компиляторами IBM VisualAge .

Очистка Callee [ править ]

В этих соглашениях вызываемый объект очищает аргументы из стека. Функции, использующие эти соглашения, легко распознать в коде ASM, потому что они раскручивают стек после возврата. Инструкция x86 ret допускает необязательный 16-битный параметр, который указывает количество байтов стека, которые нужно освободить после возврата к вызывающей стороне. Такой код выглядит так:

ret  12

Соглашения, названные fastcall или register , не были стандартизированы и были реализованы по-разному в зависимости от поставщика компилятора. [1] Обычно соглашения о вызовах на основе регистров передают один или несколько аргументов в регистрах, что уменьшает количество обращений к памяти, необходимых для вызова, и, таким образом, обычно делает их быстрее.

паскаль [ править ]

В соответствии с соглашением о вызовах языка программирования Borland Pascal параметры помещаются в стек в порядке слева направо (в противоположность cdecl), а вызываемый объект отвечает за их удаление из стека.

Возврат результата работает следующим образом:

  • Порядковые значения возвращаются в форматах AL (8-битные значения), AX (16-битные значения), EAX (32-битные значения) или DX: AX (32-битные значения в 16-битных системах).
  • Реальные значения возвращаются в формате DX: BX: AX.
  • Значения с плавающей запятой (8087) возвращаются в ST0.
  • Указатели возвращаются в EAX в 32-битных системах и в AX в 16-битных системах.
  • Строки возвращаются во временном месте, указанном символом @Result.

Это соглашение о вызовах было распространено в следующих 16-битных API: OS / 2 1.x, Microsoft Windows 3.x и Borland Delphi версии 1.x. Современные версии Windows API используют stdcall , в котором вызываемый объект все еще восстанавливает стек, как в соглашении Pascal, но параметры теперь сдвигаются справа налево.

stdcall [ править ]

Соглашение о вызовах stdcall [5] - это вариант соглашения о вызовах Паскаля, в котором вызываемый объект отвечает за очистку стека, но параметры помещаются в стек в порядке справа налево, как в соглашении о вызовах _cdecl. Регистры EAX, ECX и EDX предназначены для использования в функции. Возвращаемые значения хранятся в регистре EAX.

stdcall - это стандартное соглашение о вызовах для Microsoft Win32 API и Open Watcom C ++ .

Microsoft fastcall [ править ]

Соглашение Microsoft __fastcall (также известное как __msfastcall ) передает первые два аргумента (оцениваемые слева направо), которые подходят для ECX и EDX. [6] Остальные аргументы помещаются в стек справа налево. Когда компилятор компилируется для IA64 или AMD64 , он игнорирует ключевое слово __fastcall и вместо этого использует одно 64-битное соглашение о вызовах .

Как очень распространенное соглашение о вызовах, другие компиляторы, такие как GCC, Clang и ICC, также поддерживают fastcall. [7]

Рассмотрим следующий фрагмент c:

__attribute__ (( азЬсаЙ ))  недействительное  printnums ( INT  num1 ,  INT  пит2 ,  INT  num3 ) { Printf ( "Число вы посланные является:% d% d% d" ,  num1 ,  пит2 ,  num3 ); }int  main () { printnums ( 3 ,  2 ,  1 ); возврат  0 ; }

x86 декомпиляция основной функции будет выглядеть (в синтаксисе Intel):

основной :; настройка стека push  ebp mov  ebp ,  esp push  3  ; сразу 3 (первый аргумент помещается в стек) mov  edx ,  0x2  ; сразу 2 (второй аргумент) копируется в регистр edx. mov  ecx ,  0x1  ; сразу 1 (третий аргумент) копируется в регистр ecx. вызвать  printnums mov  eax ,  0  ; вернуть 0 оставить retn

Первые два аргумента передаются слева направо, а третий аргумент помещается в стек. Очистка стека не выполняется, так как очистка стека выполняется вызываемым пользователем. Дизассемблирование вызываемой функции:

printnums: ; настройка стека push  ebp mov  ebp ,  esp sub  esp ,  0x08 mov  [ ebp-0x04 ],  ecx  ; в x86 ecx = первый аргумент. mov  [ ebp-0x08 ],  edx  ; arg2 push  [ ebp + 0x08 ]  ; arg3 помещается в стек. нажмите  [ ebp-0x08 ]  ; arg2 помещается push  [ ebp-0x04 ]  ; arg1 нажимается push  0x8065d67  ; "Отправленные вами числа:% d% d% d"вызовите  printf ; очистка стека добавить  esp ,  0x10 nop leave retn  0x04

Поскольку два аргумента были переданы через регистры и только один параметр был помещен в стек, переданное значение очищается инструкцией retn, так как int имеет размер 4 байта в системах x86.

Microsoft vectorcall [ править ]

В Visual Studio 2013 Microsoft представила соглашение о вызовах __vectorcall в ответ на соображения эффективности со стороны разработчиков игр, графики, видео / аудио и кодеков. Схема позволяет передавать большие векторные типы ( float , double , __m128 , __m256 ) в регистры, а не в стек. [8]

Для кода IA-32 и x64 __vectorcall аналогичен __fastcall и исходным соглашениям о вызовах x64 соответственно, но расширяет их для поддержки передачи векторных аргументов с использованием регистров SIMD . В IA-32 целочисленные значения передаются как обычно, а первые шесть регистров SIMD ( XMM / YMM 0-5) содержат до шести значений с плавающей запятой, векторов или значений HVA последовательно слева направо, независимо от фактических позиций. вызвано, например, аргументом типа int, возникающим между ними. В x64, однако, по-прежнему применяется правило из исходного соглашения x64, так что XMM / YMM0-5 содержат только аргументы с плавающей запятой, векторные или HVA, когда они оказываются с первого по шестой. [9]

__vectorcall добавляет поддержку для передачи значений однородных векторных агрегатов (HVA), которые являются составными типами (структурами), состоящими только из четырех идентичных векторных типов с использованием одних и тех же шести регистров. После того, как регистры были выделены для аргументов векторного типа, неиспользуемые регистры назначаются аргументам HVA слева направо. Правила позиционирования по-прежнему действуют. Результирующий векторный тип и значения HVA возвращаются с использованием первых четырех регистров XMM / YMM. [9]

Компилятор clang и компилятор Intel C ++ также реализуют vectorcall. [10] Компилятор Intel C ++ имел аналогичное, более раннее соглашение, называемое __regcall ; он также поддерживается clang. [11]

Регистр Borland [ править ]

Оценивая аргументы слева направо, он передает три аргумента через EAX, EDX, ECX. Остальные аргументы помещаются в стек, также слева направо. [12] Это стандартное соглашение о вызовах 32-битного компилятора Delphi , где оно известно как регистр . Это соглашение о вызовах также используется Embarcadero C ++ Builder, где оно называется __fastcall . [13] В этом компиляторе Microsoft fastcall может использоваться как __msfastcall . [14]

GCC и Clang можно использовать аналогичное соглашение о вызовах , используя __stdcallс regparmатрибутом функции или -mregparm=3переключателем. (Порядок стека инвертируется.) Также можно создать вариант очистки вызывающей стороны, используя cdeclили расширив его, чтобы также использовать регистры SSE. [15] Версия на cdeclоснове A используется ядром Linux на i386, начиная с версии 2.6.20 (выпущенной в феврале 2007 г.). [16]

Регистр Watcom [ править ]

Watcom не поддерживает ключевое слово __fastcall, за исключением присвоения ему псевдонима null. Соглашение о вызове регистра может быть выбрано переключателем командной строки. (Однако IDA в любом случае использует __fastcall для единообразия.)

Аргументам назначается до 4 регистров в порядке EAX, EDX, EBX, ECX. Аргументы присваиваются регистрам слева направо. Если какой-либо аргумент не может быть назначен регистру (скажем, он слишком велик), он и все последующие аргументы назначаются стеку. Аргументы, назначенные стеку, сдвигаются справа налево. Имена искажаются добавлением суффикса подчеркивания.

Функции Variadic возвращаются к соглашению о вызовах на основе стека Watcom.

Компилятор Watcom C / C ++ также использует директиву #pragma aux [17], которая позволяет пользователю определять собственное соглашение о вызовах. Как сказано в руководстве: «Этот метод, вероятно, понадобится очень немногим пользователям, но если он понадобится, он может быть спасением».

TopSpeed ​​/ Clarion / JPI [ редактировать ]

Первые четыре целочисленных параметра передаются в регистры eax, ebx, ecx и edx. Параметры с плавающей запятой передаются в стек с плавающей запятой - регистры st0, st1, st2, st3, st4, st5 и st6. Параметры структуры всегда передаются в стек. Дополнительные параметры передаются в стек после того, как регистры исчерпаны. Целочисленные значения возвращаются в eax, указатели - в edx, а типы с плавающей запятой - в st0.

SafeCall [ править ]

В Delphi и Free Pascal в Microsoft Windows соглашение о вызовах safecall инкапсулирует обработку ошибок COM ( компонентная объектная модель ), поэтому исключения не передаются вызывающей стороне, а сообщаются в возвращаемом значении HRESULT , как того требует COM / OLE. При вызове функции safecall из кода Delphi Delphi также автоматически проверяет возвращенное значение HRESULT и при необходимости вызывает исключение.

Соглашение о вызове safecall такое же, как соглашение о вызове stdcall, за исключением того, что исключения передаются обратно вызывающей стороне в EAX как HResult (вместо FS: [0]), а результат функции передается по ссылке в стеке как хотя это был последний параметр «out». При вызове функции Delphi из Delphi это соглашение о вызовах будет выглядеть так же, как и любое другое соглашение о вызовах, потому что, хотя исключения передаются обратно в EAX, они автоматически преобразуются вызывающей стороной обратно в соответствующие исключения. При использовании COM-объектов, созданных на других языках, HResults будут автоматически возникать как исключения, а результат для функций Get будет в результате, а не в параметре. При создании COM-объектов в Delphi с помощью safecall не нужно беспокоиться о результатах HResults,как исключения можно вызывать как обычно, но на других языках они будут отображаться как результаты HResults.

функция  имя_функции ( a :  DWORD ) :  DWORD ;  SafeCall ;

Возвращает результат и вызывает исключения, как обычная функция Delphi, но передает значения и исключения, как если бы это было:

Функция  function_name ( : DWORD ; из Результат : DWORD ) : HResult ; stdcall ;      

Очистка вызывающего или вызываемого абонента [ править ]

thiscall [ править ]

Это соглашение о вызовах используется для вызова нестатических функций-членов C ++. Существуют две основные версии этого вызова, которые используются в зависимости от компилятора и от того, использует ли функция переменное количество аргументов.

Для компилятора GCC этот вызов почти идентичен cdecl : вызывающий объект очищает стек, а параметры передаются в порядке справа налево. Разница заключается в добавлении указателя this , который помещается в стек последним, как если бы он был первым параметром в прототипе функции.

В компиляторе Microsoft Visual C ++ указатель this передается в ECX, и именно вызываемый объект очищает стек, отражая соглашение stdcall, используемое в C для этого компилятора и в функциях Windows API. Когда функции используют переменное количество аргументов, именно вызывающая сторона очищает стек (см. Cdecl ).

Thiscall соглашение о вызовах может быть явно указано только на Microsoft Visual C ++ 2005 и более поздних версий. В любом другом компиляторе этот вызов не является ключевым словом. (Однако дизассемблеры, такие как IDA , должны указывать это. Поэтому IDA использует для этого ключевое слово __thiscall .)

Регистрация сохранения [ править ]

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

Сохраненные (энергозависимые) регистры вызывающего абонента [ править ]

Согласно Intel ABI, которому соответствует подавляющее большинство компиляторов, EAX, EDX и ECX должны быть бесплатными для использования в рамках процедуры или функции и не должны сохраняться [ необходима цитата ] .

Как следует из названия, эти регистры общего назначения обычно содержат временную (изменчивую) информацию, которая может быть перезаписана любой подпрограммой.

Следовательно, вызывающая сторона несет ответственность за то, чтобы поместить каждый из этих регистров в стек, если он хочет восстановить их значения после вызова подпрограммы.

Сохраненные вызываемым абонентом (энергонезависимые) регистры [ править ]

Другие регистры используются для хранения долгоживущих значений (энергонезависимых), которые должны сохраняться при вызовах.

Другими словами, когда вызывающий объект выполняет вызов процедуры, он может ожидать, что эти регистры будут содержать то же значение после возврата вызываемого объекта.

Таким образом, возлагая на вызываемого абонента ответственность за их сохранение (нажатие в начале) и восстановление (выталкивание соответственно) перед возвратом к вызывающему. Как и в предыдущем случае, эту практику следует применять только к регистрам, которые изменяет вызываемый объект.

соглашения о вызовах x86-64 [ править ]

Соглашения о вызовах x86-64 используют дополнительное пространство регистров для передачи большего количества аргументов в регистры. Также было уменьшено количество несовместимых соглашений о вызовах. Обычно используются два.

Соглашение о вызовах Microsoft x64 [ править ]

Соглашение о вызовах Microsoft x64 [18] [19] соблюдается в Windows и предзагрузочном UEFI (для длительного режима на x86-64 ). Первые четыре аргумента помещаются в регистры. Это означает RCX, RDX, R8, R9 для целочисленных аргументов, структур или указателей (в этом порядке) и XMM0, XMM1, XMM2, XMM3 для аргументов с плавающей запятой. Дополнительные аргументы помещаются в стек (справа налево). Целочисленные возвращаемые значения (аналогичные x86) возвращаются в RAX, если 64 бита или меньше. Возвращаемые значения с плавающей запятой возвращаются в XMM0. Параметры длиной менее 64 бит не расширяются нулем; старшие биты не обнуляются.

Структуры и объединения с размерами, соответствующими целым числам, передаются и возвращаются, как если бы они были целыми числами. В противном случае они заменяются указателем при использовании в качестве аргумента. Когда требуется возврат слишком большой структуры, в качестве первого аргумента добавляется другой указатель на пространство, предоставленное вызывающей стороной, смещая все остальные аргументы вправо на одно место. [20]

При компиляции для архитектуры x64 в контексте Windows (с использованием инструментов Microsoft или сторонних производителей) stdcall, thiscall, cdecl и fastcall разрешают использование этого соглашения.

В соглашении о вызовах Microsoft x64 вызывающий должен выделить 32 байта «теневого пространства» в стеке прямо перед вызовом функции (независимо от фактического количества используемых параметров) и вставить стек после вызова. Теневое пространство используется для передачи RCX, RDX, R8 и R9 [21], но должно быть доступно для всех функций, даже для тех, у которых меньше четырех параметров.

Регистры RAX, RCX, RDX, R8, R9, R10, R11 считаются энергозависимыми (с сохранением вызывающего абонента). [22]

Регистры RBX, RBP, RDI, RSI, RSP, R12, R13, R14 и R15 считаются энергонезависимыми (с сохранением вызываемого). [22]

Например, функция, принимающая 5 целочисленных аргументов, будет занимать регистры с первого по четвертый, а пятый будет помещен поверх теневого пространства. Поэтому при входе в вызываемую функцию стек будет состоять из (в порядке возрастания) адреса возврата, за которым следует теневое пространство (32 байта), за которым следует пятый параметр.

В x86-64 Visual Studio 2008 хранит числа с плавающей запятой в XMM6 и XMM7 (а также от XMM8 до XMM15); следовательно, для x86-64 подпрограммы на языке ассемблера, написанные пользователем, должны сохранять XMM6 и XMM7 (по сравнению с x86, где подпрограммы на языке ассемблера, написанные пользователем, не должны сохранять XMM6 и XMM7). Другими словами, пользовательские подпрограммы на языке ассемблера должны быть обновлены для сохранения / восстановления XMM6 и XMM7 до / после функции при переносе с x86 на x86-64 .

Начиная с Visual Studio 2013, Microsoft представила соглашение о вызовах __vectorcall, которое расширяет соглашение x64.

System V AMD64 ABI [ править ]

Вызывающий условность System V AMD64 ABI следуют на Solaris , Linux , FreeBSD , MacOS , [23] и является стандартом де - факто среди Unix и Unix-подобных операционных систем. Стандарт вызовов OpenVMS на x86-64 основан на System V ABI с некоторыми расширениями, необходимыми для обратной совместимости. [24] Первые шесть целочисленных аргументов или аргументов-указателей передаются в регистры RDI, RSI, RDX, RCX, R8, R9 (R10 используется как статический указатель цепочки в случае вложенных функций [25] : 21), в то время как XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6 и XMM7 используются для первых аргументов с плавающей запятой. [25] : 22 Как и в соглашении о вызовах Microsoft x64, в стек передаются дополнительные аргументы. [25] : 22 Целочисленные возвращаемые значения размером до 64 битов сохраняются в RAX, а значения до 128 битов сохраняются в RAX и RDX. Возвращаемые значения с плавающей запятой аналогичным образом сохраняются в XMM0 и XMM1. [25] : 25 Более широкие регистры YMM и ZMM используются для передачи и возврата более широких значений вместо XMM, когда они существуют. [25] : 26,55

Если вызываемый абонент желает использовать регистры RBX, RSP, RBP и R12 – R15, он должен восстановить их исходные значения перед возвратом управления вызывающему. Все остальные регистры должны быть сохранены вызывающей стороной, если она желает сохранить их значения. [25] : 16

Для функций листовых узлов (функций, которые не вызывают никаких других функций) 128-байтовое пространство хранится сразу под указателем стека функции. Пространство называется красной зоной . Эта зона не будет заблокирована обработчиками сигналов или прерываний. Таким образом, компиляторы могут использовать эту зону для сохранения локальных переменных. Компиляторы могут пропустить некоторые инструкции при запуске функции (настройка RSP, RBP), используя эту зону. Однако другие функции могут сбивать эту зону. Следовательно, эту зону следует использовать только для функций листовых узлов. gccи clangпредложите -mno-red-zoneфлаг для отключения оптимизации красной зоны.

Если вызываемый объект является функцией с переменным числом аргументов, то количество аргументов с плавающей запятой, переданных функции в векторных регистрах, должно быть предоставлено вызывающей стороной в регистре AL. [25] : 55

В отличие от соглашения о вызовах Microsoft, теневое пространство не предусмотрено; при входе в функцию адрес возврата находится рядом с седьмым целочисленным аргументом в стеке.

Список соглашений о вызовах x86 [ править ]

Это список соглашений о вызовах x86. [1] Это соглашения, в основном предназначенные для компиляторов C / C ++ (особенно для 64-битной части ниже) и, следовательно, в основном для особых случаев. Другие языки могут использовать другие форматы и соглашения в своих реализациях.

Ссылки [ править ]

Сноски [ править ]

  1. ^ а б в г д Агнер Туман (16.02.2010). Соглашения о вызовах для различных компиляторов C ++ и операционных систем (PDF) .
  2. ^ де Бойн Поллард, Джонатан (2010). «Генеральные соглашения о вызове функций» . Часто задаваемые ответы .
  3. ^ «GCC Bugzilla - Ошибка 40838 - gcc не должен предполагать, что стек выровнен» . 2009 г.
  4. ^ "СИСТЕМА V ПРИЛОЖЕНИЕ БИНАРНЫЙ ИНТЕРФЕЙС Intel 386 Дополнение для процессора архитектуры, четвертое издание" (PDF) .
  5. ^ "__stdcall (C ++)" . MSDN . Microsoft. Архивировано из оригинала на 2008-04-10 . Проверено 13 февраля 2019 .
  6. ^ "__fastcall" . MSDN . Проверено 26 сентября 2013 .
  7. ^ Осе, Уве. «Обзор атрибута gcc: функция fastcall» . ohse.de . Проверено 27 сентября 2010 .
  8. ^ «Представляем« Соглашение о вызовах векторов » » . MSDN . Проверено 31 декабря 2014 .
  9. ^ a b c d "__vectorcall" . MSDN . Проверено 31 декабря 2014 .
  10. ^ «Атрибуты в Clang: Соглашения о вызовах» . Документация Clang . Проверено 8 октября 2019 .
  11. ^ "_vectorcall и __regcall демистифицированы" . software.intel.com . 7 июня 2017.
  12. ^ «Управление программой: Соглашение о регистрах» . docwiki.embarcadero.com. 2010-06-01 . Проверено 27 сентября 2010 .
  13. ^ "_fastcall, __fastcall" . docwiki.embarcadero.com.
  14. ^ "__msfastcall" . docwiki.embarcadero.com.
  15. ^ "Атрибуты функции x86" . Использование коллекции компиляторов GNU (GCC) .
  16. ^ "i386: всегда включать регпарм" .
  17. ^ "Calling_Conventions: Specifying_Calling_Conventions_the_Watcom_Way" . openwatcom.org. 2010-04-27 . Проверено 31 августа 2018 .
  18. ^ a b «Соглашения о программном обеспечении x64: Соглашения о вызовах» . msdn.microsoft.com. 2010 . Проверено 27 сентября 2010 .
  19. ^ «Архитектура x64» . msdn.microsoft.com.
  20. ^ «Соглашение о вызовах x64: возвращаемые значения» . docs.microsoft.com . Проверено 17 января 2020 .
  21. ^ «Соглашения о программном обеспечении x64 - Размещение стека» . Microsoft . Проверено 31 марта 2010 .
  22. ^ a b «Сохраненные регистры вызывающего / вызываемого абонента» . Документы Microsoft . Microsoft.
  23. ^ "Модель кода x86-64" . Библиотека разработчика Mac . Apple Inc. Архивировано 10 марта 2016 года . Проверено 6 апреля 2016 . Среда x86-64 в OS X имеет только одну модель кода для кода пользовательского пространства. Она больше всего похожа на небольшую модель PIC, определенную x86-64 System V ABI.
  24. ^ "Стандарт вызовов VSI OpenVMS" (PDF) . vmssoftware.com . Май 2020 . Проверено 21 декабря 2020 .
  25. ^ Б с д е е г ч Майкл Мац; Ян Губичка; Андреас Йегер; и др., ред. (2018-01-28). «Двоичный интерфейс приложения System V: Дополнение к процессору архитектуры AMD64 (с моделями программирования LP64 и ILP32), версия 1.0» (PDF) . 1.0.
  26. ^ Руководство пользователя Borland C / C ++ версии 3.1 (PDF) . Borland. 1992. С. 158, 189–191.
  27. ^ «Зарегистрируйте использование» . Документы Microsoft . Microsoft . Проверено 15 сентября 2017 года .

Другие источники [ править ]

  • «Двоичный интерфейс приложения SYSTEM V для процессоров архитектуры Intel386» (PDF) (4-е изд.). Операция Санта-Крус, Inc. 19 марта 1997 г. Цитировать журнал требует |journal=( помощь )
  • Неманья Трифунович (22 июля 2001 г.). Шон Юингтон (ред.). «Обозначение условностей, лишенное мистики» . Кодовый проект .
  • Стивен Дж. Фридл. «Соглашения о вызове функций Intel x86 - представление сборки» . Технические советы Стива Фридла по Unixwiz.net .
  • «Visual Studio 2010 - Соглашение о вызовах Visual C ++» . Библиотека MSDN . Microsoft. 2010 г.
  • Андреас Йонссон (13 февраля 2005 г.). «Соглашения о вызовах на платформе x86» .
  • Раймонд Чен (2004-01-02). «История звонков, часть 1» . Старая новая вещь .
  • Раймонд Чен (2004-01-07). «История звонков, часть 2» . Старая новая вещь .
  • Раймонд Чен (2004-01-08). «История звонков, часть 3» . Старая новая вещь .
  • Раймонд Чен (13 января 2004). «История соглашений о вызовах, часть 4: ia64» . Старая новая вещь .
  • Раймонд Чен (2004-01-14). «История соглашений о вызовах, часть 5; amd64» . Старая новая вещь .

Дальнейшее чтение [ править ]

  • Джонатан де Бойн Поллард (2010). «Генеральные соглашения о вызове функций» . Часто задаваемые ответы .
  • Кип Р. Ирвин (2011). «Расширенные процедуры (Глава 8)». Язык ассемблера для процессоров x86 (6-е изд.). Прентис Холл. ISBN 978-0-13-602212-1.
  • Руководство пользователя Borland C / C ++ версии 3.1 (PDF) . Borland. 1992. С. 158, 189–191.
  • Томас Лауэр (1995). «Новая последовательность вызовов __stdcall». Перенос на Win32: руководство по подготовке приложений к 32-разрядному будущему Windows . Springer. ISBN 978-0-387-94572-9.