使用 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)

这与 DWARFv2DWARFv4 无关——编译器可以选择使用或不使用位置列表。您的编译器选择不这样做。

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