我在其中一个进程中遇到间歇性核心转储。 除了崩溃之外,所有线程的堆栈看起来都不错,并且正确解析。
崩溃的线程有一个明显损坏的调用堆栈。 堆栈有两个帧,都是0x00000000。 查看寄存器,PC和RA均为0(这解释了调用堆栈......) 原因寄存器是00800008。
谢谢!
答案 0 :(得分: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();
}
如果我们看一下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)
)然后跳转返回到({{ 1}})。我展示了foo(),因为它更短,但是对于bar()完全相同的结构 - 除了在bar()中还有中间的memset(),当它在堆栈上时覆盖RA(它是将50个字节写入大小为10的数组中;然后恢复到寄存器的是0,最终导致崩溃。
所以,现在我们知道崩溃的根本原因是某种堆栈损坏,这让我们回到了问题(1):有没有办法获得有关崩溃线程的更多信息?
嗯,这有点困难,而且调试变得更像是一门艺术而非科学,但这里要记住的原则是:
希望这有帮助!