验证符号

符号问题可能以多种方式出现。 也许堆栈跟踪显示不正确的信息,或者无法识别堆栈中函数的名称。 或者,调试器命令可能无法理解模块、函数、变量、结构或数据类型的名称。

如果怀疑调试器未正确加载符号,可以执行几个步骤来调查此问题。

首先,使用 lm (列出加载的模块) 命令显示包含符号信息的已加载模块的列表。 此命令最有用的形式如下:

0:000> lml 

如果使用 WinDbg, 则调试 |“模块 ”菜单命令也允许你查看此信息。

请特别注意在这些显示器中看到的任何备注或缩写。 有关这些内容的解释,请参阅 符号状态缩写

如果没有看到正确的符号文件,首先要做的是检查符号路径:

0:000> .sympath
Current Symbol Path is: d:\MyInstallation\i386\symbols\retail

如果符号路径错误,请修复它。 如果使用内核调试器,请确保本地 %WINDIR% 不在符号路径中。

然后使用 .reload (重新加载模块) 命令重新加载 符号:

0:000> .reload ModuleName 

如果符号路径正确,则应激活 干扰模式 ,以便可以查看 dbghelp 正在加载的符号文件。 然后重新加载模块。 有关如何激活干扰模式的信息,请参阅 设置符号选项

下面是 Microsoft Windows 符号的“干扰”重载的示例:

kd> !sym noisy
kd> .reload nt
 1: Kernel Version 2081 MP Checked
 2: Kernel base = 0x80400000 PsLoadedModuleList = 0x80506fa0
 3: DBGHELP: FindExecutableImageEx-> Looking for D:\MyInstallation\i386\ntkrnlmp.exe...mismatched timestamp
 4: DBGHELP: No image file available for ntkrnlmp.exe
 5: DBGHELP: FindDebugInfoFileEx-> Looking for
 6: d:\MyInstallation\i386\symbols\retail\symbols\exe\ntkrnlmp.dbg... no file
 7: DBGHELP: FindDebugInfoFileEx-> Looking for
 8: d:\MyInstallation\i386\symbols\retail\symbols\exe\ntkrnlmp.pdb... no file
 9: DBGHELP: FindDebugInfoFileEx-> Looking for d:\MyInstallation\i386\symbols\retail\exe\ntkrnlmp.dbg... OK
10: DBGHELP: LocatePDB-> Looking for d:\MyInstallation\i386\symbols\retail\exe\ntkrnlmp.pdb... OK
11: *** WARNING: symbols checksum and timestamp is wrong 0x0036a4ea 0x00361a83 for ntkrnlmp.exe

符号处理程序首先查找与它尝试加载 (第三行和第四行) 模块匹配的图像。 图像本身并非始终是必需的,但如果存在不正确的图像,符号处理程序通常会失败。 这些行显示调试器在 D:\MyInstallation\i386\ntkrnlmp.exe找到图像,但时间戳不匹配。 由于时间戳不匹配,因此搜索将继续。 接下来,调试器查找与加载的映像匹配的 .dbg 文件和 .pdb 文件。 这些位于第 6 行到第 10 行。 第 11 行指示即使加载了符号,图像的时间戳也与 (即符号错误) 不匹配。

如果符号搜索遇到灾难性故障,则会看到以下格式的消息:

ImgHlpFindDebugInfo(00000000, module.dll, c:\MyDir;c:\SomeDir, 0823345, 0) failed

这可能是由文件系统故障、网络错误和 .dbg 文件损坏等项引起的。

诊断符号加载错误

在干扰模式下,调试器在无法加载符号文件时可能会输出错误代码。 .dbg 文件的错误代码列在 winerror.h 中。 .pdb 错误代码来自另一个来源,最常见的错误以纯英文文本打印。

winerror.h 中 .dbg 文件的一些常见错误代码如下:

0xB
ERROR_BAD_FORMAT

0x3
ERROR_PATH_NOT_FOUND

0x35
ERROR_BAD_NETPATH

