Calling Convention | 함수 호출 규약
함수 호출 규약 : 함수의 호출 및 반환에 대한 약속
한 함수에서 다른 함수를 호출할 때 프로그램의 실행 흐름이 다른 함수로 이동한다. 이 호출된 함수가 반환되면 다시 원래의 함수로 돌아와서 기존의 실행 흐름을 이어나간다. 이때 호출하는 함수를 호출자(caller), 호출되는 함수를 피호출자(Callee)라고 한다.
호출자는 피호출자가 요구하는 파라미터를 전달해줘야하고, 실행이 종료되면 반환 값을 전달받아야한다.
| Segment word size | Calling Convention | Parameters in register | Stack cleanup by |
| 32bits | __cdecl | Caller | |
| __stdcall | Callee | ||
| __fastcall | ecx, edx | Callee | |
| __thiscall | ecx | Callee | |
| 64bits | Microsoft x64 Calling Convention | RCX, RDX, R8, R9 나머지 스택 | Caller |
| System V AMD64 ABI (Linux x64) | RDI, RSI, RDX, RCX, R8, R9 | Caller |
x86 Calling Convention
Linux의 경우, Calling Convention을 앞에 지정해주는 방식이 아니다.
또한, __fastcall이 존재하지 않는다.
__cdecl
- 대부분의 32bits C프로그램에서 사용하는 사용하는 기본 호출 규약
- 스택은 Caller에 의해 정리됨
- 스택으로 인자가 전달됨
#include <stdio.h>
// __cdecl 제거: 리눅스에서는 필요 없음
int sum(int a, int b) {
return a + b;
}
int main() {
int s = sum(2, 3); // 함수 호출
printf("%d\n", s); // 결과 출력
return 0;
}
Assembly
pwndbg> disassem main
Dump of assembler code for function main:
0x000011b4 <+0>: lea ecx,[esp+0x4]
0x000011b8 <+4>: and esp,0xfffffff0
0x000011bb <+7>: push DWORD PTR [ecx-0x4]
0x000011be <+10>: push ebp
0x000011bf <+11>: mov ebp,esp
0x000011c1 <+13>: push ebx
0x000011c2 <+14>: push ecx
0x000011c3 <+15>: sub esp,0x10
0x000011c6 <+18>: call 0x10a0 <__x86.get_pc_thunk.bx>
0x000011cb <+23>: add ebx,0x2e0d
0x000011d1 <+29>: push 0x3
0x000011d3 <+31>: push 0x2
0x000011d5 <+33>: call 0x119d <sum>
0x000011da <+38>: add esp,0x8
0x000011dd <+41>: mov DWORD PTR [ebp-0xc],eax
0x000011e0 <+44>: sub esp,0x8
0x000011e3 <+47>: push DWORD PTR [ebp-0xc]
0x000011e6 <+50>: lea eax,[ebx-0x1fd0]
0x000011ec <+56>: push eax
0x000011ed <+57>: call 0x1050 <printf@plt>
0x000011f2 <+62>: add esp,0x10
0x000011f5 <+65>: mov eax,0x0
0x000011fa <+70>: lea esp,[ebp-0x8]
0x000011fd <+73>: pop ecx
0x000011fe <+74>: pop ebx
0x000011ff <+75>: pop ebp
0x00001200 <+76>: lea esp,[ecx-0x4]
0x00001203 <+79>: ret
End of assembler dump.
pwndbg> disassem sum
Dump of assembler code for function sum:
0x0000119d <+0>: push ebp
0x0000119e <+1>: mov ebp,esp
0x000011a0 <+3>: call 0x1204 <__x86.get_pc_thunk.ax>
0x000011a5 <+8>: add eax,0x2e33
0x000011aa <+13>: mov edx,DWORD PTR [ebp+0x8]
0x000011ad <+16>: mov eax,DWORD PTR [ebp+0xc]
0x000011b0 <+19>: add eax,edx
0x000011b2 <+21>: pop ebp
0x000011b3 <+22>: ret
End of assembler dump.
Stack Frame


해당 스택을 보면 인자값이 호출하기 전에 push 되는 것을 확인할 수 있다.
또한, 함수 반환 이후, sub esp, 0x8 등으로 Caller가 함수를 정리한다.
__stdcall
- 주로 WinAPI 32 함수들이 __stdcall을 따름
- 스택은 Callee에 의해 정리됨
- 스택으로 인자가 전달됨
#include <stdio.h>
int __stdcall sum(int a, int b)
{
return a + b;
}
int main()
{
int s = sum(2, 3);
printf("%d", s);
return 0;
}
__fastcall
- 두 인자들이 4바이트보다 작거나 같다면 edx, ecx에 담아두고 사용함
- 나머지는 스택으로 전달
- 레지스터를 이용하기 때문에 함수 호출 비용을 줄일 수 있음
- Callee에 의해 스택을 정리함
__thiscall
- C++에서 사용하는 함수 호출 규약
- this 포인터는 ecx에 저장되고 나머지는 스택에 저장
- Callee에 의해 스택을 정리함
x64 Calling Convention
Microsoft x64 Calling Convention
- 정수 파라미터는 RCX, RDX, R8, R9로 전달됨
- 부동 소수점 파라미터는 XMM0L, XMM1L, XMM3L로 전달됨
- 16바이트 파라미터는 참조로 전달됨
- 파라미터가 많으면, 레지스터 이외의 부분은 스택으로 전달됨
- RAX로 반환됨
- Caller가 스택을 정리함
System V amd64 ABI
- RDI, RSI, RDX, RC, R8, R9 로 전달됨
- 파라미터가 많으면, 레지스터 이외의 부분은 스택으로 전달됨
- Caller가 스택을 정리함

출처
- https://learn.microsoft.com/ko-kr/cpp/build/x64-calling-convention?view=msvc-170
x64 호출 규칙
기본 x64 호출 규칙의 세부 정보에 대해 알아봅니다.
learn.microsoft.com
Linux x64 Calling Convention: Stack Frame | Red Team Notes
In 64-bit Linux system, function arguments of type integer/pointers are passed to the callee function in the following way: This lab and conclusions are based on the following C program compiled on a 64-bit Linux machine: Let's now see how arguments are pa
www.ired.team
'CS > Computer Architecture' 카테고리의 다른 글
| [Assembly] 산술 연산 (0) | 2024.09.27 |
|---|---|
| [Assembly] 데이터 이동 (1) | 2024.09.20 |
| Static Link vs Dynamic Link (0) | 2024.09.05 |
| 함수의 프롤로그 & 에필로그 (0) | 2024.08.22 |
| Register (1) | 2024.08.16 |