中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

函數的調用過程(棧幀)

發布時間:2020-07-24 17:36:11 來源:網絡 閱讀:806 作者:吳金瑞 欄目:網絡安全

    

1、什么是棧幀?

棧幀也叫過程活動記錄,是編譯器用來實現函數調用過程的一種數據結構。C語言中,每個棧幀對應著一個未運行完的函數。從邏輯上講,棧幀就是一個函數執行的環境:函數調用框架、函數參數、函數的局部變量、函數執行完后返回到哪里等等。棧是從高地址向低地址延伸的。每個函數的每次調用,都有它自己獨立的一個棧幀,這個棧幀中維持著所需要的各種信息。寄存器ebp指向當前的棧幀的底部(高地址),寄存器esp指向當前的棧幀的頂部(低地址)。

2、Add()函數的調用過程

我們以Add()函數為例深入的研究一下函數的調用過程。
先看一段簡單的代碼:

函數的調用過程(棧幀)

 1 #include <stdio. h> 2 int Add(int x, int y) 3 { 4 int z = 0; 5 z = x + y; 6 return z; 7 } 8 int main() 9 {10 int a = 10;11 int b = 20;12 int ret = Add(a, b) ;13 printf("ret = %d\n", ret) ;14 return 0;15 }

函數的調用過程(棧幀)

當講程序調試的時候, 查看【調用堆棧】(按F10進入調試-窗口-調用堆棧,或按快捷鍵ctrl+alt+C) ,用VS2015調試 如下圖:
函數的調用過程(棧幀)

如果用版本更老的,或其他如VC6.0等編輯器則可以看到更多信息,VS2008調試如圖:函數的調用過程(棧幀)

我們發現其實main函數在 __tmai nCRTStartup 函數中調用的,而 __tmai nCRTStartup 函數是在 mai nCRTStartup 被調用的。我們知道每一次函數調用都是一個過程。這個過程我們通常稱之為: 函數的調用過程。這個過程要為函數開辟棧空間, 用于本次函數的調用中臨時變量的保存、 現場保護。 這塊棧空間我們稱之為函數棧幀。
而棧幀的維護我們必須了解ebp和esp兩個寄存器。 在函數調用的過程中這兩個寄存器存放了維護這個棧的棧底和棧頂指針。比如:調用main函數, 我們為main函數分配棧幀空間, 那么棧幀維護如下:
函數的調用過程(棧幀)ebp存放了指向函數棧幀棧底的地址。esp存放了指向函數棧幀棧頂的地址。
注意:ebp指向當前位于系統棧最上邊一個棧幀的底部,而不是系統棧的底部。嚴格說來,“棧幀底部”和“棧底”是不同的概念;ESP所指的棧幀頂部和系統棧的頂部是同一個位置。

1 . 從main函數的地方開始, 要展開main函數的調用就得為main函數創建棧幀, 那我們先來看main函數棧幀的創建。轉到反匯編可以更清晰的看到過程:函數的調用過程(棧幀)

過程分析:

a.首先mainCRTStartup(),__mainCRTStartup()函數的調用,調main()函數;

b.將ebp壓棧處理,保存指向棧底的ebp的地址(方便函數返回之后的現場恢復),此時esp指向新的棧頂位置;

c.將esp的值賦給ebp,產生新的ebp;

d.給esp減去一個16進制數0E4H(為main函數預開辟空間);

e.push ebx、esi、edi;

f.lea指令,加載有效地址;

g.初始化預開辟的空間為0xcccccccc;

h.創建變量a與b。

2. 接下來是Add函數的調用。

參數傳遞過程:函數的調用過程(棧幀)
 過程分析:

a.將b存入寄存器eax,再將將eax壓棧;(傳參過程,從左向右傳遞)

b.將a存入寄存器ecx,再將將ecx壓棧;

c.call指令的調用,先要壓棧call指令下一條指令的 地址,然后跳轉(push+jmp)到Add()函數的地方(__cdecl調用約定)。執行call指令的時候按F11 , 來到了這里。
函數的調用過程(棧幀)再按F11 就進入Add函數的執行代碼處。Add函數棧幀的創建:函數的調用過程(棧幀)

過程分析:

a.首先將main()函數ebp壓棧處理,保存指向main()函數棧幀底部的ebp的地址(方便函數返回之后的現場恢復),此時esp指向新的棧頂位置;

b.將esp的值賦給ebp,產生新的ebp,即Add()函數棧幀的ebp;

c.給esp減去一個16進制數0E4H(為Add()函數預開辟空間);

d.push ebx、esi、edi;

e.lea指令,加載有效地址;

f.初始化預開辟的空間為0xcccccccc;

g.創建變量z;

h.獲取形參的a和b再相加,將結果存儲到z中;

