Dwarf DW_AT_location objdump 和 dwarfdump 不一致
Dwarf DW_AT_location objdump and dwarfdump inconsistent
我正在研究 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 [=12=]x338,%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
是正确的
DW_OP_fbreg -824
: Meaning $rbp-824
确实不是那个意思。这意味着,从 frame base(虚拟)寄存器偏移 -824
,不一定(通常也不)等于 $rbp
.
您需要查找 DW_AT_frame_base
才能知道当前函数中的框架基础是什么。
很可能它被定义为 DW_OP_call_frame_cfa
,这是 $RSP
就在 当前函数被调用之前的值,等于 $RBP-16
(由 CALL
指令保存的 return 地址的 8 个字节,由函数的第一条指令保存的先前 $RBP
的 8 个字节)。
我正在研究 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 [=12=]x338,%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
是正确的
DW_OP_fbreg -824
: Meaning$rbp-824
确实不是那个意思。这意味着,从 frame base(虚拟)寄存器偏移 -824
,不一定(通常也不)等于 $rbp
.
您需要查找 DW_AT_frame_base
才能知道当前函数中的框架基础是什么。
很可能它被定义为 DW_OP_call_frame_cfa
,这是 $RSP
就在 当前函数被调用之前的值,等于 $RBP-16
(由 CALL
指令保存的 return 地址的 8 个字节,由函数的第一条指令保存的先前 $RBP
的 8 个字节)。