[英]VS2005 C++ broken vtables
我目前正在开发一个相当大(而且很旧,叹气)的代码库,最近升级到 VS2005 (SP1)。 我和我的团队正在更改/更新/替换此代码中的模块,因为我们是 go,但我们偶尔会遇到 vtables 似乎损坏的问题。 我不是 vtables 方面的专家,但这些肯定似乎被打破了。 错误表现为以下错误:
运行时检查失败 #0 - ESP 的值未在 function 调用中正确保存。 这通常是调用使用一种调用约定声明的 function 和使用不同调用约定声明的 function 指针的结果。
当然,这个错误可能还有很多其他原因,但是在调试(调试构建)时,我实际上可以验证我想要操作的 object 的 vtables 看起来很奇怪:
引用每个 vtable 的堆栈和堆看起来都很好,指向 vtable 的指针与 map 文件完美匹配。 这向我表明这不是 memory 覆盖错误或类似错误,因为它会影响堆栈和堆而不是存储 vtable 的位置。 (它们存储在只读区域对吗?)无论如何,到目前为止一切似乎都很好。 但是当查看 vtable 的 memory 时,我发现所有值,如果我将它们解释为指针,尽管它们在同一范围内(例如 0x00f203db 0x00f0f9be 0x00ecdda7 0x00f171e1)不匹配 Z1D785DC8ED4FE244E 文件中的任何条目并且许多甚至没有对齐到 4 个字节。 我不知道 VS2005 如何构建 vtables 的所有细节,但这在我看来是错误的。 如果这是正确的行为,也许有人可以向我解释一下?
我想我的问题归结为什么会导致这种行为? 例如,当 class 层次结构过于复杂时,linker 中是否有任何已知错误? 有没有人见过类似的东西? 目前,我们能够通过将功能从受影响的 class 移动到内联(可怕的东西。)来解决我们的崩溃问题,但显然这不是一个可行的长期解决方案。
感谢您的任何见解!
更新:有人问我有关该项目的更多详细信息,我当然会提供。 然而,首先,问题与 ESP 值未保存错误并不完全相关。 我最感兴趣的是为什么我会在 vtable 中看到奇怪的值。 也就是说,这里有一些附加信息:该解决方案依赖于几个外部和内部项目,但这些项目很长时间没有改变,都使用相同的调用约定。 它似乎中断的代码都在解决方案的一个非常标准的 C++ “主”项目中。 所有代码都使用相同的编译器构建。 该解决方案也不使用任何 dll,但与大量 static 库链接:
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
我发现了问题。 真的很傻,但是导致问题的 class 层次结构有一个名为 GetObject 的虚拟 function 与 windows #define 冲突。 header 文件以不同的顺序包含这些 windows header 文件,这混淆了 Z3175B427346780BEECE873。 所以,实际上问题是 vtables 损坏了,但我没想到这是原因。 好吧,你每天都学到一些东西......
但是,非常感谢所有回复!
我认为这里的重要提示是“这通常是调用使用一个调用约定声明的 function 指针声明的一个调用约定的结果”该错误的一部分。 在我看来,调用者的 API 与处理调用的库之间存在不匹配。
此外,您可能正在混合使用不同编译器构建的代码。 关于这个项目的性质,你还能告诉我们什么? 您正在调用的 function 是否位于外部库中? 或者你可以调试整个调用堆栈吗?
编辑:您说该项目不使用任何 DLL。 static 库呢?
请注意增量链接和编辑+继续对 function 地址(包括 v-table 条目)的影响。 它的工作原理是通过跳转表间接调用方法。 这允许 linker 在需要重新定位方法时修补跳转表,而无需重新链接整个图像。 该跳转表中的地址相隔 5 个字节。 它们不会出现在 .map 文件中。 当您切换到程序集视图并跟踪调用的执行时,很容易看到。
这也是您应该用来诊断 RTC 故障的技术。 找出实际调用的方法。 最可能的原因是您已将虚拟方法添加到 class 但该 class 的客户端未重新编译。 在 v-table 中使用了错误的插槽。 在更改接口而不是 IID 时,通常还会出现 COM 问题。
我遇到了完全相同的问题-在 object 上调用重载的虚拟 function 导致“ESP 未正确保存”错误,但调试显示编译器为此调用在 vtable 中生成了错误的偏移量,因此另一个 ZC1C42542068E68384F1 与 moreC参数被调用。 被调用的 function 更新了 ESP,就好像调用者在堆栈上推送了更多参数,这反过来导致返回时无效的 ESP 值。
在我将 header 文件(包括 class 错误)放在源文件顶部后,问题就消失了。 我还没有进一步调查究竟是什么原因造成的,但我想这是在同样的情况下——有些人定义了虚拟成员的声明。
希望能帮助其他遇到同样问题的人。
每当我收到这样的消息时,答案总是涉及重新编译部分或全部代码。 作为第一步,我会尝试完全重建。 Sqook 关于外部库的建议听起来也很合理,如果可能的话,您再次使用与主代码相同的调用约定重新编译该库。
我有时发现 Build 命令可能会丢失需要重新编译的文件,这可能会导致您的消息。 再次,全面重建将使事情理顺。
当我遇到此错误之前,一直是涉及 COM 时。 它几乎总是与重入特别相关——您使用的是 COM 吗? 您使用的是 STA,消息过滤器吗?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.