I am currently trying to use C++11 multithreading in a shared library that is loaded into the main program (written in C) on Linux. This is part of a big simulation program and I cannot change anything about the library loading or change the main program in general.
我目前正在尝试在一个共享库中使用C ++ 11多线程,该库被加载到Linux上的主程序(用C语言编写)中。这是大型模拟程序的一部分,我无法改变有关库加载或更改主程序的任何内容。
The main program is compiled with gcc 4.1.2 and I don't have the sources for it (I cannot recompile it with gcc 4.8.2).
主程序是用gcc 4.1.2编译的,我没有它的源代码(我不能用gcc 4.8.2重新编译它)。
The shared library is compiled with gcc 4.8.2 in order to use C++11 multithreading. I am passing the compiler commands
共享库使用gcc 4.8.2编译,以便使用C ++ 11多线程。我正在传递编译器命令
-pthread -lpthread -std=c++11
as explained in What is the correct link options to use std::thread in GCC under linux?
正如在Linux下的GCC中使用std :: thread的正确链接选项中所解释的那样?
Compiling a standalone test program with this configuration ("-pthread -std=c++11
" and gcc 4.8) works correctly on my system. But when I start the program loading the shared library I get an exception:
使用此配置(“-pthread -std = c ++ 11”和gcc 4.8)编译独立测试程序可在我的系统上正常运行。但是当我启动程序加载共享库时,我得到一个异常:
Caught std::exception!
Exception Message: Enable multithreading to use std::thread: Operation not permitted
Terminating...
Using the -pthread
and -lpthread
(Edit: and also only -pthread
without -lpthread
) compile parameter doesn't work. The compiler arguments are (I am using the cook build system):
使用-pthread和-lpthread(编辑:也只有-pthread而不使用-lpthread)编译参数不起作用。编译器参数是(我正在使用cook构建系统):
-pthread -std=c++11 -fmessage-length=0 -fPIC -Wchar-subscripts ...(lots of -W* here)
... -Wunused-variable -m64 -D__64BIT__ -pthread -lpthread
and the linker arguments (duplicate parameters due to the build system):
和链接器参数(由于构建系统导致的重复参数):
-pthread -lpthread -std=c++11 -pthread -lpthread -std=c++11 -shared -fPIC -Wl,-Bsymbolic -Wl,--allow-shlib-undefined -pthread -lpthread
calling ldd on my library gives the following output
在我的库上调用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)
and on the main program
以及主程序
$ 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)
The pthread library is linked to my shared library but not to the main program. This answer states that you have to link pthreads to the main program but the 2nd comment on this answer (by @R..) says it isn't necessary (which sounds logically).
pthread库链接到我的共享库,但不链接到主程序。这个答案表明你必须将pthreads链接到主程序,但是对这个答案的第二个评论(由@R ..)表示没有必要(这听起来是逻辑上的)。
Unfortunately I don't know anything about the loading mechanics of the whole system, except that my library is using another C++ library as API.
不幸的是,除了我的库使用另一个C ++库作为API之外,我对整个系统的加载机制一无所知。
Note that other C++11 features do work (and libstdc++.so is in the dependencies of my library) but C++11 multithreading is not (although libpthread.so is also in the dependencies of my library).
请注意,其他C ++ 11功能可以工作(和libstdc ++。所以在我的库的依赖项中),但C ++ 11多线程不是(尽管libpthread.so也在我的库的依赖项中)。
Using a threading class from a library contained in the program itself is working (and this thread class seems to use pthreads too).
使用程序本身包含的库中的线程类是可行的(这个线程类似乎也使用pthreads)。
I have also tried to use -fabi-version=0
or -fabi-version=2
because the main program is compiled with gcc 4.1.2 with my library but it didn't change anything.
我也尝试使用-fabi-version = 0或-fabi-version = 2,因为主程序是使用gcc 4.1.2和我的库编译的,但它没有改变任何东西。
Is there anything I have overlooked or a compiler option I can use to make it work? Or does it seem to be a problem of my program environment? Any ideas are welcome.
有什么我忽略的或者我可以使用编译器选项来使其工作吗?或者它似乎是我的程序环境的问题?欢迎任何想法。
Edit:
I tried using -Wl,-no-as-needed
(as suggested in the comments) but it unfortunately didn't change anything.
我尝试使用-Wl,-no-as-needed(如评论中所建议的),但遗憾的是它没有改变任何东西。
Using clang 3.5 instead of gcc 4.8 also didn't work.
使用clang 3.5代替gcc 4.8也不起作用。
Creating a small test application which loads a shared library (like in the answer below by @chill) works (even without the compiler flag) as long as I use gcc 4.8 or clang 3.5 for both main application and shared library. When using gcc 4.1 for the main program however the main program even fails to load the library (which is working in my 'real' application). I think there might be a problem with the differen ABIs of the compilers.
只要我对主应用程序和共享库使用gcc 4.8或clang 3.5,创建一个加载共享库的小型测试应用程序(如下面@chill的答案)就可以工作(即使没有编译器标志)。当使用gcc 4.1作为主程序时,主程序甚至无法加载库(它在我的'真实'应用程序中工作)。我认为编译器的不同ABI可能存在问题。
Using pthreads directly from pthread.h
seems to work (although the program currently terminates on pthread_join
without error message, but I'm still testing there...)
直接从pthread.h使用pthreads似乎工作(虽然程序当前终止在pthread_join没有错误消息,但我仍然在那里测试...)
Edit 2:
Running the 'test program' with LD_LIBRARY_PATH=$(pwd):$LD_LIBRARY_PATH
(because the gcc 4.8 library paths need to be in there too, thanks @MvG) did run the program but crashed again with the Enable multithreading to use std::thread: Operation not permitted
exception.
使用LD_LIBRARY_PATH = $(pwd)运行'test program':$ LD_LIBRARY_PATH(因为gcc 4.8库路径也需要在那里,感谢@MvG)确实运行程序但是再次使用Enable multithreading崩溃以使用std :: thread:操作不允许异常。
I checked all other libraries that are loaded (found them with strace ./main_program 2>&1 | grep '^open(".*\.so"'
[see here]) and checked them all with ldd
. They all depend on the same libraries (with the same paths). ldd
outputs (on all of them):
我检查了所有其他加载的库(发现它们是strace ./main_program 2>&1 | grep'^ open(“。* \。so”'[见这里])并用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)
(that they all don't depend on libpthread.so.0 except my library and one other (but it's the same /lib64/libpthread.so.0
))
(他们都不依赖于libpthread.so.0,除了我的库和另外一个(但它是相同的/lib64/libpthread.so.0))
Some libraries do have more dependencies (which don't seem thread-related) but there don't seem to be any 'conflicting' dependencies (there are no dependencies to different versions/paths of the same library in any of these libraries).
有些库确实有更多的依赖项(它们似乎与线程无关)但似乎没有任何“冲突”依赖项(在这些库中没有依赖于同一个库的不同版本/路径)。
2 个解决方案
#1
12
In thread.cc
you can read that this exception is generated if __gthread_active_p
returns false. That call simply checks whether or not a given symbol is available. The symbol in question is a weak symbol: it does not have to be present, but its presence is checked to decide whether or not threading is supported.
在thread.cc中,您可以读到如果__gthread_active_p返回false则会生成此异常。该调用只是检查给定符号是否可用。有问题的符号是一个弱符号:它不必存在,但检查它的存在以决定是否支持线程。
But what does presence of a symbol mean? In this case it means that the symbol is in the list of symbol tables which the library in question (libgcc_s.so.1
in my case) searches for symbol definitions. That includes symbols exported by the application itself, but also symbols exported by all the libraries loaded before it. However, it does not include libraries loaded afterwards. Unfortunately, if libgcc
is loaded before libpthread
, then the symbol is not available on its search domain. So it reports threading as unsupported. I guess you have some other C++ module loaded before the multi-threaded one, so you encounter this problem.
但是符号的存在意味着什么?在这种情况下,它表示符号位于符号表列表中,有问题的库(在我的例子中为libgcc_s.so.1)搜索符号定义。这包括应用程序本身导出的符号,以及由之前加载的所有库导出的符号。但是,它不包括之后加载的库。不幸的是,如果在libpthread之前加载了libgcc,那么该符号在其搜索域中不可用。因此它报告线程不受支持。我猜你在多线程之前加载了一些其他的C ++模块,所以你遇到了这个问题。
One solution which works in my reproducing example is setting LD_PRELOAD=/lib64/libpthread.so.0
in the environment used to call the binary. That loads libpthread
up front, so its symbols are available to satisfy the weak symbol linkage. This won't work for setuid/setgid binaries, and might be considered an ugly hack in other cases as well, so I'm interested in cleaner solutions. Nevertheless, this gets the job done most of the time.
在我的再现示例中使用的一个解决方案是在用于调用二进制文件的环境中设置LD_PRELOAD = / lib64 / libpthread.so.0。这会在前面加载libpthread,因此它的符号可用于满足弱符号链接。这对setuid / setgid二进制文件不起作用,在其他情况下也可能被认为是丑陋的黑客,所以我对更清晰的解决方案感兴趣。然而,这大部分时间都可以完成工作。
#2
3
I have been able to reproduce something very similar to your situation.
我能够重现与你的情况非常相似的东西。
First the test shared library source:
首先是测试共享库源:
#include <thread>
void f() {}
extern "C" int foo () {
std::thread(f).join();
return 0;
}
Compiled/linked with c++ -fPIC -shared -std=c++11 -pthread thrlib.cxx -o libthrlib.so
编译/链接c ++ -fPIC -shared -std = c ++ 11 -pthread thrlib.cxx -o libthrlib.so
Then the "main program":
然后是“主程序”:
#include <dlfcn.h>
int
main () {
void *lib = dlopen ("libthrlib.so", RTLD_NOW);
int (*foo)();
foo = (int (*)()) dlsym (lib, "foo");
foo ();
}
Compiled/linked with gcc main.c -ldl
使用gcc main.c -ldl编译/链接
The result of execution is:
执行结果是:
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)
Now, build the shared library, by adding to the command line options -Wl,-no-as-needed
现在,通过添加命令行选项-Wl,-no-as-needed来构建共享库
c++ -fPIC -shared -std=c++11 -pthread thrlib.cxx -o libthrlib.so -Wl,-no-as-needed
And the main program executes without an error.
主程序执行没有错误。
I run this on Ubuntu 13.10 with gcc 4.8.1, all of the above may in fact not be applicable to your environment, but the effects are very similar to yours, so there's a good chance the same solution will work for you, worth a try anyway :)
我使用gcc 4.8.1在Ubuntu 13.10上运行它,以上所有可能实际上不适用于您的环境,但效果与您的非常相似,因此同样的解决方案很有可能对您有用,值得尝试反正:)
PS. Another thing worth trying, add an explicit hard reference to pthread_cancel
in your library:
PS。另一件值得尝试的事情是,在库中添加对pthread_cancel的显式硬引用:
#include <pthread.h>
void *zzz = (void *) pthread_cancel;
#1
12
In thread.cc
you can read that this exception is generated if __gthread_active_p
returns false. That call simply checks whether or not a given symbol is available. The symbol in question is a weak symbol: it does not have to be present, but its presence is checked to decide whether or not threading is supported.
在thread.cc中,您可以读到如果__gthread_active_p返回false则会生成此异常。该调用只是检查给定符号是否可用。有问题的符号是一个弱符号:它不必存在,但检查它的存在以决定是否支持线程。
But what does presence of a symbol mean? In this case it means that the symbol is in the list of symbol tables which the library in question (libgcc_s.so.1
in my case) searches for symbol definitions. That includes symbols exported by the application itself, but also symbols exported by all the libraries loaded before it. However, it does not include libraries loaded afterwards. Unfortunately, if libgcc
is loaded before libpthread
, then the symbol is not available on its search domain. So it reports threading as unsupported. I guess you have some other C++ module loaded before the multi-threaded one, so you encounter this problem.
但是符号的存在意味着什么?在这种情况下,它表示符号位于符号表列表中,有问题的库(在我的例子中为libgcc_s.so.1)搜索符号定义。这包括应用程序本身导出的符号,以及由之前加载的所有库导出的符号。但是,它不包括之后加载的库。不幸的是,如果在libpthread之前加载了libgcc,那么该符号在其搜索域中不可用。因此它报告线程不受支持。我猜你在多线程之前加载了一些其他的C ++模块,所以你遇到了这个问题。
One solution which works in my reproducing example is setting LD_PRELOAD=/lib64/libpthread.so.0
in the environment used to call the binary. That loads libpthread
up front, so its symbols are available to satisfy the weak symbol linkage. This won't work for setuid/setgid binaries, and might be considered an ugly hack in other cases as well, so I'm interested in cleaner solutions. Nevertheless, this gets the job done most of the time.
在我的再现示例中使用的一个解决方案是在用于调用二进制文件的环境中设置LD_PRELOAD = / lib64 / libpthread.so.0。这会在前面加载libpthread,因此它的符号可用于满足弱符号链接。这对setuid / setgid二进制文件不起作用,在其他情况下也可能被认为是丑陋的黑客,所以我对更清晰的解决方案感兴趣。然而,这大部分时间都可以完成工作。
#2
3
I have been able to reproduce something very similar to your situation.
我能够重现与你的情况非常相似的东西。
First the test shared library source:
首先是测试共享库源:
#include <thread>
void f() {}
extern "C" int foo () {
std::thread(f).join();
return 0;
}
Compiled/linked with c++ -fPIC -shared -std=c++11 -pthread thrlib.cxx -o libthrlib.so
编译/链接c ++ -fPIC -shared -std = c ++ 11 -pthread thrlib.cxx -o libthrlib.so
Then the "main program":
然后是“主程序”:
#include <dlfcn.h>
int
main () {
void *lib = dlopen ("libthrlib.so", RTLD_NOW);
int (*foo)();
foo = (int (*)()) dlsym (lib, "foo");
foo ();
}
Compiled/linked with gcc main.c -ldl
使用gcc main.c -ldl编译/链接
The result of execution is:
执行结果是:
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)
Now, build the shared library, by adding to the command line options -Wl,-no-as-needed
现在,通过添加命令行选项-Wl,-no-as-needed来构建共享库
c++ -fPIC -shared -std=c++11 -pthread thrlib.cxx -o libthrlib.so -Wl,-no-as-needed
And the main program executes without an error.
主程序执行没有错误。
I run this on Ubuntu 13.10 with gcc 4.8.1, all of the above may in fact not be applicable to your environment, but the effects are very similar to yours, so there's a good chance the same solution will work for you, worth a try anyway :)
我使用gcc 4.8.1在Ubuntu 13.10上运行它,以上所有可能实际上不适用于您的环境,但效果与您的非常相似,因此同样的解决方案很有可能对您有用,值得尝试反正:)
PS. Another thing worth trying, add an explicit hard reference to pthread_cancel
in your library:
PS。另一件值得尝试的事情是,在库中添加对pthread_cancel的显式硬引用:
#include <pthread.h>
void *zzz = (void *) pthread_cancel;