VS2005 C ++破坏了vtables

时间:2009-02-24 08:53:55

标签: c++ visual-studio-2005 vtable

我目前正在开发一个相当大的(旧的,叹气的)代码库,最近升级到VS2005(SP1)。我和我的团队正在改变/更新/替换此代码中的模块,但我们偶尔会遇到vtable似乎被破坏的问题。我不是vtable的专家,但这些确实似乎被打破了。错误表现为错误:

  

运行时检查失败#0 - ESP的值未在函数调用中正确保存。这通常是调用使用一个调用约定声明的函数的结果,函数指针使用不同的调用约定声明。

当然,这个错误还有很多其他原因,但是在调试时(Debug build),我实际上可以验证我想要操作的对象的vtable看起来很奇怪:

引用每个vtable的堆栈和堆看起来很好,并且指向vtable的指针与映射文件完全匹配。这向我表明这不是内存覆盖错误或类似错误,因为它会影响堆栈和堆,而不是存储vtable的位置。 (它们存放在一个只读区域对吗?)无论如何,到目前为止一切似乎都很好。但是当查看vtable的内存时,我发现所有值,如果我将它们解释为指针,虽然它们在相同的范围内(例如0x00f203db 0x00f0f9be 0x00ecdda7 0x00f171e1)与地图文件中的任何条目都不匹配,其中很多甚至没有与4个字节对齐。我不知道VS2005如何构建vtable的所有细节,但这对我来说是错误的。如果这是正确的行为,也许有人可以向我解释这个?

我想我的问题归结为可能导致这种行为的原因?例如,当具有过于复杂的类层次结构时,链接器中是否存在任何已知错误?以前有人见过类似的东西吗?目前我们能够通过将受影响的类中的函数移动到内联(可怕的东西!)来解决我们的崩溃问题,但显然这不是一个可行的长期解决方案。

感谢您的任何见解!

更新:我被问到有关该项目的更多细节,当然我会提供这个。首先,问题并不完全与ESP值没有保存错误有关。我最感兴趣的是为什么我在vtable中看到了奇怪的值。也就是说,这里有一些额外的信息:解决方案依赖于几个外部和内部项目,但这些项目在很长一段时间内都没有改变,都使用相同的调用约定。它似乎打破的代码都在解决方案的一个非常标准的C ++“主要”项目中。所有代码都使用相同的编译器构建。该解决方案也不使用任何dll,但链接有大量静态库:

SHFolder.lib,python25.lib,dxguid.lib,d3d9.lib,d3dx9.lib,dinput8.lib,ddraw.lib,dxerr9.lib,ws2_32.lib,mss32.lib,Winmm.lib,vtuneapi.lib ,vttriggers.lib,DbgHelp.lib,kernel32.lib,user32.lib,gdi32.lib,winspool.lib,comdlg32.lib,advapi32.lib,shell32.lib,ole32.lib,oleaut32.lib,uuid.lib,odbc32 .lib,odbccp32.lib

6 个答案:

答案 0 :(得分:2)

我发现了问题。真的很傻,但导致问题的类层次结构有一个名为GetObject的虚函数,它与具有相同名称的windows #define冲突。头文件以不同的顺序包含这些Windows头文件,这使链接器混乱。所以,事实上问题是vtable被破坏了,但我没想到这是理由!那么你每天都学到一些东西......

但是,非常感谢所有回复的人!

答案 1 :(得分:1)

我认为这里的大提示是在“这通常是一个调用一个调用约定声明的函数的结果,其中一个函数指针用不同的调用约定声明”是该错误的一部分。在我看来,调用者的API与处理该调用的库之间存在不匹配。

此外,您可能会混合使用不同编译器构建的代码。您还可以告诉我们这个项目的性质吗?您调用的函数是否位于外部库中?或者你可以调试整个调用堆栈吗?

编辑:您说该项目不使用任何DLL。静态库怎么样?

答案 2 :(得分:1)

请注意增量链接和Edit + Continue对函数地址(包括v表条目)的影响。它的工作原理是通过跳转表间接进行方法调用。这允许链接器在需要重新定位方法时修补跳转表,而无需重新链接整个映像。该跳转表中的地址相隔5个字节。它们不会出现在.map文件中。当您切换到Assembly视图并跟踪调用的执行时,很容易看到。

这也是您应该用来诊断RTC故障的技术。找出实际调用的方法。最可能的原因是您已将虚拟方法添加到类中,但该类的客户端未重新编译。使用v表中的错误插槽。在改变接口但不改变IID时,传统上也是一个COM问题。

答案 3 :(得分:0)

每当我收到这样的消息时,答案总是涉及重新编译部分或全部代码。作为第一步,我会尝试完全重建。 Sqook关于外部库的建议听起来似乎有道理,如果可能的话,再次涉及使用与主代码相同的调用约定重新编译该库。

我有时发现Build命令可能会遗漏需要重新编译的文件,这可能会导致您的消息。同样,完全重建将使事情变得更好。

答案 4 :(得分:0)

当我遇到这个错误之前,它总是在COM参与之前。 几乎总是它与重新入侵特别相关 - 你使用COM吗?您使用的是STA,消息过滤器吗?

答案 5 :(得分:0)

我遇到了完全相同的问题 - 在对象上调用重载的虚函数导致“ESP未正确保存”错误,但是调试显示编译器在此调用的vtable中生成了错误的偏移量,因此另一个调用了更多参数的函数。被调用的函数更新了ESP,好像调用者已经在堆栈上推送了更多参数,这反过来又导致返回的ESP值无效。

在将包含类的头文件放在源文件顶部后,问题就消失了。我没有进一步调查究竟是什么造成了这种情况,但我想这是出于同样的情况 - 有些人定义了虚拟成员的声明。

希望能帮助其他人遇到同样的问题。