2016-03-03 80 views
0

從這個網站: http://eli.thegreenplace.net/2011/02/04/where-the-top-of-the-stack-is-on-x86/ 我看到這個程序堆棧:爲什麼基指針值存儲在堆棧

int foobar(int a, int b, int c) 
{ 
    int xx = a + 2; 
    int yy = b + 3; 
    int zz = c + 4; 
    int sum = xx + yy + zz; 

    return xx * yy * zz + sum; 
} 

int main() 
{ 
    return foobar(77, 88, 99); 
} 

在內存中看起來像下面的圖片:

enter image description here

我不明白爲什麼基址指針需要從堆棧中獲取內存,是否不能像堆棧指針那樣只存儲在寄存器中,只需指向他需要的地方? (我知道基址指針用於在堆棧指針產生推送和彈出時更容易找到變量,但我不明白爲什麼它的值存儲在堆棧中,而不僅僅是寄存器中!),非常感謝您的幫助(恐怕我錯過了一些非常重要的東西)

編輯: 可能是這有助於更多我的困惑:在圖像中,他們顯示'EBP'(寄存器)和'saved ebp'。我不明白爲什麼有兩個...

+0

爲什麼返回地址必須存儲在堆棧中,而不是寄存器中? – MikeCAT

+0

我知道basepointer已經有一個寄存器EBP,但是返回地址沒有。在圖片中他們顯示'EBP'(註冊)和'保存的ebp'。我不明白爲什麼有兩個... – Samuel

+1

基地/框架指針工作像一個鏈表; EBP指向當前基指針,當前基指針指向前一個基指針,依此類推。 – melak47

回答

2

首先,你應該認識到這種堆棧框架的風格不再是真正必要的。大多數當前的編譯器可以(並且確實)消除在堆棧上保存[E] BP,並在輸入函數時將[E] SP複製到[E] BP。有一次(16位代碼),這是需要的,因爲BP可以充當基址寄存器,但SP不能。這意味着你不能像-7[SP]那樣做,但你可以做-7[BP]。但是,在386和32位代碼中,不再是這種情況 - 您可以使用ESP(或任何其他寄存器)作爲基址寄存器。

雖然在進入程序/函數時仍然有一些理由可以保存EBP:除非堆棧損壞,否則很容易走路。 EBP指向EBP的先前值,它指向先前的值,並一直沿着堆棧向下。如果您正在調試(例如),這使得執行堆棧跟蹤變得非常容易。如果你必須通過一些你沒有符號的代碼,你可以穿過它們,找到你理解的早期堆棧框架,然後仔細檢查它們。

相比之下,如果您直接使用ESP,則會在調用函數的入口時爲該函數的局部變量騰出空間。你需要知道它被調整了多少以回到先前的堆棧幀。如果你到了一個沒有任何信息的地步,而且你不知道在進入該功能時調整了多少ESP,那麼你幾乎被卡住了(很少拆卸代碼來找到堆棧調整,所以你可以不這樣做)。

1

因此,堆棧指針可以遞增,以適應新的堆棧幀。到了該返回的時候,基指針將彈出到堆棧指針中以恢復舊值。

你不能假設根本就沒有任何寄存器,更不用說備用寄存器,並且專門爲一個肯定不會被使用的東西提供一個寄存器,直到函數退出將浪費寶貴的資源。