Reputation: 2571
I just came to Linux c++ programming from Windows. Trying to make a shared library libso.so, which uses std::thread
. The shared library will be loaded by other people and call the export function. The test code:
// so.cpp, the .so library
#include <iostream>
#include <thread>
using namespace std;
extern "C"
void run() {
cout << "run() begin" << endl;
std::thread t([] {
});
t.join();
cout << "run() end" << endl;
}
// test.cpp, the test loader
#include <dlfcn.h>
int main() {
typedef void (*run_t)();
auto dll = dlopen("libso.so", RTLD_LAZY);
run_t run = (run_t) dlsym(dll, "run");
run();
}
// The CMakeLists.txt file
cmake_minimum_required(VERSION 3.0)
PROJECT (test)
Include_Directories(${PROJECT_SOURCE_DIR})
Link_Directories(${PROJECT_BINARY_DIR})
add_library(so SHARED so.cpp )
target_link_libraries(so pthread)
add_executable( test test.cpp )
target_link_libraries(test pthread dl)
It crashes in the run()
function, the output is:
run() begin
“./test” terminated by signal SIGSEGV (Address boundary error)
The std::thread
seems work fine in executable, but not in shared library. What do I miss?
The environment: g++ 9.3.0, cmake 3.16.3
Edited:
Try ldd.
ldd ./test
shows no pthread
, but ldd ./libso.so
has libpthread.so.0
.
The difference of linking param with SET(CMAKE_VERBOSE_MAKEFILE TRUE)
// linking executable 'test'
/usr/bin/c++ -rdynamic CMakeFiles/test.dir/test.cpp.o -o test -L/e/c/1/kali -Wl,-rpath,/e/c/1/kali -ldl -lpthread
// linking library 'libso.so'
/usr/bin/c++ -fPIC -shared -Wl,-soname,libso.so -o libso.so CMakeFiles/so.dir/so.cpp.o -L/e/c/2/kali -Wl,-rpath,/e/c/1/kali -lpthread
The only difference is -fPIC
, I googled and add set_property(TARGET test PROPERTY POSITION_INDEPENDENT_CODE ON)
to the executable, but nothing changed.
Workaround 1
Since the .so has libpthread.so.0
, I tried to add the code in .so to the executable:
int main() {
std::thread t([]{}); // to make executable linking to `pthread`
t.join();
// ... load dll and call run function
}
And it works, now the ldd ./test
shows libpthread.so.0
and no crash. Which means: if a shared library uses std::thread
and an executable wants to load it, the executable itself must also use std::thread
.
Workaround 2:
The std::thread
works fine in executable, but crashes in shared library. Found some related discuss, the walkaround is using boost::thread
instead of std::thread
and linking to boost_thread
library, no crash .
Upvotes: 3
Views: 728
Reputation: 16925
I guess the problem is more related to dynamic linking than threads.
The call dlopen("libso.so", RTLD_LAZY)
will try to
find the library in a standard location.
Except if you set the LD_LIBRARY_PATH
environment
variable to something that includes .
(the current
directory) this library won't be found.
For a simple test you can either:
export LD_LIBRARY_PATH=.
in the terminal before
launching your program,dlopen("./libso.so", RTLD_LAZY)
in your source code.After using dlopen()
or dlsym()
if you obtain a null
pointer, then dlerror()
can help displaying the reason
of the failure.
Note that on Windows the current directory and the executable path are standard search paths for dynamic libraries; on UNIX this is not the case, which could be surprising when changing the target platform.
edit
cmake
uses the -Wl,-rpath
option to hardcode a library search
path in the executable, so all of what I explained above becomes
useless for this problem.
Assuming the dynamic library is found, the only way I can reproduce
the crash is to forget pthread
in target_link_libraries
for
test
.
second edit
I finally managed to reproduce the crash with Ubuntu (in WSL).
Apparently your linker decides to ignore the libraries that are
not directly used by the executable.
This behavior suggests that the linker option --as-needed
is
switched on by default.
To contradict this default behaviour, you need to pass the linker
option --no-as-needed
before -lpthread
.
This way, you don't have to insert a dummy thread in your
executable.
Using set(CMAKE_CXX_FLAGS -Wl,--no-as-needed)
in the CMakeLists.txt
file you provide did the trick for me.
Upvotes: 3