2012-11-27 28 views
1

我在我們的某個進程中收到了間歇性核心轉儲。 所有線程的堆棧,除了崩潰的堆棧外,看起來都不錯,並且正確解析。與ra和pc相等的MIPS核心轉儲0000000

崩潰的線程有明顯損壞的調用堆棧。 堆棧有兩個幀,它們都是0x00000000。 找上了寄存器,PC和RA爲0(這也解釋了調用堆棧...) 原因寄存器00800008.

  1. 有沒有一種方法,我可以得到墜毀線程的詳細信息?
  2. 寄存器本身如何被破壞? (或者相反,在覈心轉儲中,調試器將基於堆棧填充這些寄存器?)

謝謝!

回答

3

要回答(2)第一次 - 因爲理解究竟發生了什麼是找出有關崩潰的根本原因的更多信息,重要的是:

這真的是自己的寄存器,在機器運行時,該是0;但並不是寄存器本身受到損壞;相反,內存被損壞,並且損壞的內存然後被複制回寄存器,最終導致崩潰。發生了什麼事情是這樣的:堆棧損壞,包括(a)特別是RA,,而它被存儲在堆棧內存,被清零。然後,當功能準備好返回時,它(b)從堆棧恢復RA寄存器 - 因此,RA 寄存器現在爲0 - 然後(c)跳轉返回到RA,從而將PC也指向0;下一條指令將導致崩潰,同時RA和PC均爲0.

有關RA存儲在堆棧中然後從其中恢復的業務將在例如http://logos.cs.uic.edu/366/notes/mips%20quick%20tutorial.htm(強調我的)中進行說明:

存儲在寄存器$ ra中的返回地址;如果子程序會調用其他子程序,或者是 遞歸,返回地址應該從$ ra複製到堆棧中以保留它, ,因爲jal總是將返回地址放在這個寄存器中,因此將覆蓋 以前的值。

下面是一個例子程序,其與PC和RA均爲0崩潰,並且其示出了很好的上述序列(確切的數字可能必須進行調整,取決於系統):

#include <string.h> 

int bar(void) 
{ 
    char buf[10] = "ABCDEFGHI"; 
    memset(buf, 0, 50); 
    return 0; 
} 

int foo(void) 
{ 
    return bar(); 
} 

int main(int argc, char *argv[]) 
{ 
    return foo(); 
} 

而如果我們看看富()的拆卸:

(gdb) disas foo 
Dump of assembler code for function foo: 
    0x00400408 <+0>:  addiu sp,sp,-32 
    0x0040040c <+4>:  sw  ra,28(sp) 
    0x00400410 <+8>:  sw  s8,24(sp) 
    0x00400414 <+12>: move s8,sp 
    0x00400418 <+16>: jal  0x4003a0 <bar> 
    0x0040041c <+20>: nop 
    0x00400420 <+24>: move sp,s8 
    0x00400424 <+28>: lw  ra,28(sp) 
    0x00400428 <+32>: lw  s8,24(sp) 
    0x0040042c <+36>: addiu sp,sp,32 
    0x00400430 <+40>: jr  ra 
    0x00400434 <+44>: nop 
End of assembler dump.   

我們看到非常漂亮的是RA被存儲在堆棧的函數(<+4> sw ra,28(sp))的起點上,然後在結尾處進行恢復(<+28> lw ra,28(sp)),然後跳回(<+40> jr ra)。我展示了foo(),因爲它更短,但bar()完全相同的結構是正確的 - 除了bar()中還有中間的memset(),它覆蓋堆棧中的RA(它是將50個字節寫入大小爲10的數組中);然後恢復到寄存器的是0,最終導致崩潰。

所以,現在我們明白崩潰的根本原因是某種堆棧損壞,這讓我們回到問題(1):有什麼方法可以獲取有關崩潰線程的更多信息?

好了,這是比較困難的,並且是調試變得更像是一種藝術,而不是科學,但這裏要牢記的原則:

  • 的基本思路是弄清楚什麼導致堆棧損壞 - 很可能是寫入本地緩衝區,如上例所示。
  • 嘗試儘可能多地清除腐敗發生的流向。日誌記錄可以在這裏提供很多幫助:在崩潰之前顯然發生的最後一個日誌(儘管不一定在破壞之前發生) - 在可疑區域添加更多日誌記錄以便將崩潰位置歸零。當然,如果你有權訪問調試器,你也可以通過代碼來找出崩潰的位置。
  • 一旦找到崩潰位置,從那裏開始反向工作要容易得多:首先,在崩潰之前,PC尚未設置爲0,因此您應該能夠看到回溯(儘管如此,注意回溯本身是使用存儲在上的存儲的值「計算」的 - 一旦它們被破壞,回溯不能被計算超過腐敗。但是這在這種情況下實際上是有幫助的:這可以非常精確地告訴你在內存中損壞是:回溯被截斷的點是損壞的RA(在堆棧上)。
  • 一旦你找到了什麼被損壞了,但你仍然不知道什麼造成腐敗,使用觀察點:只要你進入r放置最終覆蓋在堆棧上的RA的函數,在其上設置一個觀察點。這應該會導致一旦發生腐敗破壞...

希望這有助於!