我有两个用于linux平台的外包共享库(没有源代码,没有文档).当它们单独链接到程序时(g ++ xx.cpp lib1.so或g ++ xx.cpp lib2.so),这些库工作正常.
但是,当任何c ++程序同时链接到这两个共享库时,程序不可避免地会因"双重释放"错误而崩溃(g ++ xx.cpp lib1.so lib2.so).
即使c ++程序是一个空的 hello world程序并且与这些库无关,它仍然会崩溃.
#include <iostream>
using namespace std;
int main(){
cout<<"haha, I crash again. Catch me if you can"<<endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
Makefile文件:
g++ helloword.cpp lib1.so lib2.so
Run Code Online (Sandbox Code Playgroud)
我得到一些线索,这些lib1.so lib2.so库可能共享一些常见的全局变量,并且它们会两次销毁一些变量.我尝试过gdb和valgrind,但是无法从backtrace中提取有用的信息.
有什么方法可以隔离这两个共享库并使它们以沙箱方式工作?
EDITED(添加核心转储和gdb回溯):
我只是将前面提到的玩具空helloword程序与两个库(平台:带有gcc4.8.2的centos 7.0 64bits)联系起来:
g++ helloworld.cpp lib1.so lib2.so -o check
Run Code Online (Sandbox Code Playgroud)
Valgrind的:
==29953== Invalid free() / delete / delete[] / realloc()
==29953== at 0x4C29991: operator delete(void*) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==29953== by 0x613E589: __cxa_finalize (in /usr/lib64/libc-2.17.so) …Run Code Online (Sandbox Code Playgroud) 我想出了一个问题的解决方案,但我不确定它是否总是有效或仅适用于我的编译器。首先,问题:我注意到在许多情况下,即使给定相同的类型,也希望每次使用时都重新实例化模板类(假设您的模板类具有初始化为函数调用的静态成员)有一些重要的副作用——并且您希望每次使用模板时都会产生这种副作用)。最简单的方法是给你的模板一个额外的整数参数:
template<class T, class U, int uniqueify>
class foo
{
...
}
Run Code Online (Sandbox Code Playgroud)
但是现在您必须手动确保每次使用 foo 时都会为 uniqueify 传递一个不同的值。天真的解决方案是这样使用__LINE__:
#define MY_MACRO_IMPL(line) foo<line>
#define MY_MACRO MY_MACRO_IMPL(__LINE__)
Run Code Online (Sandbox Code Playgroud)
不过这个解决方案有一个问题——__LINE__每个翻译单元都被重置。因此,如果两个翻译单元在同一行上使用模板,则模板只会实例化一次。这似乎不太可能,但想象一下,如果确实发生了编译器错误,那么调试它会有多困难。同样,您可以尝试__DATE__以某种方式用作参数,但它只有几秒钟的精度,而且是编译开始的时间,而不是到达该行的时间,因此如果您使用的是 make 的并行版本,则有两个翻译单元是相当合理的同__DATE__。
另一种解决方案是一些编译器有一个特殊的非标准宏,__COUNTER__它从 0 开始,每次使用时递增。但它遇到了同样的问题——每次调用预处理器都会重置它,因此它会重置每个翻译单元。
另一种解决方案是一起使用__FILE__和__LINE__:
#define MY_MACRO_IMPL(file, line) foo<T, U, file, line>
#define MY_MACRO MY_MACRO_IMPL(T, U, __FILE__, __LINE__)
Run Code Online (Sandbox Code Playgroud)
但是您不能根据标准将字符文字作为模板参数传递,因为它们没有外部链接。
即使这确实有效,__FILE__标准中也没有定义是包含文件的绝对路径还是仅包含文件本身的名称,因此如果您在不同的文件夹中有两个相同的命名文件,这仍然可能会中断。所以这是我的解决方案:
#ifndef toast_unique_id_hpp_INCLUDED
#define toast_unique_id_hpp_INCLUDED
namespace {
namespace toast {
namespace detail {
template<int i>
struct translation_unit_unique {
static int …Run Code Online (Sandbox Code Playgroud)