共享库和 libpthread.so 的 g++ 问题
g++ issues with shared libraries and libpthread.so
我遇到了这个问题,我希望能在这里找到一些帮助来解决这个问题。我创建了一个小样本可执行文件和共享库来展示问题。
抱歉,我意识到这已经变成了一堵文字墙,但我试图确保包含所有相关信息。
我的设置
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 中 link 那么它似乎可以工作,但它最终会在试图抛出异常的共享库中崩溃。这样做的原因是系统运行时库是在期望线程本地存储 (TLS) 的情况下构建的,并且异常处理使用来自 TLS 的数据结构,但在这种情况下它是 NULL,它会导致崩溃。这些函数是 __cxa_allocate_exception 和 __cxa_get_globals,看起来它们正在使用 libc 中的存根函数,因为 pthread 没有被 link 编辑。
我现在遇到的问题是试图在 pthread 中 link 来纠正上面提到的问题。如果我使用 pthreads 构建,应用程序段错误会尝试加载 libpthread.so.0 作为我的共享库的依赖项。我读到的关于这次崩溃的所有内容都是应用程序是在没有 pthread 的情况下构建的,而共享库是使用 pthread 构建的。但是,我正在使用 pthreads 构建两个二进制文件,但我仍然遇到这个问题。
示例代码:
共享库文件 (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;
}
显式加载
尝试使用以下构建脚本在运行时加载共享库会导致尝试加载 libpthread.so.0.
的段错误
构建脚本:
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.
隐式加载
这使用不同的构建脚本,尝试在构建时设置依赖关系,理论上不需要显式加载调用。对于我们的真实场景,这不是一个有效的用例,但我在调查这个问题时尝试这样做。
构建脚本:
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
和 none 的依赖链中的库具有 libc.so.1
作为依赖项。他们依赖的libc版本是libc.so.6
.
构建场景结束
我真的希望这里有人知道正在发生的事情并且可以帮助我。我的 Google 和 Whosebug 技能让我失望,因为我发现的一切都指向不匹配的 pthread 使用是根本问题。
提前致谢!
${compiler} ${arch} -static -g -o main.out ./main.o -lpthread -ldl -lc
这是全静态link。
在大多数操作系统上,无法从完全静态二进制文件调用 dlopen
(libdl.a
中根本没有提供 dlopen
,link失败)。
GLIBC 是一个例外,但仅限于 dlopen
需要支持 /etc/nsswitch.conf
。几乎可以肯定,不支持将 libpthread.so.0
动态加载到包含其自己的 libpthread.a
副本的完全静态 a.out
中。简短的回答是:很痛,不要那样做。
全静态 linking 通常在任何现代 UNIX 系统上都是一个非常糟糕的主意。多线程应用程序的完全静态 linking 加倍如此。完全静态 linking 然后动态加载 libpthread
的另一个副本? 真的 坏主意。
更新:
GLIBC 包含许多库 (200+),我强烈建议不要将静态和动态 linking 混合用于任何此类库。换句话说,如果你link反对libc.a
,那就让它成为一个完全静态的link。如果你link反对libc.so
,那么不要静态地linklibpthread.a
、libdl.a
或任何其他部分GLIBC.
我遇到了这个问题,我希望能在这里找到一些帮助来解决这个问题。我创建了一个小样本可执行文件和共享库来展示问题。
抱歉,我意识到这已经变成了一堵文字墙,但我试图确保包含所有相关信息。
我的设置
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 中 link 那么它似乎可以工作,但它最终会在试图抛出异常的共享库中崩溃。这样做的原因是系统运行时库是在期望线程本地存储 (TLS) 的情况下构建的,并且异常处理使用来自 TLS 的数据结构,但在这种情况下它是 NULL,它会导致崩溃。这些函数是 __cxa_allocate_exception 和 __cxa_get_globals,看起来它们正在使用 libc 中的存根函数,因为 pthread 没有被 link 编辑。
我现在遇到的问题是试图在 pthread 中 link 来纠正上面提到的问题。如果我使用 pthreads 构建,应用程序段错误会尝试加载 libpthread.so.0 作为我的共享库的依赖项。我读到的关于这次崩溃的所有内容都是应用程序是在没有 pthread 的情况下构建的,而共享库是使用 pthread 构建的。但是,我正在使用 pthreads 构建两个二进制文件,但我仍然遇到这个问题。
示例代码:
共享库文件 (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;
}
显式加载
尝试使用以下构建脚本在运行时加载共享库会导致尝试加载 libpthread.so.0.
的段错误构建脚本:
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.
隐式加载
这使用不同的构建脚本,尝试在构建时设置依赖关系,理论上不需要显式加载调用。对于我们的真实场景,这不是一个有效的用例,但我在调查这个问题时尝试这样做。
构建脚本:
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
和 none 的依赖链中的库具有 libc.so.1
作为依赖项。他们依赖的libc版本是libc.so.6
.
构建场景结束
我真的希望这里有人知道正在发生的事情并且可以帮助我。我的 Google 和 Whosebug 技能让我失望,因为我发现的一切都指向不匹配的 pthread 使用是根本问题。
提前致谢!
${compiler} ${arch} -static -g -o main.out ./main.o -lpthread -ldl -lc
这是全静态link。
在大多数操作系统上,无法从完全静态二进制文件调用 dlopen
(libdl.a
中根本没有提供 dlopen
,link失败)。
GLIBC 是一个例外,但仅限于 dlopen
需要支持 /etc/nsswitch.conf
。几乎可以肯定,不支持将 libpthread.so.0
动态加载到包含其自己的 libpthread.a
副本的完全静态 a.out
中。简短的回答是:很痛,不要那样做。
全静态 linking 通常在任何现代 UNIX 系统上都是一个非常糟糕的主意。多线程应用程序的完全静态 linking 加倍如此。完全静态 linking 然后动态加载 libpthread
的另一个副本? 真的 坏主意。
更新:
GLIBC 包含许多库 (200+),我强烈建议不要将静态和动态 linking 混合用于任何此类库。换句话说,如果你link反对libc.a
,那就让它成为一个完全静态的link。如果你link反对libc.so
,那么不要静态地linklibpthread.a
、libdl.a
或任何其他部分GLIBC.