共享库和libpthread.so的g ++问题

我遇到了这个问题,希望能在这里find一些帮助。 我创build了一个小的示例可执行文件和共享库,展示了这个问题。

对不起,我意识到这已经变成了一面墙,但我试图确保包括所有的相关信息。

我的设置

System: CentOS release 5.11 (Final) g++: gcc version 4.4.7 20120313 (Red Hat 4.4.7-1) (GCC) libc.so.6: Compiled by GNU CC version 4.1.2 20080704 (Red Hat 4.1.2-55). 

我也在Redhat 6.6机器上尝试过,结果相似。

我的场景:

我有一个应用程序试图通过:: dlopen()在运行时加载共享库。 如果我没有在pthread中链接,那么它似乎工作,但最终会崩溃在共享库试图抛出一个exception。 这样做的原因是系统运行时库的构build期望线程本地存储(TLS),exception处理使用TLS中的数据结构,但在这种情况下它是NULL,并导致崩溃。 函数是__cxa_allocate_exception和__cxa_get_globals,看起来他们正在使用libc中的存根函数,因为pthread没有链接到。

我现在遇到的问题是试图链接到pthread来纠正上面提到的问题。 如果我用pthreads构build,应用程序段错误尝试加载libpthread.so.0作为我的共享库的依赖项。 我读过的关于这个崩溃的一切是,应用程序是在没有pthread的情况下构build的,而共享库是用pthread构build的。 不过,我用pthreads来构build二进制文件,但是我仍然遇到这个问题。

示例代码:

共享库文件(foo。*)

foo.h中

 #pragma once extern "C" { extern void DoWork(); } 

Foo.cpp中

 #include "foo.h" #include <stdio.h> void DoWork() { printf( "SharedLibrary::DoWork()\n" ); } 

应用程序文件(main.cpp)

main.cpp中

 #include "foo.h" #include <stdio.h> #include <dlfcn.h> void LoadSharedLibrary() { void* handle = 0; void(*function)(); try { printf( "Loading the shared library\n" ); handle = ::dlopen( "libfoo.so", 2 ); function = (void (*)())::dlsym( handle, "DoWork" ); printf( "Done loading the shared library\n" ); function(); } catch(...) { printf( "ERROR - Exception while trying to load the shared library\n" ); } } int main(int argc, char* argv[]) { LoadSharedLibrary(); return 0; } 

显式加载

尝试使用以下构build脚本在运行时加载共享库会导致尝试加载libpthread.so.0的段错误。

构build脚本:

 compiler=g++ arch=-m32 echo gcc architecture flag: ${arch} ${compiler} -c -fPIC -g ${arch} -pthread -o ./foo.o foo.cpp ${compiler} ${arch} -shared -g -o ./libfoo.so ./foo.o -lpthread ${compiler} -c -fPIC -g ${arch} -pthread -o ./main.o main.cpp ${compiler} ${arch} -static -g -o main.out ./main.o -lpthread -ldl -lc 

这个崩溃的堆栈跟踪是:

 #0 0x00000000 in ?? () #1 0x0089a70a in __pthread_initialize_minimal_internal () at init.c:417 #2 0x0089a218 in call_initialize_minimal () from /lib/libpthread.so.0 #3 0x00899da8 in _init () from /lib/libpthread.so.0 #4 0x0808909b in call_init () #5 0x080891b0 in _dl_init () #6 0x08063a87 in dl_open_worker () #7 0x0806245a in _dl_catch_error () #8 0x0806349e in _dl_open () #9 0x08053106 in dlopen_doit () #10 0x0806245a in _dl_catch_error () #11 0x08053541 in _dlerror_run () #12 0x08053075 in __dlopen () #13 0x0804830f in dlopen () #14 0x0804824f in LoadSharedLibrary () at main.cpp:13 #15 0x080482d3 in main (argc=1, argv=0xffffd3e4) at main.cpp:27 

加载的共享库是:

 From To Syms Read Shared Object Library 0xf7ffb3b0 0xf7ffb508 Yes libfoo.so 0x0089a210 0x008a5bc4 Yes (*) /lib/libpthread.so.0 0xf7f43670 0xf7fbec24 Yes (*) /usr/lib/libstdc++.so.6 0x009a8410 0x009c35a4 Yes (*) /lib/libm.so.6 0xf7efb660 0xf7f02f34 Yes (*) /lib/libgcc_s.so.1 0x0074dcc0 0x0084caa0 Yes (*) /lib/libc.so.6 0x007197f0 0x0072f12f Yes (*) /lib/ld-linux.so.2 (*): Shared library is missing debugging information. 

隐式加载

这使用了一个不同的构build脚本,试图在构build时设置依赖关系,理论上不需要显式的加载调用。 对于我们的真实世界的情况,这不是一个有效的用例,但是我试图在解决这个问题的时候这样做。

构build脚本:

 compiler=g++ arch=-m32 echo gcc architecture flag: ${arch} ${compiler} -c -fPIC -g ${arch} -pthread -o ./foo.o foo.cpp ${compiler} ${arch} -shared -g -o ./libfoo.so ./foo.o -lpthread ${compiler} -c -fPIC -g ${arch} -pthread -o ./main.o main.cpp ${compiler} ${arch} -static -g -L. -o main.out ./main.o -lpthread -ldl -Wl,-Bdynamic -lfoo -Wl,-static -lc 

行为:

 Starting program: /app_local/dev3/stack_overflow/main.out /bin/bash: /app_local/dev3/stack_overflow/main.out: /usr/lib/libc.so.1: bad ELF interpreter: No such file or directory /bin/bash: /app_local/dev3/stack_overflow/main.out: Success 

在启动程序中退出代码1。

奇怪的是我已经完成objdump -p <library> | grep NEEDED objdump -p <library> | grep NEEDED并且依赖链中的库都不具有libc.so.1作为依赖项。 libc的版本依赖于libc.so.6

构build场景结束

我真的希望有人在这里有一个想法,可以帮助我。 我的Google和StackOverflow技能已经使我失败了,因为我发现的一切都指向了不匹配的pthread用法作为根本问题。

提前致谢!

${compiler} ${arch} -static -g -o main.out ./main.o -lpthread -ldl -lc

这是一个完全静态的链接。

在大多数操作系统中,不能从完全静态的二进制文件中调用dlopendlopen简直不在libdl.a提供,链接失败)。

GLIBC是一个例外,但只有dlopen需要支持/etc/nsswitch.conf 。 几乎可以肯定动态加载libpthread.so.0到一个完全静态的a.out ,它不包含它自己的libpthread.a副本。 简短的回答是:伤害,不要这样做。

在任何现代UNIX系统上,全静态链接通常是一个非常糟糕的主意。 多线程应用程序的全静态链接是这样的。 完全静态链接,然后动态加载libpthread另一个副本? 真是糟糕的主意。

更新:

GLIBC由许多库(200+)组成,我强烈建议不要为任何这样的库混合静态和动态链接。 换句话说,如果你链接到libc.a ,然后使其成为一个完全静态的链接。 如果您链接到libc.so ,则不要静态链接libpthread.alibdl.alibdl.a任何其他部分。