加载 .dll 时,实例指针与 vfptr 不同

Posted

技术标签:

【中文标题】加载 .dll 时,实例指针与 vfptr 不同【英文标题】:Instance pointer differs from vfptr when .dll is loaded 【发布时间】:2017-11-09 09:34:49 【问题描述】:

所以,我在使用 Visual Studio 的测试套件时发现了一些有趣的东西:

我有一个 A 类的实例,地址是 0x0656a64c。 然后当我查看变量时,它说它的__vfptr 指向0x077e7c0c

据我所知,一个类的虚拟表指针应该位于类实例的前 4 个字节(或 64 位应用程序上的 8 个字节),除非是多重继承的情况(那么它只是偏移量)第一个 vtable 地址)。

我观察到 Visual Studio 将我的测试编译成 .dll 并运行其测试工具动态加载 .dll。

这可能是导致地址差异的原因吗?

这是 VS 调试器的截图

【问题讨论】:

【参考方案1】:

您混淆了 vptr 的地址和 vptr 指向的地址。您(大致)正确的是,通常的实现是 vptr 是对象中的第一件事 - 但是 Visual Studio 向您显示 vtable 的地址(在类的所有对象之间共享) )。

【讨论】:

啊,是的,我肯定误解了这些价值观。谢谢你的澄清【参考方案2】:

这只是 MS C++ 编译器的正常行为。 2 个创建相同类对象实例的 DLL 将具有不同的 vtable。直觉会说 vtable 应该与所有实例共享,无论 DLL 的代码如何创建(新)它。它应该对类的实现是通用的。不正确。

这会在动态释放 DLL 时产生危险。所有分配的对象可能仍处于有效(进程)堆中。但是,如果它有一个虚拟析构函数,那么它是在 vtable 中定义的。它现在处于释放空间中。因此 vtable 已损坏。任何使用它都会产生异常。

解决方案是在释放 DLL 之前清理由动态 DLL 进行的任何/所有分配,这些分配可能也有一个 vtable(例如虚拟析构函数)。在进程堆中进行的任何不指向基于 DLL 的 vtable 的分配都可以。如果没有正确跟踪,最坏的情况是内存泄漏。

【讨论】:

以上是关于加载 .dll 时,实例指针与 vfptr 不同的主要内容,如果未能解决你的问题,请参考以下文章

vfptr 中的 Visual C++ 方法以相反的顺序

在运行时加载 2 个版本的程序集

虚继承之单继承的内存布局(VC在编译时会把vfptr放到类的头部,这和Delphi完全一致)

dll与exe之间怎么共享数据

Azure SQL 托管实例错误“无法加载 DLL odsole70.dll 或其引用的 DLL 之一”

在运行时多次加载 Roslyn 编译的 DLL