操作系統-函數堆棧過程

概述

文章講的是彙編語句中執行函數時堆棧的過程,其中比較重要的是 ESPEBP 這倆個寄存器.

調用過程

1297993-20220109113942875-1893306958.png

假如讓我們來設計這個函數調用的堆棧過程,我覺得可以這樣思考
現看這個函數調用 ,調用 add方法需要傳過去參數 ,所以必須有一個地方可以讓add函數執行形成的堆棧可以取得到 main 傳過來 ,再一個 add 執行完之後還得返回main中繼續執行 ,所以返回地址也是必須保存的.

1297993-20220109114724144-660974239.png

上面左圖這是說明了,在一個進程中,堆是向下增長的, 右圖則是從調用者被調用者兩個角度在描述這個調用的過程..

1297993-20220109115020494-1266243661.png

我們常提到的IA32 寄存器就幾個 , 當我要去調用下一個函數的時候, 爲了騰出寄存器給即將調用的函數使用,所以調用之前我們必須先將寄存器中的數據入棧 ,這樣即將調用的函數就可以使用了 (我們後面會看到保存返回地址也是這個套路 ,先將返回地址保存在棧裏然後執行完函數之後的執行回到返回地址就好了)

其中需要注意的兩個寄存器 :

  • ESP (stack point) : 指向當前棧幀的頂部
  • EBP (base point) : 指向棧幀的底部

底部是不會動的,而頂部會一直增長

接下來看一個例子 :

1297993-20220109120119576-1966375804.png

1297993-20220109120257673-255875680.png

上面的例子 ,可以看到 ESPEBP始終是指向當前棧幀的頂部和底部的, 我們可以看到大致的過程可以分爲 :

  • 準備階段
  • 過程體
  • 結束階段

這裏注意幾個細節

每個過程開始的兩條指令

pushl %ebp
movl %esp, %ebp

意思就把調用人的 EBP 先入棧了 (方便後續執行找回來),然後將ESP 的值給到 EBP(上一句我們已經把 EBP的值存起來了,所以此時 EBP就可以用來新的函數形成的堆棧的棧底了,秒啊!) ,那我如果要讓 EBP 回到我之前入棧的那個值呢?

popl %ebp 

即可.

call 語句

call語句做的事情有兩個 :

  1. 保存返回地址
  2. 跳轉執行函數
    在上面例子中, 返回地址就是 :
movl %eax, -4(%ebp)

這一語句的地址

跳轉執行函數開始的語句是什麼

還是這兩句

pushl %ebp
movl %esp, %ebp

執行函數

1297993-20220109121745821-1032010805.png

上面的例子我們看到執行函數的過程中 ,爲了獲取傳進來的參數 ,可以通過 EBP 往上偏移 8或是12 ,具體的語句 :

movl 8(%ebp),%edx
movl (%edx),%ecx 

返回

ret 實際會把前面入棧的返回地址放在 EIP 寄存器 ,這樣就可以回到調用函數的下一句啦.(IA32中用EIP存放將要執行的地址)

參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章