本文主要是介绍DWARF常见section总结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
一、使用工具查看ELF中DWARF相关信息
使用objdump
可以查看ELF中DWARF相关的所有section,具体操作说明如下:
objdump <option(s)> <file(s)>-W, --dwarf[a/=abbrev, A/=addr, r/=aranges, c/=cu_index, L/=decodedline,f/=frames, F/=frames-interp, g/=gdb_index, i/=info, o/=loc,m/=macro, p/=pubnames, t/=pubtypes, R/=Ranges, l/=rawline,s/=str, O/=str-offsets, u/=trace_abbrev, T/=trace_aranges,U/=trace_info]
如想查看可执行文件variable
中的.debug_abbrev
节的内容,可使用命令:
objdump -Wa variable
objdump --dwarf=abbrev variable
二、DWARF各个section详解
.debug_info #包含DWARF调试信息项(DIE)的核心DWARF数据,其类型的定义在debug_abbrev中
.debug_abbrev #debug_info节中的所有对象的类型
.debug_aranges #代码段的内存地址和debug_info编译单元之间的映射
.debug_frame #调用帧信息
.debug_line #行号程序
.debug_loc #DW_AT_location属性中使用的位置列表
.debug_macinfo #宏描述
.debug_pubnames #全局对象和函数到编译单元的映射表
.debug_pubtypes #全局类型名字到编译单元的映射表
.debug_ranges #DW_AT_ranges属性中使用的地址范围
.debug_str #debug_info使用的字符串表
.debug_types #类型说明
2.1 debug_info 和 debug_abbrev
这两个节是天生在一起的两个节,它们是一个“实例和类型”的关系,也就是info
节中的内容是abbrev
节中的一个结构的实例。在abbrev
节中声明了很多中不同的Dwarf类型组合(我们可以想象为C语言中的结构声明,而这些类型都是DWARF格式约定好的类型),然后在info
节的每一项都声明自己使用的是abbrev
节中的那个类型,也就是说明自己是那个结构的实例。这样两者结合就可以得到系统中的所有类型声明信息。
# debug_info中的部分内容# <0>表示这是根DIE,<1>是<0>的一个子DIE,将其理解成树形结构# <b>表示这个这个DIE在debug_info节中的起始偏移,其他<b>也是字段或DIE的偏移# Abbrev Number: 1 表示这是abbrev节中第1个类型的一个实例,我们可以交叉到abbrev节的第1个类型声明看一下其中关于这个结构类型的声明<0><b>: Abbrev Number: 1 (DW_TAG_compile_unit) # DW_TAG_compile_unit表示一个编译单元,一个源文件对应一个DW_TAG_compile_unit<c> DW_AT_producer : (indirect string, offset: 0x92): GNU C++14 12.2.0 -mtune=generic -march=x86-64 -gdwarf-2 -g -O0 -O0 -std=gnu++14 -fasynchronous-unwind-tables<10> DW_AT_language : 4 (C++)<11> DW_AT_name : (indirect string, offset: 0x50): /home/data/huangqiqi/workfile/myGDB/minidbg/examples/variable.cpp<15> DW_AT_comp_dir : (indirect string, offset: 0): /home/data/huangqiqi/workfile/myGDB/minidbg/build<19> DW_AT_low_pc : 0x1139 # variable.cpp编译程序的开始位置<21> DW_AT_high_pc : 0x118a # variable.cpp编译程序的结束位置<29> DW_AT_stmt_list : 0<1><b4>: Abbrev Number: 9 (DW_TAG_subprogram) # variable.cpp文件中的main函数<b5> DW_AT_external : 1 <b6> DW_AT_name : (indirect string, offset: 0x10d): main<ba> DW_AT_decl_file : 1 # 下面三个分别对应源文件、行、列<bb> DW_AT_decl_line : 3 <bc> DW_AT_decl_column : 5 <bd> DW_AT_type : <0x57> # 函数返回类型,与debug_info节<0x57>偏移处的类型一样<c1> DW_AT_low_pc : 0x1139 # low和high是函数编译后的开始和结束位置<c9> DW_AT_high_pc : 0x118a<d1> DW_AT_frame_base : 0 (location list)<d5> DW_AT_GNU_all_tail_call_sites: 1<2><d6>: Abbrev Number: 10 (DW_TAG_variable) # main函数中的变量a<d7> DW_AT_name : a <d9> DW_AT_decl_file : 1 <da> DW_AT_decl_line : 4 <db> DW_AT_decl_column : 10<dc> DW_AT_type : <0x5e><e0> DW_AT_location : 2 byte block: 91 68 (DW_OP_fbreg: -24)
2.2 debug_aranges
debug_aranges
段是编译单元(CU)与代码段地址之间的一个映射。借助工具理解这个段的方法:
objdump -d ./a.out > disass_log
,反汇编代码objdump -Wri ./a.out > dwarf_log
,输出调试信息
在dwarf_log
文件中找到debug_aranges
段,就可以看到如下的信息,Compilation Unit @ offset
是debug_info
段中的内容。
Contents of the .debug_aranges section:Length: 764 Version: 2 Offset into .debug_info: 0 // debug_info中偏移0处的CU(Compilation Unit @ offset 0)地址映射Pointer Size: 8 Segment Size: 0 Address Length 0000000000024909 000000000000033a // 在disass_log中就可以找到地址24909处的指令,范围长度是33a
...Length: 44 Version: 2 Offset into .debug_info: 0x115f4 // debug_info中偏移0x115f4处的CU(Compilation Unit @ offset 0x115f4)地址映射Pointer Size: 8 Segment Size: 0Address Length0000000000025496 000000000000017b0000000000000000 0000000000000000
2.3 debug_ranges
DW_AT_ranges属性中使用的地址范围。
2.4 debug_line
debug_line
段包含了程序中的行号信息,通过读取、解析该段的内容,我们就能知道源代码行号与机器码指令地址之间的映射关系。如下是解析debug_line
所得(objdump -WL ./a.out
):
File name Line number Starting address
dwarf_test.c 8 0x1179
dwarf_test.c 12 0x1189
dwarf_test.c 13 0x11a5
dwarf_test.c 15 0x11ab
如果按照上面这种看上去很直观的方式来存储行号调试信息,那debug_line
段所占用的空间将会非常大。设计者就设计了一种更节省空间的规则和方法,这样可以将调试信息中的debug_line
内容压缩减少。对于调试器开发人员,他们如果想得到debug_line
中源代码行号与机器码指令地址之间的映射关系,要先读取其中的内容,再按照规则和方法解析读取到的内容,最终就可以得到如上面代码段所示的内容。
至于规则和方法是如何设计的,可以查阅dwarf 4中的 6.2 Line Number Information章节。
也发现了一个关于这块的中文介绍:https://zhuanlan.zhihu.com/p/642441074,介绍的虽不太全,参考意义却挺大。
解析dwarf信息的开源项目挺多的,我比较熟悉的有delve调试器中所用的方法:delve/pkg/dwarf,还用过libelfin,但是没有阅读过其源码。
这篇关于DWARF常见section总结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!