How debuggers work:Part3 Debugging information学习笔记
Debugging information
当您要求调试器在某个函数的入口中断时,调试器如何知道在何处停止?当您向它询问变量的值时,它是如何找到要向您显示的内容的?答案就是调试信息。
调试信息是由编译器与机器代码一起生成的。它表示可执行程序和原始源代码之间的关系。该信息编码为预定义格式(硬编码???),并与机器代码一起存储。多年来,许多这样的格式被发明用于不同的平台和可执行文件。由于本文的目的不是调查这些格式的历史,而是展示它们是如何工作的,因此我们必须解决一些问题。这个东西将成为DWARF,它现在几乎被广泛用作Linux和其他Unix-y平台上ELF可执行文件的调试信息格式。
Debug sections in ELF files
通过例子来看看DWARF信息,首先编译一个C文件
1 | |
然后用objdump -h看下信息,可以看到有些是以.debug开头的信息
1 | |
每个部分的第一个数字是它的size,第二个是它在ELF的偏移,然后调试器就是通过这些信息从可执行文件中去读取section
Finding functions
调试时,我们要做的最基本的事情之一是在某个函数上放置断点,期望调试器在其入口处就中断。为了能够执行此功能,调试器必须在高级代码中的函数名和该函数的指令开始的机器代码中的地址之间具有某种映射。
可以通过DWARF的.debug_info部分中获取信息,DWARF中的基本描述实体称为Debugging Information Entry(DIE)。通过objdump –dwarf=info查看,只关注下面两个信息
1 | |
这两个DIE一个是do_stuff函数,一个是main函数的,DW_AT_low_pc这个信息包含了函数的入口地址,可以通过objdump -d看一下是不是
1 | |
确实,0x8048604是do_stuff的开始,因此调试器可以在函数和它们在可执行文件中的位置之间建立映射。
Finding variables
同样的,通过objdump –dwarf=info查看信息
1 | |
注意到第一个尖括号里面的数字,那代表嵌套等级,数字越大等级越高,所以my_local是do_stuff的孩子,DW_AT_type代表了变量类型,要在执行进程的内存映像中实际定位变量,调试器将查看DW_at_location属性,my_local的DW_AT_location里的DW_OP_fbreg: -20代表偏移DW_AT_frame_base的-20的位置。
do_stuff的DW_AT_frame_base属性的值为0x0(location list),这意味着实际上必须在location list部分查找该值。让我们来看看: objdump –dwarf=loc (我自己的机器上没有loc的信息,很奇怪)
1 | |
breg4代表esp,breg5代表ebp
Looking up line numbers
dwarf还保存了C文件的行号,可以通过objdump –dwarf=decodedline查看
1 | |