Gdb MIPS核心转储,ra和pc等于0000000

Gdb MIPS核心转储,ra和pc等于0000000,gdb,mips,coredump,Gdb,Mips,Coredump,我在我们的一个进程中得到了间歇性的堆芯转储。 除了崩溃的线程之外,所有线程的堆栈看起来都正常,并且解析正确 崩溃的线程具有明显损坏的调用堆栈。 堆栈有两个帧,都是0x00000000。 从寄存器上看,PC和RA都是0(这解释了调用堆栈…) 原因寄存器为00800008 有没有办法让我获得更多关于崩溃线程的信息 为什么寄存器本身已损坏?(或者反过来,调试器在内核转储中根据堆栈填充这些寄存器?) 谢谢 首先回答(2)——因为了解实际发生的情况对于了解更多有关坠机根本原因的信息非常重要: 实际上,在运

我在我们的一个进程中得到了间歇性的堆芯转储。 除了崩溃的线程之外,所有线程的堆栈看起来都正常,并且解析正确

崩溃的线程具有明显损坏的调用堆栈。 堆栈有两个帧,都是0x00000000。 从寄存器上看,PC和RA都是0(这解释了调用堆栈…) 原因寄存器为00800008

  • 有没有办法让我获得更多关于崩溃线程的信息
  • 为什么寄存器本身已损坏?(或者反过来,调试器在内核转储中根据堆栈填充这些寄存器?)
  • 谢谢

    首先回答(2)——因为了解实际发生的情况对于了解更多有关坠机根本原因的信息非常重要:

    实际上,在运行时的机器中,寄存器本身是0;但并不是登记册本身被破坏了;相反,内存被损坏,损坏的内存被复制回寄存器,最终导致崩溃

    发生的情况是这样的:堆栈被破坏,包括(a)具体地说是RA,当它存储在堆栈内存中时,被归零。然后,当函数准备返回时,它(b)从堆栈恢复RA寄存器——因此RA寄存器现在为0——然后(c)跳转返回RA,从而将PC设置为也指向0;当RA和PC都为0时,下一条指令将导致崩溃

    例如,在(emphasis mine)中解释了将RA存储在堆栈上然后从中恢复的业务:

    存储在寄存器$ra中的返回地址;如果子例程将调用其他子例程,或 递归的返回地址应该从$ra复制到堆栈上以保留它, 因为日本航空公司总是把回信地址放在这个登记簿上,因此会覆盖 以前的值

    下面是一个示例程序,它在PC和RA都为0时崩溃,并且很好地说明了上述顺序(根据系统的不同,可能需要调整确切的数字):

    #包括
    内栏(空)
    {
    char buf[10]=“ABCDEFGHI”;
    memset(buf,0,50);
    返回0;
    }
    int foo(无效)
    {
    返回杆();
    }
    int main(int argc,char*argv[])
    {
    返回foo();
    }
    
    如果我们看一下foo()的反汇编:

    (gdb)disas foo
    函数foo的汇编程序代码转储:
    0x00400408:附加sp,sp,-32
    0x0040040c:sw ra,28(sp)
    0x00400410:sw s8,24(sp)
    0x00400414:移动s8,sp
    0x00400418:jal 0x4003a0
    0x0040041c:否
    0x00400420:移动sp,s8
    0x00400424:lw ra,28(sp)
    0x00400428:lw s8,24(sp)
    0x0040042c:附加sp,sp,32
    0x00400430:jr ra
    0x00400434:否
    汇编程序转储结束。
    
    我们非常清楚地看到,RA存储在函数开始时的堆栈上(
    sw-RA,28(sp)
    ),然后在结束时恢复(
    lw-RA,28(sp)
    ),然后跳转返回(
    jr-RA
    )。我显示了For(),因为它较短,但是对于Bar(),完全相同的结构是真的——除了在Bar()中,还有MeMeSET()在中间,它在堆栈上覆盖RA(它将50字节写入大小为10的数组);然后恢复到寄存器中的是0,最终导致崩溃

    所以,现在我们知道崩溃的根本原因是某种堆栈损坏,这让我们回到问题(1):有没有办法获得关于崩溃线程的更多信息

    好吧,这有点困难,调试更像是一门艺术而不是一门科学,但这里有一些要记住的原则:

    • 基本思想是找出导致堆栈损坏的原因——很可能是对某个本地缓冲区的写入,如上面的示例所示
    • 尽可能多地关注流程中发生损坏的位置。日志记录在这里有很大的帮助:您看到的最后一个日志显然发生在崩溃之前(虽然不一定发生在崩溃之前!)——在可疑区域添加更多日志记录,以锁定崩溃位置。当然,如果您有权访问调试器,还可以单步执行代码以找出崩溃的位置
    • 一旦找到崩溃位置,从那里向后工作就容易多了:首先,在崩溃之前,PC还没有设置为0,因此您应该能够看到回溯(不过,请注意回溯本身是“计算的”使用存储在堆栈上的值——一旦这些值损坏,就无法计算出超出损坏范围的回溯。但在这种情况下,这实际上很有用:这可以非常准确地告诉您损坏在内存中的位置:回溯被截断的点是损坏的RA(在堆栈上)
    • 一旦发现了损坏的内容,但仍然不知道是什么导致了损坏,请使用观察点:一旦进入将最终被覆盖的RA放在堆栈上的函数,就在其上设置一个观察点。一旦腐败发生,这应该会导致中断
    希望这有帮助

    #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.