스택이란? + SP

https://velog.io/@got1_c/Stack

스택 프레임

스택 프레임(Stack Frame)은 함수가 호출될 때 스택 내에 새롭게 형성되는 독립적인 영역이다.

이 영역에는 함수가 실행되는 동안 필요한 매개변수(Arguments),
함수 실행 후 돌아갈 복귀 주소(Return Address),
그리고 함수 내부에서 선언된 지역 변수(Local Variables) 등이 저장된다.

스택 프레임이 필요한 이유

프로그램은 한 번에 여러개의 함수를 호출할 수 있고, 재귀 호출도 가능하다.

이때, 각 함수의 매개변수, 복귀 주소, 지역 변수가 섞이지 않도록 함수마다 독립적인 저장 공간이 필요하다.

그 공간이 바로 스택 프레임이다.


call과 ret & 명령어 포인터

명령어 포인터(EIP)

CPU가 다음에 실행할 명령어의 메모리 주소를 가리키는 레지스터이다.

call

call명령은 두 가지 일을 동시에 수행한다.
1. 명령어 포인터(EIP)가 가리키는 주소값(복귀 주소)을 스택에 push한다.
2. EIP주소를 함수의 시작 주소로 바꾼다.

ret

ret은 스택에 저장돼 있던 복귀 주소pop하여 EIP에 넣는다.

간단한 흐름 예시

main:
	call func
    mov eax, 5 
    
func:
	mov ebx, 10
    ret
  1. main에서 call 실행시 EIP가 가리키는 주소값(복귀 주소)을 스택에 push
  2. func의 시작 주소를 EIP로 바꿈 = func 시작
  3. ebx에 10이 저장
  4. 스택에 저장된 복귀주소를 pop하여 EIP에 넣음 = EIP에 mov eax, 5의 주소를 저장
  5. eax = 5, ebx = 10이 저장됨

BP(Base Pointer)

BP는 메모리의 주소값을 담는 레지스터이다.

그렇다면 BP는 어떤 때에 사용이 될까?

BP는 함수가 호출될 때, 해당 함수의 스택 프레임기준점(base)로 사용된다.

즉, 함수 내부의 지역변수매개변수들에 접근할 때 "스택의 어디에 있는지"를 BP를 기준으로 계산할 수 있게 해준다.


함수 호출 과정

예를 들어, 이러한 C코드가 있을때

void fn()
{
    int a = 1, b = 2, c;
    c = function(a, b, 3);
}

int function(int x, int y, int z)
{
    int sum;
    sum = x + y + z;
    return sum;
}

fn() 함수가 실행되다가 function()을 호출하면,
CPU는 함수 호출을 위해 스택을 이용해 여러 가지 데이터를 차례로 저장한다.
이 과정을 단계별로 살펴보면 다음과 같다.

1.인자(Arguments) 저장

먼저 호출자(fn)는 피호출자(function)에게 전달할 인자 a, b, 3을
오른쪽에서 왼쪽 순서로 스택에 push한다.
이제 스택의 최상단(top)에는 x, y, z가 차례로 쌓여 있다.

push 3        ; z
push b        ; y
push a        ; x
call function ; 복귀 주소 push + EIP 변경

2. call 명령어 실행

  1. return adress(복귀 주소) = call 다음 명령의 주소를 스택에 push
  2. EIP를 function의 시작 주소로 변경

현재 스택 구조

z
y
x
return adress	<--- SP

3. 스택 프레임 생성

함수가 시작되면, 기존의 BP값을 저장하고 새로운 스택 프레임을 만든다.

push ebp		;이전 함수의 bp 저장
mov ebp, esp	;현재 sp를 새 bp로 설정
sub esp, 4		;지역 변수(sum)를 위한 공간 확보
int sum

int는 4바이트이므로 top을 가리키는 주소에서 4만큼 낮은 주소로 이동시켜 그만큼의 공간을 마련해야한다.

생성된 스택프레임

z        		 <---- ebp+16
y        		 <---- ebp+12
x        		 <---- ebp+8
return address   <---- ebp+4
previous ebp     <---- ebp
sum (local)      <---- ebp-4
ESP(top)	     <---- esp

4. 함수 본문 실행

스택 프레임이 생성되면 함수의 본문이 실행된다.
함수의 본문에서는 EBP를 기준으로 지역 변수, 매개 변수에 접근할 수 있다.

sum = x + y + z

이 c코드는 어셈블리어로 다음과 같다

mov eax, [ebp+8]    
add eax, [ebp+12]   
add eax, [ebp+16]   
mov [ebp-4], eax    

5. 스택 프레임 정리 및 복귀

함수 실행이 끝나면, 스택을 정리하고 저장해두었던 return address로 돌아가기 위해 SPBP를 복원한다.

mov esp, ebp   ; 스택 포인터 복원 (지역 변수 제거)
pop ebp        ; 이전 함수의 EBP 복원
ret            ; 복귀 주소 pop → EIP로 이동

6. 스택 정리

ret으로 복귀하면, 스택에는 여전히 호출할 때 push했던 인자(x, y, z)가 남아 있다.

스택을 그대로 두면 ESP가 함수 호출 전 위치로 돌아가지 않기 때문에, 다음 함수 호출 시 스택이 겹치거나 주소 계산이 꼬일 수 있다.

따라서 함수 호출이 끝나면 인자들이 차지했던 공간을 해제해 스택을 원래 상태로 되돌리는 과정이 필요하다.

이 과정이 바로 “스택 정리(Stack Cleanup)”이며, 누가 이 정리를 담당하느냐(호출자 or 피호출자)는 호출 규약(Calling Convention) 에 따라 달라진다.
(호출 규약은 다음 글에서 다룬다.)

궁금한점

호출규약관련된 모든것들

0개의 댓글