为啥调试器需要符号来重建堆栈?
Posted
技术标签:
【中文标题】为啥调试器需要符号来重建堆栈?【英文标题】:Why does the debugger need symbols to reconstruct the stack?为什么调试器需要符号来重建堆栈? 【发布时间】:2010-09-15 13:21:46 【问题描述】:在 Visual Studio 中调试时,如果缺少调用堆栈的符号,例如:
00 > HelloWorld.exe!my_function(int y=42) Line 291
01 dynlib2.dll!10011435()
[Frames below may be incorrect and/or missing, no symbols loaded for dynlib2.dll]
02 dynlib2.dll!10011497()
03 HelloWorld.exe!wmain(int __formal=1, int __formal=1) Line 297 + 0xd bytes
04 HelloWorld.exe!__tmainCRTStartup() Line 594 + 0x19 bytes
05 HelloWorld.exe!wmainCRTStartup() Line 414
06 kernel32.dll!_BaseProcessStart@4() + 0x23 bytes
调试器将显示警告Frames below may be incorrect and/or missing
。
(请注意,只有第 01 行和第 02 行没有符号。第 00 行,我设置了一个断点,所有其他行都加载了符号。)
现在,我知道如何修复警告(->get pdb 文件),但我不太明白为什么会显示它!我上面贴的栈完全没问题,只是我没有dynlib2.dll模块的pdb文件。
为什么调试器需要符号文件来确保堆栈正确?
【问题讨论】:
【参考方案1】:我认为这是因为并非所有函数都遵循“标准”堆栈布局。通常每个函数都以:
push ebp
mov ebp,esp
以
结尾pop ebp
ret
由此,每个函数都会创建其所谓的堆栈帧。 EBP
始终指向顶部堆栈帧的开头。在每一帧中,前两个值是指向前一个堆栈帧的指针,以及函数返回地址。
使用此信息可以轻松地重构堆栈。然而:
-
此堆栈信息不包括函数名称和参数信息。
并非所有函数都遵循此堆栈框架布局。如果启用了某些优化(例如 /Oy,省略堆栈帧指针) - 堆栈布局会有所不同。
【讨论】:
【参考方案2】:I tried to understand this myself a while ago.
截至 2013 年,FPO 不在 MSFT 中使用,而且通常不受欢迎。我确实遇到过一种内部使用的不同 MS 二进制技术,这可能会阻碍简单的 EBP 链遍历:Basic Block Tools。
如帖子中所述,PDB 确实包含“StackFrameTypeEnum”,并且在其他地方暗示它们包含堆栈帧的“展开程序”。所以总而言之,它们仍然是需要的,而且关于为什么 - 的血腥细节没有记录在案。
【讨论】:
FPO 代表帧点省略吗?谢谢。 @Sabuncu 是:docs.microsoft.com/en-us/cpp/build/reference/…【参考方案3】:符号与相关的二进制代码分离,以减少传送二进制文件的大小。检查您的 PDB 文件有多大——特别是与匹配的二进制文件 (EXE/DLL) 相比。每次交付、安装和使用二进制文件时,您都不希望有这种开销。这在加载时尤其重要。符号信息毕竟只是用于调试,而不是正确运行代码所必需的。只要您保留与您交付的二进制文件匹配的符号,您仍然可以在加载所有符号后调试问题。
【讨论】:
这个答案与这个问题到底有什么关系? (除了大致匹配相同的标签?) 我的意思是,如果没有符号文件,您将不会在重构的调用堆栈中看到完整的符号调试信息,因为二进制文件中不包含符号调试信息。我的猜测是@valdo 暗示符号文件中有信息可以始终生成完整的调用堆栈 - 我相信情况并非如此,但很高兴知道我错了。以上是关于为啥调试器需要符号来重建堆栈?的主要内容,如果未能解决你的问题,请参考以下文章