리눅스 x64 호출규약
2019년 10월 31일
x64 Linux Calling Convention
- 스택 메모리에서 서브루틴은 호출자의 아래쪽으로 생성된다.
- 인자는 순서대로 rdi, rsi, rdx, r8, r9, r10 레지스터를 통해 전달된다. 6번째 이후의 인자는 32비트 호출 규약과 동일하게 rbp 위 함수의 return address 위에 쌓인다.
- rax 레지스터는 서브루틴의 리턴값을 전달한다.
예를 통해 직접 과정을 따라가보는 것이 가장 이해하기 좋다.
예
단순하게 두 수를 더해서 다른 변수에 저장하는 c코드를 작성해보았다:
int add(int a, int b){
return a+b;
}
int main(){
int a,b,c;
a = 3;
b = 7;
c = add(a, b);
return 0;
}
를 다음과 같이 64비트 컴파일을 하고 gdb로 확인해보자.
gcc -m64 add.c -o add
gdb add
그러면 아래와 같은 어셈블리 코드를 볼 수 있는데 main의 시작 지점부터 어셈블리 instruction을 한 단계씩 실행시켜서 메모리와 레지스터 정보를 확인하면 아래와 같은 과정을 그려볼 수 있다. 모바일 환경에서는 잘려서 보이니 데스크탑 환경으로 보자.
- 메모리를 표로 그렸는데, 기본적으로 한 칸은 8바이트이지만 movl과 같이 4바이트 단위로 쪼개지는 instruction이 발생할 경우 예외적으로 4바이트 띄어서 표현했다.
- rip는 레지스터 표가 아닌, 왼쪽에 어셈블리 코드에 화살표로 나타냈고, rsp와 rbp는 메모리 표에 위치를 나타낼 수 있을 때 메모리에 표시했다.
관련된 Assembly Instructions
push Reg
: rsp를 8만큼 감소시키고mov Reg, (%rsp)
한다.pop Reg
:mov (%rsp), Reg
후 rsp를 8 증가시킨다.call Dest
:push 다음 인스트럭션
후mov Dest, %rip
한다.ret
:pop %rip
leave
:mov %rsp, %rbp