函式的堆疊呼叫

2021-09-02 17:49:31 字數 3100 閱讀 8344

今天我們來介紹一下函式的堆疊呼叫過程,首先,我們以乙個簡單的例子進行說明:

#includeint sum(int a,int b)

int main()

將上面**轉到反彙編

sum函式:

int sum(int a,int b)

00e21401 pop edi //出棧

00e21402 pop esi

00e21403 pop ebx

00e21404 mov esp,ebp //將ebp賦給esp,相當於把esp拉到ebp的位置(回退棧幀),注意回退棧幀時並不清0

00e21406 pop ebp //出棧,esp向下(高位址)挪,並把出棧的元素(main函式的棧底指標的值)賦給ebp,即ebp重新回到main函式的棧底

main函式:

int main()

00e21483 pop edi

00e21484 pop esi

00e21485 pop ebx

00e21486 add esp,0e4h

00e2148c cmp ebp,esp

00e2148e call __rtc_checkesp (0e21136h)

00e21493 mov esp,ebp

00e21495 pop ebp

00e21496 ret

通過上述反彙編**,我們可以看到,無論是sum函式還是main函式,其反彙編**前面都有一部分類似於這樣的**:

其對應的解釋為:

每乙個函式開始呼叫時的彙編指令固定做的事,我們可以總結為以下三步:

(1)把主調方函式的棧底位址入棧,然後讓ebp指標指向當前函式的棧底;

(2)通過esp的減等於操作,給被呼叫函式開闢棧幀;

(3)把esp與ebp之間的所有棧記憶體全部初始化為0xcccccccc

上述過程的描述如下圖:

1.形參開闢記憶體嗎?是由誰來開闢的?呼叫方還是被呼叫方?

答:形參開闢記憶體;是由呼叫方開闢的

2.形參的入棧順序?

答:形參按從右向左的順序入棧;實參與形參匹配型別是從左向右匹配的

3.被調函式的返回值由誰帶出?

答:若0《返回值<4個位元組,由eax暫存器帶出 

若4《返回值<8個位元組,由eax和edx暫存器帶出

若返回值》8個位元組,用臨時量帶出,臨時量在記憶體中儲存

4.被呼叫方回退後怎麼會到main函式棧幀上?

5.函式呼叫完成後,如何知道要繼續進行下一行指令而不是從頭開始執行?

開棧

1.形參初始化:壓入實參,將對應的實參值從右向左依次壓入棧頂

3.壓入呼叫方的棧底指標暫存器的值。原因同上退棧後能回到呼叫方,此時還處在呼叫方棧幀上

4.移動ebp到被呼叫方棧底,跳轉到被呼叫方函式棧幀

5.為被呼叫方開闢活動空間,並初始化為cccc cccc

清棧過程與開棧正好相反。

在真正體會函式堆疊呼叫時,必須要看懂反彙編**,下面列出了一些常見的反彙編指令:

mov, dword ptr[a],0ah     

mov, 0ah, dword, ptr[a]   

上述兩行會反彙編語句的意思都是 :把a放在[a]對應的記憶體塊上放4個位元組   

mov, dword ptr[ebp-4]     //移值的指令:ebp棧底暫存器

lea, eax, [ebp-4]      //移位址的指令,eax累加暫存器

push 0ah   //壓棧

pop eax       //====>>    eax=pop();將棧頂元素取出放入暫存器裡

add eax, oah     //====>>  eax+=0a

sub eax, 0ah      //====>>eax-=0a

ret   返回值指令 

eax:"累加器"(accumulator), 它是很多加法乘法指令的預設暫存器;

ecx:計數器(counter), 是重複(rep)字首指令和loop指令的內定計數器;

edx:總是被用來放整數除法產生的餘數;

esp:棧頂指標暫存器;

pc:下一行指令暫存器;

call  呼叫函式,潛在的還做了:

(2)跳到被呼叫方函式

不返回區域性比變數的位址,清棧後資料還存在,只不過是告訴系統該區域可被再次分配。

函式的呼叫約定:

1.c的標準呼叫約定:__cdecl      (所有c和c++體系中的全域性函式(普通函式)預設__cedcl呼叫約定)

2.windows標準呼叫約定:__stdcall

3.快速呼叫約定:__fastcall

4.成員方法的呼叫約定:__thiscall

函式的呼叫堆疊

在學習c 的過程中,有面向過程和物件導向兩種程式設計方式。對於面向過程來說,函式的書寫是最基本的,所以了解函式的呼叫過程和函式呼叫的底層原理也是必須要會的事情。那麼函式棧幀的開闢和回退是怎麼進行的呢?下面我們先用乙個簡單的例子,通過乙個模擬模型和反彙編來了解一下函式堆疊的呼叫。題外話 1 說到彙編我...

函式的堆疊呼叫

型參在 開闢記憶體?型參的入棧順序?函式返回值怎麼帶出來?函式的返回值為什麼會回退到棧裡?函式呼叫結束為什麼會沿著呼叫點繼續執行?我們先來了解一下堆與棧是怎樣的一種存在 什麼是棧?棧用於維護函式呼叫的上下文,離開棧,函式就沒有辦法實現。棧通常在使用者空間的最高位址處分配,通常有數兆位元組大小。棧在程...

函式呼叫堆疊

一 函式呼叫堆疊 認真體會每一行指令位址!include intsum int a,int b mov ebp,esp 讓esp回退到ebp的位置,回退棧幀的過程中,沒有對棧幀中的值進行清0的操作 pop ebp 出棧並把出棧的值賦給ebp int main 下圖為上面示例函式,程式在sum函式中,...