DWARF格式对于debug信息的支持
by Peter 4 YEARS AGO 4 MINUTEREAD
http://tsecer.blog.163.com/blog/static/150181720118395251117/

1、总体思想:
由于一般的大型软件中包含了非常多的调试信息,所以如何将调试信息在dwarf中进行压缩是一个符号系统的重要问题。这种压缩体现在各个方面,例如,其中使用的证书压缩方法,就是一个数是通过特殊的字节压缩方式而不是通常意义上的ASCII内码;大量使用“状态机”增量表示状态的变化,例如,当行号和汇编指令的对应关系转换,栈帧中寄存器和变量的变化都是用的增量迭代的表示方法。
2、.debug_info和.debug_abbrev
这两个节是天生在一起的两个节,它们是一个“实例和类型”的关系,也就是info节中的内容是abbrev节中的一个结构的实例。在abbrev节中声明了很多中不同的Dwarf类型组合(我们可以想象为C语言中的结构声明,而这些类型都是DWARF格式约定好的类型),然后在info节的每一项都声明自己使用的是abbrev节中的那个类型,也就是说明自己是那个结构的实例。这样两者结合就可以得到系统中的所有类型声明信息。
现在依然是一个例子实例来说明这个问题
Contents of the .debug_info section:
Compilation Unit @ offset 0x0:
Length: 0x88 (32-bit)
Version: 3
Abbrev Offset: 0
Pointer Size: 4 一个标准节头信息。
<0>: Abbrev Number: 1 (DW_TAG_compile_unit) 这里声明了自己是abbrev节中第一个类型的一个实例,我们可以交叉到abbrev节的第一个类型声明看一下其中关于这个结构类型的声明,通过这个类型的声明,我们可以知道这里存放的字节流如何划分,它们的长度,代表的意义等逻辑信息。
< c> DW_AT_producer : (indirect string, offset: 0x5c): GNU C 4.4.2 20091027 (Red Hat 4.4.2-7)
<10> DW_AT_language : 1 (ANSI C)
<11> DW_AT_name : (indirect string, offset: 0x0): Hello.c <15> DW_AT_comp_dir : (indirect string, offset: 0x8):/home/tsecer/gdb7.2/Src/Obj/gdb 如果编译时使用的是绝对路径,那么这个dir项就不存在,如果是相对路径,那么这个编译路径是存在的。 <19> DW_AT_low_pc : 0x80483b4 代码段的起始地址 <1d> DW_AT_high_pc : 0x80483d0 代码段的结束地址 <21> DW_AT_stmt_list : 0x0
<1><25>: Abbrev Number: 2 (DW_TAG_base_type)
<26> DW_AT_byte_size : 4
<27> DW_AT_encoding : 7 (unsigned)
<28> DW_AT_name : (indirect string, offset: 0x3c): unsigned int
<1><2c>: Abbrev Number: 2 (DW_TAG_base_type) 可以看到此处有很多的都是基本类型,也就是我们通常所说的内置类型,例如char int short 等结构,这些是编译器内置识别的一些结构,它们使用的格式都是相同的,都是abbrev节中的第二项。
<2d> DW_AT_byte_size : 1
<2e> DW_AT_encoding : 8 (unsigned char)
<2f> DW_AT_name : (indirect string, offset: 0x49): unsigned char
<1><33>: Abbrev Number: 2 (DW_TAG_base_type)
<34> DW_AT_byte_size : 2
<35> DW_AT_encoding : 7 (unsigned)
<36> DW_AT_name : (indirect string, offset: 0x83): short unsigned int
<1><3a>: Abbrev Number: 2 (DW_TAG_base_type)
<3b> DW_AT_byte_size : 4
<3c> DW_AT_encoding : 7 (unsigned)
<3d> DW_AT_name : (indirect string, offset: 0x37): long unsigned int
<1><41>: Abbrev Number: 2 (DW_TAG_base_type)
<42> DW_AT_byte_size : 1
<43> DW_AT_encoding : 6 (signed char)
<44> DW_AT_name : (indirect string, offset: 0x4b): signed char
<1><48>: Abbrev Number: 2 (DW_TAG_base_type)
<49> DW_AT_byte_size : 2
<4a> DW_AT_encoding : 5 (signed)
<4b> DW_AT_name : (indirect string, offset: 0x28): short int
<1><4f>: Abbrev Number: 3 (DW_TAG_base_type)
<50> DW_AT_byte_size : 4
<51> DW_AT_encoding : 5 (signed)
<52> DW_AT_name : int
<1><56>: Abbrev Number: 2 (DW_TAG_base_type)
<57> DW_AT_byte_size : 8
<58> DW_AT_encoding : 5 (signed)
<59> DW_AT_name : (indirect string, offset: 0x96): long long int
<1><5d>: Abbrev Number: 2 (DW_TAG_base_type)
<5e> DW_AT_byte_size : 8
<5f> DW_AT_encoding : 7 (unsigned)
<60> DW_AT_name : (indirect string, offset: 0x32): long long unsigned int
<1><64>: Abbrev Number: 2 (DW_TAG_base_type)
<65> DW_AT_byte_size : 4
<66> DW_AT_encoding : 5 (signed)
<67> DW_AT_name : (indirect string, offset: 0x9b): long int
<1><6b>: Abbrev Number: 4 (DW_TAG_base_type)
<6c> DW_AT_byte_size : 4
<6d> DW_AT_encoding : 7 (unsigned)
<1><6e>: Abbrev Number: 2 (DW_TAG_base_type)
<6f> DW_AT_byte_size : 1
<70> DW_AT_encoding : 6 (signed char)
<71> DW_AT_name : (indirect string, offset: 0x52): char
<1><75>: Abbrev Number: 5 (DW_TAG_subprogram) 这个是前面说的第一个项的子结构,因为它的前面有一个<1>,就表示他是第一项的一个子项,同样前面的所有的项都是第一项的子项。表示子项的方式就是在abbrev中会说明它是否有子项。例如,abbrev的第一项的就有“has children”标志,所以接下来的所有项都是它的子项,直到遇到一个00项为止。以此类推,从而形成一个树形结构。
<76> DW_AT_external : 1
<77> DW_AT_name : (indirect string, offset: 0x57): main
<7b> DW_AT_decl_file : 1
<7c> DW_AT_decl_line : 2
<7d> DW_AT_type : <0x4f>
<81> DW_AT_low_pc : 0x80483b4
<85> DW_AT_high_pc : 0x80483d0
<89> DW_AT_frame_base : 1 byte block: 9c (DW_OP_call_frame_cfa)
Contents of the .debug_abbrev section:
Number TAG
1 DW_TAG_compile_unit [has children]
DW_AT_producer DW_FORM_strp
DW_AT_language DW_FORM_data1
DW_AT_name DW_FORM_strp
DW_AT_comp_dir DW_FORM_strp
DW_AT_low_pc DW_FORM_addr
DW_AT_high_pc DW_FORM_addr
DW_AT_stmt_list DW_FORM_data4
2 DW_TAG_base_type [no children]
DW_AT_byte_size DW_FORM_data1
DW_AT_encoding DW_FORM_data1
DW_AT_name DW_FORM_strp
3 DW_TAG_base_type [no children]
DW_AT_byte_size DW_FORM_data1
DW_AT_encoding DW_FORM_data1
DW_AT_name DW_FORM_string
4 DW_TAG_base_type [no children]
DW_AT_byte_size DW_FORM_data1
DW_AT_encoding DW_FORM_data1
5 DW_TAG_subprogram [no children]
DW_AT_external DW_FORM_flag
DW_AT_name DW_FORM_strp
DW_AT_decl_file DW_FORM_data1
DW_AT_decl_line DW_FORM_data1
DW_AT_type DW_FORM_ref4
DW_AT_low_pc DW_FORM_addr
DW_AT_high_pc DW_FORM_addr
DW_AT_frame_base DW_FORM_block1
3、.debug_frame格式
该节主要是为了表示函数栈帧的关系。也就是当执行一个函数的时候,这个函数中的各个寄存器的存放位置及变化情况,栈帧的计算方法的变化情况等。这些是编译器最为清楚的布局,所以最好让便一起来完成这个操作,从而不必通过指令码来进行猜测。该节的开始可以有若干个CIE(Common Information Entry),顾名思义,这些项主要是用来保存一些通用的信息,这些信息可以被接下来的FDE(Frame Description Entry)引用,从而提高存储的压缩效率。
Contents of the .debug_frame section:
00000000 00000010 ffffffff CIE
Version: 1
Augmentation: “”
Code alignment factor: 1
Data alignment factor: -4
Return address column: 8
DW_CFA_def_cfa: r4 (esp) ofs 4 这里定义了CFA(Canonical Frame Address)的计算方法,也就是计算出当前的堆栈基准位置的计算方法,之后的大部分变量都将会通过这个CFA偏移来表示。这个指令有两个操作数,一个是寄存器,一个是偏移量,还有一种表示就是直接通过寄存器定义,DW_CFA_DEF_CFA_REGISTER,这种定义CFA的时候只需要一个操作数,就是寄存器编号。
DW_CFA_offset: r8 (eip) at cfa-4 这里定义一个寄存器的存放位置或者说计算方法,就是上面定义的CFA偏移4个字节。表示EIP寄存器在cfa-4的位置存放。
DW_CFA_nop
DW_CFA_nop
00000014 0000001c 00000000 FDE cie=00000000 pc=080483b4…080483d0
DW_CFA_advance_loc: 1 to 080483b5
DW_CFA_def_cfa_offset: 8 表示当前的CFA值向下偏移8个字节,但是CFA使用的寄存器中的值并没有变化。
DW_CFA_advance_loc: 2 to 080483b7
DW_CFA_offset: r5 (ebp) at cfa-8
DW_CFA_def_cfa_register: r5 (ebp)
DW_CFA_advance_loc: 24 to 080483cf
DW_CFA_restore: r5 (ebp)
DW_CFA_def_cfa: r4 (esp) ofs 4 这些DW_CFA_XXX的意义以及它们使用的操作数都已经在DWARF格式中规定,大家不要猜测,也不要自己发挥。
DW_CFA_nop
DW_CFA_nop
DW_CFA_nop
下面是汇编代码
080483b4 :
80483b4: 55 push %ebp
80483b5: 89 e5 mov %esp,%ebp
80483b7: 83 e4 f0 and $0xfffffff0,%esp
80483ba: 83 ec 10 sub $0x10,%esp
80483bd: c7 04 24 94 84 04 08 movl $0x8048494,(%esp)
80483c4: e8 27 ff ff ff call 80482f0 puts@plt
80483c9: b8 00 00 00 00 mov $0x0,%eax
80483ce: c9 leave
80483cf: c3 ret
可以看到,在80483b5指令之后,堆栈的栈顶指针向下偏移了8个字节(call自动push的EIP加上开始的时候push的EBP),但是EBP的值尚未变化。
4、.debug_line
这里包含的是行号和机器指令之间的映射关系,这也是实现源代码级调试的重要依据。用户在源代码中设置断点,我们就必须能够通过这个“源文件+行号”的信息来在制定的程序中打断点。它同样使用了一个状态机增量表示这个变化,并且在每个项中同样是通过《行号增加+地址增加》的格式来表示它们映射关系的变化。这里比较复杂一些,所以说的可能多一些。
它基本的思想就是通过一个字节来表示一个<行号变化,地质变化>pair关系,也就是DWARF中说明的special Opcode,这些special Opcode只有一个字节;这个字节中同时还要去掉两外两种操作类型:standard opcode和extended opcode,
standard 是DWARF自己保留的若干个操作符,它们的意义、格式、操作数的个数及大小都是规定好的。但是它们的个数可能之后还会增加,所以每个.debug_line节中都要说明自己使用了多少个标准操作符,这个就是节中的op_base的意义,也就是说,这个节之下的所有操作符都是标准操作符。这样一个低版本的调试器如果不识别某个格式也不会出问题。
extended 类型是以0开始的一个字节,它接下来是一个字节表示自己的长度,这样扩展性更强。具体的意义则由扩展类型确定,当然,dwarf也保留了标准的扩展操作符。
special 类型就是一个字节中剩下的数值了。它是一个和line_base和line_range相关的变量。它的根本目的是为了用一个字节表示大部分<行号偏移、指令偏移对>。它使用的方法就是使用不同的基数来表示,其中商为一个行号偏移,而余数为一个指令偏移。从而对一个字节的地址空间进行划分。
Raw dump of debug contents of section .debug_line:
Offset: 0x0
Length: 52
DWARF Version: 2
Prologue Length: 30
Minimum Instruction Length: 1
Initial value of ‘is_stmt’: 1
Line Base: -5
Line Range: 14
Opcode Base: 13
Opcodes:
Opcode 1 has 0 args
Opcode 2 has 1 args
Opcode 3 has 1 args
Opcode 4 has 1 args
Opcode 5 has 1 args
Opcode 6 has 0 args
Opcode 7 has 0 args
Opcode 8 has 0 args
Opcode 9 has 1 args
Opcode 10 has 0 args
Opcode 11 has 0 args
Opcode 12 has 1 args
The Directory Table is empty.
The File Name Table:
Entry Dir Time Size Name
1 0 0 0 Hello.c
Line Number Statements:
Extended opcode 2: set Address to 0x80483b4
Special opcode 7: advance Address by 0 to 0x80483b4 and Line by 2 to 3
Special opcode 132: advance Address by 9 to 0x80483bd and Line by 1 to 4
Special opcode 174: advance Address by 12 to 0x80483c9 and Line by 1 to 5
Special opcode 76: advance Address by 5 to 0x80483ce and Line by 1 to 6
Advance PC by 2 to 0x80483d0
Extended opcode 1: End of Sequence
上面对应的line number statements对应的内存值
00 05 02 B4 83 04 08 14 91 BB
59 02 02 00 01 01 00 00
00 05 02 B4 83 04 08 这里最开始的00 05说明是一个扩展命令,并且接下来的总长度为5个字节,2表示扩展操作码为2,也就是定义基准地址的命令。接下来四个字节为操作数,表示基地址为xxxxxx。
0x14 大于op_base 13,所以为特殊节,20-13 = 7 7 % 14 = 0 7 + (-5) =2,对应这里显示的地址增加0,行号增加2的意义,其它一次类推。
5、.debug_str
这个我们最不陌生,就是C语言中的字符串组,它们以零结束,放在一个单独的节是为了提高存储效率。因为字符串的长度是任意的,所以在考虑节对其的时候比较有意义。这里只是列出一个其内容,大家一看便知
Contents of the .debug_str section:
0x00000000 48656c6c 6f2e6300 2f686f6d 652f7473 Hello.c./home/ts
0x00000010 65636572 2f676462 372e322f 5372632f ecer/gdb7.2/Src/
0x00000020 4f626a2f 67646200 73686f72 7420696e Obj/gdb.short in
0x00000030 74006c6f 6e67206c 6f6e6720 756e7369 t.long long unsi
0x00000040 676e6564 20696e74 00756e73 69676e65 gned int.unsigne
0x00000050 64206368 6172006d 61696e00 474e5520 d char.main.GNU
0x00000060 4320342e 342e3220 32303039 31303237 C 4.4.2 20091027
0x00000070 20285265 64204861 7420342e 342e322d (Red Hat 4.4.2-
0x00000080 37290073 686f7274 20756e73 69676e65 7).short unsigne
0x00000090 6420696e 74006c6f 6e67206c 6f6e6720 d int.long long
0x000000a0 696e7400

