在共享库中使用 std::thread 会导致 SIGSEGV
using std::thread in shared library causes SIGSEGV
我刚从 Windows 转入 Linux c++ 编程。尝试制作共享库 libso.so,它使用 std::thread
。共享库会被其他人加载并调用导出函数。测试代码:
// 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)
在run()
函数中崩溃,输出为:
run() begin
“./test” terminated by signal SIGSEGV (Address boundary error)
std::thread
在可执行文件中似乎工作正常,但在共享库中却不行。我想念什么?
环境:g++9.3.0,cmake 3.16.3
已编辑:
试试 ldd.
ldd ./test
显示没有 pthread
,但 ldd ./libso.so
有 libpthread.so.0
。
链接参数与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
唯一的区别是 -fPIC
,我搜索并添加 set_property(TARGET test PROPERTY POSITION_INDEPENDENT_CODE ON)
到可执行文件,但没有任何改变。
解决方法 1
由于.so有libpthread.so.0
,我尝试将.so中的代码添加到可执行文件中:
int main() {
std::thread t([]{}); // to make executable linking to `pthread`
t.join();
// ... load dll and call run function
}
它起作用了,现在 ldd ./test
显示 libpthread.so.0
并且没有崩溃。这意味着:如果共享库使用 std::thread
并且可执行文件想要加载它,则可执行文件本身也必须使用 std::thread
.
解决方法 2:
std::thread
在可执行文件中运行良好,但在共享库中崩溃。发现一些 ,walkaround 使用 boost::thread
而不是 std::thread
并链接到 boost_thread
库,没有崩溃。
我猜这个问题与动态链接更相关
比线程。
呼叫dlopen("libso.so", RTLD_LAZY)
将尝试
在标准位置找到图书馆。
除非你设置 LD_LIBRARY_PATH
环境
变量为包含 .
的内容(当前
目录)将找不到该库。
对于简单的测试,您可以:
- 之前在终端使用
export LD_LIBRARY_PATH=.
启动您的程序,
- 在您的源代码中使用
dlopen("./libso.so", RTLD_LAZY)
。
在使用 dlopen()
或 dlsym()
之后,如果您获得一个 null
指针,那么 dlerror()
可以帮助显示原因
的失败。
注意 Windows 当前目录和可执行文件
path 是动态库的标准搜索路径;在 UNIX 上
事实并非如此,这在改变时可能会令人惊讶
目标平台。
编辑
cmake
使用 -Wl,-rpath
选项硬编码库搜索
可执行文件中的路径,所以我上面解释的所有内容都变成了
对这个问题没用。
假设找到了动态库,只有这样我才能重现
崩溃是 忘记 pthread
in target_link_libraries
for
test
.
第二次编辑
我终于设法用 Ubuntu 重现了崩溃(在 WSL 中)。
显然你的链接器决定忽略那些是
不直接被可执行文件使用。
此行为表明链接器选项 --as-needed
是
默认开启。
为了与这种默认行为相矛盾,您需要传递链接器
选项 --no-as-needed
之前 -lpthread
.
这样,您就不必在您的
可执行文件。
在 CMakeLists.txt
中使用 set(CMAKE_CXX_FLAGS -Wl,--no-as-needed)
你提供的文件对我有用。
我刚从 Windows 转入 Linux c++ 编程。尝试制作共享库 libso.so,它使用 std::thread
。共享库会被其他人加载并调用导出函数。测试代码:
// 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)
在run()
函数中崩溃,输出为:
run() begin
“./test” terminated by signal SIGSEGV (Address boundary error)
std::thread
在可执行文件中似乎工作正常,但在共享库中却不行。我想念什么?
环境:g++9.3.0,cmake 3.16.3
已编辑:
试试 ldd.
ldd ./test
显示没有 pthread
,但 ldd ./libso.so
有 libpthread.so.0
。
链接参数与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
唯一的区别是 -fPIC
,我搜索并添加 set_property(TARGET test PROPERTY POSITION_INDEPENDENT_CODE ON)
到可执行文件,但没有任何改变。
解决方法 1
由于.so有libpthread.so.0
,我尝试将.so中的代码添加到可执行文件中:
int main() {
std::thread t([]{}); // to make executable linking to `pthread`
t.join();
// ... load dll and call run function
}
它起作用了,现在 ldd ./test
显示 libpthread.so.0
并且没有崩溃。这意味着:如果共享库使用 std::thread
并且可执行文件想要加载它,则可执行文件本身也必须使用 std::thread
.
解决方法 2:
std::thread
在可执行文件中运行良好,但在共享库中崩溃。发现一些 boost::thread
而不是 std::thread
并链接到 boost_thread
库,没有崩溃。
我猜这个问题与动态链接更相关 比线程。
呼叫dlopen("libso.so", RTLD_LAZY)
将尝试
在标准位置找到图书馆。
除非你设置 LD_LIBRARY_PATH
环境
变量为包含 .
的内容(当前
目录)将找不到该库。
对于简单的测试,您可以:
- 之前在终端使用
export LD_LIBRARY_PATH=.
启动您的程序, - 在您的源代码中使用
dlopen("./libso.so", RTLD_LAZY)
。
在使用 dlopen()
或 dlsym()
之后,如果您获得一个 null
指针,那么 dlerror()
可以帮助显示原因
的失败。
注意 Windows 当前目录和可执行文件 path 是动态库的标准搜索路径;在 UNIX 上 事实并非如此,这在改变时可能会令人惊讶 目标平台。
编辑
cmake
使用 -Wl,-rpath
选项硬编码库搜索
可执行文件中的路径,所以我上面解释的所有内容都变成了
对这个问题没用。
假设找到了动态库,只有这样我才能重现
崩溃是 忘记 pthread
in target_link_libraries
for
test
.
第二次编辑
我终于设法用 Ubuntu 重现了崩溃(在 WSL 中)。
显然你的链接器决定忽略那些是
不直接被可执行文件使用。
此行为表明链接器选项 --as-needed
是
默认开启。
为了与这种默认行为相矛盾,您需要传递链接器
选项 --no-as-needed
之前 -lpthread
.
这样,您就不必在您的
可执行文件。
在 CMakeLists.txt
中使用 set(CMAKE_CXX_FLAGS -Wl,--no-as-needed)
你提供的文件对我有用。