Notes/System

함수 호출 규약 정리 Function Calling Convention

ReTeu 2023. 5. 18. 18:06

  어느 함수에서 다른 함수를 호출하는 상황을 생각해보자. 기존 함수에서 현재 스택의 상태로 복귀할 수 있도록 현재의 스택 포인터가 가리키고 있는 위치를 기억하고(Caller의 스택 프레임 상태), 리턴 주소(Caller가 Callee를 호출한 주소값)를 어딘가에 저장해둘 필요가 있겠다. 함수 호출 규약이란 간단히 말해 이러한 정보를 어떻게 저장하고 로드할 것인가에 대한 약속이라고 생각하면 된다. 물론 이러한 과정은 컴파일러에 의해 처리되는 것이 일반적이기 때문에 사용자는 컴파일러의 configuration에서 본인이 원하는 규약을 선택해주기만 하면 된다. ( MSVC기준으로 언어별로 자동 선택되는 것인줄 알았는데, 컴파일러에서 컴파일중인 장치의 아키텍처를 보고 적절한 호출 규약을 알아서 선택하게 된다고 한다.) 하지만, 각 호출규약에 따라 그 특성에서 기인하는 성능 차이가 존재하기 때문에 이러한 부분에 민감한 프로그램을 작성하고 있거나, 어셈블리어를 통한 코딩을 하고 있다면 반드시 이러한 개념을 정리해두고 있는 것이 좋겠다.

 

일반적으로 많이 사용하는 호출규약으로는 _cdecl, _stdcall, fastcall 세 가지가 있는데 각각의 특징을 정리하면 다음과 같다.

 

 

1. _cdecl

  cdecl은 x86 시스템에서 도입된 개념이다. 해당 아키텍처의 특성상 레지스터의 수가 넉넉하지 않다는 특징을 가진다. 때문에 씨디클에서는 스택을 이용해서 인자를 전달하게 된다. 매개변수를 순차적으로 푸쉬해 준 다음 callee를 호출하고, 스택의 정리는 caller에서 진행하게 된다.

void __attribute__((cdecl)) callee(int a, int b);

void main(int argc, char* argv[]) {
	callee(34, 1212);
}
# Caller Assembly

PUSH 34				// 마지막 인자부터 전달
PUSH 1212			// 첫 인자 전달
CALL callee
ADD ESP, 8			// 스택 정리는 Caller 함수에서 진행한다.

  씨디클에서는 스택을 이용해서 인자를 전달하기 때문에 printf(...)와 같이 가변적으로 매개변수를 입력받을 수 있는 장점을 가지게 된다. 

 

2. _stdcall

  스탠다드콜은 시디클과 유사한 방식으로 작동한다. 씨디클과 마찬가지로 스택을 이용해서 입력받은 인자를 함수에 전달하지만, callee 함수가 모두 실행된 이후에 callee 단계에서 직접 스택을 정리하고 caller 함수로 돌아가게 된다는 차이점이 있다. 어셈블리어로 살펴보면 다음과 같이 표현할 수 있다.

# Caller Assembly

PUSH 34				// 마지막 인자부터 전달
PUSH 1212			// 첫 인자 전달
CALL callee			// 스택 정리는 Callee 함수에서 진행된다.

 

스탠다드 콜에서는 Callee에서 스택을 모두 정리하기 때문에 Caller의 코드 길이가 많이 줄어드는 특징을 가진다. 

 

3. fastcall

  fast라는 이름처럼 기대에 부응해 닉값하는 호출 규약이다. 기본적으로는 스탠다드콜과 동일한 방식으로 작동하지만, 맨 앞의 매개변수 두 개까지는 레지스터를 이용해서 전달하기 때문에 스탠다드 콜보다 빠르게 작동할 수 있다. ( 레지스터를 이용하면, 메모리에 접근하여 스택에 담긴 값을 읽어오고 캐싱한 다음 레지스터에 옮기는 과정이 생략되기 때문에 바르게 작동할 수 있다 )

# Caller Assembly

MOV EDX, 34			// 마지막 인자부터 전달
MOV ECX, 1212		// 첫 인자 전달
CALL callee			// 스택 정리는 Callee 함수에서 진행된다.

 

4. 실제 예시 살펴보기

다음은 Windows에서 제공하는 기본 API에 포함된 GetMessageA() 함수를 대상으로 어떤 호출 방식을 사용하는지 확인해본 예시이다. (참고로 일반적으로 Windows API들은 stdcall을 사용하는 것으로 알려져있다.)