符号级调试基础
符号级调试,依赖于编译器、链接器生成的调试信息,调试信息如何生成是由不同的调试信息标准确定的,如DWARF调试信息标准。DWARF现在被广泛使用,如go编译工具链也是采用DWARF指导生成调试信息,调试器gdb、delve等都支持基于DWARF调试信息进行调试。
下面我们提到调试信息,如不特殊强调,均指DWARF调试信息。
根据描述对象的不同,调试信息又可细分为多种类型:
- 描述数据类型;
- 描述变量;
- 描述函数定义;
- 或者描述符号表;
- 描述行号表;
- 描述调用栈信息表;
- 其他。
此外,不同的编程语言也会有自己的取舍,一方面要生成必要的sections来兼容现有二进制的工具,另外,也会生成一些额外的sections来支持其他语言特性。
比如go语言编译器、链接器会生成DWARF调试信息(.[z]debug_* sections)供调试器使用,也会生成.symtab供readelf等通用的二进制分析工具使用。另外,它还额外生成.gosymtab、.gopclntab用于go runtime来跟踪调用栈信息,生成.note.go.buildid来保留go buildid信息。
不管是.symtab中的符号信息,还是.[z]debug_*调试符号信息,还是.note*自定义vendor信息,都使用了“符号”这个术语,明确它们的不同是很重要的:
- .symtab是给一些通用的二进制分析工具使用的;
- .(z)debug_*符号信息是给调试器使用的;
- .note*给语言等vendor留作扩展使用的。
这些不同的sections,其设计目标、要解决的问题是不同的,读者不要混淆。
接下来先介绍一些符号级调试必须掌握的基础知识,我们将在第9章进入符号级调试开发。