suminworld

Make the world a better place using technology

자세히보기

concept

x86-64 스택 프롤로그(Stack Prologue)와 스택 프레임(Stack Frame)

숨usm 2025. 10. 2. 05:08

스택 프롤로그(Stack Prologue)란?

함수가 시작될 때 자기만의 작업 공간을 만드는 과정

함수마다 자기만의 지역 변수 공간이 필요합니다. 이 공간을 스택에 만드는 게 바로 프롤로그입니다.

 
 
main() 함수 실행 중
   ↓
func() 함수 호출
   ↓
func()는 자기 공간이 필요함
   ↓
스택 프롤로그 실행!

전형적인 3단계 과정

 
 
assembly
push   %rbp           # (1) 호출자의 RBP를 스택에 저장
mov    %rsp, %rbp     # (2) 현재 RSP를 새 RBP로: 기준점 설정
sub    $N, %rsp       # (3) 지역변수용 공간 N바이트 확보

실제 예시로 이해하기

C 코드

 
 
c
// demo.c
int f(void) {
    int x = 5;
    return x;
}

컴파일

 
 
bash
gcc -S -O0 -fno-omit-frame-pointer -masm=att demo.c

어셈블리 출력

 
 
assembly
f:
    push   %rbp              # (1) 호출자 RBP 저장
    mov    %rsp, %rbp        # (2) 새 프레임 기준점 설정
    sub    $16, %rsp         # (3) 지역변수용 공간 확보 (16바이트 정렬)
    movl   $5, -4(%rbp)      # (4) x = 5 저장
    mov    -4(%rbp), %eax    # (5) return x → EAX에 로드
    leave                    # (6) 프레임 정리
    ret                      # (7) 복귀

단계별 스택 변화

호출 직후 (프롤로그 전)

 
 
높은 주소
┌─────────────────────┐
│  caller의 데이터    │
├─────────────────────┤
│  return address     │ ← call이 push한 복귀 주소
└─────────────────────┘ ← %rsp
낮은 주소

(1) push %rbp

효과: rsp -= 8; *(rsp) = rbp

 
 
높은 주소
┌─────────────────────┐
│  caller의 데이터    │
├─────────────────────┤
│  return address     │
├─────────────────────┤
│  old %rbp           │ ← 이전 함수의 기준점 저장
└─────────────────────┘ ← %rsp
낮은 주소

왜 저장? 함수가 끝나면 이전 함수로 돌아가야 하니까!

(2) mov %rsp, %rbp

현재 스택 꼭대기를 프레임 기준점으로 고정

 
 
높은 주소
┌─────────────────────┐
│  caller의 데이터    │
├─────────────────────┤
│  return address     │
├─────────────────────┤ ← %rbp, %rsp (함께 위치)
│  old %rbp           │     새 함수의 기준점
└─────────────────────┘
낮은 주소

%rbp의 역할: 현재 함수의 고정된 기준점

  • 지역 변수는 %rbp - 4, %rbp - 8 같은 오프셋으로 접근

(3) sub $16, %rsp

지역변수 공간 확보 - 스택은 아래(주소 감소)로 자람

 
 
높은 주소
┌─────────────────────┐
│  caller의 데이터    │
├─────────────────────┤
│  return address     │
├─────────────────────┤ ← %rbp (고정된 기준점)
│  old %rbp           │
├─────────────────────┤
│                     │
│  지역변수 영역      │
│  (16바이트)         │
│                     │
└─────────────────────┘ ← %rsp (16바이트 내려감)
낮은 주소

왜 16바이트? SysV AMD64 규약의 스택 16바이트 정렬 요구사항 때문

(4) movl $5, -4(%rbp)

지역 변수 x에 5 저장

 
 
              %rbp (고정)
                ↓
┌─────────────────────┐
│  old %rbp           │
├─────────────────────┤
│  (여유 공간)        │
├─────────────────────┤
│  x = 5              │ ← -4(%rbp)
└─────────────────────┘
        ↑
      %rsp

스택 프레임 전체 구조

Stack Frame = 각 함수의 전용 작업 공간

 
 
높은 주소
┌───────────────────────────┐
│   main()의 Stack Frame    │
│   - main의 지역 변수      │
│   - main의 매개 변수      │
├───────────────────────────┤ ← 저장된 main의 %rbp
│   func()의 Stack Frame    │
│   - func의 지역 변수      │
│   - func의 매개 변수      │
├───────────────────────────┤ ← 저장된 func의 %rbp
│   ...                     │
└───────────────────────────┘
낮은 주소

각 함수는 자기 프레임 안에서만 작업하며, 다른 함수 영역은 건드리지 못합니다.

스택 에필로그 (함수 종료)

프롤로그의 반대 과정

 
 
assembly
leave    # mov %rbp, %rsp; pop %rbp 와 동일
ret      # 호출한 곳으로 돌아감

분해하면:

 
 
assembly
mov  %rbp, %rsp    # (1) rsp를 rbp로: 지역 변수 공간 버림
pop  %rbp          # (2) 저장했던 이전 rbp 복원
ret                # (3) return address로 점프

왜 %rbp를 사용하나?

고정된 기준점이 필요하기 때문

  • %rsp는 계속 변함: push/pop/sub/add 때마다 이동
  • %rbp는 고정: 함수 실행 내내 같은 값 유지
  • 결과: 지역 변수 접근이 쉬워짐
 
 
c
int x;  → %rbp - 4
int y;  → %rbp - 8
int z;  → %rbp - 12

// %rbp가 고정이니까 항상 같은 offset으로 접근 가능!

메모리 오프셋 규칙

지역 변수/임시 공간

  • (%rbp - 양수) 형태
  • 예: -4(%rbp), -8(%rbp)

저장된 값/함수 인자

  • (%rbp + 양수) 형태
  • 예: 8(%rbp), 16(%rbp)

AT&T 문법 표기법

 
 
assembly
$0x3        # 즉값(상수)
%edi        # 레지스터
-4(%rbp)    # 메모리 참조 (rbp - 4 주소의 값)

추가 예시: return x + 3

 
 
c
int f(void) {
    int x = 5;
    return x + 3;
}

어셈블리

 
 
assembly
movl   $5, -4(%rbp)     # x = 5
mov    -4(%rbp), %eax   # eax = x
addl   $0x3, %eax       # eax += 3 ← 즉값 더하기
leave
ret

차이점:

  • addl $0x3, %edi: 인자 a가 %edi로 들어왔을 때 사용
  • addl $0x3, %eax: 지역변수를 %eax로 가져와서 사용

핵심 정리

스택 프롤로그 3단계

  1. push %rbp: 이전 함수의 기준점 저장
  2. mov %rsp, %rbp: 새 함수의 기준점 설정
  3. sub $N, %rsp: 지역 변수 공간 확보

스택 프레임의 구성

  • 저장된 이전 함수의 RBP
  • 반환 주소 (return address)
  • 지역 변수 / 임시 저장 공간
  • 필요 시 저장된 레지스터

    개념 정리
  • %rsp: 동적으로 변하는 스택 포인터
  • %rbp: 고정된 프레임 기준점
  • 스택 방향: 높은 주소 → 낮은 주소로 성장
  • 16바이트 정렬: x86-64 호출 규약 요구사항