棧幀%ebp,%esp詳解


首先應該明白,棧是從高地址向低地址延伸的。每個函數的每次調用,都有它自己獨立的一個棧幀,這個棧幀中維持着所需要的各種信息。寄存器ebp指向當前的棧幀的底部(高地址),寄存器esp指向當前的棧幀的頂部(地址地)。下圖為典型的存取器安排,觀察棧在其中的位置

入棧操作:push eax; 等價於 esp=esp-4,eax->[esp];如下圖

出棧操作:pop eax; 等價於 [esp]->eax,esp=esp+4;如下圖

我們來看下面這個C程序在執行過程中,棧的變化情況

void func(int m, int n) {

    int a, b;

    a = m;

    b = n;

}

main() {

...

    func(m, n);

L:  下一條語句

...

在main調用func函數前,棧的情況,也就是說main的棧幀:

從低地址esp到高地址ebp的這塊區域,就是當前main函數的棧幀。當main中調用func時,寫成匯編大致是:

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的棧幀。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM