상세 컨텐츠

본문 제목

[함수 호출 규약] __cdecl, __clrcall, __stdcall, __fastcall, __thiscall ,__vectorcall

C

by 메타샤워 2023. 7. 25. 12:53

본문

Calling Convention ( 함수 호출 규약 )

 
 
__cdecl
 
 
__cdecl 은 C 및 C++ 프로그램의 기본 호출 규칙이다. 
 
스택은 호출자에 의해 정리되기 때문에 vararg 함수를 수행할수 있다.
 
__cdecl 호출규칙은 각 함수 호출에 스택 정리르 코드를 포함시키기 때문에
 
__stdcall 보다 큰 실행 가능 명령문을 생성한다.
 
요소구현
인수 전달 순서 오른쪽에서 왼쪽
스택 유지 관리 책임 호출하는 함수가 스택에서 인수를 꺼냅니다.
이름 데코레이션 규칙 밑줄 문자(_)는 C 링크를 사용하는 __cdecl 함수를 내보내는 경우를 제외하고 이름 앞에 붙습니다.
대/소문자 변환 규칙 대/소문자 변환은 수행되지 않습니다.
변수나 함수이름 앞에 __cdecl 한정자를 넣는다. 
 
C 명명 규칙 및 호출 규칙은 기본값이므로 __cdecl 은 
 
/Gv(vectorcall), /Gz(stdcall) 또는 /Gr(fastcall) 컴파일 옵션을 지정했을때만
 
x86 코드에서 사용된다. /Gd 컴파일 옵션은 __cdecl 호출 규칙을 강제한다.
 
 
ARM 및 x64 프로세서에서는 컴파일러가 __cdecl을 수락하지만 일반적으로 무시한다.
 
ARM 및 x64 에서는 규칙에 따라 인수는 가능한 경우 레지스터로 전달되고
 
이후 인수는 스택에 전달된다. 
 
x64 코드에서는 __cdecl을 사용하여 /Gv 컴파일러 옵션을 재정의학도 기본 
 
x64 호출 규칙을 사용한다.
 
 
비정적 클래스 함수의 경우 함수의 out of line 으로 정의되면 호출 규칙 한정자를
 
out of line 정의에서 지정하지 않아도 됩니다. 즉, 클래스 비정적 멤버 메서드의 경우
 
선언하는 동안 지정된 호출 규칙이 정의 시 가정된다.
 
 
 
 
__stdcall
 
 
__stdcall 호출 규칙은 Win32 API 함수를 호출하는데 사용된다.
 
호출 수신자가 스택을 정리하므로 컴파일러는 vararg 함수를 __cdecl로 만든다.
 
이 호출 규칙을 사용하는 함수에는 함수 프로토타입이 필요하다.
 
 
return_type   __stdcall   func_name(args);
 
요소구현
인수 전달 순서 오른쪽에서 왼쪽
인수 전달 규칙 포인터 또는 참조 형식이 전달되지 않는 경우 값으로 전달
스택 유지 관리 책임 호출된 함수가 스택에서 자신의 인수를 꺼냅니다.
이름 데코레이션 규칙 밑줄(_)이 이름 앞에 붙습니다. 이름 뒤에는 기호(@)가 오고 그 위에 인수 목록의 바이트 수(10진수)가 옵니다. int func( int a, double b ) is decorated as follows: _func@12" xml:space="preserve">따라서 int func( int a, double b )로 선언된 함수는 _func@12로 데코레이팅됩니다.
대/소문자 변환 규칙 없음
/Gz 컴파일러 옵션은 다른 호출 규칙으로 명시적으로 선언되지 않은 모든 함수에 대해
 
__stdcall을 지정한다. __stdcall 한정자를 사용하여 선언된 함수는 __cdecl을 사용하여
 
선언된 함수와 같은 방식으로 값을 반환한다.
 
 
 
ARM 및 x64 프로세서에서는 __stdcall이 컴파일러에서 허용되고 무시된다.
 
ARM 및 x63 아키텍처에서는 규칙에 따라 가능한 경우 인스가 레지스터로 전달되고
 
이수 인수틑 스택으로 전달된다.
 
 
 
비정적 클래스 함수의 경우 함수가 out of line 으로 정의되면 호출 규칙 한정자를
 
