我在 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 应该可以工作。
您的程序是多线程的,还是安装了任何信号处理程序?
一个合理的场景:
(或者其他一些我没有想到的解释。)
更新:如果一个分支向后跳到这里,就像@ChrisDodd 建议的那样,这可能是链表遍历循环的一部分。
这也是非常有道理的。
objdump
不会在其反汇编中标记分支目标( Agner Fog's objconv
disassembler 会),因此忽略该函数的其余部分可能会产生很大的误导。关于c++ - 为什么我的寄存器不包含我从 asm 计算的值,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57155104/