함수 호출 규약 (Calling Convention)
카테고리: C++
함수 호출 규약
함수를 호출하는 방식을 의미하며 인자 전달 순서, 인자 전달 방법, 스택 프레임 정리 방법에 따라 6가지로 나누어진다.
__cdecl
- 인자 전달 순서: 우측부터 좌측
- 인자 전달 방법: 스택
- 스택 관리: Caller
C와 C++ 프로그램의 디폴트 함수 호출 규약이다.
스택 정리를 Caller에서 하기 때문에 __cdecl 함수는 가변 인자를 사용할 수 있다.
기본적으로 모든 함수는 명시적으로 선언하지 않았다면 __cdecl로 선언되어 있지만, /Gv, /Gz 등 다른 함수 호출 규약을 디폴트 값으로 사용하도록 명시하였다면 함수 타입과 이름 사이에 __cdecl을 넣어주어야 한다.
__stdcall
- 인자 전달 순서: 우측부터 좌측
- 인자 전달 방법: 스택
- 스택 관리: Callee
Win32 API 함수들에 사용되는 함수 호출 규약이다.
스택 정리는 Callee에서 이루어지기 때문에 __stdcall은 가변 인자를 사용할 수 없다.
__fastcall
- 인자 전달 순서: 첫 두 인자 좌측부터 우측, 그 외 우측부터 좌측
- 인자 전달 방법: 첫 두 인자 ECX와 EDX 레지스터, 그 외 스택
- 스택 관리: Callee
stdcall과 비슷하게 동작하지만, 가능하다면 레지스터를 먼저 사용하여 인자를 전달하는 함수 호출 규약이다.
x86 개발 환경에서만 적용이 가능하다.
__thiscall
- 인자 전달 순서: this 포인터 전달
- 인자 전달 방법: 스택, this 주소는 ECX 레지스터
- 스택 관리: Callee
함수에서 this 호출 시 사용되는 함수 호출 규약이다.
스택 관리를 Callee가 하기 때문에 가변 인자를 사용할 수 없다.
__clrcall
- 인자 전달 순서: 좌측부터 우측
- 인자 전달 방법: 스택
- 스택 관리: X
관리 코드에서만 함수를 호출하도록 지정하는 함수 호출 규약이다.
C++에서 선언된 함수는 기본적으로 관리 코드에서의 진입점과 비관리 코드(Native Code)에서의 진입점 두가지를 생성하는 Double Thunking이라는 것을 진행한다.
하지만 __clrcall로 선언된 함수는 비관리 코드 진입점을 생성하지 않기 때문에 비관리 코드에서의 호출이 불가능하다.
관리 코드에서만 호출되는 모든 가상 함수들은 __clrcall로 선언됩니다.
__vectorcall
- 인자 전달 순서: 우측에서 좌측
- 인자 전달 방법: 레지스터에 저장 후, 역순으로 스택에 삽입
- 스택 관리: Callee
가능하다면 모든 인자를 레지스터로 이동하게 하여 __fastcall보다 많은 레지스터를 사용하는 함수 호출 규약.
__fastcall을 확장하여 2개로 제한되었던 레지스터를 늘려 인자 전달 속도를 늘린 함수 호출 규약이라 볼 수 있다.
참조: https://learn.microsoft.com/en-us/cpp/cpp/argument-passing-and-naming-conventions?view=msvc-170