카테고리:

함수 호출 규약

함수를 호출하는 방식을 의미하며 인자 전달 순서, 인자 전달 방법, 스택 프레임 정리 방법에 따라 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