CodeView 符号记录¶
简介¶
本文档描述了 LLVM 理解的各种 CodeView 符号记录的用法和序列化格式。与CodeView 类型记录类似,我们只描述了现代 C++ 工具链生成的重要的类型。
记录类别¶
符号记录与类型记录具有一个主要的相似之处:它们以相同的记录前缀开头,我们不再赘述(请参考前面的链接了解说明)。因此,可以使用与处理类型记录基本相同的代码来处理一系列符号记录。符号记录和类型记录之间存在一些重要的区别
类型记录仅出现在TPI 和 IPI 流中。
虽然类型通过类型索引从其他 CodeView 记录中引用,但符号记录通过其出现在流中的记录的字节偏移量来引用。
类型可以引用类型(通过类型索引),符号可以引用类型(通过类型索引)和符号(通过偏移量),但类型永远不能引用符号。
某些特殊的符号记录开始一个“作用域”。对于这些记录,所有后续记录直到下一个
S_END
记录都是此符号记录的“子记录”。例如,给定一个描述某个函数的符号记录,该函数的所有局部变量都将出现在该函数之后,直到相应的S_END
记录。
最后,符号记录有三大类,按其在 PDB 文件中允许出现的位置进行分组。公共符号(仅出现在publics 流中)、全局符号(仅出现在globals 流中)和模块符号(出现在模块信息流中)。
公共符号¶
公共符号是 CodeView 等同于 DWARF .debug_pubnames
。程序中每个具有修饰名称的函数或变量都有一个公共符号记录。Publics 流包含这些记录,此外还包含一个哈希表,允许人们通过修饰名称快速定位记录。
S_PUB32 (0x110e)¶
只有一种类型的公共符号,即S_PUB32
,它描述了一个修饰名称、一个指示其是什么类型的符号的标志(例如函数、变量)以及符号的地址。节映射子流在DBI 流中可以查询以确定此地址对应于哪个模块,然后可以查询该模块的模块调试流以找到具有给定地址的符号的完整信息。
全局符号¶
虽然程序中每个具有外部链接的符号都有一个公共符号,但程序中每个具有链接的符号(包括内部链接)都有一个全局符号。因此,全局符号不描述修饰名称或地址,因为具有内部链接的符号根本不需要任何修饰,并且也可能没有地址。因此,所有全局符号都只是通过模块/偏移量组合直接引用完整符号记录。
与公共符号类似,所有全局符号都包含在一个Globals 流中,该流包含一个哈希表,将完全限定名称映射到 globals 流中的相应记录(如前所述,然后包含允许人们在相应的模块符号流中找到完整记录的信息)。
请注意,此设计的必然结果和限制是,除了编译器决定发出的完全匹配的文本完全限定名称之外,以任何其他方式进行程序范围内的查找都是不切实际的。这与 DWARF 不同,在 DWARF 中,即使我们不一定在给定作用域内按基本名称进行 O(1) 查找(包括 O(1) 作用域),但至少在给定作用域内有 O(n) 访问。
重要
除了完全匹配的文本完全限定名称之外,无法通过任何其他方式进行程序范围内的名称查找。