使用 DWARF 版本 4 查找框架基址和变量位置
Find frame base and variable locations using DWARF version 4
我正在关注 Eli Bendersky's blog on parsing the DWARF debug information。他在他的博客中展示了使用 DWARF 版本 2 解析二进制文件的示例。可以从 location list
:
中检索函数 的 框架基数(进一步用于检索局部变量)
<1><71>: Abbrev Number: 5 (DW_TAG_subprogram)
<72> DW_AT_external : 1
<73> DW_AT_name : (...): do_stuff
<77> DW_AT_decl_file : 1
<78> DW_AT_decl_line : 4
<79> DW_AT_prototyped : 1
<7a> DW_AT_low_pc : 0x8048604
<7e> DW_AT_high_pc : 0x804863e
<82> DW_AT_frame_base : 0x0 (location list)
<86> DW_AT_sibling : <0xb3>
...
$ objdump --dwarf=loc tracedprog2
Contents of the .debug_loc section:
Offset Begin End Expression
00000000 08048604 08048605 (DW_OP_breg4: 4 )
00000000 08048605 08048607 (DW_OP_breg4: 8 )
00000000 08048607 0804863e (DW_OP_breg5: 8 )
但是,我发现在 DWARF 版本 4 中没有这样的 .debug_loc
部分。这是我机器上的功能信息:
<1><300>: Abbrev Number: 17 (DW_TAG_subprogram)
<301> DW_AT_external : 1
<301> DW_AT_name : (indirect string, offset: 0x1e0): do_stuff
<305> DW_AT_decl_file : 1
<306> DW_AT_decl_line : 3
<307> DW_AT_decl_column : 6
<308> DW_AT_prototyped : 1
<308> DW_AT_low_pc : 0x1149
<310> DW_AT_high_pc : 0x47
<318> DW_AT_frame_base : 1 byte block: 9c (DW_OP_call_frame_cfa)
<31a> DW_AT_GNU_all_tail_call_sites: 1
行<318>表示框架基数为1 byte block: 9c (DW_OP_call_frame_cfa)
。知道如何找到 DWARF v4 二进制文件的框架基础吗?
根据@Employed Russian 的回答更新:
子程序的frame_base好像是指向Canonical Frame Address (CFA),也就是call指令之前的RBP值。
<2><329>: Abbrev Number: 19 (DW_TAG_variable)
<32a> DW_AT_name : (indirect string, offset: 0x7d): my_local
<32e> DW_AT_decl_file : 1
<32f> DW_AT_decl_line : 5
<330> DW_AT_decl_column : 9
<331> DW_AT_type : <0x65>
<335> DW_AT_location : 2 byte block: 91 6c (DW_OP_fbreg: -20)
因此 CFA 可以使用以下计算定位局部变量(上例中的 my_local
):&my_local
= CFA
- 20 = (current RBP
+ 16) - 20 = current RBP
- 4。
通过检查程序集来验证它:
void do_stuff(int my_arg)
{
1149: f3 0f 1e fa endbr64
114d: 55 push %rbp
114e: 48 89 e5 mov %rsp,%rbp
1151: 48 83 ec 20 sub [=13=]x20,%rsp
1155: 89 7d ec mov %edi,-0x14(%rbp)
int my_local = my_arg + 2;
1158: 8b 45 ec mov -0x14(%rbp),%eax
115b: 83 c0 02 add [=13=]x2,%eax
115e: 89 45 fc mov %eax,-0x4(%rbp)
my_local
位于 -0x4(%rbp)
。
这与 DWARFv2
与 DWARFv4
无关——编译器可以选择使用或不使用位置列表。您的编译器选择不这样做。
Any idea how to find the frame base for the DWARF v4 binaries?
它告诉你:使用 CFA
伪寄存器,也称为“规范帧地址”。
那个“虚数”寄存器的值与 %rsp
在调用当前函数之前的值相同。也就是说,当前函数的 return 地址总是存储在 CFA+0
,并且在进入函数时 %rsp == CFA+8
。
如果函数使用帧指针,那么%rbp
的前一个值通常存储在CFA+8
。
更多信息here。
我正在关注 Eli Bendersky's blog on parsing the DWARF debug information。他在他的博客中展示了使用 DWARF 版本 2 解析二进制文件的示例。可以从 location list
:
<1><71>: Abbrev Number: 5 (DW_TAG_subprogram)
<72> DW_AT_external : 1
<73> DW_AT_name : (...): do_stuff
<77> DW_AT_decl_file : 1
<78> DW_AT_decl_line : 4
<79> DW_AT_prototyped : 1
<7a> DW_AT_low_pc : 0x8048604
<7e> DW_AT_high_pc : 0x804863e
<82> DW_AT_frame_base : 0x0 (location list)
<86> DW_AT_sibling : <0xb3>
...
$ objdump --dwarf=loc tracedprog2
Contents of the .debug_loc section:
Offset Begin End Expression
00000000 08048604 08048605 (DW_OP_breg4: 4 )
00000000 08048605 08048607 (DW_OP_breg4: 8 )
00000000 08048607 0804863e (DW_OP_breg5: 8 )
但是,我发现在 DWARF 版本 4 中没有这样的 .debug_loc
部分。这是我机器上的功能信息:
<1><300>: Abbrev Number: 17 (DW_TAG_subprogram)
<301> DW_AT_external : 1
<301> DW_AT_name : (indirect string, offset: 0x1e0): do_stuff
<305> DW_AT_decl_file : 1
<306> DW_AT_decl_line : 3
<307> DW_AT_decl_column : 6
<308> DW_AT_prototyped : 1
<308> DW_AT_low_pc : 0x1149
<310> DW_AT_high_pc : 0x47
<318> DW_AT_frame_base : 1 byte block: 9c (DW_OP_call_frame_cfa)
<31a> DW_AT_GNU_all_tail_call_sites: 1
行<318>表示框架基数为1 byte block: 9c (DW_OP_call_frame_cfa)
。知道如何找到 DWARF v4 二进制文件的框架基础吗?
根据@Employed Russian 的回答更新: 子程序的frame_base好像是指向Canonical Frame Address (CFA),也就是call指令之前的RBP值。
<2><329>: Abbrev Number: 19 (DW_TAG_variable)
<32a> DW_AT_name : (indirect string, offset: 0x7d): my_local
<32e> DW_AT_decl_file : 1
<32f> DW_AT_decl_line : 5
<330> DW_AT_decl_column : 9
<331> DW_AT_type : <0x65>
<335> DW_AT_location : 2 byte block: 91 6c (DW_OP_fbreg: -20)
因此 CFA 可以使用以下计算定位局部变量(上例中的 my_local
):&my_local
= CFA
- 20 = (current RBP
+ 16) - 20 = current RBP
- 4。
通过检查程序集来验证它:
void do_stuff(int my_arg)
{
1149: f3 0f 1e fa endbr64
114d: 55 push %rbp
114e: 48 89 e5 mov %rsp,%rbp
1151: 48 83 ec 20 sub [=13=]x20,%rsp
1155: 89 7d ec mov %edi,-0x14(%rbp)
int my_local = my_arg + 2;
1158: 8b 45 ec mov -0x14(%rbp),%eax
115b: 83 c0 02 add [=13=]x2,%eax
115e: 89 45 fc mov %eax,-0x4(%rbp)
my_local
位于 -0x4(%rbp)
。
这与 DWARFv2
与 DWARFv4
无关——编译器可以选择使用或不使用位置列表。您的编译器选择不这样做。
Any idea how to find the frame base for the DWARF v4 binaries?
它告诉你:使用 CFA
伪寄存器,也称为“规范帧地址”。
那个“虚数”寄存器的值与 %rsp
在调用当前函数之前的值相同。也就是说,当前函数的 return 地址总是存储在 CFA+0
,并且在进入函数时 %rsp == CFA+8
。
如果函数使用帧指针,那么%rbp
的前一个值通常存储在CFA+8
。
更多信息here。