【发布时间】:2019-11-30 23:10:38
【问题描述】:
我正在一个 gdb 会话中对崩溃进行事后分析,并且正在分析这个函数的汇编代码,从函数的开头开始:
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 的 null 取消引用:
(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 程序集。
【问题讨论】:
-
@ThomasMatthews 为什么这很重要?相反,我会调查该地址在哪里(看起来像一些共享库?)以及它是否在核心文件中正确(我假设这个事后分析来自核心文件?)。
-
一个疯狂且不太可能的理论是多个线程之间的某些共享状态位于地址 0x7f4fb26b96a8;在执行
mov 0x18(%rdi),%rdi;时它为零,但在读取和取消引用之间被一个非零值覆盖。 -
仅供参考,您可以在 GDB 表达式中使用
$rbx,例如 `x /a ($rbx + 0x18) -
最好的猜测——这是循环的顶部,所以后面的代码中有一个向后分支到 +12 或 +16,并且在以后的迭代中会命中 null。看起来它可能正在遍历一个链表而不检查最后是否为空?由于
rdi是第一个参数(通常是方法中的this),这可能是一个递归调用,已被尾调用优化为循环? -
你能发布更多的反汇编,看看克里斯的简单建议是否适合循环?