함수의 호출규약
1. 함수 호출 규약(Calling Convention)
▶ 파라미터를 전달하는 방법에 대한 약속을 정의한 것이다.
2. 32비트 모드
(1) __cdecl 방식(C declaration)
[1] C와 C++(가변인자)의 기본 호출규약이다.
[2] 인자(argument)는 스택을 사용해서 오른쪽에서 왼쪽 순서로 전달한다.
[3] callee(호출자)가 스택포인터를 정리한다. (c언어에서는 내부적으로, assembly에서는 직접)
[4] 함수이름 앞에 _(언더바) 기호가 붙는다.
(2) __stdcall 방식(Standard Call)
[1] Win32 API[Windows OS의 System call]의 기본 호출 규약이다.
[2] __cdecl방식과 동일하게 인자(argument)는 스택을 사용해서 오른쪽에서 왼쪽 순서로 전달한다.
[3] caller(피호출자)가 스택포인터를 정리(복원)한다.
[4] 함수이름 앞에 _(언더바) 기호, 함수이름 끝에 @(at, 편하게 골뱅이) 기호가 붙는다.
[5] 장점
▶ 함수의 독립성이 좋다.
▶ __cdecl방식보다 코드 양이 적다.(여러 곳에서 호출되더라도 스택정리 코드는 함수 내 1번만 존재)
(3) __fastcall 방식
[1] 인텔 CPU에서만 사용이 가능하다.
[2] 함수 호출 시 첫 번째 인자는 ecx에, 두 번째 인자는 edx에 저장한다.
[3] 세 번째 인자부터는 오른쪽에서 왼쪽순서로 스택에 저장한다.
[4] 함수이름 앞에 @(at) 기호가 붙는다.
[5] 그 외는 __stdcall 방식과 동일하다.
[6] 장점
▶ __stdcall과 동일하지만 인자가 2개 이하 시 레지스터를 이용하기 때문에 속도가 빠르다.
(4) __thiscall 방식
[1] C++(가변인자를 사용하지 않는 함수)의 기본 호출 규약(컴파일할 때 가변인자는 __cdecl 방식으로 변경)이다.
[2] 인자(argument)는 스택을 사용해서 오른쪽에서 왼쪽 순서로 전달한다.
[3] caller(피호출자)가 스택포인터를 정리(복원)한다.
[4] ecx에 클래스의 this포인터를 전달한다.
[5] 직접적으로 호출규약을 사용할 수 없다.
[6] 멤버함수는 __thiscall을 사용하지만 직접 지정해서 다른 호출규약 사용이 가능하다.
▶ 다른 호출규약 사용 시 첫 번째 인자로 this포인터가 전달한다.
(5) 스택정리방식
[1] __cdecl방식을 제외한 나머지 함수 호출 규약은 호출당한 쪽에서 스택을 정리할까?
▶ __cdecl방식은 ret 시 [pop eip / jmp eip]와 같이 복귀주소를 꺼내고 리턴한다.
▶ 이 후 호출한 쪽에서 add 명령어를 이용해서 스택을 복원한다.
▶ 나머지 함수 호출 규약은 [ret 8] 시 [pop eip]와 같이 우선 복귀주소를 꺼낸다.
▶ 예를 [ret 8]로 들었기 때문에 8바이트이므로 pop을 2번하고 [jmp eip]로 복귀한다.
3. 64비트 모드
▶ 64비트는 함수 호출 규약을 하나만 사용한다.
▶ 즉, __fastcall을 업그레이드해서 사용한다.
▶ 같은 __fastcall이지만 파일 포맷에 따라 세부 규약은 다르다.
(1) Linux(ELF)
[1] 인자가 정수일 때
▶ 함수 호출 시 rdi(1), rsi(2), rdx(3), rcx(4), r8(5), r9(6) 총 6개의 레지스터를 사용해서 순서대로 사용해서 인자를 전달한다.
▶ 인자가 7개 이상인 경우 스택을 이용해서 전달한다.
▶ 인자는 오른쪽에서 왼쪽 순서로 전달한다.
+ 예를 들면 7개부터는 오른쪽에서 왼쪽 순서대로 push하고, 6개째부터 r9(6), r8(5), rcx(4), rdx(3), rsi(2), rdi(1) 순서대로 인자를 전달한다.