같은 아키텍쳐 라도, 컴파일러에 따라 적용되는 호출 규약이 다를 수 있음
프로그래머가 코드에 명시하지 않는다면, CPU의 아키텍쳐에 적합한 호출 규약을 선택
CPU의 아키텍쳐에 적합한 호출 규약이란?
1. x86 → 피호출자의 인자를 전달하기에는 레지스터의 수가 적으므로, 스택으로 인자를 전달하는 규약을 사용
| 함수호출규약 | 사용 컴파일러 | 인자 전달 방식 | 스택 정리 | 적용 |
|---|---|---|---|---|
| stdcall | MSVC | Stack | Callee | WINAPI |
| cdecl | GCC, MSVC | Stack | Caller | 일반 함수 |
| fastcall | MSVC | ECX, EDX | Callee | 최적화된 함수 |
| thiscall | MSVC | ECX(인스턴스), | ||
| Stack(인자) | Callee | 클래스의 함수 |
2. x86-64 → 레지스터가 많으므로 적은 수의 인자는 레지스터만 사용해서 인자를 전달하고, 인자가 너무 많을 때만 스택을 사용
| 함수호출규약 | 사용 컴파일러 | 인자 전달 방식 | 스택 정리 | 적용 |
|---|---|---|---|---|
| MS ABI | MSVC | RCX, RDX, R8, R9 | Caller | 일반 함수, |
| Windows Syscall | ||||
| System ABI | GCC | RDI, RSI, RDX, RCX, R8, R9, XMM0–7 | Caller | 일반 함수 |
callee() 함수를 cdecl 호출 규약으로 호출하기// Name: cdecl.c
// Compile: gcc -fno-asynchronous-unwind-tables -nostdlib -masm=intel \
// -fomit-frame-pointer -S cdecl.c -w -m32 -fno-pic -O0
void __attribute__((cdecl))
callee(int a1, int a2){}
// cdecl로 호출
void caller(){
callee(1, 2);
} // callee 함수를 호출하는 caller 함수

callee() 함수를 SYSV 호출 규약으로 호출하기// Name: sysv.c
// Compile: gcc -fno-asynchronous-unwind-tables -masm=intel \
// -fno-omit-frame-pointer -S sysv.c -fno-pic -O0
#define ull unsigned long long
ull callee(ull a1, int a2, int a3, int a4, int a5, int a6, int a7) {
ull ret = a1 + a2 + a3 + a4 + a5 + a6 + a7;
return ret;
}
void caller() { callee(123456789123456789, 2, 3, 4, 5, 6, 7); }
int main() { caller(); }