函數壓棧的過程(轉 知乎)


例如:
void func_A(arg_A1, arg_A2);
void func_B(arg_B1, arg_B2);

int main(int argc, char *argv[], char **envp)
{
func_A(arg_A1, arg_A2);
}

void func_A(arg_A1, arg_A2)
{
var_A;
func_B(arg_B1, arg_B2);
}

void func_B(arg_B1, arg_B2)
{
var_B1;
var_B2;
}

 

在main函數調用func_A的時候,首先在自己的棧幀中壓入函數返回地址,然后為func_A創建新棧幀並壓入系統棧
在func_A調用func_B的時候,同樣先在自己的棧幀中壓入函數返回地址,然后為func_B創建新棧幀並壓入系統棧
在func_B返回時,func_B的棧幀被彈出系統棧,func_A棧幀中的返回地址被“露”在棧頂,此時處理器按照這個返回地址重新跳到func_A代碼區中執行
在func_A返回時,func_A的棧幀被彈出系統棧,main函數棧幀中的返回地址被“露”在棧頂,此時處理器按照這個返回地址跳到main函數代碼區中執行

在實際運行中,main函數並不是第一個被調用的函數,程序被裝入內存前還有一些其他操作,上圖只是棧在函數調用過程中所起作用的示意圖

 

ESP:棧指針寄存器(extended stack pointer),其內存放着一個指針,該指針永遠指向系統棧最上面一個棧幀的棧頂
EBP:基址指針寄存器(extended base pointer),其內存放着一個指針,該指針永遠指向系統棧最上面一個棧幀的底部
函數棧幀:ESP和EBP之間的內存空間為當前棧幀,EBP標識了當前棧幀的底部,ESP標識了當前棧幀的頂部。

 


EIP:指令寄存器(extended instruction pointer), 其內存放着一個指針,該指針永遠指向下一條待執行的指令地址。

函數調用大致包括以下幾個步驟:

參數入棧:將參數從右向左依次壓入系統棧中
返回地址入棧:將當前代碼區調用指令的下一條指令地址壓入棧中,供函數返回時繼續執行
代碼區跳轉:處理器從當前代碼區跳轉到被調用函數的入口處
棧幀調整:具體包括
保存當前棧幀狀態值,已備后面恢復本棧幀時使用(EBP入棧)
將當前棧幀切換到新棧幀。(將ESP值裝入EBP,更新棧幀底部)
給新棧幀分配空間。(把ESP減去所需空間的大小,抬高棧頂)



————————————————分割線——————————————————————

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM