过程(栈帧结构是干货)
【0】寫在前面
過程(棧幀結構是干貨);本文總結于csapp, 加上自己的理解;【1】棧幀結構
每個函數(shù)的每次調用,都有它自己獨立的一個棧幀,這個棧幀中維持著所需要的各種信息。
過程調用:函數(shù)調用另一個詞語表示叫作過程;
IA32 程序 用程序棧 來支持過程調用;
【2】轉移控制
(此處非常重要:關系到對函數(shù)調用和返回理解是否到位)
解說:顯然是地址80483dc的call調用sum函數(shù), call指令的效果是將返回地址0x080483e1壓入棧中,再跳轉到sum函數(shù)的第一條指令(0x0804394),直到遇到ret指令為止,即是說,ret指令是一個函數(shù)的結束標志;
call == push ip ; jmp near ptr 標號;(壓棧后跳轉)
ret == pop ip;(出棧)
【3】寄存器使用慣例
慣例——我們必須保證當一個過程(調用者)調用另一個過程(被調用者)時, 被調用者不會覆蓋某個調用者稍后會使用的寄存器的值。- 寄存器%eax, %edx 和 %ecx 被劃分為調用者保存寄存器。(意思就是左邊3個寄存器實現(xiàn)覆蓋時,需要保存到調用者的棧幀結構中)
- 寄存器%ebx, %esi 和 edi 被劃分為被調用者保存寄存器。(意思就是左邊3個寄存器實現(xiàn)覆蓋時,需要保存到被調用者的棧幀結構中)
看個荔枝:
int P(int x) {int y = x * x;int z = Q(y);return y + z; }過程P在調用Q之前計算y, 但它必須保證y的值在Q返回后是可用的。有兩個方法可以實現(xiàn):
- (1)Q調用之前,將y的值保存到調用者P的棧幀結構中;當Q返回時,過程P從棧中取出y的值;
- (2)將y保存在被調用者Q所保存的寄存器中,如%ebx等3個寄存器;如果Q或者其他的程序要使用這個寄存器的話,先把該寄存器的值壓入棧幀中,并在返回前恢復該值;(因為每個函數(shù)或過程都有棧幀結構,誰要使用保存y的寄存器,誰就把y保存在其對應的棧幀中)
【4】過程實例
函數(shù)A調用函數(shù)B有三個過程:(其實上述的轉移控制的解說已經說的很清楚了)
- (1)建立部分,初始化棧幀;(把call指令的下一條指令的地址壓入棧幀結構)
- (2)主體部分,執(zhí)行過程的實際計算;(執(zhí)行被調用函數(shù)或者過程)
- (3)結束部分,恢復棧的狀態(tài),以及過程返回;(將call指令的下一條指令的地址出棧到ip 或者叫做程序計數(shù)器pc)
我的觀點 -干貨:
(1)說說%ebp:
它其實是%esp的一個副本,作用在于記錄每個執(zhí)行函數(shù)或過程的棧幀結構的首地址(注意是每個函數(shù)或過程),如函數(shù)A調用函數(shù)B, 函數(shù)B的匯編代碼的第一句就要把函數(shù)A的棧幀首地址壓入棧,以便函數(shù)B結束標志ret指令執(zhí)行前的一條指令,將其調用者——函數(shù)A的棧幀首地址彈回到%ebp;(參看上上圖中的swap_add的匯編指令接近尾部部分)
為什么GCC分配從不使用的空間?
GCC 堅持一個X86編程指導方針,也就是一個函數(shù)使用的所有棧空間必須是16字節(jié)的整數(shù)倍;采用這個規(guī)則是為了保證訪問數(shù)據(jù)的嚴格對齊。
再來看一個荔枝
執(zhí)行時,%esp=0x800040, 而%ebp=0x800060, scanf返回后 的棧幀圖結構, 如下:
干貨-這里又是一個——很好的解釋了C語言中的傳值和傳址的問題
為什么分配的棧幀空間中,還有沒有被使用的?
這個問題,你不要問我了,自己多思考,答案就在附近,哈哈。
【5】遞歸過程
先看個荔枝(對于理解遞歸運算過程——至關重要)
干貨-(這里, 你也可以看到, 對于參數(shù)n,它是存儲在調用者的棧幀結構中的;從而解釋了為什么取用參數(shù)的時候,都是%ebp+8;Bingo!)
總結
以上是生活随笔為你收集整理的过程(栈帧结构是干货)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网站怎么创建论坛(网站怎么创建论坛啊)
- 下一篇: 可重定位目标文件