函數調用棧比較有意思

函數調用棧比較有意思
作者:liigo

原文鏈接:http://blog.csdn.net/liigo/archive/2006/12/23/1456938.aspx

轉載請註明出處:http://blog.csdn.net/liigo

 

昨天和海洋一塊研究了下函數調用棧,順便寫兩句。不足或錯誤之處請包涵!

理解調用棧最重要的兩點是:棧的結構,EBP寄存器的作用。

首先要認識到這樣兩個事實:

1、一個函數調用動作可分解爲:零到多個PUSH指令(用於參數入棧),一個CALL指令。CALL指令內部其實還暗含了一個將返回地址(即CALL指令下一條指令的地址)壓棧的動作。

2、幾乎所有本地編譯器都會在每個函數體之前插入類似如下指令:PUSH EBP; MOV EBP ESP;

即,在程序執行到一個函數的真正函數體時,已經有以下數據順序入棧:參數,返回地址,EBP。
由此得到類似如下的棧結構(參數入棧順序跟調用方式有關,這裏以C語言默認的CDECL爲例):

+| (棧底方向,高位地址) |
 | .................... |
 | .................... |
 | 參數3                |
 | 參數2                |
 | 參數1                |
 | 返回地址             |
-| 上一層[EBP]          | <-------- [EBP]

“PUSH EBP”“MOV EBP ESP”這兩條指令實在大有深意:首先將EBP入棧,然後將棧頂指針ESP賦值給EBP。“MOV EBP ESP”這條指令表面上看是用ESP把EBP原來的值覆蓋了,其實不然——因爲給EBP賦值之前,原EBP值已經被壓棧(位於棧頂),而新的EBP又恰恰指向棧頂。

此時EBP寄存器就已經處於一個非常重要的地位,該寄存器中存儲着棧中的一個地址(原EBP入棧後的棧頂),從該地址爲基準,向上(棧底方向)能獲取返回地址、參數值,向下(棧頂方向)能獲取函數局部變量值,而該地址處又存儲着上一層函數調用時的EBP值!

一般而言,ss:[ebp+4]處爲返回地址,ss:[ebp+8]處爲第一個參數值(最後一個入棧的參數值,此處假設其佔用4字節內存),ss:[ebp-4]處爲第一個局部變量,ss:[ebp]處爲上一層EBP值。

由於EBP中的地址處總是“上一層函數調用時的EBP值”,而在每一層函數調用中,都能通過當時的EBP值“向上(棧底方向)能獲取返回地址、參數值,向下(棧頂方向)能獲取函數局部變量值”。
如此形成遞歸,直至到達棧底。這就是函數調用棧。

編譯器對EBP的使用實在太精妙了。

從當前EBP出發,逐層向上找到所有的EBP是非常容易的:

unsigned int _ebp;
__asm _ebp, ebp;
while (not stack bottom)
{
    //...
    _ebp = *(unsigned int*)_ebp;
}

如果要寫一個簡單的調試器的話,注意需在被調試進程(而非當前進程——調試器進程)中讀取內存數據。
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章