이 글은 포스텍 김종 교수님의 컴퓨터SW시스템개론(CSED211) 강의를 기반으로 재구성한 것입니다.
이 글에서는 calling convention에 대해 알아본다. calling convention은 함수가 호출되었을 때 어떻게 parameter를 처리하고 결과를 return할지에 대한 규약이다.
Stack의 구조
C언어나 Java 등을 배우면서 '메모리는 code, stack, heap 등으로 나뉘어져 있어요~'라는 내용을 한 번쯤은 들어봤을 것이다. stack에는 local variable과 parameter 등이 저장되며, 이 값들이 저장되고 반환되는 방식이 calling convention이다.
intel 계열 중 하나인 x86에서는 stack memory가 더 작을수록 top에 위치한다. 즉 stack이 증가하면 address가 0으로 가까워진다는 것이며, 이는 little endian ordering이 적용되는 것을 생각하면 이해하기 더 쉽다. 이 글에서는 x86에서 calling convention을 다룬다.
Calling Convention
calling convention을 살펴보기 이전에 컴퓨터에서 사용하는 몇 가지 register에 대해 알아야 한다.
- pc : program counter. 다음에 실행될 instruction의 주소를 가지고 있다.
- rsp : stack pointer. 현재 stack top address를 가지고 있다.
- register의 개수는 한정되어 있다.
어떤 procedure P가 procedure Q를 호출하고, Q의 ret이 실행되면 다시 P가 실행된다고 하자. (P가 Q를 call하는 상태. 예외는 생각하지 않음) 이 때 아래 3가지의 요소를 신경써야 한다.
- Passing Control : pc는 새 procedure인 Q에 진입하기 전 Q instruction의 시작 주소로 설정되며, return 시에는 P에서 Q를 호출하는 instruction 다음의 instruction으로 설정한다.
- Passing Data : P는 1개 이상의 parameter를 Q에게 넘기며, Q는 1개 이상의 return value를 P에게 전달해야 한다.
- Managing Local Data : Q는 실행될 떄 local variable을 위한 공간을 할당할 수 있고, return 시 이 공간을 반납한다.
Passing Control 제어권 넘기기
P에서 Q를 call하는 instruction이 실행되었을 때 아래와 같은 순서로 제어권을 넘긴다.
P가 Q 호출 시
- call Q 다음에 있는 instruction address를 stack에 push한다. 이를 return address라 한다.
- PC를 Q instruction의 시작 주소로 설정한다.
- 다음 instruction은 PC에 있는 값이 실행된다. (Q 안의 내용이 실행된다.)
Q 종료 시 (ret)
- stack에서 return address를 pop한다.
- pc를 return address로 돌린다.
- 다음 instruction은 pc에 있는 값이 실행되며, call Q 다음에 있는 instruction이 실행된다.
Passing Data 정보 넘기기
위에서 P의 control을 Q로 넘기는 방법을 살펴보았다. 이 때 parameter로 전달하는 data도 있고, Q의 결과를 받아와야 한다.
parameter는 %rdi, %rsi, %rdx, %rcs, %r8, %r9 6개의 register를 사용하며, 만약 parameter가 6개보다 많다면 stack에 push해 추가적으로 할당받은 공간을 사용한다.
Q의 실행 결과는 %rax register를 사용한다.
Managing Local Data
기본적으로 모든 정보들은 최대한 register를 이용하려 하지만 register의 숫자가 부족한 경우 / local variable이 struct나 array나 pointer인 경우 register에 모든 값을 담을 수 없기 때문에 stack에 local variable들을 저장해야 한다. 이 때 stack frame이라는 것을 사용한다. stack frame은 call instruction으로 procedure에 진입 시 stack에 공간을 할당받으며, ret instruction으로 procedure가 끝날 때 할당받은 공간을 삭제한다.
Stack Frame
어떤 procedure가 호출될 때 그 procedure만이 사용할 수 있는 stack의 공간이며, 아래 3가지 요소로 이루어져 있다.
- return information
- local storage (필요 시)
- temporary space (필요 시)
Register Saving Convention
stack frame에 local variable을 저장하는 것은 알았다. 컴퓨터는 기본적으로 register를 사용해 값을 계산하려 한다. 그러나 callee에서 너무 많은 parameter를 사용해서 register가 부족한 경우는 어떻게 해야 할까? 또 callee가 caller가 나중에 사용할 register의 값을 바꾸면 프로그래밍 예상한 대로 작동하지 않을 것이다. 이를 대비해 local variable, 즉 stack에 기존 register의 값들을 백업해 두는 방식을 채택한다. 이것이 바로 register saving convention이다.
- caller-saved register : callee를 호출하기 전에 미리 caller stack frame에 저장하는 register. 따라서 callee에서 변경해도 되는 register이다.
- callee-saved register : callee가 해당 register를 사용하기 전에 미리 callee stack frame에 저장하는 register. callee에서 해당 register를 사용할 예정이기 때문에 원래 있던 값을 백업해 둔 후 callee procedure 종료 시 기존 register 값을 복원시킨다.
아래 몇 가지는 관습적으로 역할이 정해져 있다.
- %rax : return value가 저장되는 register. callee 연산의 값이 저장되기 때문에 callee에 무조건 사용된다. 따라서 caller-saved register.
- %rdi, %rsi, %rdx, %rcs, %r8, %r9 : 이 6개의 register들은 parameter로 사용되기 때문에 callee에서 무조건 수정된다고 봐야 한다. 따라서 caller-saved register. 이외에도 %r10, %r11는 caller-saved register로 사용한다.
- %rsp는 stack pointer이기 때문에 callee procedure가 끝이 나면 원복시켜야 하기 때문에 callee-saved이다.
- %rbp는 stack frame pointer로 사용되기 때문에 %rsp와 같은 이유로 callee-saved이다.
- 이외에도 %rbx, %r12, %r13, %r14, %r15는 callee-saved register로 사용한다.
stack frame structure
register saving convention가 적용된 stack frame의 구조는 아래와 같다.
caller stack frame에는 caller-saved register 값들이, 그리고 return address가 저장된다. callee stack frame에는 callee-saved register, callee의 local variable들이 들어있다.
요약
caller procedure P가 callee procedure Q를 call하는 상황에서 passing control, passing data, managing local data가 반여된 calling convention은 아래와 같다.
- caller-saved register의 값들을 caller stack frame에 저장한다. (push)
- return address를 caller stack frame에 저장한다. (push)
- %rbp, %rsp register의 값이 변경되었으므로 callee procedure로 제어권이 넘어간다.
- callee saved register의 값들을 callee stack frame에 저장한다. (push)
- callee procedure를 실행한다.
- ...
- callee procedure 종료 시, callee saved register를 callee stack frame에서 가져온다. (pop)
- return value를 %rax에 저장한다.
- caller stack frame에서 %rbp를 복구한다. (pop)
- caller procedure로 복귀한다.
'CS > OS' 카테고리의 다른 글
[컴퓨터 SW] Storage - RAM & Disk (0) | 2023.06.14 |
---|---|
[컴퓨터 SW] Buffer Overflow (0) | 2023.06.11 |
[컴퓨터 SW] Array/Structure/Union의 할당과 접근 (0) | 2023.06.11 |
[컴퓨터 SW] Byte Ordering (0) | 2023.06.08 |
[컴퓨터 SW] Bit를 이용한 컴퓨터의 정보 표현 (2) | 2023.06.07 |