拿到coredump後,如果看到的位址都是????,那麼基本上可以确定,程式的棧被破壞掉了。gdb也是使用函數的調用棧去還原“事故現場”的。是以了解函數調用棧,是使用gdb進行現場調試或者事後調試的基礎,如果不了解調用棧,基本上也從gdb得不到什麼有用的資訊。當然了,也有可能你非常“幸運”, 一個bt就把哪兒越界給标出來了。但是,大多數的時候你不夠幸運,通過log,通過簡單的code walkthrough,得不到哪兒出的問題;或者說隻是推測,不能确診。我們需要通過gdb來最終确定coredump産生的真正原因。
本文還可以幫助你深入了解c++函數的局部變量。我們學習時知道局部變量是是存儲到棧裡的,記憶體管理對程式員是透明的。通過本文,你将明白這些結論是如何得出的。
棧,是lifo(last in first out)的資料結構。c++的函數調用就是通過棧來傳遞參數,儲存函數傳回後下一步的執行位址。接下來我們通過一個具體的例子來探究。
可以使用以下指令将上述code程式設計成彙編代碼:
g++ -g -s -o0 -m32 main.cpp -o-|c++filt >main.format.s
c++filt 是為了demangle symbols。-m32是為了編譯成x86-32的。因為對于x86-64來說,函數的參數是通過寄存器傳遞的。
main的彙編代碼:
對于call指令,這個指令有兩個作用:
<code>func0</code>函數調用完之後要傳回到<code>call</code>的下一條指令繼續執行,是以把<code>call</code>的下一條指令的位址壓棧,同時把<code>esp</code>的值減4。
修改程式計數器<code>eip</code>,跳轉到<code>func0</code>函數的開頭執行。
至此,調用func0的棧就是下面這個樣子:
Linux Debugging(一): 使用反彙編了解C++程式函數調用棧 下面看一下func0的彙編代碼:
需要注意的是esp也是留了5個位址空間給func0使用。并且ebp的下一個位址就是留給局部變量b的,調用棧如圖:
Linux Debugging(一): 使用反彙編了解C++程式函數調用棧 通過調用棧可以看出,8(%ebp)其實就是傳入的參數1234。
func1的代碼:
<code>leave</code>指令,這個指令是函數開頭的<code>push %ebp</code>和<code>mov %esp,%ebp</code>的逆操作:
把<code>ebp</code>的值賦給<code>esp</code>
現在<code>esp</code>所指向的棧頂儲存着<code>foo</code>函數棧幀的<code>ebp</code>,把這個值恢複給<code>ebp</code>,同時<code>esp</code>增加4。注意,現在esp指向的是這次調用的傳回位址,即上次調用的下一條執行指令。
最後是<code>ret</code>指令,它是<code>call</code>指令的逆操作:
現在<code>esp</code>所指向的棧頂儲存着傳回位址,把這個值恢複給<code>eip</code>,同時<code>esp</code>增加4,<code>esp指向了目前frame的棧頂</code>。
修改了程式計數器<code>eip</code>,是以跳轉到傳回位址繼續執行。
調用棧如下:
Linux Debugging(一): 使用反彙編了解C++程式函數調用棧 至此,func1傳回後,控制權交還給func0,目前的棧就退化成func0的棧的情況,因為棧儲存了一切資訊,是以指令繼續執行。直至func0執行
leave
ret
以同樣的方式将控制權交回給main。
到這裡,你應該知道下面問題的答案了:
1. 局部變量的生命周期,
2. 局部變量是怎麼樣使用記憶體的;
3. 為什麼傳值不會改變原值(因為編譯器已經幫你做好拷貝了)
4. 為什麼會有棧溢出的錯誤
5. 為什麼有的寫壞棧的程式可以運作,而有的卻會crash(如果棧被破壞的是資料,那麼資料是髒的,不應該繼續運作;如果破壞的是上一層調用的bp,或者傳回位址,那麼程式會crash,or unexpected behaviour...)
小節一下:
1. 在32位的機器上,c++的函數調用的參數是存到棧上的。當然gcc可以在函數聲明中添加_attribute__((regparm(3)))使用eax, edx,ecx傳遞開頭三個參數。
2. 通過bp可以通路到調用的參數值。
3. 函數的傳回位址(函數傳回後的執行指令)也是存到棧上的,有目的的修改它可以使程式跳轉到它不應該的地方。。。
4. 如果程式破壞了上一層的bp的位址,或者程式的傳回位址,那麼程式就很有可能crash
5. 拿到一個coredump,應該首先先看有可能出問題的線程的的frame的棧是否完整。
6. 64位的機器上,參數是通過寄存器傳遞的,當然寄存器不夠用就會通過棧來傳遞
支援原創,轉載請注明出處:anzhsoft http://blog.csdn.net/anzhsoft/article/details/18730605