out of line정의에서 지정하지 않아도 됩니다. 즉, 클래스 비정적 멤버 메서드의 경우 
 
선언하는 동안 지정된 호출 규칙이 정의 시 가정됩니다.
 
 
 
__fastcall
 
__fastcall 호출 규칙은 가능하면 함수의 인수가 레지스터로 전달되도록 지정한다.
 
이 호출은 x86 아키텍처에서만 적용된다.
 
요소구현
인수 전달 순서 왼쪽에서 오른쪽으로 인수 목록에서 발견된 처음 두 개의 DWORD 이하 인수는 ECX 및 EDX 레지스터로 전달되고, 다른 모든 인수는 오른쪽에서 왼쪽으로 스택에 전달됩니다.
스택 유지 관리 책임 호출된 함수가 스택에서 인수를 꺼냅니다.
이름 데코레이션 규칙 이름 앞에 at 기호(@)가 붙습니다. 이름 뒤에는 at 기호 다음에 매개 변수 목록의 바이트 수(10진수)가 붙습니다.
대/소문자 변환 규칙 대/소문자 변환은 수행되지 않습니다.
/Gr 컴파일러 옵션을 사용하면 함수가 충돌하는 특성을 사용하여 선언되거나 함수 
 
이름이 main 인경우를 제외하고는 모듈의 각 함수가 __fastcall 로 컴파일 된다.
 
 
__fastcall 키워드는 ARM 및 x64 아키텍처를 대상으로 하는 컴파일러에서 허용되고
 
무시됩니다.  x64 칩에서는 규칙에 따라 처음 네 개의 인수가 가능하면 레지스터로
 
전달되고 추가 인수틑 스택에 전달됩니다.  ARM 칩의 경우 최대 4개의 정수 인수와
 
8개의 부동소수 점 인스가 레지스터로 전달될수 있으며 추가 인수는 스택에 전달된다.
 
 
 
비정적 클래슽 함수의 경우 함수가 out of line 으로 정의되면 호출 규칙 한정자를
 
out of line 정의에서 지정하지 않아도 된다. 즉, 클래스 비정적 멤버 메서드의 경우
 
선언하는 동안 지정된 호출 규칙이 정의 시 가정 된다.
 
 
 
 
 
__clrcall
 
관리 코드에서만 함수를 호출할 수 있도록 지정한다. 관리 코드에서만 호출되는
 
모든 가상 함수에 __clrcall을 사용하라. 그러나 네이티브 코드에서 호출되는 함수에는
 
이 호출 규칙을 사용할 수 없다.
 
관리 되는 함수에서 관리되는 가상 함수를 호출하거나 관리되는 함수에서 포인터를 
 
통한 관리되는 함수를 호출할 때 __clrcall을 사용하여 성능을 개선하자
 
진입점은 컴팡일러에서 생성된 별도의 함수이다. 함수에 네이티브 진입점과 관리되는
 
진입점이 둘 다 있을 경우 둥 중 하나는 함수 구현이 포함된 실제 함수이다. 다른 
 
함수는 실제 함수를 호출하고 공용 언어 런타임에서 (CLR) 에서 PInvoke 를 수행하게 
 
하는 별도의 함수이다.  함수는 __clrcall로 표시할 경우 함수 구현이 MSIL이어야 하며
 
네이티브 진입점 함수가 생상되지 않음을 나타낸다.
 
 
 
__vectorcall
 
__vectorcall 호출 규칙은 가능하면 함수의 인수가 레지스터에 전달되도록 지정한다.
 
__vectorcall은 __fastcall 또는 기본 x64 호출 규칙보다 인수에 레지스터를 더 많이
 
사요하도록 한다. __vectorcall 호출 규칙은 SSE2(스트리밍 SIMD 확장 2) 이상을 
 
포함하는 x86 및 x64 프로세서의 네이티브 코드에서만 지원됩니다. __vectorcall을
 
사용하여 여러 부동 소수점 또는 SIMD 벡터 인수를 전달하는 함수의 속도를 높이고
 
레지스터에 로드된 인수를 활용하는 작업을 수행합니다. 다음목록은 __vectorcall을
 
x86 및 x64 프로세서에서 구현할 때 공통적으로 사용되는 기능으 보여준다.
 
