【问题标题】:Why doesn't my register contain the value I calculate from the asm为什么我的寄存器不包含我从 asm 计算的值
【发布时间】: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),这可能是一个递归调用,已被尾调用优化为循环?
  • 你能发布更多的反汇编,看看克里斯的简单建议是否适合循环?

标签: c++ assembly gdb x86-64


【解决方案1】:

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

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

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

一个似是而非的场景:

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

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

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

这也很有道理。 objdump 不会在其反汇编中标记分支目标(Agner Fog's objconv disassembler 会),因此忽略函数的其余部分可能具有高度误导性。

【讨论】:

  • 感谢您的回答。它是多线程的。这可能是这种行为的原因吗?但是,我们正在查看堆栈变量:另一个线程如何编写它们。 rdi 是多态类型的 this 指针。
  • @firebush:如果之前的某个函数给出了一个指向本地的指针,然后在另一个线程仍在使用它时返回,这可以解释这种情况。
  • 谢谢彼得。 (也感谢 $rbx 语法,顺便说一句。)正如在回复 Chris Dodd 时提到的,这部分代码没有跳转。只有一次跳跃,它向前跳跃,而不是向后跳跃。我确实怀疑您是对的,多线程存在问题,即另一个线程覆盖了此地址中的值。
猜你喜欢
  • 2021-06-13
  • 2022-01-13
  • 2020-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-18
相关资源
最近更新 更多