由于网络错误,符号文件可能无法加载。 如果看到ERROR_BAD_FORMAT或ERROR_BAD_NETPATH并且正在从网络上的另一台计算机加载符号,请尝试将符号文件复制到主计算机,并将其路径放入符号路径中。 然后尝试重新加载符号。

验证搜索路径和符号

让“c:\MyDir;c:\SomeDir“表示符号路径。 应在何处查找调试信息?

如果二进制文件已去除调试信息(例如 Windows 的免费版本),请先在以下位置查找 .dbg 文件:

c:\MyDir\symbols\exe\ntoskrnl.dbg
c:\SomeDir\symbols\exe\ntoskrnl.dbg
c:\MyDir\exe\ntoskrnl.dbg
c:\SomeDir\exe\ntoskrnl.dbg
c:\MyDir\ntoskrnl.dbg
c:\SomeDir\ntoskrnl.dbg
current-working-directory\ntoskrnl.dbg

接下来,在以下位置查找 .pdb 文件:

c:\MyDir\symbols\exe\ntoskrnl.pdb
c:\MyDir\exe\ntoskrnl.pdb
c:\MyDir\ntoskrnl.pdb
c:\SomeDir\symbols\exe\ntoskrnl.pdb
c:\SomeDir\exe\ntoskrnl.pdb
c:\SomeDir\ntoskrnl.pdb
current-working-directory\ntoskrnl.pdb

请注意,在搜索 .dbg 文件时,调试器会相互搜索 MyDir 和 SomeDir 目录,但在 .pdb 搜索中,它不会。

Windows XP 和更高版本的 Windows 不使用任何 .dbg 符号文件。 有关详细信息 ,请参阅符号和符号文件

不匹配的内部版本

在经常更新的计算机上调试故障时,最常见的问题之一是不同版本的符号不匹配。 此问题的三个常见原因是:指向错误生成的符号、使用没有相应符号的私有生成的二进制文件,以及使用多处理器计算机上的单处理器硬件抽象级别 (HAL) 和内核符号。 前两个只是匹配二进制文件和符号的问题:可以通过将 hal*.dbg 和 ntkrnlmp.dbg 重命名为 hal.dbg 和 ntoskrnl.dbg 来更正第三个。

若要了解目标计算机上安装的 Windows 版本,请使用 vertarget (显示目标计算机版本) 命令:

kd> vertarget 
Windows XP Kernel Version 2505 UP Free x86 compatible
Built by: 2505.main.010626-1514
Kernel base = 0x804d0000 PsLoadedModuleList = 0x80548748
Debug session time: Mon Jul 02 14:41:11 2001
System Uptime: 0 days 0:04:53 

测试符号

测试符号更加困难。 它涉及到在调试器上验证堆栈跟踪,并查看调试输出是否正确。 下面是一个可尝试的示例:

kd> u videoprt!videoportfindadapter2
Loading symbols for 0xf2860000     videoprt.sys ->   videoprt.sys

VIDEOPRT!VideoPortFindAdapter2:
f2856f42 55               push    ebp
f2856f43 8bec             mov     ebp,esp
f2856f45 81ecb8010000     sub     esp,0x1b8
f2856f4b 8b4518           mov     eax,[ebp+0x18]
f2856f4e 53               push    ebx
f2856f4f 8365f400         and     dword ptr [ebp-0xc],0x
f2856f53 8065ff00         and     byte ptr [ebp-0x1],0x0
f2856f57 56               push    esi

u 命令在 videoprt.sys 中取消组合 videoportfindadapter 字符串。 符号在调试器上是正确的,因为常见的堆栈命令(如 pushmov )显示在堆栈上。 大多数函数首先使用基本指针 (ebp) 或堆栈指针 (esp) 进行添加、子或推送操作。

当符号无法正常工作时,这通常是显而易见的。 Glintmp.sys 在此示例中没有符号,因为 Glintmp 旁边未列出函数:

