티스토리 뷰

   
출 규약 (calling convention) 출처 : http://www.winapi.co.kr/clec/cpp2/16-1-3.htm
관련자료 : http://www.myevan.net/phpBB/viewtopic.php?t=2602&highlight=%C7%D4%BC%F6%BF%CD

인수 전달에 사용한 스택 영역은 호출원이 정리했는데 이는 C/C++ 언어의 기본 호출 규약인 __cdecl의 스택 프레임 모양일 뿐이다. 호출 규약이 바뀌면 스택 프레임의 모양과 관리 방법도 달라질 수 있다.

호출 규약 인수 전달 스택 정리 이름 규칙
__cdecl 오른쪽 부터 호출한곳에 추가 _함수명
__stdcall 오른쪽 부터 함수에서 정리 _함수명@인수크기 (예:int Test(int a, double b)는 _Test@12)
__fastcall EDX(첫번째), ECX(두번째)에 우선 전달. 나머지는 오른쪽 부터 함수에서 정리 @함수명@인수크기
thiscall 오른쪽 부터, this 포인터는 ecx 레지스터로 전달된다. 함수에서 정리 C++ 이름 규칙을 따름.
naked 오른쪽 부터 함수에서 정리 없음

리턴값을 돌려 주는 방식도 호출 규약에 따라 달라질 수 있는데 다행히 현존하는 모든 호출 규약의 리턴 방식은 동일하다. 4바이트의 값을 돌려줄 때는 eax 레지스터를 사용하며 8바이트의 값을 리턴할 때는 edx:eax 레지스터 쌍을 사용한다. 8바이트를 초과하는 큰 리턴값, 예를 들어 구조체 등은 임시 영역에 리턴할 값을 넣어 두고 그 포인터를 eax에 리턴한다.

__cdecl

기본 호출 규약이므로 /Gz (stdcall) 또는 /Gr (fastcall) 옵션이 켜졌을 때, 필요한 변수나 함수 이름 앞에 __cdecl을 놓으면 된다.
/Gd 옵션은 강제로 __cdecl 규약으로 호출한다.

__stdcall

__stdcall은 윈도우즈 API 함수들의 기본 호출 규약이며 비주얼 베이직도 이 호출 규약을 사용한다. __cdecl과 인수를 전달하는 방법은 동일하되 인수 전달에 사용된 스택을 정리하는 주체가 호출원이 아니라 함수라는 점이 다르다.
int __stdcall Add(int a, int b) 
{ 
   int c,d,e; 
   c=a+b; 
   return c; 
} 

Add(1,2); 를 사용할 경우

push 2 
push 1 
call Add 
result=eax 
인수를 스택에 밀어 넣는 것과 인수를 푸시하는 순서는 동일하다. 단, 함수가 리턴된 후에 인수 전달에 사용한 스택을 복구하지 않는다는 점이 __cdecl과 다르다.
push ebp 
 
.... 
 
ret 8 
복귀 코드가 ret에서 ret 8로 바뀌었으며 복귀하면서 esp를 8만큼 증가시킨다. 이 코드에 의해 함수는 실행을 마치고 복귀함과 동시에 인수 영역을 해제한다. Add 함수 자신이 복귀하면서 스택을 정리하므로 호출원에서는 스택을 정리할 필요가 없다. 호출하는 곳에서 인수를 순서대로 스택에 푸시한 후 함수만 호출하면 된다.

__cdecl과의 차이점
첫번째, 속도차이 거의 없고, 용량상 스택 정리하는 코드가 없으므로 아주 약간 __stdcall이 작아진다.
두번째, __stdcall은 함수가 직접 스택을 정리하기 때문에 가변 인수 함수를 지원하지 않는다. 함수 접미에 스택 정리 코드를 작성하려면 인수의 총 크기를 미리 알아야 하는데 가변 인수 함수는 전달되는 인수 개수가 가변이므로 이 크기가 고정적이지 않아 접미에서 스택을 직접 정리할 수 없다. 컴파일러가 접미의 ret n 명령에 대해 n을 결정할 수 없는 것이다.