DWARF格式对于debug信息的支持相关推荐

  1. C 预处理器 —— __DATE__ # __TIME__ # __FILE__ # __LINE__ # __STDC__ (预处理宏的使用 —— 打印debug信息:)

    预定义宏: ANSI C 定义了许多宏.在编程中您可以使用这些宏,但是不能直接修改这些预定义的宏. 宏    描述 __DATE__    当前日期,一个以 "MMM DD YYYY&quo ...

  2. .NetCore获取Json和Xml格式的配置信息

    本篇将和大家分享的是:如何获取Json和Xml格式的配置信息,主要介绍的是Configuration扩展方法的使用,因为netcore的web应用在Startup中已经默认嵌入appsettings. ...

  3. cocos2d-x 输出debug信息

    在Classes目录下添加文件AppDef.h #ifndef _APP_DEF_H_ #define _APP_DEF_H_ #include <android/log.h> #defi ...

  4. log4j 配置,tomcat 启动或有后台操作时,控制台会显示很多 DEBUG 信息

    log4j 配置,tomcat 启动或有后台操作时,控制台会显示很多 DEBUG 信息 日志信息可以以文件形式显示,也可以在控制台输出,在 log4j.properties 文件设置. 控制台有很多 ...

  5. Scrapy框架的学习(7. 了解Scrapy中的debug信息以及Scrapy shell的使用)

    认识程序中的debug信息 https://blog.csdn.net/wei18791957243/article/details/86157707  这个博客里写了,怎么关闭这些debug信息 因 ...

  6. Tensorflow Summary: 查看Tensorflow Model pb格式模型的信息

    参考文链:如何查看Tensorflow SavedModel格式模型的信息; 参考:summary方法--小酒窝的博客; 参考:tensorflow中输出参数的方法--详细; tensorflow生成 ...

  7. andorid 查看OpenCv Mat的Debug信息

    在进行Android调试时,不能再Console显示Debug信息,只能在LogCat上显示,显示信息如下图: 代码段: public void printMat2Txt(Mat ElemM, Str ...

  8. php获取视频信息,支持优酷土豆新浪腾讯等多家网站

    video.php类 <?php /** * 解析 视频信息 类 * * 支持 优酷, 土豆 酷6 56 新浪 qq播客 乐视 乐视 **/class class_video{// 超时时间va ...

  9. debug信息的认识

    常见的debug信息 如果我们的爬取的url地址不在我们设置的allowed_domains即是被爬取网站的域名下面,会出现什么样的情况呢? allowed_domains = ['sun0769de ...

最新文章

  1. 密度聚类算法DBSCAN实战及可视化分析
  2. 接口或抽象类:使用哪一个?
  3. Linux命令整理 - 文件搜索【4】
  4. 教你认清MVC,MVP和MVVM
  5. 科研人员的办公室是怎样的?
  6. boost::lambda模块ll_static_cast,ll_dynamic_cast,ll_const_cast,ll_reinterpret_cast的测试程序
  7. spring 事物合并_Spring系列合并
  8. JavaScript知识笔记(三)——内置对象、浏览器对象
  9. python importlib_学习python importlib的导入机制
  10. load control template file /_controltemplates/taxonomypicker.ascx failed
  11. SpringMVC(入门案例)
  12. RestExpress response中addHeader 导致stackOverflow
  13. OpenSSL API: SSL对象和SSL_CTX对象的使用
  14. 扫描网络计算机mac地址,局域网MAC地址查询扫描器
  15. 谈谈值得注意的高危端口
  16. MBR10200FAC-ASEMI塑封肖特基二极管MBR10200FAC
  17. 证明:如果向量组A可由向量组B线性表示,那么A的秩小于等于B的秩
  18. 7-6 愤怒的牛 (25 分)
  19. kubelet源码分析(一)之 NewKubeletCommand
  20. [论文阅读]Which Is Plagiarism: Fashion Image Retrieval Based on Regional Representation for Design Prote

热门文章

  1. 外业作业安全第一 测绘安全知识手册(PPT可下载)
  2. 人力资源数字化如何导致组织成功的 3 个示例
  3. 吉比特第三季营收13亿:靠“羊了个羊”走红 卢竑岩获分红3亿
  4. 【MATLAB教程案例80】matlab在大学数学中的应用——高等数学
  5. 黑马点评项目-短信登录功能
  6. kafka灰度发布解决方案
  7. 国家高新技术企业申报流程及条件
  8. Unity安卓应用闪退-使用DDMS工具查看安卓日志/ADB真机调试
  9. 拼团小程序源码_如何做好小程序拼团?
  10. 大学物理实验长度的测量实验报告_(完整精品)大学物理实验报告之长度基本测量.doc...