當發生函式呼叫的時候,棧空間中存放的資料是這樣的:
1、呼叫者函式把被調函式所需要的引數按照與被調函式的形參順序相反的順序壓入棧中,即:從右向左依次把被調函式所需要的引數壓入棧;
2、呼叫者函式使用call指令呼叫被調函式,並把call指令的下一條指令的位址當成返回位址壓入棧中(這個壓棧操作隱含在call指令中);
3、在被調函式中,被調函式會先儲存呼叫者函式的棧底位址(push ebp)(從高內在位址--》低記憶體位址),然後再儲存呼叫者函式的棧頂位址,即:當前被調函式的棧底位址(mov ebp,esp);
4、在被調函式中,從ebp的位置處開始存放被調函式中的區域性變數和臨時變數,並且這些變數的位址按照定義時的順序依次減小,即:這些變數的位址是按照棧的延伸方向排列的,先定義的變數先入棧,後定義的變數後入棧;
所以,發生函式呼叫時,入棧的順序為:
引數n引數n-1
引數n-2
.....
引數3引數2
引數1函式返回位址
上一層呼叫函式的ebp/bp
區域性變數1
區域性變數2
....
區域性變數n
函式呼叫棧如下圖所示:
解釋:首先,將呼叫者函式的ebp入棧(push ebp),
然後將呼叫者函式的棧頂指標esp賦值給被調函式的ebp(作為被調函式的棧底,mov ebp,esp),
此時,ebp暫存器處於乙個非常重要的位置,該暫存器中存放著乙個位址(原ebp入棧後的棧頂),
以該位址為基準,向上(棧底方向)能獲取返回位址、引數值,向下(棧頂方向)能獲取函式的區域性變數值,而該位址處又存放著上一層函式呼叫時的ebp值;
一般而言,ss:[ebp+4]處為被調函式的返回位址,
ss:[ebp+8]處為傳遞給被調函式的第乙個引數(最後乙個入棧的引數,此處假設其占用4位元組記憶體)的值,
ss:[ebp-4]處為被調函式中的第乙個區域性變數,
而在每一層函式呼叫中,都能通過當時的ebp值"向上(棧底方向)能獲取返回位址、引數值,向下(棧頂方向)能獲取被調函式的區域性變數值";
如此遞迴,就形成了函式呼叫棧;
函式內區域性變數布局示例:
#include
#include
struct c
;int test2(int x, int y, int z)
int test(int x, int y, int z)
int main(int argc, char** argv)
列印輸出如下:
addr x = 4288282272
addr y = 4288282276
addr z = 4288282280
addr a = 4288282260
addr b = 4288282256
addr c = 4288282252
addr st = 4288282240
addr st.a = 4288282240
addr st.b = 4288282244
addr st.c = 4288282248
a = 1; b = 2; c = 3;
a = 0; b = 0; c = 3;
示例效果圖:
該圖中的區域性變數都是在該示例中定義的;
這個中反映的是乙個典型的函式呼叫棧的記憶體布局;
組合語言--函式呼叫棧
C語言彙編 函式呼叫棧
函式呼叫大家都不陌生,呼叫者向被呼叫者傳遞一些引數,然後執行被呼叫者的 最後被呼叫者向呼叫者返回結果,還有大家比較熟悉的一句話,就是函式呼叫是在棧上發生的,那麼在計算機內部到底是如何實現的呢?對於程式,編譯器會對其分配一段記憶體,在邏輯上可以分為 段,資料段,堆,棧 段 儲存程式文字,指令指標eip...
組合語言呼叫Linux系統呼叫
首先查詢系統呼叫檔案 find name unistd.h root linux include unistd.h usr include linux unistd.h usr include sys unistd.h usr include bits unistd.h usr include un...
棧幀 組合語言詳解
原來我以為在c語言中指標已經是非常麻煩了,沒想到棧幀給我甜蜜一擊,但最後一路學習下來也不是多麼麻煩的事。首先我們得明確為什麼有函式,其作用是 在面向過程語言的重要組成成分,它將具有相同功能的語句組合到一塊,便於我們使用,提高程式可讀性,減少 量。以main函式為例,在使用過程中首先呼叫 tmainc...