2009-02-24 37 views
3

我目前正在一个相当大(和老,叹)代码库,最近升级到VS2005(SP1)。我和我的团队正在改变/更新/替换此代码中的模块,但我们偶尔会遇到vtables似乎破裂的问题。我不是vtables的专家,但这些确定似乎已被打破。错误表现与此错误:VS2005 C++破vtables

Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.

当然可以有很多其他原因造成的错误,但调试(调试版本)时,其实我可以确认为对象的虚函数表我想上一下运行奇怪:

引用每个vtable的堆栈和堆看起来很好,并且指向vtable的指针与映射文件完全匹配。这表明这不是内存覆盖错误或类似的问题,因为那样会影响堆栈和堆,而不是存储vtables的位置。 (它们被存储在只读区域中吗?)无论如何,目前所有这些看起来都不错。但是当查看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

回答

2

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

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

1

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

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

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

+0

嗨,谢谢!上面的问题增加了一些更多的信息。 – Dan 2009-02-24 09:32:04

+0

bwah ...我很难过,因为你的构建环境看起来很健全。让我考虑一下这个问题,并回来一些新的建议/问题。 – 2009-02-24 14:06:38

+0

呵呵,是的,我在这里也画了一个空白,除了我看到的奇怪的vtable。感谢您提出任何想法:) – Dan 2009-02-25 03:52:13

0

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

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

+0

嗨,谢谢你的建议。我也看到有时通过重建来解决这个问题,但这次似乎并非如此。我们已经重新构建,包括几台机器上的所有库,并获得每隔一段时间的相同行为。 – Dan 2009-02-24 09:31:04

0

当我在发生COM错误之前就已经发生了这个错误。 几乎总是与重入特别有关 - 你使用COM吗?你在使用STA,消息过滤器吗?

1

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

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

0

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

在将包含类的头文件错误地放在源文件的顶部之后,问题就消失了。我还没有进一步调查究竟是什么导致了这一点,但我想这是为了同样的情况 - 一些定义与虚拟成员的声明混乱。

希望能帮助别人绊倒同样的问题。