3

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

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

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

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

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

感谢您的任何见解!

更新:有人问我有关该项目的更多详细信息,我当然会提供。然而,首先,问题与 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

4

6 回答 6

2

我发现了问题。真的很傻,但是导致问题的类层次结构有一个名为 GetObject 的虚函数,它与同名的 Windows #define 冲突。头文件以不同的顺序包含这些 windows 头文件,这使链接器感到困惑。所以,实际上问题是 vtables 损坏了,但我没想到这是原因!好吧,你每天都学到一些东西......

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

于 2009-02-26T06:04:42.080 回答
1

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

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

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

于 2009-02-24T09:01:57.490 回答
1

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

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

于 2009-02-25T20:32:55.123 回答
0

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

我有时发现 Build 命令可能会丢失需要重新编译的文件,这可能会导致您的消息。再次,全面重建将使事情理顺。

于 2009-02-24T09:22:05.023 回答
0

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

于 2009-02-24T14:17:17.593 回答
0

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

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

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

于 2009-08-24T09:00:14.687 回答