본문 바로가기
정상을향해/Program Analysis

Calling Convention

by 사이테일 2013. 11. 10.

출처 : www.reversecore.com

 

 

Calling Convention

 

우리 말로 '함수 호출 규약'이라고 한다.

'함수를 호출할 때 파라미터를 어떤 식으로 전달하고, 스택을 어떻게 정리하는가'에 대한 일종의 약속이다.

 

우린 이미 함수 호출 전에 파라미터가 스택을 통해 전달된다는 것을 알고 있다.

 

스택은 프로세스에서 정의된 메모리 공간이며 아래 방향(높은 주소에서 낮은 주소로)으로 자란다.

 

또한 PE header에 그 크기가 명시되어 있다.

, 프로세스가 실행될 때 스택 메모리의 크기가 결정된다. (malloc, new와 같은 동적 메모리 할당과는 다르다.)

 

질문) 그렇다면 함수가 실행 완료 되었을 때 스택에 들어있던 파라미터는 어떻게 해야 할까?

 

그대로 놔두면 된다.

스택에 저장된 값을 임시로 사용하는 값이기 때문에 더 이상 사용하지 않는다 하더라도 값을 지우려고 하면 쓸데없이 CPU 자원을 소모하게 된다. 어차피 다음 스택에 값을 입력할 때 저절로 덮어쓴다.

 

또한 스택 메모리는 이미 고정되어 있기 때문에 메모리 해제를 할 수 없고, 할 필요도 없다.

 

질문) ESP는 어떻게 되어야 할까?

 

ESP 값은 함수 호출 전으로 복원되어야 한다.

이와 같이 해야 참조 가능한 스택의 크기가 줄어들지 않게 된다.

 

스택 메모리는 고정되어 있고 ESP로 스택의 현재 위치를 가리키는데

만약 ESP가 스택의 끝을 가리킨다면 더 이상 스택을 사용할 수 없다.

 

함수 호출 후에 ESP를 어떻게 정리하는지에 대한 약속이 바로 Calling Convention이다.

 

주요한 Calling Convention은 다음과 같다.

 

 - cdecl

 - stdcall

 - fastcall

 

Windows 프로그램의 디버깅에서는 cdecl stdcall의 차이점을 확실히 알아야 한다.

 

간단한 용어 소개!

 - Caller (호출자) : 함수를 호출한 쪽

 - Callee (피호출자) : 호출 당한 함수

 ) main() 함수에서 printf() 함수를 호출했다면 Caller main(), Callee printf()가 된다.

 

 

#1. cdecl

 

C언어에서 사용되는 방식이며, Caller에서 스택을 정리한다.

 

 

위와 같은 테스트용 C코드를 빌드하면 다음과 같은 disasm코드가 생성된다. (최적화 옵션 Off)

 

 

진하게 표시된 코드를 보면 add( ) 함수의 파라미터 1, 2를 역순으로 스택에 저장하고,

add()함수를 호출한 다음 ADD ESP, 8명령으로 스택을 정리하고 있다.

 

cdecl 방식의 장점은 printf() 함수와 같이 가변길이 파라미터를 전달할 수 있다는 것이다.

 

 

#2. stdcall

 

Win32 API에서 사용되는 방식이며, Callee에서 스택을 정리한다.

 

C언어는 기본적으로 cdecl방식을 사용한다.

만약 stdcall 방식으로 컴파일하고 싶다면 _stdcall 키워드를 붙여주면 된다.

 

 

위와 같은 테스트용 C코드를 빌드하면 다음과 같은 disasm코드가 생성된다. (최적화 옵션 Off)

 

 

위 코드를 살펴보면, main()함수에서 add()함수 호출 후에 스택 정리코드(ADD ESP, 8)가 생략되어 있다는 것을 알 수 있다.

 

스택의 정리는 add()함수 마지막 코드인 RETN 8 명령에서 수행된다.

RETN 8 명령의 의미는 RETN + POP이다. , 리턴 후 지정된 크기만큼 ESP를 증가시키는 것이다.

 

stdcall의 장점은 호출되는 함수(Callee) 내부에 스택 정리 코드가 존재하므로

함수를 호출할 때마다 ADD ESP, X 명령을 써줘야 하는 cdecl방식에 비해 코드 크기가 작아진다는 것이다.

 

Win32 API는 분명 C언어로 된 라이브러리이지만 기본 cdecl 방식이 아닌 stdcall방식을 사용한다.

이는 C이외의 다른 언어 (Delphi(Pascal), Visual Basic, etc)에서 API를 직접 사용할 때 호환성을 좋게 하기 위한 것으로 보인다.

 

 

#3. fastcall

 

기본적으로 stdcall과 같다.

함수에 전달하는 파라미터 일부(2개까지)를 스택 메모리가 아닌 레지스터를 이용하여 전달한다는 것이 특징이다.

 

만약 어떤 함수의 파라미터가 4개라면, 앞의 2개의 파라미터는 각각 ECX, EDX를 이용해 파라미터를 전달하는 것이다.

 

fastcall의 장점은 이름 그대로 좀 더 빠른 함수 호출이 가능하다는 것이다.

(CPU 입장에서는 멀리 있는 메모리보다 CPU와 같이 붙어있는 레지스터에 접근하는 것이 빠르다.)

 

단점은 호출 자체는 빠르지만 ECX, EDX 레지스터를 관리하는 추가적인 오버헤드가 필요한 경우가 생길 수 있다는 것이다.

가량 함수 호출 전에 ECX, EDX에 중요한 갑이 저장되어 있다면 이 값을 백업해 놓아야 할 필요가 생기게 된다.

 

따라서 fastcall은 함수 호출을 빠르게 하려는 용도보다는

레지스터를 사용하여 더 편하게 파라미터를 전달할 때 주로 사용한다고 보면 된다.

'정상을향해 > Program Analysis' 카테고리의 다른 글

PE File Format (3)  (0) 2013.11.10
PE File Format (2)  (0) 2013.11.10
PE File Format (1)  (0) 2013.11.10
Stack Frame  (0) 2013.11.10
IA-32 Register  (0) 2013.11.10
[ASM] 어셈블러 : 범용 레지스터  (0) 2011.05.16