理解呼叫棧最重要的兩點是:棧的結構,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)
x86架構下的函式引數
這裡就拿x86 64架構下來舉例子,在這個架構下分別用rdi,rsi,rdx,rcx,r8,r9作為第1 6個引數。rax作為返回值 當我們去呼叫函式的時候 long test long a,long b,long c long sum long a,long b,long c,long d,lon...
x86架構系統啟動過程
x86結構下作業系統啟動 計算機加電後,段暫存器cs 0xf000h,指令指標暫存器eip fff0h,在cs暫存器中隱含的乙個基址base ffff0000h,80386中實際位址是base eip ffff0000h 0000fff0h fffffff0h 在8086中最初執行的位址是pc 16...
x86架構中特權級
特權級,可以分為三種 第 一 描述符中的特權級dpl,表示這個段的特權 第 二 選擇子的rpl表示請求方的特權級 第 三 當前特權級,表示正在執行的 段所具有的特權 下面有關特權級知識的總結 第一 對於資料段來說,特權級dpl表示了可以訪問該資料的最低特權。若資料段的dpl為1,那麼只有特權級為0或...