我在我們的某個進程中收到了間歇性核心轉儲。 所有線程的堆棧,除了崩潰的堆棧外,看起來都不錯,並且正確解析。與ra和pc相等的MIPS核心轉儲0000000
崩潰的線程有明顯損壞的調用堆棧。 堆棧有兩個幀,它們都是0x00000000。 找上了寄存器,PC和RA爲0(這也解釋了調用堆棧...) 原因寄存器00800008.
- 有沒有一種方法,我可以得到墜毀線程的詳細信息?
- 寄存器本身如何被破壞? (或者相反,在覈心轉儲中,調試器將基於堆棧填充這些寄存器?)
謝謝!
我在我們的某個進程中收到了間歇性核心轉儲。 所有線程的堆棧,除了崩潰的堆棧外,看起來都不錯,並且正確解析。與ra和pc相等的MIPS核心轉儲0000000
崩潰的線程有明顯損壞的調用堆棧。 堆棧有兩個幀,它們都是0x00000000。 找上了寄存器,PC和RA爲0(這也解釋了調用堆棧...) 原因寄存器00800008.
謝謝!
要回答(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):有什麼方法可以獲取有關崩潰線程的更多信息?
好了,這是比較困難的,並且是調試變得更像是一種藝術,而不是科學,但這裏要牢記的原則:
希望這有助於!