kd> kb
Loading symbols for 0xf28d0000     videoprt.sys ->   videoprt.sys
Loading symbols for 0xf9cdd000      glintmp.sys ->   glintmp.sys
*** ERROR: Symbols could not be loaded for glintmp.sys
ChildEBP RetAddr  Args to Child
f29bf1b0 8045b5fa 00000001 0000a100 00000030 ntoskrnl!RtlpBreakWithStatusInstruction
f29bf1b0 8044904e 00000001 0000a100 00000030 ntoskrnl!KeUpdateSystemTime+0x13e
f29bf234 f28d1955 f9b7d000 ffafb2dc f9b7d000 ntoskrnl!READ_REGISTER_ULONG+0x6
f29bf248 f9cde411 f9b7d000 f29bf2b0 f9ba0060 VIDEOPRT!VideoPortReadRegisterUlong+0x27
00000002 00000000 00000000 00000000 00000000 glintMP+0x1411 [No function listed.] 

为此堆栈跟踪加载了错误的生成符号。 请注意,前两个调用没有列出函数。 此堆栈跟踪类似于 win32k.sys 绘制矩形的问题:

1: kd> 
1: kd> kb                      [Local        9:50 AM]
Loading symbols for 0xf22b0000       agpcpq.sys ->   agpcpq.sys
*** WARNING: symbols checksum is wrong 0x0000735a 0x00000000 for agpcpq.sys
*** ERROR: Symbols could not be loaded for agpcpq.sys
Loading symbols for 0xa0000000       win32k.sys ->   win32k.sys
*** WARNING: symbols checksum is wrong 0x00191a41 0x001995a9 for win32k.sys
ChildEBP RetAddr  Args to Child
be682b18 f22b372b 82707128 f21c1ffc 826a70f8 agpCPQ+0x125b [No function listed.]
be682b4c a0140dd4 826a72f0 e11410a8 a0139605 agpCPQ+0x372b [No function listed.]
be682b80 a00f5646 e1145100 e1cee560 e1cee560 win32k!vPatCpyRect1_6x6+0x20b
00000001 00000000 00000000 00000000 00000000 win32k!RemoteRedrawRectangle+0x32 

下面是正确的堆栈跟踪。 问题在于 AGP440.sys。 堆栈跟踪上出现的第一个项通常出错。 请注意,win32k.sys 矩形错误消失:

1: kd> kb                      [Local        9:49 AM]
ChildEBP RetAddr  Args to Child
be682b18 f22b372b 82707128 f21c1ffc 826a70f8 agpCPQ!AgpReleaseMemory+0x88
be682b30 f20a385c 82703638 e183ec68 00000000 agpCPQ!AgpInterfaceReleaseMemory+0x8b
be682b4c a0140dd4 826a72f0 e11410a8 a0139605 VIDEOPRT!AgpReleasePhysical+0x44
be682b58 a0139605 e1cee560 e11410a8 a00e5f0a win32k!OsAGPFree+0x14
be682b64 a00e5f0a e1cee560 e11410a8 e1cee560 win32k!AGPFree+0xd
be682b80 a00f5646 e1145100 e1cee560 e1cee560 win32k!HeapVidMemFini+0x49
be682b9c a00f5c20 e1cee008 e1cee008 be682c0c win32k!vDdDisableDriver+0x3a
be682bac a00da510 e1cee008 00000000 be682c0c win32k!vDdDisableDirectDraw+0x2d
be682bc4 a00da787 00000000 e1843df8 e1843de8 win32k!PDEVOBJ__vDisableSurface+0x27
be682bec a00d59fb 00000000 e1843de8 00000000 win32k!PDEVOBJ__vUnreferencePdev+0x204
be682c04 a00d7421 e1cee008 82566a98 00000001 win32k!DrvDestroyMDEV+0x30
be682ce0 a00a9e7f e1843e10 e184a008 00000000 win32k!DrvChangeDisplaySettings+0x8b3
be682d20 a008b543 00000000 00000000 00000000 win32k!xxxUserChangeDisplaySettings+0x106
be682d48 8045d119 00000000 00000000 00000000 win32k!NtUserChangeDisplaySettings+0x48
be682d48 77e63660 00000000 00000000 00000000 ntkrnlmp!KiSystemService+0xc9 

