我有两个用于linux平台的外包共享库(没有源代码,没有文档)。当它们单独链接到程序时(g ++ xx.cpp lib1.so或g ++ xx.cpp lib2.so),这些库工作正常。
然而,当任何c ++程序同时链接到这两个共享库时,该程序不可避免地会因为" double free"而崩溃。错误(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;
}
生成文件:
g++ helloword.cpp lib1.so lib2.so
我得到一些线索,这些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
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)
==29953== by 0x549B725: ??? (in /home/fanbin/InventoryManagment/lib1.so)
==29953== by 0x5551720: ??? (in /home/fanbin/InventoryManagment/lib1.so)
==29953== by 0x613E218: __run_exit_handlers (in /usr/lib64/libc-2.17.so)
==29953== by 0x613E264: exit (in /usr/lib64/libc-2.17.so)
==29953== by 0x6126AFB: (below main) (in /usr/lib64/libc-2.17.so)
==29953== Address 0x6afb780 is 0 bytes inside a block of size 624 free'd
==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)
==29953== by 0x4F07AC5: ??? (in /home/fanbin/InventoryManagment/lib2.so)
==29953== by 0x5039900: ??? (in /home/fanbin/InventoryManagment/lib2.so)
==29953== by 0x613E218: __run_exit_handlers (in /usr/lib64/libc-2.17.so)
==29953== by 0x613E264: exit (in /usr/lib64/libc-2.17.so)
==29953== by 0x6126AFB: (below main) (in /usr/lib64/libc-2.17.so)
gdb backtrace消息:
(gdb) bt
#0 0x00007ffff677d989 in raise () from /lib64/libc.so.6
#1 0x00007ffff677f098 in abort () from /lib64/libc.so.6
#2 0x00007ffff67be197 in __libc_message () from /lib64/libc.so.6
#3 0x00007ffff67c556d in _int_free () from /lib64/libc.so.6
#4 0x00007ffff7414aa2 in __tcf_0 () from ./lib1.so
#5 0x00007ffff678158a in __cxa_finalize () from /lib64/libc.so.6
#6 0x00007ffff739f726 in __do_global_dtors_aux () from ./lib1.so
#7 0x0000000000600dc8 in __init_array_start ()
#8 0x00007fffffffe2c0 in ?? ()
#9 0x00007ffff7455721 in _fini () from ./lib1.so
#10 0x00007fffffffe2c0 in ?? ()
#11 0x00007ffff7debb98 in _dl_fini () from /lib64/ld-linux-x86-64.so.2
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
更新
感谢@RaduChivu的帮助,我发现了一个非常类似的场景:segmentation fault at __tcf_0 when program exits,看起来确实两个库之间存在全局变量冲突。考虑到我没有这两个外部共享库的源文件,除了使用两个单独的进程外,还有其他方法可以解决这个冲突吗?
答案 0 :(得分:2)
一种可能的解决方案是永远不要致电exit
。要终止您的计划,只需致电_exit
即可。如果您需要做的任何具体事情通常由exit
完成,请在致电_exit
之前自行完成。
答案 1 :(得分:1)
经过一天的搜索,我已经解决了这个问题,并在此处留下备注,以防其他人在将来遇到这种情况。
它证明@RaduChivn和我的猜测是正确的:两个共享库可以共享一个共同的全局变量。即使空程序同时链接到两个共享库,当它退出时,也会尝试将公共全局变量释放两次,从而实现双重自由损坏。
线索来自gdb backtrace中的此消息:
#4 0x00007ffff7414aa2 in __tcf_0 () from ./lib1.so
如本主题所述:
What is function __tcf_0? (Seen when using gprof and g++),
tcf_0是由g ++生成的函数,用于在触发exit()时破坏静态对象。此消息提示当一个共享库尝试在另一个共享库之后退出时,会发生双重释放。
由于这两个库旨在协同工作,因此腐败是一项令人无法接受的工程师灾难。对于五个版本的发布,这样一个低质量但又明显的bug怎么能存活下来?这可能是由于大多数图书馆用户在Windows平台上工作(其包工作正常)。然而,这个假设提供了另一个关于错误来源的暗示:共享库在Windows上运行良好,而在Linux上崩溃;然后它必须是一些依赖于操作系统的行为差异导致错误。这个主题提供了一些见解:
简而言之,&#34; extern globals&#34;从共享库获取Linux上的单个副本,但在Windows上获得多个副本。
(1)当然,我们可以创建两个流程,每个流程分别链接到一个库。
(2)@DavidSchwartz提供了在程序结束时使用_exit(0)的另一种解决方法,而不是公共&#34;返回0&#34;或者&#34;退出(0)&#34;,它有效。根据
What is the difference between using _exit() & exit() in a conventional Linux fork-exec?
,必须手动刷新文件并检查atexit作业;对于内存事物,由于程序正在退出,OS无论如何都会回收所有进程内存,无需担心。
(3)另一种方法是使用dlopen(xx.so,RTLD_LOCAL),首先使所有符号变为盲,然后手动使用你需要的函数符号
(@ JonathanWakely在此注释RTLD_LOCAL有副作用,请参阅评论)。
在这种情况下,图书馆编码人员甚至没有使用&#34; extern C&#34;在他们的共享库中,在so文件中使名称变形无法读取;如果其他人喜欢这个,以下主题可能有所帮助:
Getting undefined symbol error while dynamic loading of shared library
如果您的共享库不受支持,就像我的情况一样,仍然可以使用解决方案。我手动整理出所有必需的函数,并使用nm查找.so文件中的每个相应符号,逐个链接它们,并且它有效。