正在导出系统过程对象和操作 卡住_dll导入导出及local vftable问题

博客详细描述了一个在动态库中new对象并保存到另一动态库容器,导致程序退出时崩溃的问题。问题源于对象的虚表在释放时变为无效,原因是编译器生成的localvftable在模块卸载后失效。解决方法是避免在使用类的模块上使用dllimport,以防止生成localvftable,但这样可能导致静态成员变量的链接错误。通过不导入类并在静态成员变量上单独使用dllimport可以解决这个问题。
摘要由CSDN通过智能技术生成

最近在新项目中遇到一个崩溃的问题,大致的过程就是在一个动态库中new了另一个动态库中导出的类对象,然后将new的指针又保存回另一个动态库的容器中,当整个程序退出时,释放这个容器中保存的指针,在释放时发生崩溃,此时观察对象的虚表,发现已经野掉。

遇到问题的第一反应是这个对象是不是已经被释放过了,由于重复释放导致的崩溃,于是在类的析构函数中放上断点,再次运行,结果断点没进,直接崩溃掉。好吧,再次调试,在new创建对象以后放上数据断点监视虚表,退出程序,这一次断点停下来了,停在了new这个对象的模块释放的地方,咦,这不很奇怪,在我已有的认识里面,对象的虚表应该在类所在的模块才对啊,怎么跑到其它模块去了呢?带着疑问又一次调试,反汇编跟踪对象的构造过程,跟进对象的构造函数,赋给对象的虚表就是类所在的模块啊,这就奇怪了,为什么在释放的时候虚表变成了其它模块呢?难道是有内存溢出把虚表覆盖了?继续跟踪,在跳出对象的构造函数以后,看到了一行奇怪的代码,在new对象的模块生成了一个local vftable再次赋给了虚表。这是什么骚操作,对象已经有虚表了,再赋一个local vftable干啥。

下面用一个简单的程序模拟上面的过程:

7623eb31fd2947be3ae9a2fc2ad587f6.png

在module1动态库中定义一个TestA,整个类导出导入

229937962fc8643b68b2fce4ab26a637.png

在module1中再定义另外两个导出函数

c6c5cfbdab9441844e60752d4dbf8659.png

在module2动态库中定义一个函数,在堆上创建TestA的对象,并调用module1中的saveTestA函数保存创建的指针

07a72a44a433f6e5e9152b787917a82e.png

在LocalVftable主程序中采用LoadLibrary的方式隐式使用module1及module2,当调用clearTestA函数时崩溃

902e5ddef309dd83bc8e65d22ba8d71e.png

调试TestA的构造过程,会将TestA::vftable赋给对象首地址内存,并且观察内存地址范围可以发现TestA::vftable在module1中

4671f7bca320783fe84b67b7f08739f2.png

跳出构造,继续调试会发现编译器又用一个local vftable覆盖了对象原有的虚表!并且这个local vftable在module2中

410a8bbfb6d5f6e903aa03a8732f1f0f.png

后面在释放创建的TestA对象时由于module2已经卸载,所以虚表中的虚函数地址都已经野掉

好了,经过上面的示例程序的分析,问题很清晰了,那该怎么解决呢?最佳实践当然是哪个模块new就由哪个模块释放,这样就不会存在问题,可是现实情况很多时候有这种需求。应该有其他人遇到过同样的问题吧,于是开始百度搜索local vftable,有用的资料极少,后面在谷歌上找到了一些有用信息。

84bfc4e8bd36eabea189418a8571323c.png

大致的意思就是微软为了解决new和delete不匹配(不在同一个模块)的问题,在5.0以后的版本就搞了一个local vftable,这个local vftable解决了一些问题,当也引入了新问题,如果提前FreeLibrary了相应模块,那么local vftable就不存在了,好在有非常简单的方法避免生成local vftable。

1a61a16e62abb26eda1b15785228f647.png

解决方法很简单,就是整个类导出时另一个使用这个类的模块不要使用dllimport或者在单独需要导出的函数上使用dllimport。不过这又会带来一个问题,就是如果类上有静态成员变量,则无法导出,另一个使用的模块会报链接错误,好在也有解决方法,就是在静态成员变量上单独使用dllimport。最终的类变成下面这样:

1810c0da1cf57959f92cccde0f99d35b.png

类TestA导出不导入,这样在module2中编译器就不会额外生成一个local vftable

总结:local vftable的问题并不一定会暴露出来,需要满足很多条件,如果主模块是显示依赖动态库或者不满足特定的卸载顺序都不会出现问题,但是一旦出现崩溃,那就比较难调试,并且百度能查到的资料还很少。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值