/Gz 옵션은 호출규약이 명시적으로 선언되지 않은 모든 함수를 __stdcall로 만든다. __stdcall 형식으로 선언된 함수는 __cdecl 형식으로 값을 리턴한다

__fastcall

mov edx,2 
mov ecx,1 
call Add 
result=eax 
첫번째 인수와 두번째 인수를 차례대로 edx, ecx에 집어 넣는다.
push ebp 
mov ebp,esp 
sub esp,14h 
mov [ebp-8],edx       // 첫번째 인수를 지역 변수로 
mov [ebp-4],ecx       // 두번째 인수를 지역 변수로 
mov eax,[ebp-4] 
add eax,[ebp-8] 
mov [ebp-0ch],eax        // c는 세번째 지역 변수가 된다. 
mov eax,[ebp-0ch] 
mov esp,ebp 
pop ebp 
ret 
인수 전달을 위해 스택을 쓰지 않고 레지스터를 우선적으로 사용하므로 인수 전달 속도가 빨라진다는 이점이 있다.

스택 정리는 함수가 하는데 Add 함수의 경우 인수가 두 개 뿐이므로 인수 전달을 위해 스택을 사용하지 않았으며 그래서 정리할 내용이 없다. 만약 인수가 세 개라면 제일 끝의 ret는 ret 4가 될 것이다.

레지스터는 스택보다 훨씬 더 빠르게 동작하기 때문에 __fastcall은 이름대로 호출 속도가 빠르다. 대신 이식성에 불리하다는 단점이 있다. 이 호출 규약은 ecx, edx 레지스터를 사용하도록 되어 있는데 이 두 레지스터가 모든 CPU에 공통적으로 존재하는 것이 아니기 때문이다. 그래서 윈도우즈 API는 이 호출 규약을 지원하기는 하지만 사용하지는 않는다. 볼랜드의 델파이가 __fastcall을 사용한다.

/Gr 옵션은 선언된 함수가 충돌하지 않고 이름이 main이 아니라면, 모듈 내 각 함수를 fastcall로 컴파일한다.

thiscall

클래스의 멤버 함수에 대해서만 적용되는데 ecx로 객체의 포인터(this)가 전달된다는 것이 특징이며 나머지 규칙은 __stdcall과 동일하다. 예외적으로 가변 인수를 사용하는 멤버 함수는 __cdecl로 작성되며 이때 this는 스택의 제일 마지막에(그러므로 첫 번째 인수로) 전달된다.

이 호출 규약은 컴파일러가 멤버 함수에 대해서만 특별히 적용하는 것이므로 일반 함수에는 이 호출 규약을 적용할 수 없다. thiscall은 이 호출 규약의 이름일 뿐 키워드가 아니기 때문에 함수 원형에 thiscall이라고 쓸 수도 없다. 멤버 함수이기만 하면 컴파일러가 알아서 thiscall 호출 규약을 적용한다.

__naked

__naked 호출 규약은 컴파일러가 접두, 접미를 작성해 주지 않는 호출 규약이다. 스택 프레임의 상태 보존을 위해 컴파일러가 어떤 코드도 작성하지 않으므로 접두, 접미는 사용자가 직접 작성해야 한다. 스택은 어셈블리 수준에서만 다룰 수 있으므로 인라인 어셈블리를 사용해야 하며 제약점도 많기 때문에 일반적인 목적으로는 사용되지 않는다.

이 호출 규약이 반드시 필요한 경우는 C/C++이 아닌 언어에서 호출하는 함수를 작성할 때이다. 예를 들어 어셈블리에서는 인수 전달에 스택을 쓰지 않고 범용 레지스터만으로도 인수를 전달할 수 있다. 이런 경우는 C컴파일러가 만들어주는 접두, 접미가 불필요하다. 또한 속도가 지극히 중요한 디바이스 드라이버를 작성할 때도 이 호출 규약을 사용한다. __naked 호출 규약을 사용하려면 함수의 정의부에 __declspec(naked)를 적어주면 된다.


PythonPowered EditText of this page (last modified 2008-04-21 19:25:10)
FindPage by browsing, searching, or an index
Or try one of these actions: DeletePage, DeleteUploadedFile, LikePages, SpellCheck, UploadFile
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함