C++ 测试两个 DLL 是否共享同一堆

Gab*_*ers 10 c++ dll memory-management heap-memory shared-libraries

众所周知,堆内存的释放必须使用与分配它的分配器相同的分配器来完成。在跨 DLL 边界交换堆分配的对象时需要考虑这一点。

一种解决方案是为每个对象提供一个析构函数,就像在 C API 中一样:如果 DLL 允许创建对象 A,则它必须提供一个函数A_free或类似的东西1

另一个相关的解决方案是将所有分配包装到其中,因为它们存储到释放器2shared_ptr的链接。

另一种解决方案是将顶级分配器“注入”到所有加载的 DLL(递归地)3中。

另一种解决方案是不交换堆分配的对象,而是使用某种协议4

另一种解决方案是绝对确保 DLL 将共享相同的堆,如果它们共享兼容的编译选项(编译器、标志、运行时等),则应该(将会?)发生这种情况5 6。这似乎很难保证,特别是如果人们想使用包管理器而不是立即构建所有内容。

有没有办法在运行时检查多个 DLL 之间的堆实际上是否相同,最好以跨平台的方式?

为了可靠性和易于调试,这似乎比希望应用程序立即崩溃而不是默默地破坏东西更好。

MSa*_*ers 8

我认为这里最大的问题是你对“堆”的定义。这假设有一个唯一的定义。

问题是 Windows 有HeapAlloc,而 C++ 通常使用“堆”作为 分配的内存::operator new。这两者可以相同、不同、子集或部分重叠。

对于两个 DLL,两者都可以用 C++ 编写并使用::operator new,但它们都可以链接自己独特的版本。因此,对于上一段中的观察结果可能有多个答案。

现在我们假设一个例子,一个 DLL::operator new直接转发到HeapAlloc,但另一个 DLL 在调用 之前计算分配数HeapAlloc。显然,两者不能正式混合,因为第二个分配器保存的计数将是错误的。但代码非常简单,两个news 可能都是内联的。因此,在汇编级别,您只需调用HeapAlloc.

即使您动态反汇编代码(!),您也无法在运行时检测到这一点 - 内联计数器增量指令与周围的代码无法区分。

  • 它们甚至可以直接调用“HeapAlloc()”,但使用不同的堆句柄。 (2认同)