DWARF数据结构

DWARF (Debugging With Attributed Record Formats) 使用一系列数据结构来存储调试信息,这些信息允许调试器提供源代码级别的调试体验。核心概念是 调试信息条目 (DIE, Debugging Information Entry),以及支持这些条目的关键表结构。

调试信息条目 (DIE)

DWARF 使用 调试信息条目 (DIE) 来表示程序中的各种构造,例如变量、常量、类型、函数、编译单元等。每个 DIE 包含以下关键元素:

  • Tag: 一个标识符(例如 DW_TAG_variableDW_TAG_pointer_typeDW_TAG_subprogram),指示DIE代表的程序构造的类型。 这些tag定义了DIE的语义。

  • Attributes: 键值对,提供关于DIE的额外信息。 例如,一个变量的DIE可能会有 name(变量名), type (变量类型), location (变量在内存中的位置) 等属性。

  • Children: DIE可以包含其他DIE作为其子节点。 这些子节点构成了层级结构,用于描述复杂的程序构造。 例如,一个函数的DIE可能会包含其参数的DIE,以及其局部变量的DIE。

DIE之间的引用通过属性实现。例如,一个描述变量的DIE会包含一个DW_AT_type 属性,该属性指向描述变量数据类型的另一个DIE。这种层级关系允许DWARF描述复杂的类型和作用域结构。

重要的表结构

为了支持源代码级别的调试,符号级调试器需要两张重要的表:行号表 (Line Number Table) 和调用栈信息表 (Call Frame Information)。

  1. 行号表 (Line Number Table): 建立了程序代码指令地址和源文件位置(file:line:col)之间的映射关系,它通常包含源文件名称、行号、列号、以及对应的指令地址。通过这里的映射表,允许调试器调试期间将当前执行到的位置(PC)转换为源代码中的位置进行显示;调试器参照此表可以将源码位置转换为内存指令地址,并在指令地址处添加断点,使我们可以用源文件位置添加断点。|

    行号表中记录了如下细节信息,使我们可以做更多事情:

    • 对一个函数,指示函数序言 (prologue) 和函数结尾 (epilogue) 的指令,可以据此绘制函数的callgraph。
    • 对一行源码,可能包含一个或多个表达式、语句,对应多条指令,它能指示第一条指令的位置,以在准确位置添加断点。
  2. 调用栈信息表 (Call Frame Information): 允许调试器根据指令地址确定其在调用栈上的栈帧。这对于跟踪函数调用和理解程序的执行流程至关重要。它记录了执行时指令地址PC,与当前的 "栈指针SP" 和 "帧指针FP" 的值,以及返回地址。

为了减小上述表的存储占用,DWARF 使用状态机和字节码指令来编码这些表。这些指令指示状态机如何处理行号信息和栈帧信息,从而避免了冗余数据的存储。调试器加载这些编码后的数据,并将其交给状态机执行,状态机的输出结果就是调试器所需要的表。这种编码方式显著减少了调试信息的大小,使得DWARF能够在各种平台上使用。

后续章节将更详细地描述行号表和调用栈信息表的结构和编码方式。

results matching ""

    No results matching ""