2012-03-21 35 views
93

我有以下的堆棧跟蹤。是否有可能做出有用的調試呢?GDB損壞的堆棧幀 - 如何調試?

Program received signal SIGSEGV, Segmentation fault. 
0x00000002 in ??() 
(gdb) bt 
#0 0x00000002 in ??() 
#1 0x00000001 in ??() 
#2 0xbffff284 in ??() 
Backtrace stopped: previous frame inner to this frame (corrupt stack?) 
(gdb) 

從哪裏開始看代碼的時候我們得到一個Segmentation fault和堆棧跟蹤不是那麼有用嗎?

注:如果我張貼代碼,然後將這樣的專家會給我答案。我想從SO的指導中找到答案,所以我不在這裏發佈代碼。道歉。看到

+0

也許你的程序跳下到雜草 - 您可以恢復堆棧指針什麼? – 2012-03-21 17:36:50

+1

要考慮的另一件事是如果幀指針設置正確。你是在沒有優化的情況下建立或傳遞像'-fno-omit-frame-pointer'這樣的標誌嗎?另外,對於內存損壞,如果它是您的選擇,'valgrind'可能是更合適的工具。 – FatalError 2012-03-21 17:36:51

回答

128

那些虛假不會忽略(0x00000002等)實際上是PC值,而不是SP值。現在,當你得到這樣的SEGV的,用假(很小)PC的地址,它是由於通過一個虛假的函數指針調用99%的時間。請注意,C++中的虛擬調用是通過函數指針實現的,所以虛擬調用的任何問題都可以用相同的方式顯示出來。

間接調用指令只是推動PC呼叫到堆棧後,然後設置PC目標值(假在這種情況下),因此,如果這發生了什麼事,你可以很容易地通過手動撤消將PC從堆棧中彈出。在32位x86代碼,你只是做:

(gdb) set $pc = *(void **)$esp 
(gdb) set $esp = $esp + 4 

採用64位x86代碼,你需要

(gdb) set $pc = *(void **)$rsp 
(gdb) set $rsp = $rsp + 8 

然後,你應該能夠做一個bt,並找出其中的代碼確實是。

其他1%的時間,錯誤將由於覆蓋堆棧,通常通過溢出存儲在堆棧上的數組。在這種情況下,您可能會通過使用類似valgrind

+2

真棒回答,謝謝! – 2012-05-09 11:23:04

+0

當你沒有運行程序時,有沒有辦法獲得bt,你只需要核心轉儲? – George 2014-02-05 17:26:52

+3

@George:'GDB可執行corefile'將打開GDB與可執行文件和核心文件,在這一點上,你可以做'bt'(或者上面的命令,然後'bt')... – 2014-03-27 18:58:26

6

看看你的一些其他的寄存器,如果他們中的一個在他們緩存的堆棧指針。從那裏,你可能能夠檢索一個堆棧。另外,如果這是嵌入式的,通常棧被定義在一個非常特殊的地址。使用它,你有時也可以得到一個體面的堆棧。這一切都假定,當你上升到多維空間,你的程序沒有吐遍佈沿途的記憶...

21

假設堆棧指針是有效的...

可能無法確切地知道SEGV從回溯中出現 - 我認爲前兩個堆棧幀被完全覆蓋。 0xbffff284看起來像一個有效的地址,但接下來的兩個不是。對於在堆棧仔細一看,你可以嘗試以下方法:

GDB $ X/32ga $ RSP

或變體(更換32與另一個號碼)。這將從巨型(g)大小的堆棧指針開始打印出一些字(32),格式爲地址(a)。請輸入'help x'獲取更多格式信息。

與插入檢測前哨一些「printf」式的代碼可能不是一個壞主意,在這種情況下。

+0

非常有幫助,謝謝 - 我有一個堆棧,只返回三個幀,然後點擊「Backtrace停止:與此幀相同的前一幀(損壞的堆棧?)」;我之前在CPU異常處理程序中的代碼中完成了與此類似的操作,但除了'info symbol'外,不記得如何在gdb中執行此操作。 – leander 2013-03-08 19:05:24

+13

32位ARM器件的FWIW:'x/256wa $ sp' =) – leander 2013-03-08 19:05:58

+1

@leander您能告訴我什麼是X/256wa?我需要它用於64位ARM。一般來說,如果你能解釋它是什麼,這將是有幫助的。 – 2015-04-17 12:15:46

34

這樣的工具來獲得更清晰的情況。如果情況非常簡單,那麼Chris Dodd's answer是最好的。它看起來像是通過一個NULL指針跳轉。

但是,在程序崩潰之前,程序在腳,膝,脖子和眼睛中拍攝時會覆蓋堆棧,弄亂框架指針和其他惡魔。如果是這樣,那麼解開哈希不太可能會顯示你土豆和肉。

更有效的解決方案將是在調試器下運行的程序,和跨過功能,直到程序崩潰。一旦確定崩潰的功能,再次啓動並進入該功能,並確定它調用哪個功能導致崩潰。重複,直到找到唯一違規的代碼行。 75%的時間,修復將是顯而易見的。

在情況另外25%,代碼的所謂的問題的行是一個紅色的鯡魚。它會對(無效)條件做出反應,在—之前建立許多行,之前可能有數千行。如果是這樣的話,選擇最好的當然取決於很多因素:主要是你的代碼和經驗的理解它:

  • 也許設置一個調試監視點或插入關鍵變量診斷printf的會導致必要阿哈!
  • 也許改變與不同的輸入的測試條件將提供比調試更深入的瞭解。
  • 也許第二一雙眼睛會迫使你檢查你的假設或收集的證據被忽視。
  • 有時候,只需要吃飯和思考收集的證據。

祝你好運!

+11

如果第二雙眼睛不可用,那麼橡皮鴨已經被證明可以作爲替代品。 – Matt 2012-03-21 18:52:33

+1

註銷緩衝區的末尾也可以做到這一點。它可能不會在您將緩衝區的末尾寫入的地方崩潰,但是當您退出該功能時,則會死亡。 – phyatt 2016-09-23 20:15:58