函式呼叫過程

2021-10-01 12:33:00 字數 2806 閱讀 6265

主要流程:

呼叫前的準備 —— 引數入棧,eip 入棧,ebp 入棧,eip跳轉

函式執行

恢復到呼叫前狀態 —— 返回值 eax,恢復ebp,恢復 eip

#include 

intf()

intmain()

call  f (0b711d6h)

pushl %ebp

movl %esp, %ebp

subl $16, %esp

。。。movl %ebp %esp

pop %ebp

ret

這些是函式呼叫的必經步驟!

看懂這個之前還得先了解計算機的堆疊結構

一:計算機一般是小端——儲存順序和常識相反(僅供理解)

即 高位址(棧底)—————— 低位址(棧頂)

ebp 和 esp 是兩個暫存器,分別儲存棧底指標和棧頂指標(位址)

二:棧——相當於往有底的杯子裡倒水

那麼,上面的彙編語句就可以開始解析了

首先:

call  f (

0b711d6h)

eip 入棧——記錄當前語句執行到哪了,之後好恢復

eip跳轉 ——跳轉到要呼叫的函式處

(通過括號內的位址實現跳轉,這個位址是編譯器給的)

入棧前:

然後 push 指令—— esp 往低位址移動4個位元組

( esp 的值為4位元組)

然後把 ebp 的值存到 esp 所指的空間

(指標所指的是資料的起始位址,從低位址開始)

ps:因為計算機是小端,所以才會有東西從棧底進的感覺

其實,在棧頂存入新元素,這其實就是入棧 push

這裡一定要好好理解!!!!

把 esp 的值賦給ebp

這時 esp 和 ebp 指向同一位址

( 這裡是暫存器之間的賦值!)

esp 減16 的實際意義

棧頂和棧頂分開,形成了新的棧

——他們之間就可以做好多事情了

ps:他們之間的並不是函式內容

函式語句轉成的彙編語句存在記憶體的其他地方

他們之間存的一般是我們口中的區域性變數~

正是因為呼叫函式時用的棧是臨時的

函式執行完之後,ebp 和 esp 恢復到原先的值

區域性變數的生命週期也就結束了~

(在記憶體中的值可能不變,但是已經訪問不了了)

這裡舉個例子吧:

intf(

)// 賦值語句對應彙編語句: movl $1, -4(%ebp)

棧中存的 1 就是我們說的區域性變數啦

(函式結束後,ebp esp 轉移,區域性變數就無效咯~)

intf(

)//return a 對應的彙編語句

movl -4(

%ebp)

,%eax

//把a的值賦到eax暫存器中

return 返回值其實就儲存在 eax 暫存器中

接下來,把 esp 和 ebp 的值還原回去

movl %ebp %esp

pop %ebp

ret

把 ebp 的值賦給 esp

此時,esp 和 ebp 都指向棧底那唯一的 —— 「 ebp的值 」

然後,pop 指令 —— 把棧頂指向的值(即上圖的「 ebp 的值」)

賦到ebp 暫存器中

同時,esp 往高位址移動4位元組(出棧了乙個4位元組的值嘛)

此時,ebp 和 esp 都回到了原本的狀態

然後

ret
把返回位址出棧,並且賦值給 eip

從而返回到呼叫函式之前的狀態

這裡再提一下帶參的函式呼叫

(在 call語句 呼叫函式之前先把這些引數入棧就行啦~)

(之後以 ebp 為基準就可以訪問這些傳進的引數了)

(參數列為: 引數1,引數2 時先引數2入棧,再引數1入棧)

後面的引數放高位址嘛,就先入棧啦~

(然後再 eip 入棧)

函式呼叫過程

每乙個未執行完的函式都對應著乙個棧幀,系統為單個函式分配的那部分棧空間就叫做棧幀,棧幀儲存了函式的資訊。以下面的 為例,通過彙編 的執行過程介紹棧幀建立和銷毀的過程 include int add int x,int y int main 從main函式建立自己的棧幀開始 其他內容先忽略 初始狀態 ...

函式呼叫過程

c語言種有三種迴圈 do.while while for 初始化 條件判斷 步進 主函式 main 庫函式自定義函式函式的發明,使得變成可以以函式為單位進行模組化,叫做面向過程。軟體工程中,有 高內聚,低耦合 的要求。函式就是為了實現以上要求發明的產物。函式是面向過程的 介面 其介面包含了 引數 返...

函式呼叫過程

引數代入順序 引數入棧的順序是從右向左入棧的。8位元組的引數代入 push入棧的方式將引數傳遞 8位元組的引數代入 先在main函式的棧頂向上移動12位元組,然後將引數的資料拷貝到main函式棧頂開闢的記憶體。int fun1 int a,int b int main 第一步進行函式引數入棧,如圖 ...