함수 호출 규약
함수 호출 규약
함수를 호출하면서 Caller(호출자)의 스택 상태와 리턴주소를 저장하고, Callee(피호출자)의 인자를 전달해줘야하는데, 이걸 컴파일러가 상황에 맞게 선택해서 컴파일하는 것을 함수호출 규약이라고 한다.
CPU 아키텍쳐에 따라, 아키텍쳐가 같아도 컴파일러에 따라 달라질 수 있고, 같은 호출규약이라도 컴파일러마다 다르게 구현하기도 한다.
x86
cdcel
; caller
0x00001011 <+7>: push 0x2
0x00001013 <+9>: push 0x1
0x00001015 <+11>: call 0x1000 <callee>
0x0000101a <+16>: add esp,0x8
; callee
0x00001000 <+0>: endbr32
0x00001004 <+4>: push ebp
0x00001005 <+5>: mov ebp,esp
; 대략적인 그림
+-----------------+ 낮은주소
| caller ebp | <- esp/ebp
+-----------------+
| return address |
+-----------------+
| stack param (1) |
+-----------------+
| stack param (2) |
+-----------------+ 높은주소
- x86에서는 레지스터 수가 적어서 스택을 통해 인자를 전달하게 된다. 순서는 마지막 인자부터 push해서 첫번째 인자가 가장 ebp에 가깝게 저장한다.
- 마찬가지로 caller에서 스택을 정리한다.
x86-64
System V AMD64 ABI (SYSV)
리눅스에서 file 명령을 사용해보면 SYSV 문자열을 확인할 수 있다.
; caller function
0x55555555518a <caller+5> mov rbp, rsp
0x55555555518d <caller+8> push 7 ; stack 사용
0x55555555518f <caller+10> mov r9d, 6
0x555555555195 <caller+16> mov r8d, 5
0x55555555519b <caller+22> mov ecx, 4
0x5555555551a0 <caller+27> mov edx, 3
0x5555555551a5 <caller+32> mov esi, 2
0x5555555551aa <caller+37> movabs rax, 0x1b69b4bacd05f15
0x5555555551b4 <caller+47> mov rdi, rax
0x5555555551b7 <caller+50> call 0x555555555129 <callee>
0x5555555551bc <caller+55> add rsp,0x8 ; 사용했던 stack 만큼만 정리
; callee function
0x555555555129 <callee>: endbr64
0x55555555512d <callee+4>: push rbp
0x55555555512e <callee+5>: mov rbp,rsp
0x555555555131 <callee+8>: mov QWORD PTR [rbp-0x18],rdi ; 인자들을 전부 스택에 넣는다. (rbp)
0x555555555135 <callee+12>: mov DWORD PTR [rbp-0x1c],esi
0x555555555138 <callee+15>: mov DWORD PTR [rbp-0x20],edx
0x55555555513b <callee+18>: mov DWORD PTR [rbp-0x24],ecx
0x55555555513e <callee+21>: mov DWORD PTR [rbp-0x28],r8d
0x555555555142 <callee+25>: mov DWORD PTR [rbp-0x2c],r9d
; 대략적인 그림
+-----------------+ 낮은주소
| r9d,...,rdi, etc| <- rbp-0x2c (이 위치부터 파라미터가 저장됨)
+-----------------+
| caller rbp | <- rsp/rbp
+-----------------+
| return address |
+-----------------+
| stack param (7) | <- 레지스터 부족으로 스택에서 관리하는 파라미터
+-----------------+ 높은주소
- 6개의 인자를 rdi, rsi, rdx, rcx, r8, r9, 스택 순으로 저장해서 전달된다. x86처럼 저장은 뒤쪽 파라미터부터 한다.
예를들어서 인자가 3개면 rdx(3), rsi(2), rdi(1) 순서이다. - caller에서 함수 리턴 이후 사용된 스택을 정리한다.
- 함수의 반환값은 rax에 저장된다.
Comments