在函式的呼叫過程中為函式開闢的棧空間用於本次函式的呼叫中臨時變數的儲存,現場保護。這塊棧空間被稱為棧幀。
為了了解函式呼叫棧空間的詳細過程,我們先寫一段**,通過**的反彙編來觀察函式的呼叫過程中彙編指令都做了什麼。首先,寫入下面的函式:
int add(int
x, int
y)int main()
{ int a = 10;
int b = 20;
int ret = 0;
add(a, b);
ret = add(a, b);
printf("%d ",ret);
return
0;
可以看出上面的**中主要包含了乙個main函式和乙個add函式,在main函式中呼叫了add函式。所以在申請棧空間的先後順序上來說應該是先為main函式申請再為add函式申請。
並將該**轉為反彙編指令來觀察:
00401060
push ebp //用ebp來存放棧底的位址,方便後面恢復
00401061
mov ebp,esp //用esp和ebp兩個暫存器來維護main函式的空間
00401063
sub esp,4ch //給esp加上4ch後為main函式開闢空間
00401066
push ebx
00401067
push esi
00401068
push edi //把ebx,esi.edi三個數壓棧,esp的位置向棧頂的方向移動
00401069 lea edi,[ebp-4ch]
0040106c mov ecx,13h
00401071
mov eax,0cccccccch
00401076 rep stos dword ptr [edi] //對main函式的內部空間初始化為0xcccccccc
11: int a = 10
;00401078
mov dword ptr [ebp-4],0ah
12: int b = 20
;0040107f mov dword ptr [ebp-8],14h
13: int ret = 0
;00401086
mov dword ptr [ebp-0ch],0 //建立a,b,c三個區域性變數
14: add(a, b);
0040108d mov eax,dword ptr [ebp-8]
00401090
push eax //把b的值傳給eax,生成形參y並壓棧
00401091
mov ecx,dword ptr [ebp-4]
00401094
push ecx //把a的值傳給ecx,生成形參x並壓棧
00401094
push ecx
00401095
call @ilt+0(_add) (00401005) //呼叫函式,把call指令的下一條指令的位址存入棧頂,以便後面返回到函式
0040109a add esp,8
以上為main函式的建立棧空間和建立變數以及呼叫add函式的過程,每個函式都要在棧上開闢一塊空間由esp和ebp這兩個暫存器來維護。 下面按步驟來分析這個過程中具體組合語言都需要做哪些事情:
(1)把ebp的位址存放起來
(2)esp指向main函式空間的頂部。
(3)把ebp指向esp,然後esp-4ch,也就是為main函式開闢了4ch個位元組的空間。
(4)在main函式中放入ebx,esi,edi三個資料,esp繼續向上移動3個資料。
(5)對main函式內部資料進行初始化,在vc6.0上初始化為0xcccccccc。
(6)向記憶體中申請空間存放三個區域性變數。
(7)建立臨時變數存放函式的引數,產生形參,與實引數值相同,位址不同。形參的存放順序為從右到左。
(8)呼叫add函式,把call指令下一條指令的位址存入棧頂,方便後面返回。
012c13c0 push ebp //用ebp來存放棧底的位址,方便後面恢復
012c13c1 mov ebp,esp //把esp賦值給ebp
012c13c3 sub esp,0cch //esp向上移動建立新的函式空間
012c13c9 push ebx
012c13ca push esi
012c13cb push edi
012c13cc lea edi,[ebp-0cch]
012c13d2 mov ecx,33h
012c13d7 mov eax,0cccccccch
012c13dc rep stos dword ptr es:[edi] //將add函式內部全初始化為0xcccccccc
012c13de mov dword ptr [z],0 //建立區域性變數z
012c13e5 mov eax,dword ptr [x] // 形參需要函式通過ebp加上12和8在main函式中去找
012c13e8 add eax,dword ptr [y] //將獲取的形參相加
012c13eb mov dword ptr [z],eax
012c13ee mov eax,dword ptr [z] 把相加的結果儲存到eax暫存器當中,通過暫存器帶回函式的返回值
012c13f1 pop edi
012c13f2 pop esi
012c13f3 pop ebx //讓edi,esi,ebx出棧
012c13f4 mov esp,ebp //把ebp的值給esp,即esp下移
012c13f6 pop ebp //把存放在main棧頂的ebp的值賦給ebp暫存器,ebp和esp重新返回main函式空間,z被銷毀
012c13f7 ret
對add函式的反彙編部分做出如下總結
(1)在main函式頂部存入ebp的位址方便函式返回使用
(2)與main函式的建立類似,主要是esp和ebp建立空間,並進行資料初始化和區域性變數的建立。
(3)通過ebp的位置在main函式中找到形參,並且使用形參進行運算。
(4)把運算所得的結果存入z內,然後再放入暫存器eax中。
(5)三次出棧後,把ebp賦值給esp,esp下移,再popmain函式頂部存放的ebp之後,ebp和esp重新返回main函式空間。z被銷毀。
(6)把a和b形參的結果消除,eax把結果帶回給ret。
資料入棧順序以及esp和ebp的指向位置
main函式跟add函式在棧空間中的存放大致關係
函式的棧幀
includeint add int x,int y int main 我們發現其實main函式在 tmaincrtstartup 函式中調 用的,tmaincrtstartup 函式是在 maincrtstartup 被調 用的。我們知道每一次函式呼叫都是乙個過程。這個過程我們通常稱之為 函式的調...
函式的棧幀過程
幾乎所有的函式都使用了棧,沒有棧就沒有函式,沒有區域性變數。在程式執行過程中,棧儲存了乙個函式呼叫所需要的維護資訊,也可稱為堆疊幀或者活動記錄。堆疊幀一般包括以下幾個方面 1 函式的返回位址和引數 2 臨時變數 包括函式的非靜態區域性變數以及編譯器自動生成的其他臨時變數 3 儲存的上下文 包括在函式...
函式的呼叫棧幀
今天來給大家分享一下函式的呼叫過程。我也是剛剛開始學的,還請大家多多指教 我們最常說的乙個 是從main 函式開始的,但是main 函式就不呼叫引數麼?那倒未必。現在就來看看main 函式的呼叫。int add int x,int y int main 先看看這個 然後關於main 函式的呼叫。啟用...