【问题标题】:Dwarf DW_AT_location objdump and dwarfdump inconsistentDwarf DW_AT_location objdump 和 dwarfdump 不一致
【发布时间】:2020-07-13 00:31:45
【问题描述】:

我正在使用 CPython 并试图了解调试器的工作原理。 具体来说,我正在尝试获取最后一个 PyFrameObject 的位置,以便可以遍历它并获取 Python 回溯。

在文件ceval.c中,第689行有函数的定义:

PyObject * PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)

我感兴趣的是f 在堆栈上的位置。当使用dwarfdump 转储二进制文件时,我知道f 位于$rbp-824,但如果我使用objdump 转储二进制文件,我会发现位置是$rbp-808 - 差异为16。此外,在使用调试时GDB,我知道正确的答案是$rbp-808,就像objdump 给我的一样。为什么存在差异,为什么 dwarfdump 不正确?我不明白什么?

如何从技术上重现问题: 从 Python 网站下载python-2.7.17.tgz。提取。

我使用调试符号 (./configure --enable-pydebug && make) 从源代码编译了 python-2.7.17。对生成的 python 二进制文件运行以下命令:

dwarfdump Python-2.7.17/python 有以下输出:

                        DW_AT_name                  f           
                        DW_AT_decl_file             0x00000001 /home/meir/code/python/Python-2.7.17/Python/ceval.c
                        DW_AT_decl_line             0x000002b1                         
                        DW_AT_type                  <0x00002916>
                        DW_AT_location              len 0x0003: 91c879: DW_OP_fbreg -824

我知道这是正确的f,因为声明变量的行是689 (0x2b1)。如您所见,位置是:

DW_AT_location len 0x0003: 91c879: DW_OP_fbreg -824:意思是$rbp-824

运行命令objdump -S Python-2.7.17/python有如下输出:

PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
{
   f7577:       55                      push   %rbp
   f7578:       48 89 e5                mov    %rsp,%rbp
   f757b:       41 57                   push   %r15
   f757d:       41 56                   push   %r14
   f757f:       41 55                   push   %r13
   f7581:       41 54                   push   %r12
   f7583:       53                      push   %rbx
   f7584:       48 81 ec 38 03 00 00    sub    $0x338,%rsp
   f758b:       48 89 bd d8 fc ff ff    mov    %rdi,-0x328(%rbp)
   f7592:       89 b5 d4 fc ff ff       mov    %esi,-0x32c(%rbp)
   f7598:       64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
   f759f:       00 00 
   f75a1:       48 89 45 c8             mov    %rax,-0x38(%rbp)
   f75a5:       31 c0                   xor    %eax,%eax

调试此输出将显示相关行是: f758b: 48 89 bd d8 fc ff ff mov %rdi,-0x328(%rbp) 可以清楚地看到 f 正在从 -0x328(%rbp) 加载,即 $rbp-808。此外,GDB 支持这一发现。

所以,问题是,我错过了什么以及为什么dwarfdump 和现实之间存在 16 字节的差异?

谢谢

编辑: 包含上述函数的dwarfdump为:

< 1><0x00004519>    DW_TAG_subprogram
                      DW_AT_external              yes(1)
                      DW_AT_name                  PyEval_EvalFrameEx
                      DW_AT_decl_file             0x00000001 /home/meir/code/python/Python-2.7.17/Python/ceval.c
                      DW_AT_decl_line             0x000002b1
                      DW_AT_prototyped            yes(1)
                      DW_AT_type                  <0x00000817>
                      DW_AT_low_pc                0x000f7577
                      DW_AT_high_pc               <offset-from-lowpc>53969
                      DW_AT_frame_base            len 0x0001: 9c: DW_OP_call_frame_cfa
                      DW_AT_GNU_all_tail_call_sites yes(1)
                      DW_AT_sibling               <0x00005bbe>
< 2><0x0000453b>      DW_TAG_formal_parameter
                        DW_AT_name                  f
                        DW_AT_decl_file             0x00000001 /home/meir/code/python/Python-2.7.17/Python/ceval.c
                        DW_AT_decl_line             0x000002b1
                        DW_AT_type                  <0x00002916>
                        DW_AT_location              len 0x0003: 91c879: DW_OP_fbreg -824

根据下面的答案,DW_OP_fbreg 从框架底座偏移 - 在我的情况下为 DW_OP_call_frame_cfa。我无法识别框架底座。我的寄存器如下:

(gdb) info registers
rax            0xfffffffffffffdfe       -514
rbx            0x7f6a4887d040   140094460121152
rcx            0x7f6a48e83ff7   140094466441207
rdx            0x0      0
rsi            0x0      0
rdi            0x0      0
rbp            0x7ffd24bcef00   0x7ffd24bcef00
rsp            0x7ffd24bceba0   0x7ffd24bceba0
r8             0x7ffd24bcea50   140725219813968
r9             0x0      0
r10            0x0      0
r11            0x246    582
r12            0x7f6a48870df0   140094460071408
r13            0x7f6a48874b58   140094460087128
r14            0x1      1
r15            0x7f6a48873794   140094460082068
rip            0x5559834e99c0   0x5559834e99c0 <PyEval_EvalFrameEx+46153>
eflags         0x246    [ PF ZF IF ]
cs             0x33     51
ss             0x2b     43
ds             0x0      0
es             0x0      0
fs             0x0      0
gs             0x0      0

如上所述,我已经知道%rbp-808 有效。使用我拥有的寄存器的正确方法是什么?

编辑: 我终于明白了答案。我需要展开另一个函数,并找到调用我的函数的位置。在那里,我正在寻找的变量确实在$rsp$rsp-824 中是正确的

【问题讨论】:

  • 我对 CPython 一点也不熟悉,但在堆栈跟踪方面,有很多很棒的工具,例如 FRIDAGEF
  • CPython 只是一个例子。我的问题是差异,我试图了解 DWARF 以及为什么存在这个问题。有很多工具可以做我想做的事,但我想了解实现

标签: gdb reverse-engineering


【解决方案1】:

DW_OP_fbreg -824:含义$rbp-824

确实不是的意思。这意味着,从 frame base(虚拟)寄存器偏移-824,它不一定(也不通常)等于$rbp

你需要查找DW_AT_frame_base才能知道当前函数中的frame base是什么。

很可能它被定义为DW_OP_call_frame_cfa,这是$RSP的值就在当前函数被调用之前,并且等于$RBP-16(8字节的返回地址由CALL 指令,8 个字节用于之前的 $RBP 由函数的第一条指令保存)。

【讨论】:

  • 谢谢!我编辑了上面的答案,正如你所说,框架基础是DW_OP_call_frame_cfa,但$rsp 对我没有帮助。你能告诉我为什么(我发布了我的寄存器)吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-27
  • 2014-04-27
  • 1970-01-01
  • 1970-01-01
  • 2014-05-15
相关资源
最近更新 更多