프로시저 P가 프로시저 Q를 호출하고,
Q가 실행한 후에 다시 P로 리턴한다고 가정하자.
다음과 같은 하나 이상의 mechanism이 연관된다.
- Passing control (제어권 전달)
: To beginning of procedure code
: Back to retuun point
- 프로그램 카운터는 진입할 때 Q에 대한 시작 주소로 설정되고,
리턴할 때 P에서 Q를 호출하는 instruction 다음 instruction으로 설정되어야 한다.
- Passing data (데이터 전달)
: Procedure arguments
: Return value
- P는 하나 이상의 매개변수를 Q에 제공할 수 있어야 하며,
Q는 다시 P로 하나의 값을 리턴할 수 있어야 한다
- Memory management (메모리 할당과 반납)
: Allocate during procedure execution
: Deallocate upon return
- Q는 시작할 때 지역변수들을 위한 공간을 할당할 수도 있고,
리턴할 때 이 저장소를 반납할 수 있다.
Machine instructions implement the mechanisms,
but the choices are determined by designers.
These choices make up the Application Binary Interface(ABI)
Memory Layout
- stack
- local variables and procedure context
- all function calls and variables in function
- 밑으로 내려가면서 data를 쌓기 때문에 맨 밑을 top으로 본다.
- managed "automatically" by compiler/assembly
- permissions : writable; not executable
- recursive할 때 못 빠져나오는 경우 -> stack overflow
- Heap (Dynamic Data)
- Since space of stack is limited, we use dynamic allocation for flexible use
- variables allocated with new or malloc
- 위로 올라가면서 쌓는다
- managed "dynamically" by programmer (allocate / deallocate data)
- permissions : writable; not executable
- Static Data
- static variables (including global variables)
- declared outside of all the functions
- managed "statically" initialized when process starts
- permissions : writable; not executable
- Literals
- large literals / constants
- managed "statically" initialized when process starts
- permissions : read-only; not executable
- Instructions
- all program code
- managed "statically" initialized when process starts
- permissions : read-only; executable
Stack
-
스택에서 Top은 가장 낮은 주소, Bottom은 가장 높은 주소를 의미한다.
-
스택은 작은 주소 방향으로 성장하며,
-
스택 포인터 %rsp는 스택의 최상위 원소를 가리킨다.
%rsp = address of "top" element, the most-recently-pushed item that is not yet popped
-
데이터는 pushq와 popq instruction을 이용해 스택에 저장되고 읽어올 수 있다.
- pushq Src
: degrement %rsp by 8. 감소시킨다는 건 더 늘린다는 의미
: store value at address giben by %rsp
- popq Dest
: increment %rsp by 8. 증가시킨다는 건 줄인다는 의미
: store value at Dest
: 이렇게 줄여도, it remains in memory at old %rsp. 여전히 남아있다.
-
특정 값으로 초기화되지 않은 데이터를 위한 공간은 스택 포인터를 감소시켜서 간단히 할당될 수 있다. 이와 유사하게 공간은 스택 포인터를 증가시켜서 반납할 수 있다.
Calling Conventions
Passing Control 제어의 이동
- 제어를 P에서 Q로 전달하는 것은 일단 PC를 Q를 위한 코드의 시작주소로 설정하는 것과 관련된다.
- 나중에 Q가 리턴하면 P를 다시 실행해야 하는 코드 위치의 일부 기록을 갖고 있어야 하기 때문에 instruction call Q로 프로시저 Q를 호출에서 기록한다.
- 이 instructions은 주소 A를 스택에 push하고, PC를 Q의 시작으로 설정한다.
- push된 주소 A는 return address라고 불리고, call instruction 바로 다음 instruction의 주소로 계산되나.
- 여기에 대응하는 instruction ret는 주소 A를 스택에서 pop하고 PC를 A로 세팅한다.
Flow
- Use stack to support procedure call and return
- Procedure call : call label
- push return address on stack
- Return address
: Address of the next instruction immediately
after call instruction
- jump to label
- Procedure return :: ret
- pop return address from stack
- jump to address
ppt 그림 좀 보면서 이해해보자
Passing data 데이터 전송
- 호출/리턴될 때 프로시저 콜은 데이터를 인자로 전달하는 것과, 다시 어떤 값을 리턴하는 것에 관련되어 있다.
- 프로시저로부터의 데이터 전달은 레지스터를 통해서 일어난다.
- x86에서는 최대 6개의 정수형 인자가 레지스터로 전달될 수 있다. 이 레지스터들은 전달되는 데이터 type의 길이에 따라 레지스터 이름을 이용해 정해진 순서로 이용된다.
- 만약 함수가 6개 이상의 정수형 인자를 가질 때, 다른 인자들은 스택으로 전달된다. 인자 1~6은 적절한 레지스터에 복사되고, 인자 7~n까지는 인자 7을 stack Top에 넣는 방법으로 저장한다.
- 매개변수들을 스택으로 전달할 때, 모든 데이터 길이는 8의 배수로 반올림된다.
Flow
- Registers (NOT in Memory)
- First 6 arguments
- %rdi
- %rsi
- %rdx
- %rcx
- %r8
- %r9
- return value
Use more than 6 parameters, you have to use Memory
- Return Values
By convention, values returned by procedures are placed in %rax. Choice of %rax is arbitrary.
- Caller must make sure to save the contents of %rax before calling a callee that returns a value
- Callee places return value into %rax
- For return values greater than 8 bytes, best to return a pointer to them
- Upon return, caller finds the return value in %rax
Managing local data
- Stack allocated in Frames
: state for a single procedure instantiation
Stack Frame
- Contents
- return information
- local storage
- temporary space (추가 공간)
- Pointer
- Stack pointer : %rsp
- Frame pointer : %rbp (starting point of current frame)
(base pointer)
- 얘네 8 bytes 씩 증가.
- Management
- space allocated when enter procedure
- "set-up" code
- includes push by call instruction
- dealocated when return
- "finish" code
- includes pop by ret instruction
Exercise
int main() {
int i, x = 0;
for (i = 0; i < 3; i++)
x = randSum(x);
printf("x = %d\n", x);
return 0;
}
int randSum(int n) {
int y = rand() % 20;
return n + y;
}
Q. Higher/larger address is x or y?
A. x. x is first defined than when y is defined in randSum()
Q. How many total stack frames are created?
A. 8. main(), randSum() 3, rand() 3, print()
Q. What is the maximum depth(# of frames) of the Stack?
A. 3.
x86-64/Linux Stack Frame
- Caller's Stack Frame
: Extra arguments (if > 6 args) for this call
- Callee Stack Frame
- return address (pushed by call instruction)
- old frame pointer(optinal)
- saved register context (when reusing registers)
- local variables (if can't be kept in registers)
- "Argument build" area (if callee needs to call another function - parameter for function about to call, if needed)
Exercise
long increment(long *p, long val) {
long x = *p;
long y = x + val;
*p = y;
return x;
}
long call_incr() {
long v1 = 351;
long v2 = increment(&v1, 100);
return v1 + v2;
}
1st arg(p) : %rdi
2nd arg(val), y : %rsi
x, return value : %rax
increment :
movq (%rdi), %rax // x = *p
addq %rax, %rsi // val = val + x (y랑 val 혼용?)
movq %rsi, (%rdi) // *p = val
ret
call_incr :
// setsup space for local variables.
subq $16, %rsp
movq $351, 8(%rsp) // %rsp + 8에 351 저장 -> 이거 꼭 ppt 그림 보기!!!
// setup parameters for call to increment
movl $100, %rsi // %rsi에 100 저장
leaq 8(%rsp), %rdi // v1(%rdi)에 %rsp + 8 저장. 즉, 351을 저장 -> 이것도 그림 꼭 보자. 왜 %rax에 351이 들어오는지!!
//
call increment
addq 8(%rsp), %rax // %rax = v1 + v2
// de-allocate space for local vars
addq $16, %rsp