함수의 호출규약은 함수 호출 시 전달된 매개변수를 함수가 종료될 때 정리하는 방법으로서 cdecl, stdcall, fastcall, thiscall의 4가지가 존재합니다. 이 중 thiscall은 C++언어의 멤버함수에서 쓰이고 나머지는 C언어에서 사용됩니다.
cdecl 호출 규약
먼저 cdecl은 C 함수 라이브러리에서 표준으로 쓰이는 호출규약으로 스택으로 전달한 매개변수를 함수를 호출한 위치에서 정리하는 방식입니다. 예를 들어 아래와 같은 C함수가 있습니다.
int __cdecl _fn_cdecl(int a, int b) { return a+b; }
이를 어셈블리어로 다시 작성해봅니다. 시스템 기본 정수는 4바이트로 가정합니다.
_fn_cdecl: sub esp, 4 ; 함수 종료 후 돌아갈 위치가 있으므로 1워드만큼 뒤로 물림 pop eax ; b 매개변수 pop edx ; a 매개변수 add esp, 12 ; 스택 포인터 원래대로 ; 스코프 시작 push ebp mov ebp, esp add eax, edx ; eax = eax + edx; 스코프 끝 mov esp, ebp pop ebp ret ; 함수 끝. 결과는 eax로 전달
이 함수를 실행해보겠습니다. a = 10과 b = 5를 전달해 15를 얻겠습니다.
; fn_cdecl(10, 5); push dword ptr 10 ; 4 바이트 정수를 스택에 넣기 push dword ptr 5 ; 다시 4바이트 정수를 스택에 넣기 call _fn_cdecl ; 결과인 15가 eax에 기억 sub esp, 8 ; 스택에 넣은 2개의 4바이트 정수를 정리하여 없앰
위와 같이 cdecl로 만든 함수는 매개변수 전달을 위해 사용한 스택을 call 명령 다음에 정리합니다. 함수를 호출한 위치에서는 매개변수의 총 크기를 정확히 알고 있으므로 스택을 안전하게 정리할 수 있는 특징이 있습니다.
stdcall 호출 규약
stdcall은 스택의 정리를 함수 내부에서 처리합니다. 함수를 호출하는 위치에서는 함수를 호출만하고 스택정리를 하지 않는 방식입니다. Windows API들이 이 방식을 채택합니다.
두 수를 합을 반환하는 함수를 stdcall로 다시 작성해보겠습니다.
int __stdcall fn_stdcall(int a, int b) { return a+b; }
이것을 어셈블리어로 다시 작성하면
_fn_stdcall@8: sub esp, 4 ; 돌아갈 위치는 건너뛰고 pop eax ; b 매개변수를 eax에 기억 pop edx ; a 매개변수를 edx에 기억 add esp, 12 ; 스택포인터를 원래대로 ; 스코프 시작 push ebp mov ebp, esp add eax, edx ; eax = eax + edx ; 스코프 종료 mov esp, ebp pop ebp ; 8 바이트만큼의 스택을 CPU가 알아서 정리 ret 8
cdecl 함수와 달리 stdcall 함수는 이름 뒤에 @ 기호를 붙인 뒤 정리할 스택의 크기를 명시합니다. 이 크기는 고정되어 있기 때문에 가변인수를 받는 함수에는 stdcall 규약을 사용할 수 없습니다.
이를 실행시켜보겠습니다.
; fn_cdecl(10, 5); push dword ptr 10 ; 4 바이트 정수를 스택에 넣기 push dword ptr 5 ; 다시 4바이트 정수를 스택에 넣기 call _fn_stdcall@8 ; 결과인 15가 eax에 기억
fastcall 호출 규약
fastcall은 처음 n개의 매개변수를 스택 대신 레지스터로 전달하는 방식입니다. 매개변수의 수가 n개 이하면 스택 정리 과정이 아예 생략되므로 실행이 빠르다는 특징이 있습니다. 함수 본문은 컴파일러마다 다르게 번역이 되므로 여기에서는 호출 하는 방식만 보겠습니다.
; int __fastcall fn_fastcall(int a, int b); ; MSVC는 ecx, edx의 2개의 레지스터를 사용합니다. mov ecx, 10 mov edx, 5 call _fn_fastcall@8\
thiscall 호출 규약
thiscall은 ecx에 인스턴스의 주소가 기억된 후 호출되는 함수입니다. 그 외에는 stdcall과 동일하고 가변인수를 사용하는 함수에 한해 cdecl 규약으로 처리됩니다.
C++ 클래스 내에 foo 함수가 있다면
; eax = this->foo(10, 5); mov ecx, (this)의 주소 call foo
'Programming Language > Assembly' 카테고리의 다른 글
[GAS/XCode]64비트 어셈블리어에서 메모리 번지 지정 오류 해결 (0) | 2014.12.17 |
---|---|
Visual Studio 2013에서 어셈블리어 코딩 후 실행하기 (12) | 2014.06.09 |
C-Style의 문자열 출력 (0) | 2013.12.03 |
산술 비교 알고리즘 - 3편. "<"와 ">=" (0) | 2013.04.12 |
산술 비교 알고리즘 - 2편. ">"와 "<=" (0) | 2013.04.12 |