首先應該明白,棧是從高位址向低位址延伸的。每個函式的每次呼叫,都有它自己獨立的乙個棧幀,這個棧幀中維持著所需要的各種資訊。暫存器ebp指向當前的棧幀的底部(高位址),暫存器esp指向當前的棧幀的頂部(位址地)。下圖為典型的訪問器安排,觀察棧在其中的位置
入棧操作:push eax; 等價於 esp=esp-4,eax->[esp];如下圖
出棧操作:pop eax; 等價於 [esp]->eax,esp=esp+4;如下圖
我們來看下面這個c程式在執行過程中,棧的變化情況
void func(int m, int n)
在main呼叫func函式前,棧的情況,也就是說main的棧幀:
push m
push n; 兩個引數壓入棧
call func; 呼叫func,將返回位址填入棧,並跳轉到func
當跳轉到了func,來看看func的彙編大致的樣子:
__func:
push ebp; 這個很重要,因為現在到了乙個新的函式,也就是說要有自己的棧幀了,那麼,必須把上面的函式main的棧幀底部儲存起 ; 來,棧頂是不用儲存的,因為上乙個棧幀的頂部講會是func的棧幀底部。(兩棧幀相鄰的)
mov ebp, esp; 上一棧幀的頂部,就是這個棧幀的底部
;暫時先看現在的棧的情況
;到這裡,新的棧幀開始了
sub esp, 8 ; int a, b 這裡宣告了兩個int,所以esp減小8個位元組來為a,b分配空間
mov dword ptr [esp+4], [ebp+12]; a=m
mov dword ptr [esp], [ebp+8]; b=n
這樣,棧的情況變為:
ret 8 ; 返回,然後8是什麼意思呢,就是引數占用的位元組數,當返回後,esp-8,釋放引數m,n的空間
由此可見,通過ebp,能夠很容易定位到上面的引數。當從func函式返回時,首先esp移動到棧幀底部(即釋放區域性變數),然後把上乙個函式的棧幀底部指標彈出到ebp,再彈出返回位址到cs:ip上,esp繼續移動劃過引數,這樣,ebp,esp就回到了呼叫函式前的狀態,即現在恢復了原來的main的棧幀。
觀察棧幀和EBP ESP暫存器
本文將用windbg觀察棧幀以及ebp esp暫存器的變化過程。首先我們先寫一段簡單的 進行實驗。int main int a int a 禁用visualstudio的自動優化,然後編譯成release版本,然後用windbg開啟可執行檔案。觀察main函式的反彙編 0 000 uf main t...
棧幀 組合語言詳解
原來我以為在c語言中指標已經是非常麻煩了,沒想到棧幀給我甜蜜一擊,但最後一路學習下來也不是多麼麻煩的事。首先我們得明確為什麼有函式,其作用是 在面向過程語言的重要組成成分,它將具有相同功能的語句組合到一塊,便於我們使用,提高程式可讀性,減少 量。以main函式為例,在使用過程中首先呼叫 tmainc...
程式棧 棧幀
乙個由c c 編譯的程式占用的記憶體分為以下幾個部分 棧區 stack 由編譯器自動分配釋放 存放函式的引數名,區域性變數的名等。其操作方式類似於資料結構中的棧。堆區 heap 由程式設計師分配釋放,若程式設計師不釋放,程式結束時可能由os 注意它與資料結構中的堆是兩回事,分配方式倒是類似於鍊錶。靜...