상위 문서: Assembly
개요
함수 f가 함수 g를 호출할 때, f는 caller에 해당하고, g는 callee에 해당한다. caller와 callee는 정상적으로 작동하기 위해 다음 정보들을 알아야 한다.
- Callee는 사용할 매개변수가 어디에 저장되어 있는지 알아야 한다.(보통 스택, 레지스터에 저장)
- Callee는 return address가 어디에 저장되어 있는지 알아야 한다.(보통 스택에 저장)
- Caller는 return value가 어디에 저장되어 있는지 알아야 한다.
이때 caller와 caller는 같은 레지스터를 사용하고 있으므로, calling convention이 존재하여 매개변수, 반환값, return address 등을 어디에 저장할 지를 약속한다.
Procedure Control Flow
Procedure control flow의 주축을 이루는 명령어는 ret와 call이다. 이때 두 명령어를 구현하는 데에는 메모리의 스택 자료구조가 사용된다.
Function call
함수 호출에는 call Dest 명령어가 사용된다. 이는 현재에서 지정된 함수 주소(Dest)로 jump하며, 돌아올 주소(스택 포인터)를 스택에 저장한다. 이는 다음과 같은 순서로 작동한다:
- 현재 명령어의 바로 다음 주솟값(return address)을 스택에 push한다.
- call 명령의 크기는 5bytes 크기이므로 %rip+5 (call 명령의 다음 주소)를 스택에 저장한다.
- Dest가 가리키는 주소로 jump한다.
- 프로그램 카운터[1]로 사용되는 %rip에 지정된 함수 주소(Dest)를 저장한다.
이때 스택에 추가적으로 return address를 저장하므로 %rsp(스택 포인터)의 값은 8 감소한다.
Function Return
Call 명령어를 통해 호출된 함수의 실행이 끝나면, ret 명령어를 사용해 원래의 주소(return address)로 복귀해야 한다. 이는 다음과 같은 방식으로 작동한다:
- 스택에서 값을 pop하고, return address를 얻는다. 이때 %rsp의 값은 8 증가한다.
- %rip에 return address를 할당하고, 이를 통해 return address로 jump한다.
즉, ret는 pop %rip과 동일한 효과이다.
x86-64 Return Values
x86-64 시스템에서 기본적인 규칙은 함수가 종료된 뒤의 반환값은 %rax에 저장된다는 것이다. 이때 caller와 callee는 각각 주의할 점이 존재한다.
- Caller 주의점: Callee를 호출하기 전에 %rax의 값을 먼저 백업해두어야 한다.
- Callee 주의점: 반환값을 %rax에 저장하되, 해당 값이 8byte를 초과할 경우, 포인터를 대신 반환해야 한다.
Stack based languages
Stack 기반 언어(Stack based language)는 말 그대로 스택을 기반으로 하는 언어이다. 이러한 언어는 재귀 함수를 지원하며, 여러 번 같은 함수를 호출할 수 있으므로, 각 호출마다 상태(지역 변수, return address 등)를 스택에서 따로 관리하고 저장해야 한다. 이때 스택은
Function call in Assembly
자세한 내용은 Function call in Assembly 문서를 참조하십시오.
Memory management
자세한 내용은 Memory management 문서를 참조하십시오.
각주
- ↑ 프로그램 카운터(%rip)다음에 실행될 명령어의 주소를 저장한다.