前言:我很抱歉为我的问题准备了很长时间,这样做的原因是为了确保这篇文章是独立的,并希望包括我找到的所有必要信息。
我的问题与先生的这篇好文章相关。Eli Bendersky
因此,我将使用下面的输入代码来回答我的问题:
#include <stdio.h>
void do_stuff(int my_arg)
{
int my_local = my_arg + 2;
int i;
for (i = 0; i < my_local; ++i)
printf("i = %d\n", i);
}
int main()
{
do_stuff(2);
return 0;
}
以上代码编译为gcc -g tracedprog2.c -o tracedprog2
此外,我将使用这里分享的libdwarf示例https://github.com/timsnyder/libdwarf-code/tree/3e75142a5d8938466e00a942c41a04f69510915d,它可以通过以下步骤轻松构建,以使用该程序复制我的发现(这不是必需的,只是想分享一下,以防有人可能正在寻找它):
cd libdwarf-code
mkdir build && cd build
cmake -DBUILD_DWARFEXAMPLE=TRUE ..
make -j4
// built binaries will be available in the directory: $HOME/libdwarf-code/build/src/bin/dwarfexample
问题是如标题所述,如何使用libdwarf收集的信息来获取局部变量的位置?
正如先生所说。Bendersky的帖子中,要做的第一件事是通过objdump --dwarf=info ./tracedprog2
获取libdwarf信息,它将输出如下信息(我只包含有用的信息):
<1><8a>: Abbrev Number: 5 (DW_TAG_subprogram)
<8b> DW_AT_external : 1
<8b> DW_AT_name : (indirect string, offset: 0x29): do_stuff
...
<92> DW_AT_low_pc : 0x1135
<9a> DW_AT_high_pc : 0x43
<a2> DW_AT_frame_base : 1 byte block: 9c (DW_OP_call_frame_cfa)
<a4> DW_AT_GNU_all_tail_call_sites: 1
...
<2><b3>: Abbrev Number: 7 (DW_TAG_variable)
<b4> DW_AT_name : (indirect string, offset: 0x0): my_local
...
<bb> DW_AT_type : <0x57>
<bf> DW_AT_location : 2 byte block: 91 68 (DW_OP_fbreg: -24)
我的理解是,为了找出局部变量的位置,需要许多信息(如操作码所示):
- libdwarf的框架基础:
DW_OP_call_frame_cfa
- libdwarf的局部变量偏移量:
DW_OP_fbreg
现在这里是事情变得相当棘手的地方,在阅读DWARF指南(https://dwarfstd.org/doc/DWARF5.pdf)后,它指出:The DW_OP_call_frame_cfa operation pushes the value of the CFA, obtained from the Call Frame Information (see Section 6.4 on page 171)
这就是上面共享的来自dwarfexample
的二进制frame1
(https://github.com/timsnyder/libdwarf-code/tree/3e75142a5d8938466e00a942c41a04f69510915d/src/bin/dwarfexample)试图将此CFA信息解析为用户可读格式的地方。
在运行./frame1 tracedprog2
代码时,您得到的输出看起来像这样(这个程序将从帧描述条目(FDE)解析调用信息条目(CIE)信息);下面是函数do_stuff
的帧信息,因为这是这个问题的焦点。我发现了一种更好的方法,使用readelf -w ./tracedprog2
输出数据
00000088 000000000000001c 0000005c FDE cie=00000030 pc=0000000000001135..0000000000001178
DW_CFA_advance_loc: 1 to 0000000000001136
DW_CFA_def_cfa_offset: 16
DW_CFA_offset: r6 (rbp) at cfa-16
DW_CFA_advance_loc: 3 to 0000000000001139
DW_CFA_def_cfa_register: r6 (rbp)
DW_CFA_advance_loc: 62 to 0000000000001177
DW_CFA_def_cfa: r7 (rsp) ofs 8
DW_CFA_nop
DW_CFA_nop
DW_CFA_nop
从DWARF 5书的描述来看,
15. DW_CFA_def_cfa takes two unsigned LEB128 arguments representing a
register number and an offset. The required action is to define the
current CFA rule to use the provided register and offset.
16. DW_CFA_def_cfa_register takes a single unsigned LEB128 argument
representing a register number. The required action is to define the
current CFA rule to use the provided register (but to keep the old
offset).
17. DW_CFA_def_cfa_offset takes a single unsigned LEB128 argument
representing an offset. The required action is to define the current CFA
rule to use the provided offset (but to keep the old register).
重要的信息似乎是DW_CFA_def_cfa
和DW_CFA_def_cfa_register
的值,我认为这可能是我正在寻找的帧基。
因此,要获取变量my_local
的位置,我认为需要做以下工作:
首先,CFA是DW_CFA_def_cfa
中定义的RSP + 8
。接下来,DW_CFA_offset
是cfa - 16
,这使它成为RSP - 8
?从那里,有DW_CFA_def_cfa_offset: 16
,这似乎表明我需要像这样添加RSP - 8 + 16
,使其成为RSP + 8
。然后,使用值DW_CFA_def_cfa_register: r6 (rbp)
,RSP
更改为RBP
,因此现在是RBP + 8
。从这里开始,添加my_local
变量的DW_OP_fbreg: -24
以获得RBP - 0x10
。但是,我看到在objdump
中,它是-0x14(%rbp),%eax
。
0000000000001135 <do_stuff>:
1135: 55 push %rbp
1136: 48 89 e5 mov %rsp,%rbp
1139: 48 83 ec 20 sub $0x20,%rsp
113d: 89 7d ec mov %edi,-0x14(%rbp)
1140: 8b 45 ec mov -0x14(%rbp),%eax
1143: 83 c0 02 add $0x2,%eax
1146: 89 45 f8 mov %eax,-0x8(%rbp)
我相信我能够找到计算局部变量location所需的所有必要信息,但似乎我在某个地方遗漏了一些东西。有人能告诉我我错过了什么吗?先谢谢你。
1条答案
按热度按时间j9per5c41#
所以看起来我误解了
objdump -S ./tracedprog2
向我展示的内容,导致了我上面所说的错误结论。例如,使用DWARF信息转储将显示反汇编+源代码,如下所示:
如你所见
my_local
就在这条线的正上方1140: 8b 45 ec mov -0x14(%rbp),%eax
这让我相信我试图找到
-0x14(%rbp)
的偏移计算。现在我想我有一个很好的想法是什么发生后,阅读许多额外的来源,采取了一点找到(我会引用他们下面的情况下,任何人都想验证我的答案)。
长话短说,让我扩展我上面显示的信息,看看我是否可以澄清我的理解以及我是如何得到解决方案的:
以上附加信息可以通过使用
DWARF2
而不是默认的DWARF5
进行编译来找到(源代码:https://blog.tartanllama.xyz/writing-a-linux-debugger-variables/)。首先,CFA寄存器初始设置为
rsp + 8
(源代码:https://lists.dwarfstd.org/pipermail/dwarf-discuss/2010-August/000915.html)。然后,在到达地址
0x1135
处的do_stuff
帧时,我们在FDE
表中的0x1136
处插入一个新行(因此,这就是值1所表示的)。现在,由于语句DF_CFA_def_cfa_offset
,该列的偏移量为16。这是什么意思?而不是我们之前看到的
rsp + 8
,现在从0x1136
开始,直到该行结束,现在将是rsp + 16
。因此,接下来,我们在将3添加到当前地址(例如,
0x1139
)后创建一个新行,并从这里开始,我们将CFA寄存器定义为rbp
。因为我们直到现在才改变偏移量,这意味着从0x1139
开始,它将是rbp + 16
而不是rsp + 16
。基本上,就是这样,我们正在寻找的帧基,以计算局部变量
my_local
是rsp + 16
。现在我们看一下.debug_loc部分的内容,结果似乎与我上面的解释一致。现在回到
有一个
DW_OP_fbreg: -24
值,你只需把它加到我们找到的帧基上,这意味着rbp + 16 - 24
=rbp - 8
,这将是一个等价的mov %eax, -0x8(%rbp)
。现在我看着先生。Bendersky的帖子再次,这似乎与他的答案一致,但我不知何故错过了它,显然开始DWARF版本5,
.debug_loc
似乎默认不包括在内,这误导了我最初追求错误的结论。我希望这个解决方案是正确的(我认为它对我有意义),如果它是不正确的,请让我知道,因为我也仍然不确定DWARF(这是非常复杂的新手像我一样)。