DIE数据编码
DWARF中的所有调试信息条目(DIE, Debugging Information Entry),它们可以用来描述程序中的数据、类型、代码,在之前内容中我们已经见识过了描述不同类型程序构造的DIE Tags、Attributes。DIE之间还存在两种可能的链接或者引用关系:1)Children关系:表示DIE与其子DIE之间的父子关系;2)Siblings关系:表示同级DIE之间的兄弟关系。这种结构使得DWARF能够完整地描述程序的调试信息,如果要不加优化完整地存储这样的结构,也会面临数据冗余和存储空间问题。
所以本节我们来看看DIE数据的编码和存储方式。
数据压缩的必要性
DWARF调试信息通常包含大量重复和冗余数据,例如:
- 相同类型的变量可能有相同的属性列表
- 相似的函数可能有相似的结构信息
- 大量的类型信息可能被多次引用
为了减少存储空间占用,DWARF提供了几种数据压缩的措施。
措施1:树前序遍历扁平化
原理:
- 采用前序遍历的方式访问DIE树
- 按照访问顺序将DIE依次存储
- 不再显式存储DIE之间的链接关系
实现方式:
- 使用特殊属性来维护DIE之间的关系
DW_AT_sibling
:指向下一个兄弟DIEDW_AT_type
:指向类型DIE- 其他属性:根据需要维护其他关系
优势:
- 消除了显式的链接指针
- 简化了数据存储结构
- 便于顺序访问和解析
措施2:缩写表机制(Abbreviation Table)
原理:
- 将DIE的tag值和attributes类型存储在缩写表中
- DIE中只存储缩写表的索引和属性值
- 通过复用相同的tag和属性列表来减少存储空间
缩写表结构: 每个缩写包含:
- tag值:DIE的类型
- has_children标志:指示该DIE是否有子DIE
- 属性列表:包含属性类型和值类型
示例: 假设有多个变量DIE,它们具有相同的tag(DW_TAG_variable)和属性列表(DW_AT_name, DW_AT_type),但属性值不同:
- 在缩写表中存储一次tag和属性列表
- 每个DIE只存储缩写表索引和具体的属性值
- 大大减少了重复数据的存储
图 9 缩写表示例:
措施3:跨编译单元引用
DWARF v3引入了一种允许跨编译单元引用DWARF数据的机制:
- 允许一个编译单元引用另一个编译单元中的DIE
- 通过特殊的引用属性实现
- 这种方式不常用,但在某些场景下可以进一步减少数据冗余
总结
DWARF的数据编码和压缩策略主要针对DIE树结构,1)扁平化存储减少链接开销 2)缩写表机制减少重复数据 3)跨编译单元引用提供额外的优化空间。通过这些策略的共同作用,显著减少了调试信息在二进制文件中的存储空间占用,同时保持了调试信息的完整性和可访问性。