0x1000: call foo
0x1005: mov rax, 1
0x2000: foo:
0x2000: push rbp
0x2001: mov rbp, rsp
0x2004: sub rsp, 16
0x2008: mov DWORD PTR [rbp-4], 10
0x200F: mov DWORD PTR [rbp-8], 20
0x2016: mov rsp, rbp
0x2019: pop rbp
0x201A: retfoo() 호출우선 call foo를 실행하기 직전 상태를 가정
RIP = 0x1000
RSP = 0x7fff1000
RBP = 0x7fff1020
의미:
RIP = 0x1000 → 이제 call foo를 실행할 차례RSP = 0x7fff1000 → 현재 스택 top 위치RBP = 0x7fff1020 → 지금 이 코드를 호출한 함수(예: main)의 프레임 기준점아직 foo의 프레임은 시작 전
call foo코드:
0x1000: call foo
0x1005: mov rax, 1
call foo가 실행되면 CPU는 자동으로:
0x1005를 스택에 저장RIP를 foo 시작 주소 0x2000으로 변경개념적으로는:
RSP = RSP - 8 ; 0x7fff0ff8
[RSP] = 0x1005
RIP = 0x2000
상태:
RIP = 0x2000
RSP = 0x7fff0ff8
RBP = 0x7fff1020
스택 모습:
높은 주소
...
0x7fff1000
0x7fff0ff8 : [0x1005] <- foo 끝나면 돌아갈 주소
낮은 주소
여기서 핵심:
RSP는 복귀 주소가 들어가면서 내려감RBP는 아직 호출자 함수 기준점 그대로foo는 아직 자기 프레임 기준점을 안 잡음push rbpfoo 첫 줄 시작
0x2000: push rbp
현재 RBP에는 호출자 함수의 기준점 0x7fff1020이 들어 있다. (예를 들면 main함수)
이걸 저장해둬야 나중에 복구할 수 있음
동작:
RSP = RSP - 8 ; 0x7fff0ff0
[RSP] = RBP ; 0x7fff1020 저장
상태:
RIP = 0x2001
RSP = 0x7fff0ff0
RBP = 0x7fff1020
스택:
높은 주소
...
0x7fff0ff8 : [0x1005] <- 복귀 주소
0x7fff0ff0 : [0x7fff1020] <- 이전 함수의 RBP 저장
낮은 주소
핵심:
RSP는 또 내려감mov rbp, rsp다음 줄:
0x2001: mov rbp, rsp
이건 스택에 뭘 쌓는 명령이 아닌 그냥 현재 RSP 값을 RBP에 복사한다.
→ 프레임의 기준점을 현재 함수의 시작점으로 갱신
즉:
RBP = RSP = 0x7fff0ff0
상태:
RIP = 0x2004
RSP = 0x7fff0ff0
RBP = 0x7fff0ff0
RSP = 현재 topRBP = foo 프레임의 기준점스택을 foo 입장에서 해석하면:
[RBP] = 저장된 이전 RBP = 0x7fff1020
[RBP+8] = 복귀 주소 = 0x1005
여기서 드디어 프레임 기준점이 생기게 된다.
sub rsp, 16이제 지역 변수/임시값 공간을 확보한다.
0x2004: sub rsp, 16
뜻:
동작 후:
RSP = 0x7fff0fe0
RBP = 0x7fff0ff0
상태:
RIP = 0x2008
RSP = 0x7fff0fe0
RBP = 0x7fff0ff0
스택:
높은 주소
...
0x7fff0ff8 : [0x1005] <- 복귀 주소
0x7fff0ff0 : [0x7fff1020] <- 이전 RBP <- RBP
0x7fff0fe8 : [지역변수 공간]
0x7fff0fe0 : [지역변수 공간] <- RSP
낮은 주소
이제부터 foo는 이 확보한 공간을 자기 지역 변수/임시값 저장용으로 사용한다.
핵심:
RBP는 그대로RSP만 내려감예시:
0x2008: mov DWORD PTR [rbp-4], 10
0x200F: mov DWORD PTR [rbp-8], 20
이건 각각:
[rbp-4] 위치에 10 저장[rbp-8] 위치에 20 저장입니다.
주소로 보면:
RBP = 0x7fff0ff0[rbp-4] = 0x7fff0fec[rbp-8] = 0x7fff0fe8즉 지역 변수는 이런 식으로 놓일 수 있다.
스택:
높은 주소
...
0x7fff0ff8 : [0x1005] <- 복귀 주소
0x7fff0ff0 : [0x7fff1020] <- 이전 RBP
0x7fff0fec : [10] <- 지역 변수 a
0x7fff0fe8 : [20] <- 지역 변수 b
0x7fff0fe0 : [남은 임시 공간] <- RSP
낮은 주소
여기서 중요한 점:
RSP가 아니라 RBP 기준RSP가 흔들려도 [rbp-4], [rbp-8]은 안정적 (→ RBP의 존재 이유 중 하나)mov rsp, rbp이제 함수를 끝내기 위한 작업에 돌입한다.
0x2016: mov rsp, rbp
현재:
RSP = 0x7fff0fe0
RBP = 0x7fff0ff0
이 명령을 수행하면:
RSP = 0x7fff0ff0
즉 지역 변수/임시값으로 잡았던 공간을 한 번에 정리한다.
→ RSP를 함수 기준점으로 보냄으로서 함수에서 사용했던 공간을 정리할 수 있다.
상태:
RIP = 0x2019
RSP = 0x7fff0ff0
RBP = 0x7fff0ff0
핵심:
[rbp-4], [rbp-8]에 있던 값들을 하나하나 pop하는 게 아님RSP를 프레임 기준점으로 되돌려서 그 아래 영역을 더 이상 현재 함수 공간으로 안 쓰겠다고 하는 것pop rbp다음:
0x2019: pop rbp
현재 [RSP]에는 저장해두었던 이전 RBP가 있음.
즉:
[0x7fff0ff0] = 0x7fff1020pop rbp 수행:
RBP = [RSP] = 0x7fff1020
RSP = RSP + 8 = 0x7fff0ff8
상태:
RIP = 0x201A
RSP = 0x7fff0ff8
RBP = 0x7fff1020
이제 호출자 함수의 프레임 기준점이 복구되었음.
ret마지막:
0x201A: ret
현재 [RSP]에는 복귀 주소가 있다.
[0x7fff0ff8] = 0x1005ret 수행:
RIP = [RSP] = 0x1005
RSP = RSP + 8 = 0x7fff1000
최종 상태:
RIP = 0x1005
RSP = 0x7fff1000
RBP = 0x7fff1020
즉 call foo 하기 전 호출자 상태로 복원되었고, 이제 0x1005: mov rax, 1부터 이어서 실행한다.