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