[시스템] 윈도우 x64 호출 규약 리뷰

윈도우 x64 호출 규약은 굉장히 심플하다. 파라미터가 첫번째부터 rcx, rdx, r8d, r9d를 통해서 전달되고 그 다음 추가 파라미터는 스택을 통해 전달된다. 스택 정리는 호출한 쪽에서 수행한다. 그런데 여기서 한 가지 주의해야 할 것이 홈 공간이다. 홈 공간이란 파라미터가 레지스터를 통해서 전송되더라도 그걸 저장하기 위한 스택 공간을 호출하는 쪽에서 제공해야 한다는 것을 의미한다.

void Func1(int param1);

위 코드를 보자. 위의 파라미터 전송 설명대로라면 Func1(3)은 아래와 같이 호출하면 될 거 같다.

mov rcx, 3
call Func1

하지만 그렇지 않다. 홈 공간이 필요하기 때문이다. 그래서 아래와 같이 값을 저장하지는 않더라도 공간은 확보해 주어야 한다. sub rsp, 8을 통해서 Func1이 param1 저장을 위해서 사용할 공간을 확보한 후에 호출해야 한다. 이게 홈 공간이다.

sub rsp, 8
mov rcx, 3
call Func1
add rsp, 8

조금 복잡하게 파라미터가 5개인 함수를 살펴보자.

void Func2(int p1, int p2, int p3, int p4, int p5);

Func2(1, 2, 3, 4, 5)를 호출한다고 생각해보자. 그러면 아래와 같이 호출하면 된다. push 5를 통해서 스택에 마지막 값을 기록한다. 이후 앞에 4개의 값을 기록할 공간을 확보한 후, rcx, rdx, r8d, r9d에 변수를 지정하고 Func2를 호출하면 된다.

push 5
sub rsp, 32
mov rcx, 1
mov rdx, 2
mov r8d, 3
mov r9d, 4
call Func2
add rsp, 40

실제로 컴파일러는 매 함수 호출마다 이렇게 스택을 조작하지는 않는다. 여유 있는 스택 공간을 함수 도입부에 확보해놓고 지속적으로 사용한다. 어쨌든 우리가 기억해야 할 사항은 이것이다. 앞에 4개는 레지스터로 뒤에꺼는 스택으로, 그리고 파라미터 개수만큼 스택 공간은 항상 확보해 주어야 한다는 점이다. 호출 규약에 관한 더 자세한 내용은 호출의 예술을 참고하자.