我在 gdb session 中对崩溃进行事后分析,并且正在分析函数的这个汇编代码,从函数的开头开始:

   0x0000000001b0af00 <+0>:     push   %rbp
   0x0000000001b0af01 <+1>:     mov    %rsp,%rbp
   0x0000000001b0af04 <+4>:     push   %rbx
   0x0000000001b0af05 <+5>:     mov    %rdi,%rbx
   0x0000000001b0af08 <+8>:     sub    $0x8,%rsp
   0x0000000001b0af0c <+12>:    mov    0x18(%rdi),%rdi
=> 0x0000000001b0af10 <+16>:    mov    (%rdi),%rdx

崩溃发生在 rdi 的空引用:
(gdb) info registers rdi
rdi            0x0      0

美好的。但是阅读该程序集(非常简单),我不明白 rdi 值是如何变为空的。请注意,rbx 具有来自 +5 指令偏移量的 rdi 的原始值,它具有以下内容:
(gdb) info registers rbx
rbx            0x7f4fb26b9690   139980272539280

从那里,rdi 应该具有该地址的取消引用值加上 0x18(由于指令偏移量 +12):
(gdb) x/a (0x7f4fb26b9690 + 0x18)
0x7f4fb26b96a8: 0x7f4f74632bb0

请注意,这不是零。在我看来, rdi 的值应该是 0x7f4f74632bb0 。我有什么误解?

这是从 GCC 4.8 编译 C++ 代码生成的 x64 程序集。

最佳答案

是的,您正确理解了 asm。要么您的核心文件不是 mem+regs 的准确快照,要么在第一次和第二次加载之间发生了某种对内存的异步修改(如建议的 fifoforlifo)。

我猜 0x7f4f74632bb0 也是一个有效的指针,因此如果该值早点出现,则 load + deref 应该可以工作。

您的程序是多线程的,还是安装了任何信号处理程序?

一个合理的场景:

  • 函数将本地地址传递给另一个线程
  • 然后它返回,而另一个线程仍然有指针。所以现在它指向 RSP 以下未使用的空间。
  • 然后调用另一个函数,它的一个局部变量恰好位于其他线程仍在修改的位置。


  • (或者其他一些我没有想到的解释。)

    更新:如果一个分支向后跳到这里,就像@ChrisDodd 建议的那样,这可能是链表遍历循环的一部分。

    这也是非常有道理的。 objdump 不会在其反汇编中标记分支目标( Agner Fog's objconv disassembler 会),因此忽略该函数的其余部分可能会产生很大的误导。

    关于c++ - 为什么我的寄存器不包含我从 asm 计算的值,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57155104/

    10-12 14:49