我在Windows 10主机上安装了一台虚拟Windows 7 x64计算机,并使用windbg 10.0.10586.567
对内核进行调试。我正在运行我自己的应用程序,我有完整的源代码和私有符号。每当我闯入并询问应用程序线程的堆栈跟踪时,当我的应用程序的某个二进制文件被击中时,回溯始终会停止。"
所以,例如,如果我闯入,切换到进程,并请求带有!thread [thread address] 1f
的堆栈跟踪,我得到类似的东西(注意"早期"最后的零返回地址线):
fffff880`0534e870 fffff800`026d6992 nt!KiSwapContext+0x7a
fffff880`0534e9b0 fffff800`026d81a2 nt!KiCommitThreadWait+0x1d2
fffff880`0534ea40 fffff800`029c7a2e nt!KeDelayExecutionThread+0x186
fffff880`0534eab0 fffff800`026d08d3 nt!NtDelayExecution+0x59
fffff880`0534eae0 00000000`76e7165a nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ fffff880`0534eae0)
00000000`00276708 000007fe`fcf91203 ntdll!NtDelayExecution+0xa
00000000`00276710 00000001`410e7dd9 KERNELBASE!SleepEx+0xab
00000000`002767b0 00000000`00000000 MyApp!MainMessageLoop+0x4b1 [d:\whatever\path\myapplication.cpp @ 3024]
这看起来非常类似于在调试x64进程的用户模式转储(缺少展开数据)时缺少二进制文件,除非在这种情况下堆栈跟踪通常不会停止"这突然& #34;相反,它在那一点上误入歧途,并显示出虚假的价值。
我尝试了一些额外的信息/事情:
.exepath
),在主机上包含匹配的二进制文件(我已经完全确定了这一点;将二进制文件直接从guest虚拟机复制到主机)0000000000274b40 0000000000000000 MyAppDLL!SomeExportedFunction+0x32 [d:\whatever\path\myapplicationDLL.cpp @ 232]
.process /i
,.process /r /p
,!process -1 7
,.reload
s,.reload /user
s ,.reload /f MyApp.exe
,!thread [address] 1f
等)没有成功6.11.0001.404
),结果相同 !sym noisy
输出(省略不相关的行):
0: kd>.process /i [address]
0: kd>g
0: kd>.reload /user
0: kd> !process -1 2
0: kd> !thread [address] 1f
[...]
DBGHELP: d:\symbolcache\MyApp.pdb\76931C5A6C284779AD2F916CA324617E1\MyApp.pdb already cached
DBGHELP: MyApp - private symbols & lines
[...]
lmvm MyApp
输出:
[...]
Loaded symbol image file: MyApp.exe
Image path: C:\MyApp\MyApp.exe
[...]
有什么想法吗?
答案 0 :(得分:3)
我偶然发现了一个解决了这个问题的链接器开关:/DEBUGTYPE
带有PDATA
参数。如果您将二进制文件与此开关链接,则展开信息将被复制到您的PDB中。
我使用/DEBUGTYPE:CV,PDATA
重新编译/重新链接了相关应用程序(如果指定/DEBUGTYPE:CV
,/DEBUG
是默认设置,请参阅documentation),现在一切都像魅力一样,我总是得到完整的调用栈。
这有一个奇怪的方面:windbg愉快地使用在PDB中找到的展开数据,但忽略了映射二进制文件中的相同数据(两者都在主机上)。
答案 1 :(得分:2)
这不是问题的完美解决方案(或任何解决方案,有人可能会说),但我提供了这个临时答案的解决方法。
您应该能够获得所需的信息,尽管使用dps @rsp L10
之类的格式不是很好。
在x86-64中,您没有x86 ebp链的并行,但返回地址仍在堆栈中。这些将为您提供堆栈中的函数,它们之间的值将是传递给函数的参数(以及堆栈中保存的寄存器等)。一个random example from Google(因为我现在不在我的Windows机器上):
0:017> dps @rsp
00000000`1bb0fbb8 00000000`00000020
00000000`1bb0fbc0 00000000`00000000
00000000`1bb0fbc8 00000000`008bc6c6 Dolphin!ReadDataFromFifoOnCPU+0xb6 [d:\sources\comex\source\core\videocommon\fifo.cpp @ 245]
00000000`1bb0fbd0 00000000`1ba0ffeb
00000000`1bb0fbd8 00000000`00000020
00000000`1bb0fbe0 00000000`00000020
00000000`1bb0fbe8 00000000`00000800
00000000`1bb0fbf0 00000000`1ba0ffeb
00000000`1bb0fbf8 00000000`008c2ff5 Dolphin!InterpretDisplayListPreprocess+0x45 [d:\sources\comex\source\core\videocommon\opcodedecoding.cpp @ 87]
00000000`1bb0fc00 00000000`00000000
00000000`1bb0fc08 00000000`008bc041 Dolphin!RunGpu+0x81 [d:\sources\comex\source\core\videocommon\fifo.cpp @ 389]
00000000`1bb0fc10 00000000`8064cbc0
00000000`1bb0fc18 00000000`1bb0fcc0
00000000`1bb0fc20 00000000`00000000
00000000`1bb0fc28 00000000`008c2dda Dolphin!OpcodeDecoder_Preprocess+0x14a [d:\sources\comex\source\core\videocommon\opcodedecoding.cpp @ 326]
00000000`1bb0fc30 00000000`8064cbe0
鉴于您有符号,返回地址很容易区分。
答案 2 :(得分:2)
展开数据是为用户模式模块延迟加载的,因此除非有人需要,否则不会映射它。不幸的是,内核调试器并不强制为用户图像提供信息,因此有时您会遇到此行为。您可以通过转储PE头(!dh)并检查异常目录的状态(!pte imagename + offset)来查看数据是否已映射。
鉴于您拥有该应用程序,请尝试通过在应用程序中的某处执行堆栈遍历NOP来强制信息驻留:
PVOID stack[2];
(VOID)CaptureStackBackTrace(0, 2, (PVOID*)&stack, NULL);
这并不能保证整个目录都存在,但通常足够好。