admin管理员组

文章数量:1638817

调试信息(debugging information)——解析DWARF文件

  • 调试信息 & DWARF
  • 解析dwarf文件
    • 函数
    • 变量
    • 行号
  • 参考

本文介绍gdb调试的调试信息和dwarf文件解析。

调试信息 & DWARF

在使用gdb调试时候,当你在函数打断点时候,你期望的是停在函数的第一条指令的地方,gdb如何获取函数第一条指令地址?当你在c语言某一行打断点时候,其实gdb要转换成对应的pc地址。当你期望观测某个变量的地址时候,gdb如何获取变量的地址?这些调试信息都是以DWARF格式形式嵌入到ELF文件的几个section当中的。通过命令:objdump -h xxx 可以看到调试信息在ELF文件中的位置,.debug_xxx的section段。

解析dwarf文件

本文的测试代码如下:

使用gcc编译 gcc -g -o test test.c的到可执行文件test。

函数

gdb调试时候,断点打在函数上,其实就是打在函数首条指令的地址上。这部分信息存在.debug_info段中,通过objdump --dwarf=info test得到info信息,部分截取如下:

dward文件的基本的描述条目叫做Debugging Information Entry (DIE),每个DIE有多个Tag组成,每个Tag又有多个attribution组成,DIE通过兄弟和孩子连接起来。

上图中,Tag DW_TAG_subprogram表明了一个函数,从属性DW_AT_name可以得到函数名:name,DW_AT_low_pc是函数首条指令的pc指针,这里是0x400526,DW_AT_high_pc是地址长度,那最后一条指令pc是 0 x 400526 + 0 x 2 b = 0 x 400551 0x400526+0x2b=0x400551 0x400526+0x2b=0x400551,这个可以通过汇编指令得到验证:

变量

这里还有一个DW_AT_frame_base,这是栈基址,这里是DW_op_call_frame_cfa,这个可以参考dwarf格式说明

The DW_OP_call_frame_cfa operation pushes the value of the CFA, obtained from the Call Frame Information (see Section 6.4).
Although the value of DW_AT_frame_base can be computed using other DWARF expression operators, in some cases this would require an extensive location list because the values of the registers used in computing the CFA change during a subroutine. If the Call Frame Information is present, then it already encodes such changes, and it is space efficient to reference that.

全局变量比较简单,在info里面直接就确定的。比如xx的地址0x601038,数据类型是0x57,找到0x57对应的DIE可以指导这个是4个字节的int类型。变量申明的行号在源码的第3行。


对于局部变量,找到地址则相对复杂一点,例如aa,起位置在DW_OP_fbreg:-20,也即 fbreg-20,fbreg是frame base register,再根据DW_AT_frame_base知道,此时的freg就是CFA,CFA(Canonical Frame Address)是标准框架地址,指调用者栈帧中调用点处的栈指针值。

CFA信息(CFI)在ELF的.eh_frame段中,通过readelf -wF test得到可以得到,这里截取部分信息如下啊,根据局部变量函数地址范围可以找到局部变量所在LOC范围,但此时还不能确定是那一行,这里有4行,每一行的前面LOC是pc指针,也即CFA没次发生变化的开始的pc地址。

这里需要再gdb运行时候,根据停留时候的pc,再查上面的CFI获取CFA的值,如下调试过程,pc指针为0x400537,所以此时的CFA为rbp+16,结合前面的信息,则aa的位置为rbp+16-24=rbp-8,下面中可以得到证实。

此外,aa地址为rbp-8,这个可以从汇编代码中也可以得到间接证实。

行号

在调试源代码时候,我们经常将断点打在源码中的某一行,gdb需要对应到汇编的起始代码位置。

上图可以清楚地看到源码行号和汇编指令地址的对应关系。

本文只是简单地介绍了常见的调试信息和dwarf文件的一个查找关系,让我们初步了解gdb是如何获取这些调试信息的,这里的介绍只是dwarf文件的冰山一角,dwarf文件的内容远不止于此,更加详细的dwarf文件内容介绍参考dwarf的pdf文件。

参考

  • [1] https://dwarfstd/doc/Debugging%20using%20DWARF-2012.pdf
  • [2] https://dwarfstd/doc/DWARF4.pdf
  • [3] https://eli.thegreenplace/2011/02/07/how-debuggers-work-part-3-debugging-information
  • [4] https://www.jianshu/p/20dfe4fe1b3f

本文标签: 文件信息informationdebuggingDWARF