选择要删除的公共符号
PDBCopy 提供 -f 和 -F 选项,以便可以从剥离的符号文件中删除任意一组公共符号,只保留访问群体为了执行调试而需要访问的那些符号。
PDBCopy 的常见用途是创建特殊版本的符号文件,供 Microsoft 在其联机崩溃分析 (OCA) 程序中使用。 OCA 可以将某些函数指定为惰性,这意味着如果在堆栈跟踪上找到该函数,则忽略该函数。 如果一个函数只是一个包装器或不执行重要计算的“传递”函数,那么它通常会被声明为惰性函数。 如果在故障分析中在堆栈上发现了这样的函数,则可以假设该函数本身没有故障,并且它最多传递了从堆栈早期例程接收的无效或损坏的数据。 通过忽略此类函数,OCA 可以更好地确定错误或损坏的实际原因。
当然,任何想要声明为“惰性”的函数都需要包含在 OCA 使用的符号文件的公共符号表中。 然而,正如下面的示例所示,这些并不是唯一需要包含的函数。
假设你编写 Windows 驱动程序,并使用 PDBCopy 从其符号文件中删除所有公共符号,但 FunctionOne 和 FunctionSix 这两个惰性函数除外。 你的期望是,如果在崩溃后在堆栈上找到 FunctionOne 或 FunctionSix,则 OCA 将忽略它们。 如果驱动程序的任何其他部分位于堆栈上,Microsoft 将提供相应的内存地址,并且你可以使用该地址调试驱动程序。
然而,让我们假设你的驱动程序占用内存如下布局:
Address | 内存内容 |
---|---|
0x1000 |
模块的基址 |
0x2000 |
FunctionOne 的开头 |
0x203F |
FunctionOne 的结尾 |
0x3000 |
FunctionSix 的开头 |
0x305F |
FunctionSix 的结尾 |
0x7FFF |
内存中模块的末尾 |
如果调试器在堆栈上找到地址,则选择具有下一个较低地址的符号。 由于公共符号表包含每个符号的地址,但没有大小信息,因此调试器无法知道地址是否实际位于任何特定符号的边界内。
因此,如果在地址 0x2031 发生错误,Microsoft OCA 运行的调试器将正确地识别故障位于 FunctionOne 内。 由于这是一个惰性函数,调试器将继续遍查堆栈以找出崩溃的原因。
但是,如果在 0x2052 处发生故障,调试器仍然将此地址与 FunctionOne 匹配,即使它超出了此函数的实际末尾 (0x203F)。
因此,必须在剥离的符号文件中不仅包括要公开的函数,还包括紧跟在这些函数之后的符号。 在本例中,你想要公开 FunctionOne、FunctionTwo、FunctionSix 和 FunctionSeven:
Address | 内存内容 |
---|---|
0x1000 |
模块的基址 |
0x2000 |
FunctionOne 的开头 |
0x203F |
FunctionOne 的结尾 |
0x2040 |
FunctionTwo 的开头 |
0x3000 |
FunctionSix 的开头 |
0x305F |
FunctionSix 的结尾 |
0x3060 |
FunctionSeven 的开头 |
0x7FFF |
内存中模块的末尾 |
如果将这四个函数全部包含在剥离的符号文件中,则 Microsoft OCA 分析不会错误地将地址 0x2052 视为 FunctionOne 的一部分。 在本例中,假设此地址是 FunctionTwo 的一部分,但这并不重要,因为你尚未向 OCA 注册 FunctionTwo 作为惰性函数。 重要的是,地址 0x2052 被识别为不属于惰性函数,因此 OCA 会将其识别为驱动程序中的一个有意义的故障,并可以通知你该故障。
如果不希望公布每个惰性函数后面的函数的名称,可以在每个惰性函数之后的代码中插入不重要的函数,以便将这些函数的名称包含在公共符号文件中。 请确保验证这些添加的函数是否确实遵循二进制文件地址空间中的惰性函数,因为一些优化例程可能会改变这一点,甚至完全删除一些函数。