目錄
1.函數(shù)棧幀的含義
概念?
要用到的匯編語言的知識
示例
2.理解棧幀
2.1 main函數(shù)棧幀的創(chuàng)建
2.2 局部變量的創(chuàng)建
2.3 函數(shù)傳參
2.4 調(diào)用函數(shù)
2.5 函數(shù)返回?
? 一個.c文件在調(diào)用函數(shù)的時候(包括main 函數(shù)),其內(nèi)存中的棧區(qū)有什么變化?要壓棧、出棧哪些寄存器呢?函數(shù)的參數(shù)是如何進行傳遞的呢?函數(shù)調(diào)用結(jié)束之后棧區(qū)又是如何變化的呢?本文通過使用匯編語言,對這些內(nèi)容進行了較為詳細的剖析。
1.函數(shù)棧幀的含義 概念???首先,棧的概念想必不需要過多解釋,那么什么是棧幀?引用百度百科:C語言中,每個棧幀對應(yīng)著一個未運行完的函數(shù)。棧幀中保存了該函數(shù)的返回地址和局部變量。從這句話中,可以提煉以下幾點信息:
· 棧幀是一塊因函數(shù)運行而臨時開辟的空間。
· 每調(diào)用一次函數(shù)便會創(chuàng)建一個獨立棧幀。
· 棧幀中存放的是函數(shù)中的必要信息,如局部變量、函數(shù)傳參、返回值等。
· 當(dāng)函數(shù)運行完畢棧幀將會銷毀。
? 我們知道,C語言的內(nèi)存區(qū)分成了 靜態(tài)區(qū)、棧區(qū)、堆區(qū),函數(shù)棧幀無疑是在棧區(qū)創(chuàng)建和銷毀的,所以其要符合?!昂筮M先出”的特點。
要用到的匯編語言的知識? 在這里使用匯編語言方面知識的原因是:通過它,我們可以深入底層了解一個程序是如何運行的,在何時——什么東西壓棧,什么東西出棧,寄存器(匯編語言中一些用來暫時存儲數(shù)據(jù)的東西)如何變化等等。這些都是C語言無法直觀體現(xiàn)的,我們可以通過Visual Stdio 的在調(diào)試時的反匯編功能,將C語言代碼轉(zhuǎn)換成匯編語言代碼,以便更好地觀察。(另,C語言也是匯編語言編寫的。)所以,簡單地說,本文主要是在分析匯編語言的執(zhí)行過程。
? 我們首先要了解幾個匯編語言方面的東西,其中ESP和EBP時專門維護函數(shù)棧幀的,分別指向棧頂和棧底:
寄存器? | 用途 |
---|---|
EAX | 累加寄存器:用于乘除法、函數(shù)返回值 |
EBX | 用于存放內(nèi)存數(shù)據(jù)指針 |
ECX | 計數(shù)器 |
EDX | 用于乘除法、IO指針 |
ESP | 存放棧頂指針(其值是地址) |
EBP | 存放棧底指針(其值是地址) |
匯編指令 | 用途 |
---|---|
mov | mov A,B 將數(shù)據(jù)B移動到A |
push | 壓棧 |
pop | 出棧 |
call | 函數(shù)調(diào)用 |
add | 加法 |
sub | 減法 |
rep | 重復(fù) |
lea | 加載有效地址 |
? 比如,我們寫下一個如下的C語言程序,非常容易,只有main() 函數(shù)和一個 Add() 函數(shù),主函數(shù)里面調(diào)用了 Add() 。
#includeint Add(int x, int y)
{
return x + y;
}
int main()
{
int a = 10;
int b = 20;
int c = Add(a, b);
return 0;
}
? 那么,一開始調(diào)用主函數(shù)的時候,主函數(shù)的函數(shù)棧幀就壓棧;然后在主函數(shù)里面,調(diào)用了Add() 函數(shù),此時Add() 的函數(shù)棧幀也要壓棧。那么現(xiàn)在面臨一個問題,是維護Add() 函數(shù),還是維護main() 函數(shù),亦或兩者都維護?
? 其實,從平時使用Visual Stdio 調(diào)試的時候就可以看出來,當(dāng)主函數(shù)內(nèi)部調(diào)用一個函數(shù)A,按F11分步調(diào)試,會進入函數(shù)A的內(nèi)部,函數(shù)A調(diào)用結(jié)束,會返回主函數(shù)。同理,實際上從進入函數(shù)A,一直到A 函數(shù)調(diào)用結(jié)束,這個過程都在維護函數(shù)A。所以,在main() 函數(shù)內(nèi)部調(diào)用 Add() 函數(shù)之后,會出現(xiàn)如下圖所示的情況,ebp和esp來維護Add() 函數(shù)的棧幀。當(dāng)Add() 函數(shù)調(diào)用結(jié)束,它的函數(shù)棧幀自然就會出棧,此時ebp、esp又會返回維護main() 函數(shù)的棧幀。
? 實際上,main() 函數(shù)也是由其他函數(shù)調(diào)用的,其調(diào)用鏈條如下:
? 創(chuàng)建main函數(shù)的函數(shù)棧幀代碼如下,匯編語言的注釋是用 ; 所以這里 ; 后面的內(nèi)容是注釋,幫助理解代碼。
006117A0 push ebp ; ebp 壓棧
006117A1 mov ebp,esp ; 將 esp里面的值 賦給ebp
006117A3 sub esp,0E4h ; esp減去0E4h(十六進制),得到的結(jié)果賦給esp
006117A9 push ebx ; ebx 壓棧
006117AA push esi ; esi 壓棧
006117AB push edi ; edi 壓棧
006117AC mov edi,[ebp-24h] ; 將ebp往上數(shù),第24h(16進制)個字節(jié)開始,向下4個字節(jié)的值賦給edi
006117AF mov ecx,9 ; 這里到結(jié)束的意思是:
006117B4 mov eax,0CCCCCCCCh ; 將0CCCCCCCCh 賦值給某塊空間,這塊空間從附加段中 edi 指向的位置開始
006117B9 rep stos dword ptr es:[edi] ; 一共執(zhí)行九次(0CCCCCCCCh 是四個字節(jié)的內(nèi)容,每次操作四個字節(jié),所以一共操作了36字節(jié))
第一行?
? 我們來開始逐句剖析上方代碼,首先執(zhí)行第一行(圖中紅色圓圈圈出來的黃色箭頭,表示已經(jīng)執(zhí)行完其上一行,按F10調(diào)試就執(zhí)行當(dāng)前行),由于是壓棧操作,所以esp的值會有所變化,如下圖右邊監(jiān)視窗口,esp的值(十六進制顯示的)相較之前改變了,所以變成紅色:
? 如下,ebp壓棧,同時esp上移:
第二行
該行是將esp的值賦給ebp,效果也如下圖,右邊監(jiān)視窗口的紅色部分所示。
? 如下,將esp的值賦給ebp之后,ebp和esp指向同一塊地方:
第三行
? 該行是將esp的值減去0E4h(十六進制),得到的結(jié)果賦給esp,如下圖。
如圖所示,由于圖中從下往上是地址高處到地址低處,所以esp值變小,實際上圖中是上移。并且,現(xiàn)在ebp和esp維護的空間,就是main函數(shù)的函數(shù)棧幀:
第四行
? 壓棧,壓入ebx,改變棧頂指針esp的值。
? 如下,壓棧,esp上移:
第五行
? 壓入esi,改變esp的值。
? 如下,和上一步類似:
第六行
? 壓入edi,改變esp的值。?
? 和上一步也類似:
第七行
? 將[ebp-24h] 表示的地址賦值給edi。
? 這里就是把 edi 里面的值改變,從函數(shù)棧幀看不出什么,看上面的監(jiān)視圖就可以直到確實是改變了。
最后三行
? 如之前代碼里的注釋所說。
? 如下,兩個箭頭指示的值是一樣的,其代表的是edi所表示的地址,從該地址開始,往后9個dw(double word 雙字,一個雙字等于四個字節(jié))的內(nèi)容,都賦值為cccccccc (十六進制)。
? 這三行代碼效果如下:
? 整個過程可以用一張動圖生動形象地展示:
? 接下來,我們在匯編代碼中鼠標(biāo)右擊,然后將下圖紅色箭頭所指示的"顯示符號名" 的勾去掉。
? 發(fā)生改變的是下圖中紅色圓圈圈出來的,可以看出,原本所有的變量名,都變成了寄存器減去某個十六進制數(shù)字。他們實際上是等價的,即變量的地址就等于替換后的地址。
? 接下來分析局部變量創(chuàng)建過程。
? 首先,創(chuàng)建變量a,代碼如下,其含義就是,將0Ah 這個十六進制數(shù)字,從ebp的地址低八位處開始放,占四個字節(jié):
002C17C5 mov dword ptr [ebp-8],0Ah
? 如下圖,可以通過兩個紅色箭頭看到,右邊監(jiān)視的ebp的值就是左邊 地址處的箭頭指向的地址,說明這就是ebp的地址,然后減去八位,再根據(jù)棧從下往上使用以及Visual Stdio小端存儲的特點,就成了內(nèi)存區(qū)里面紅色方框框出來的內(nèi)容。(注意,比如 cc cc cc cc ,cc占據(jù)的是一個字節(jié)的空間,四個cc 就占據(jù)四個字節(jié),而匯編語言中,地址-1,只跳過一個c,所以ebp-8是跳過8個c,即四個字節(jié))
? 如下,圖中一個小格子代表四個字節(jié),不難看出,變量a存儲的位置,在棧底指針往上跳過四個字節(jié)的地方。
? 然后創(chuàng)建局部變量b,通過內(nèi)存圖可以看出,變量b和變量a是間隔八個字節(jié)的:
? 如下圖:
? 代碼如下:
002C17D3 mov eax,dword ptr [ebp-14h] ; 將變量b的值賦給eax
002C17D6 push eax ; eax壓棧
002C17D7 mov ecx,dword ptr [ebp-8] ; 將變量a的值賦給ecx
002C17DA push ecx ; ecx壓棧
? 執(zhí)行前兩行代碼,確實將變量b的值賦給了eax,然后eax引起的壓棧導(dǎo)致了esp改變:
? 如下圖,不要忘了eax里面的值和變量b是一樣的哦:
? 執(zhí)行后兩行代碼:
? 其效果和前兩行類似,同時ecx里面存的是變量a的值:
? 上面的內(nèi)容執(zhí)行完之后,要執(zhí)行如下語句,其意思是,執(zhí)行?002C10B4 地址處的內(nèi)容:
002C17DB call 002C10B4
? 然后我們將其滑倒該地,發(fā)現(xiàn)是這樣的,意思是跳到002C1740地址處:
又找到該地址,發(fā)現(xiàn)如下,所以,通過這兩步調(diào)用Add() 函數(shù),如下紅色部分,和創(chuàng)建main函數(shù)的函數(shù)棧幀類似,實際上就是創(chuàng)建了Add() 的函數(shù)棧幀:
? 效果如下,建立了Add函數(shù)的函數(shù)棧幀:
? 紅色個方框后面兩行代碼不是很重要,是用來檢查bug的,如下代碼和圖片:
002C1757 mov ecx,2CC003h
002C175C call 002C130C
? 函數(shù)返回:
002C1761 mov eax,dword ptr [ebp+8]
002C1764 add eax,dword ptr [ebp+0Ch]
? 第一行代碼:?將ebp+8 地址處的數(shù)據(jù)放到eax?。
? 第二行代碼:將ebp+0Ch 地址處的數(shù)據(jù)和eax相加,結(jié)果存到eax里面。
? 如下圖中,由于圖片從下往上是地址從高到低,所以圖片中ebp+8是在ebp下方。實際上就是ecx和eax的值相加,然后存到eax里面。eax里面存儲變量b的值,ecx里面存儲變量a的值,最后eax的值就是變量a、b之和。并且eax是不會隨著Add() 函數(shù)的函數(shù)棧幀銷毀而改變值。
? 通過監(jiān)視也可以看出,eax的值變成0x0000001e,轉(zhuǎn)換成十進制就是30。
? 此時已經(jīng)拿到返回值,存儲在eax里面,還要執(zhí)行以下幾行代碼:
00AA13F1 pop edi
00AA13F2 pop esi
00AA13F3 pop ebx
00AA13F4 mov esp,ebp
00AA13F6 pop ebp
00AA13F7 ret
就是出棧、賦值等等,結(jié)果如下,回到了調(diào)用Add() 函數(shù)之前的狀態(tài):
? 然后執(zhí)行main() 函數(shù)后續(xù)代碼代碼,如下圖紅色框出:
? 第一行:esp加8,即esp在途中向下移動四個字節(jié)。
? 第二行,將eax的值賦給ebp-20h 地址處。
? 執(zhí)行完之后,調(diào)試圖如下,通過對比兩個紅色方框的內(nèi)容,左邊紅色方框的地址,和右邊&c 的值一樣,說明那就是變量c 存儲的地方,其值也是變量c 的值:
? 示意圖如下:
? 通過對函數(shù)棧幀創(chuàng)建、銷毀過程的剖析使我們不僅了解計算機做了什么,還了解了它是如何做的。通過函數(shù)棧幀嘗試解析遞歸等問題相信也會更加直觀。由于本人水平有限,不足之處還請大家多多指教。
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧
當(dāng)前題目:【C語言】你知道程序是如何調(diào)用函數(shù)的嗎?-創(chuàng)新互聯(lián)
文章URL:http://chinadenli.net/article22/ddiojc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站設(shè)計、外貿(mào)建站、響應(yīng)式網(wǎng)站、網(wǎng)站設(shè)計公司、ChatGPT、搜索引擎優(yōu)化
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容