I'm following Eli Bendersky's blog on parsing the DWARF debug information. He shows an example of parsing the binary with DWARF version 2 in his blog. The frame base of a function (further used for retrieving local variables) can be retrieved from the 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 )
However, I find in DWARF version 4 there is no such .debug_loc
section. Here is the function info on my machine:
<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
Line <318> indicates the frame base is 1 byte block: 9c (DW_OP_call_frame_cfa)
. Any idea how to find the frame base for the DWARF v4 binaries?
Update based on @Employed Russian's answer: The frame_base of a subprogram seems to point to the Canonical Frame Address (CFA), which is the RBP value before the call instruction.
<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)
So a local variable (my_local
in the above example) can be located by the CFA using this calculation: &my_local
= CFA
- 20 = (current RBP
+ 16) - 20 = current RBP
- 4.
Verify it by checking the assembly:
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 $0x20,%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 $0x2,%eax
115e: 89 45 fc mov %eax,-0x4(%rbp)
my_local
is at -0x4(%rbp)
.
This isn't about DWARFv2
vs. DWARFv4
-- using either version the compiler may chose to use or not use location lists. Your compiler chose not to.
Any idea how to find the frame base for the DWARF v4 binaries?
It tells you right there: use the CFA
pseudo-register, also known as "canonical frame address".
That "imaginary" register has the same value that %rsp
had just before the current function was called. That is, current function's return address is always stored at CFA+0
, and %rsp == CFA+8
on entry into the function.
If the function uses frame pointer, then previous value of %rbp
is usually stored at CFA+8
.
More info here.