我目前正在尝试在一个共享库中使用C++11多线程,该库被加载到Linux上的主程序(用C编写)中。这是一个大的模拟程序的一部分,我不能改变任何关于库加载的事情,也不能改变主程序。
主程序是用gcc 4.1.2编译的,我没有源代码(我不能用gcc 4.8.2重新编译它)。
共享库是用gcc 4.8.2编译的,目的是为了使用C++11多线程。我正在传递编译器命令
-pthread -lpthread -std=c++11
如What is the correct link options to use std::thread in GCC under linux?中所述
使用这种配置("-pthread -std=c++11
“和gcc 4.8)编译一个独立的测试程序,在我的系统上可以正常工作。但是当我启动加载共享库的程序时,我得到了一个异常:
Caught std::exception!
Exception Message: Enable multithreading to use std::thread: Operation not permitted
Terminating...
使用-pthread
和-lpthread
(编辑:以及仅使用-pthread
而不使用-lpthread
)编译参数不起作用。编译器参数是(我使用的是库克构建系统):
-pthread -std=c++11 -fmessage-length=0 -fPIC -Wchar-subscripts ...(lots of -W* here)
... -Wunused-variable -m64 -D__64BIT__ -pthread -lpthread
和链接器参数(由于构建系统而导致重复参数):
-pthread -lpthread -std=c++11 -pthread -lpthread -std=c++11 -shared -fPIC -Wl,-Bsymbolic -Wl,--allow-shlib-undefined -pthread -lpthread
在我的库上调用ldd会产生以下输出
$ ldd calc3/build/amd64_linux26_RH5/library.so
linux-vdso.so.1 => (0x00007fff4d1fd000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00002ae6ec124000)
libstdc++.so.6 => /afs/bb/data/d6833/util/gcc_482/lib64/libstdc++.so.6 (0x00002ae6ec340000)
libm.so.6 => /lib64/libm.so.6 (0x00002ae6ec655000)
libgcc_s.so.1 => /afs/bb/data/d6833/util/gcc_482/lib64/libgcc_s.so.1 (0x00002ae6ec8d8000)
libc.so.6 => /lib64/libc.so.6 (0x00002ae6ecaef000)
/lib64/ld-linux-x86-64.so.2 (0x00000032cb400000)
在主程序上
$ ldd .../bin-64/main_program
linux-vdso.so.1 => (0x00007fff64595000)
libdl.so.2 => /lib64/libdl.so.2 (0x00000032cc000000)
libz.so.1 => /usr/lib64/libz.so.1 (0x00000032cc800000)
libc.so.6 => /lib64/libc.so.6 (0x00000032cb800000)
/lib64/ld-linux-x86-64.so.2 (0x00000032cb400000)
pthread库链接到我的共享库,但没有链接到主程序。此answer声明您必须将pthread链接到主程序,但此答案的第二个注释(由@R..)说这是不必要的(这听起来合乎逻辑)。
不幸的是,我对整个系统的加载机制一无所知,除了我的库使用了另一个C++库作为API。
请注意,其他C++11特性可以工作( libstdc++.so在我的库的依赖项中),但C++11多线程不能(尽管libpthread.so也在我的库的依赖项中)。
使用程序本身包含的库中的线程类是可行的(而且这个线程类似乎也使用了pthread)。
我也尝试过使用-fabi-version=0
或-fabi-version=2
,因为我的库中的主程序是用gcc 4.1.2编译的,但它没有改变任何东西。
有没有什么我忽略的地方,或者我可以用来使它工作的编译器选项?或者这似乎是我的程序环境的问题?欢迎任何想法。
编辑:
我尝试使用-Wl,-no-as-needed
(正如评论中所建议的),但不幸的是,它并没有改变任何事情。
使用clang 3.5而不是gcc 4.8也不起作用。
只要我对主应用程序和共享库都使用gcc 4.8或clang 3.5,就可以创建一个加载共享库的小型测试应用程序(就像下面@chill回答的那样)(即使没有编译器标志)。然而,当使用主程序的gcc 4.1时,主程序甚至无法加载库(它在我的“真正”应用程序中工作)。我认为编译器的不同ABI可能有问题。
直接从pthread.h
使用pthread似乎是可行的(虽然程序目前在pthread_join
上终止,没有出现错误消息,但我仍在那里测试……)
编辑2:
使用LD_LIBRARY_PATH=$(pwd):$LD_LIBRARY_PATH
运行‘测试程序’(因为gcc 4.8的库路径也需要在那里,谢谢@MvG)确实运行了程序,但再次崩溃,并出现Enable multithreading to use std::thread: Operation not permitted
异常。
我检查了所有其他加载的库(使用strace ./main_program 2>&1 | grep '^open(".*\.so"'
找到它们[参见here]),并使用ldd
检查了所有这些库。它们都依赖于相同的库(具有相同的路径)。ldd
输出(在所有这些输出上):
linux-vdso.so.1 => (0x00007fff4d3fd000)
libstdc++.so.6 => /afs/bb/data/d6833/util/gcc_482/lib64/libstdc++.so.6 (0x00002ade28774000)
libm.so.6 => /lib64/libm.so.6 (0x00002ade28ab0000)
libgcc_s.so.1 => /afs/bb/data/d6833/util/gcc_482/lib64/libgcc_s.so.1 (0x00002ade28d33000)
libc.so.6 => /lib64/libc.so.6 (0x00002ade28f49000)
/lib64/ld-linux-x86-64.so.2 (0x00000032ea200000)
(除了我的库和另一个库之外,它们都不依赖于libpthread.so.0 (但它是相同的/lib64/libpthread.so.0
))
一些库确实有更多的依赖项(看起来与线程无关),但似乎没有任何“冲突”的依赖项(在这些库中没有对同一个库的不同版本/路径的依赖项)。
发布于 2013-12-17 00:44:22
在thread.cc
中,您可以看到,如果__gthread_active_p
返回false,则会生成此异常。该调用只是检查给定的符号是否可用。所讨论的符号是一个弱符号:它不一定要存在,但会检查它的存在,以确定是否支持线程。
但是符号的存在意味着什么呢?在本例中,这意味着符号在符号表的列表中,相关的库(在我的例子中是libgcc_s.so.1
)将搜索符号定义。这包括由应用程序本身导出的符号,也包括由在其之前加载的所有库导出的符号。但是,它不包括后来加载的库。不幸的是,如果在libpthread
之前加载libgcc
,则符号在其搜索域中不可用。所以它报告线程不受支持。我猜您在加载多线程模块之前已经加载了一些其他C++模块,所以您会遇到这个问题。
在my reproducing example中工作的一种解决方案是在用于调用二进制文件的环境中设置LD_PRELOAD=/lib64/libpthread.so.0
。这会预先加载libpthread
,因此它的符号可用于满足弱符号链接。这对setuid/setgid二进制文件不起作用,在其他情况下也可能被认为是丑陋的技巧,所以我对更干净的解决方案感兴趣。尽管如此,这在大多数情况下都能完成工作。
发布于 2013-12-16 18:18:31
我已经能够复制出与你的情况非常相似的东西。
首先是测试共享库的源代码:
#include <thread>
void f() {}
extern "C" int foo () {
std::thread(f).join();
return 0;
}
使用c++ -fPIC -shared -std=c++11 -pthread thrlib.cxx -o libthrlib.so
编译/链接
然后是“主程序”:
#include <dlfcn.h>
int
main () {
void *lib = dlopen ("libthrlib.so", RTLD_NOW);
int (*foo)();
foo = (int (*)()) dlsym (lib, "foo");
foo ();
}
使用gcc main.c -ldl
编译/链接
执行的结果是:
velco@sue:~/tmp$ LD_LIBRARY_PATH=`pwd` ./a.out
terminate called after throwing an instance of 'std::system_error'
what(): Enable multithreading to use std::thread: Operation not permitted
Aborted (core dumped)
现在,通过向命令行添加选项-Wl,-no-as-needed
来构建共享库
c++ -fPIC -shared -std=c++11 -pthread thrlib.cxx -o libthrlib.so -Wl,-no-as-needed
并且主程序执行时没有错误。
我在Ubuntu 13.10和gcc 4.8.1上运行了这个,上面的所有这些实际上可能不适用于你的环境,但效果与你的非常相似,所以有一个很好的机会同样的解决方案将适用于你,无论如何值得一试:)
PS。另一件值得尝试的事情是,在你的库中添加一个对pthread_cancel
的显式硬引用:
#include <pthread.h>
void *zzz = (void *) pthread_cancel;
https://stackoverflow.com/questions/20568235
复制相似问题