为什么调试器需要符号来重建堆栈?

时间:2010-09-15 13:21:47

标签: c++ visual-c++ debugging callstack

在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行,我设置了一个断点,所有其他行都加载了符号。)

现在,我知道如何修复警告( - >获取pdb文件),我完全不知道为什么它会显示出来!我上面粘贴的堆栈完全没问题,只是我没有dynlib2.dll模块的pdb文件。

为什么调试器需要一个符号文件来确保堆栈是否正确?

3 个答案:

答案 0 :(得分:4)

我认为这是因为并非所有功能都遵循“标准”堆栈布局。通常每个函数都以:

开头
push        ebp  
mov         ebp,esp 

结束
pop         ebp  
ret

通过这个,每个函数都会创建所谓的堆栈帧。 EBP始终指向顶部堆栈帧的开头。在每个帧中,前两个值是指向前一个堆栈帧的指针,以及函数返回地址。

使用此信息可以轻松地重建堆栈。但是:

  1. 此堆栈信息不包含函数名称和参数信息。
  2. 并非所有函数都遵循此堆栈框架布局。如果启用了某些优化(例如,/ Oy,省略堆栈帧指针) - 堆栈布局不同。

答案 1 :(得分:1)

I tried to understand this myself a while ago.

截至2013年,FPO未在MSFT中使用,并且通常不赞成。我确实遇到过内部使用的不同MS二进制技术,这可能会妨碍天真的EBP链遍历:Basic Block Tools

如帖子中所述,PDB确实包含'StackFrameTypeEnum',而其他地方则暗示它们包含堆栈帧的'展开程序'。总而言之,它们仍然是必需的,而且为什么确切的血腥细节 - 没有记录。

答案 2 :(得分:0)

符号与相关的二进制代码分离,以减少发送二进制文件的大小。检查PDB文件有多大 - 特别是与匹配的二进制文件(EXE / DLL)相比。每次运送,安装和使用二进制文件时,您都不希望出现这种开销。这在加载时尤为重要。符号信息毕竟只用于调试,不是正确操作代码所必需的。如果您保留符合已发送二进制文件的符号,则仍可以在加载所有符号的情况下调试问题。