有用的命令和扩展

以下命令和扩展可用于跟踪符号问题:

lm (列出加载的模块)
列出所有模块,并提供这些模块中所有符号的加载状态。

!dh image-header-base
显示从 image-header-base 开始的已加载图像的标头信息。

.reload /n
重新加载所有内核符号。

.reload [image-name]
(CDB 或 WinDbg 仅) 重新加载映像 映像名称的符号。 如果未指定 图像名称 ,则为所有图像重新加载符号。 (更改符号路径后,必须重新加载符号。)

!sym noisy
为符号加载启用详细模式。 这可用于获取有关模块加载的信息。 有关详细信息 ,请参阅设置符号选项

.sympath [new-symbol-path]
设置新的符号路径,或显示当前符号路径。 有关详细信息 ,请参阅符号路径

如果内核符号正确,但未获得完整的堆栈,则以下命令可能也很有用:

X*!
这将列出当前加载了符号的模块。 如果内核符号正确,这很有用。

.reload /user
这会尝试重新加载所有用户模式符号。 如果在一个进程正在运行时加载了符号,并且稍后在另一个进程中发生了中断,则在执行内核调试时需要执行此操作。 在这种情况下,除非执行此命令,否则不会加载新进程中的用户模式符号。

X wdmaud!*start\*
这将仅列出 wdmaud 模块中名称包含“start”字符串的符号。 这样做的优点是强制重新加载 wdmaud 中的所有符号,但只显示其中带有“start”的符号。 (这意味着列表较短,但由于其中总是有一些带有“开始”的符号,因此会进行一些验证,证明加载已发生。)

验证符号的另一种有用技术是取消组合代码。 大多数函数都从使用基本指针 (ebp) 或堆栈指针 (espsp) 的添加、子或推送操作开始。 尝试取消组合 (U 函数) 堆栈上的某些函数 (偏移零) 来验证符号。

网络和端口问题

符号文件和连接到调试器时会出现问题。 遇到问题时,请记住以下几点:

  • 确定调试电缆在测试系统上连接到的 COM 端口。

  • 检查测试系统的 boot.ini 设置。 查找 /debug 开关,检查波特率和 COM 端口设置。

  • 如果通过网络访问符号文件,网络问题可能会干扰调试。

  • .dll 和 .sys 具有相同名称的文件 (例如 , mga64.sys 和 mga64.dll) 如果它们未分隔到符号树的正确目录中,则它们会使调试器混淆。

  • 内核调试器并不总是喜欢将生成符号文件替换为专用符号文件。 双检查符号路径,对行为不一的符号执行 .reloadFileName!dlls 命令有时很有用。

问题和误解

问: 我已成功加载符号,但堆栈似乎错了。 调试器是否损坏?

答: 不一定。 问题的最可能原因是符号不正确。 完成本部分中概述的步骤,以确定是否已加载有效符号。 请勿假设某些操作有效,因此您具有有效的符号。 例如,你很可能能够执行 dd nt!ntbuildnumberu nt!带有错误符号的 KeInitializeProcess 。 使用上述过程验证它们是否正确。

问: 调试器是否仍使用不正确的符号?

答: 是和否。 通常,你可以避开不完全匹配的符号。 例如,以前 Windows 版本中的符号通常在某些情况下起作用,但对于何时工作以及何时不起作用没有规则。

问: 我在内核调试器中停止,我想查看用户模式进程的符号。 我能做到这一点吗?

答: 主要。 此方案的支持很差,因为内核调试器没有保留足够的信息来跟踪每个进程的模块负载,但有一个合理的解决方法。 若要加载用户模式模块的符号,请执行 .reload -user 命令。 这将加载当前上下文的用户模式模块。

问: 以下消息是什么意思?

*** WARNING: symbols checksum and timestamp is wrong 0x0036d6bf 0x0036ab55 for ntkrnlmp.exe

答: 这意味着 ntkrnlmp.exe 的符号是错误的。