i.將結果存儲到eax寄存器,通過寄存器帶回函數的返回值。
剩下的就是是函數返回部分:函數的調用過程(棧幀)

過程分析:

a.pop3次,edi、esi、ebx依次出棧,esp 會向下移動;

b.將ebp賦給esp,使esp指向ebp指向的地方

c.ebp 出棧,將出棧的內容給ebp(即main()函數ebp),回到main()函數的棧幀;

d.ret 指令,出棧一次,并將出棧的內容當做地址,并跳轉到該地址處(pop+jmp)。

函數的調用過程(棧幀)

注: 棧幀這部分內容在不同的編譯器上實現存在差異, 但是思想都是一致的。

棧幀的一般總結:

1. 堆棧是C語言程序運行時必須的一個記錄調用路徑和參數的空間:
函數調用框架;
傳遞參數;
保存返回地址;
提供局部變量空間;
等等。
以x86體系結構為例
2. 堆棧寄存器和堆棧操作
 堆棧相關的寄存器
esp,堆棧指針(stack pointer)
ebp,基址指針(base pointer)
堆棧操作
push 棧頂地址減少4個字節(32位)
pop 棧頂地址增加4個字節
ebp在C語言中用作記錄當前函數調用基址
3. 利用堆棧實現函數調用和返回
其他關鍵寄存器
cs : eip:總是指向下一條的指令地址
● 順序執行:總是指向地址連續的下一條指令
● 跳轉/分支:執行這樣的指令的時候, cs : eip的值會根據程序需要被修改
● call:將當前cs : eip的值壓入棧頂, cs : eip指向被調用函數的入口地址
● ret:從棧頂彈出原來保存在這里的cs : eip的值,放在cs : eip中
● 發生中斷時???
4. 函數堆棧框架的形成

call xxx
執行call之前;
執行call時,cs:eip原來的值指向call下一條指令,該值被保存到棧頂,然后cs:eip的值指向xxx的入口地址
進入xxx
第一條指令:pushl %ebp
第二條指令:movl %esp,%ebp
函數體中的常規操作,壓棧,出棧等
退出xxx
movl %ebp,%esp
popl %ebp
ret

函數的調用過程(棧幀)

5. 堆和棧的關系
我們平時說的堆棧其實是指棧,而實際上堆和棧是兩種不同的內存分配。簡單羅列如下各方面的異同點。
1).堆需要用戶在程序中顯式申請,棧不用,由系統自動完成。申請/釋放堆內存的API,在C中是malloc/free,在C++中是new/delete。申請與釋放一定要配對使用,否則會造成內存泄漏(memory leak),久而久之系統就無內存可用了,出現OOM(Out Of Memory)錯誤。一般在return/exit或break/continue等語句時容易忘記釋放內存,所以檢查內存泄漏的代碼時要關注這些語句,看它們前面是否有必要的釋放語句free/delete。
2).堆的空間比較大,棧比較小。所以申請大的內存一般在堆中申請;棧上不要有較大的內存使用,比如大的靜態數組;而且除非算法必要,否則一般不要使用較深的迭代函數調用,那樣棧消耗內存會隨著迭代次數的增加飛漲。
3).關于生命周期。棧較短,隨著函數退出或返回,本函數的棧就完成了使用;堆就要看什么時候釋放,生命周期就什么時候結束。
函數的調用過程(棧幀)我們發現解析Coredump還是跟棧的關系相對緊密,跟堆的關系是有一種產
生Coredump的原因是訪問堆內存出錯。

為什么研究棧幀?看一個題目 :
在VC6.0環境中, 下面代碼的結果是什么?

函數的調用過程(棧幀)

 1 #include <stdi o. h> 2 void fun() 3 { 4 int tmp = 10; 5 int *p = (int *) (*(&tmp+1) ) ; 6 *(p-1) = 20; 7 } 8 int main() 9 {10 int a =0;11 fun() ;12 printf("a = %d\n", a) ;13 return 0;14 }

函數的調用過程(棧幀)

事實上在不同平臺下這段代碼有不同的輸出,可自行驗證。

    

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

弋阳县| 且末县| 绥滨县| 额济纳旗| 曲阜市| 钟祥市| 石泉县| 黄山市| 东乌珠穆沁旗| 东海县| 喀喇沁旗| 肇源县| 米林县| 锦州市| 开封市| 宜昌市| 额尔古纳市| 逊克县| 依兰县| 景洪市| 宜都市| 延安市| 大余县| 休宁县| 梁河县| 莲花县| 格尔木市| 都江堰市| 澳门| 卫辉市| 马边| 城步| 元江| 盐边县| 五华县| 会同县| 余江县| 曲沃县| 江西省| 高安市| 吴旗县|