棧幀
那先有個問題,什么是棧?
在數據結構中, 棧是限定僅在表尾進行插入或刪除操作的線性表。棧是一種數據結構,它按照后進先出的原則存儲數據,先進入的數據被壓入棧底,最后的數據在棧頂,需要讀數據的時候從棧頂開始彈出數據。
在計算機系統中,棧也可以稱之為棧內存是一個具有動態內存區域,存儲函數內部(包括main函數)的局部變量和方法調用和函數參數值,是由系統自動分配的,一般速度較快;存儲地址是連續且存在有限棧容量,會出現溢出現象程序可以將數據壓入棧中,也可以將數據從棧頂彈出。壓棧操作使得棧增大,而彈出操作使棧減小。
棧用於維護函數調用的上下文,離開了棧函數調用就沒法實現。
而什么是棧幀(Stack Frame)呢?
每一次函數的調用,都會在調用棧(call stack)上維護一個獨立的棧幀(stack frame).每個獨立的棧幀一般包括:
- 函數的返回地址和參數
- 臨時變量: 包括函數的非靜態局部變量以及編譯器自動生成的其他臨時變量
- 函數調用的上下文
棧是從高地址向低地址延伸,一個函數的棧幀用ebp 和 esp 這兩個寄存器來划定范圍.ebp 指向當前的棧幀的底部,esp 始終指向棧幀的頂部;</br>
ebp 寄存器又被稱為幀指針(Frame Pointer);</br>
esp 寄存器又被稱為棧指針(Stack Pointer);

image
在函數調用的過程中,有函數的調用者(caller)和被調用的函數(callee).
調用者需要知道被調用者函數返回值;
被調用者需要知道傳入的參數和返回的地址;
函數調用
函數調用分為以下幾步:
- 參數入棧: 將參數按照調用約定(C 是從右向左)依次壓入系統棧中;
- 返回地址入棧: 將當前代碼區調用指令的下一條指令地址壓入棧中,供函數返回時繼續執行;
- 代碼跳轉: 處理器將代碼區跳轉到被調用函數的入口處;
- 棧幀調整: </br>
1.將調用者的ebp壓棧處理,保存指向棧底的ebp的地址(方便函數返回之后的現場恢復),此時esp指向新的棧頂位置;push ebp
</br>
2.將當前棧幀切換到新棧幀(將eps值裝入ebp,更新棧幀底部), 這時ebp指向棧頂,而此時棧頂就是old ebpmov ebp, esp
</br>
3.給新棧幀分配空間sub esp, XXX
</br>
函數返回
函數返回分為以下幾步:
- 保存被調用函數的返回值到 eax 寄存器中
mov eax, xxx
- 恢復 esp 同時回收局部變量空間
mov ebp, esp
- 將上一個棧幀底部位置恢復到 ebp
pop ebp
- 彈出當前棧頂元素,從棧中取到返回地址,並跳轉到該位置
ret