요소구현
C 이름 데코레이션 규칙 함수 이름은 두 개의 "at" 기호(@@)가 접미사로 붙고 그 뒤에 매개 변수 목록의 바이트 수(10진수)가 이어집니다.
대/소문자 변환 규칙 대/소문자 변환은 수행되지 않습니다.
/Gv 컴파일러 옵션을 사용하면 모둘의 각 함수를 __vectorcall 컴파일한다.
 
이때 함수는 멤버 함수이거나 충돌하는 호출 규칙 특성으로 선언되거나
 
vararg 변수 인수 목록을 사용하거나 이름이 main 인경우가 아니어야 한다.
 
__vectorcall 함수에 등록하여 다음 세가지 인수 정수 형식 값, 벡터 형식 값 및 HVA
 
(균질 벡터 집합체) 값을 전달할 수 있다.
 
정수 형식은 프로세서의 네이티브 레지스터 크기에 적합하며 ( x86 컴퓨터에서는 
 
4바이트 또는 x64 컴퓨터에서는 8바이트) 레징스터 길이의 정수로 변환 후 비트
 
표현을 변경하지 않고 다시 변환하는 두 요구 사항을 중족한다.
 
예를 들어 x86의 int ( x64 에서는 long long) 로 승격할 수 있는 형식 ( char 또는 
 
short) 또는 int(x64에서는 long long)로 캐스팅 되고 변경없이 원래 형식으로 되돌릴
 
수 있는 형식은 정수 형식이다.  정수 형식은 4바이트 ( x64에는 8바이트) 이하 포인터, 
 
참조 및 struct 또는 union 형식을 포함한다. x64 플랫폼에서 큰 struct 및 union 형식은
 
호출자에 의해 할당된 메모리에 대한 참조에 의해 전달된다.
 
 
 
 
 
__thiscall
 
 
__thiscall 호출 컨벤션은 멤버 함수에서 사용되고  가변 인수를 사용하지 않는
 
C++멤버가 사용하는 디폴트 호출 컨벤션이다. __thiscall 아래에 호출 수신자가 스택을
 
정리하여 vararg 함수를 이용할 수 없다.  인수틑 x86 아키텍처에서 스택이 아닌 
 
레지스터 ECX를 통해 전달되는 this 포인터를 사용하여 오른쪽에서 왼쪽으로 스택에
 
푸시 된다.
 
__thicall 사용하는 이유 중 하나는 그 멤벗 함수가 기본저긍로 __clrcall 사용하는 클래스
 
안에 있다. 이 경우 __thiscall을 사용하여 네이티브 코드에서 개발 멤버 함수를 호출 할
 
수 있다.  
 
/clr:pure 로 컴파일 하는 경우 다른 함수가 지정되지 않는 한 모든 함수 및 함수 
 
포인터는 __clrcall 이다. 
 
visual c++ 2005 이전의 릴리즈의 경우 thiscall이 키워드가 아니었기때문에 프로그램에
 
thiscall 호출 규칙을 명시적으로 지정할 수 없었다.
 
vararg멤버 함수는 __cdecl 호울 규칙을 사용한다. 모든 함수 인수가 스택에 푸시되고
 
this 포인터가 스택에 마지막으로 배치된다.
 
이 호출 규칙이 c++에만 적용되기 때문에 C 이름 데코레이션 구성표가 없다.
 
ARM 및 x64 시스템에서는 컴파일러가 __thiscall을 수락할지 무시할지 결정한다.
 
 
 
키워드스택 정리매개 변수 전달
__cdecl 호출자 매개 변수를 스택에 역순으로(오른쪽에서 왼쪽으로) 푸시합니다.
__clrcall 해당 없음 CLR 식 스택에 매개 변수를 순서대로(왼쪽에서 오른쪽으로) 로드합니다.
__stdcall 호출 수신자 매개 변수를 스택에 역순으로(오른쪽에서 왼쪽으로) 푸시합니다.
__fastcall 호출 수신자 레지스터에 저장된 다음 스택에 푸시됩니다.
__thiscall 호출 수신자 스택에 푸시됩니다. this 포인터가 ECX에 저장됩니다.
__vectorcall 호출 수신자 레지스터에 저장된 다음 스택에 역순으로(오른쪽에서 왼쪽으로) 푸시됩니다.

관련글 더보기