C++ 在加载了dlopen的库中使用std::thread将导致sigsev
我最近使用C++ 在加载了dlopen的库中使用std::thread将导致sigsev,c++,glibc,dlopen,stdthread,C++,Glibc,Dlopen,Stdthread,我最近使用std::thread和dlopen发现了一个奇怪的行为 基本上,当我在使用dlopen加载的库中执行std::thread时,我会收到一个sigsev。库本身是针对pthread链接的,调用dlopen的可执行文件不是 一旦我将可执行文件链接到pthread或库本身,一切正常。但是,我们使用的是基于插件的基础设施,我们不知道应用程序本身是否与pthread链接。因此,始终针对pthread链接可执行文件不是一个选项 请参阅附件中的一些代码,以复制该问题。目前我不确定是什么导致了这个问
std::thread
和dlopen
发现了一个奇怪的行为
基本上,当我在使用dlopen
加载的库中执行std::thread
时,我会收到一个sigsev。库本身是针对pthread链接的,调用dlopen
的可执行文件不是
一旦我将可执行文件链接到pthread
或库本身,一切正常。但是,我们使用的是基于插件的基础设施,我们不知道应用程序本身是否与pthread链接。因此,始终针对pthread链接可执行文件不是一个选项
请参阅附件中的一些代码,以复制该问题。目前我不确定是什么导致了这个问题。这是gcc、glibc、libstdc++还是ld.so的问题?有没有解决这个问题的便捷方法?看起来这是相关的,但我使用的是glibc2.27(debian测试)
从库中调用pthread\u create
本身似乎可以工作
你好,cpp
编辑
我忘了提到:如果我使用LD_DEBUG=all
运行错误示例(即示例1),则程序在查找pthread_create
时崩溃。更有趣的是,以前对pthread\u create
的查找成功:
8111: symbol=_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_; lookup in file=/usr/lib/x86_64-linux-gnu/libstdc++.so.6 [0]
8111: binding file ./libhello.so [0] to /usr/lib/x86_64-linux-gnu/libstdc++.so.6 [0]: normal symbol `_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_' [GLIBCXX_3.4]
8111: symbol=pthread_create; lookup in file=./example1 [0]
8111: symbol=pthread_create; lookup in file=/lib/x86_64-linux-gnu/libdl.so.2 [0]
8111: symbol=pthread_create; lookup in file=/usr/lib/x86_64-linux-gnu/libstdc++.so.6 [0]
8111: symbol=pthread_create; lookup in file=/lib/x86_64-linux-gnu/libm.so.6 [0]
8111: symbol=pthread_create; lookup in file=/lib/x86_64-linux-gnu/libgcc_s.so.1 [0]
8111: symbol=pthread_create; lookup in file=/lib/x86_64-linux-gnu/libc.so.6 [0]
8111: symbol=pthread_create; lookup in file=/lib64/ld-linux-x86-64.so.2 [0]
8111: symbol=pthread_create; lookup in file=./libhello.so [0]
8111: symbol=pthread_create; lookup in file=/usr/lib/x86_64-linux-gnu/libstdc++.so.6 [0]
8111: symbol=pthread_create; lookup in file=/lib/x86_64-linux-gnu/libm.so.6 [0]
8111: symbol=pthread_create; lookup in file=/lib/x86_64-linux-gnu/libgcc_s.so.1 [0]
8111: symbol=pthread_create; lookup in file=/lib/x86_64-linux-gnu/libpthread.so.0 [0]
8111: binding file ./libhello.so [0] to /lib/x86_64-linux-gnu/libpthread.so.0 [0]: normal symbol `pthread_create' [GLIBC_2.2.5]
8111: symbol=_ZTVNSt6thread6_StateE; lookup in file=./example1 [0]
8111: symbol=_ZTVNSt6thread6_StateE; lookup in file=/lib/x86_64-linux-gnu/libdl.so.2 [0]
8111: symbol=_ZTVNSt6thread6_StateE; lookup in file=/usr/lib/x86_64-linux-gnu/libstdc++.so.6 [0]
8111: binding file ./libhello.so [0] to /usr/lib/x86_64-linux-gnu/libstdc++.so.6 [0]: normal symbol `_ZTVNSt6thread6_StateE' [GLIBCXX_3.4.22]
...
8111: binding file ./libhello.so [0] to ./libhello.so [0]: normal symbol `_ZNSt10_Head_baseILm0EPNSt6thread6_StateELb0EE7_M_headERS3_'
8111: symbol=_ZNSt6thread15_M_start_threadESt10unique_ptrINS_6_StateESt14default_deleteIS1_EEPFvvE; lookup in file=./example1 [0]
8111: symbol=_ZNSt6thread15_M_start_threadESt10unique_ptrINS_6_StateESt14default_deleteIS1_EEPFvvE; lookup in file=/lib/x86_64-linux-gnu/libdl.so.2 [0]
8111: symbol=_ZNSt6thread15_M_start_threadESt10unique_ptrINS_6_StateESt14default_deleteIS1_EEPFvvE; lookup in file=/usr/lib/x86_64-linux-gnu/libstdc++.so.6 [0]
8111: binding file ./libhello.so [0] to /usr/lib/x86_64-linux-gnu/libstdc++.so.6 [0]: normal symbol `_ZNSt6thread15_M_start_threadESt10unique_ptrINS_6_StateESt14default_deleteIS1_EEPFvvE' [GLIBCXX_3.4.22]
8111: symbol=pthread_create; lookup in file=./example1 [0]
8111: symbol=pthread_create; lookup in file=/lib/x86_64-linux-gnu/libdl.so.2 [0]
8111: symbol=pthread_create; lookup in file=/usr/lib/x86_64-linux-gnu/libstdc++.so.6 [0]
8111: symbol=pthread_create; lookup in file=/lib/x86_64-linux-gnu/libm.so.6 [0]
8111: symbol=pthread_create; lookup in file=/lib/x86_64-linux-gnu/libgcc_s.so.1 [0]
8111: symbol=pthread_create; lookup in file=/lib/x86_64-linux-gnu/libc.so.6 [0]
8111: symbol=pthread_create; lookup in file=/lib64/ld-linux-x86-64.so.2 [0]
./build.sh: line 18: 8111 Segmentation fault (core dumped) LD_DEBUG=all ./example1
===== end =====
一旦我将可执行文件链接到pthread或库本身,一切都正常。但是,我们使用的是基于插件的基础设施,我们不知道应用程序本身是否与pthread链接。因此,始终针对pthread链接可执行文件不是一个选项
相反:很少有系统支持应用程序突然变成“多线程”(您的系统显然不支持)
如果您需要支持潜在的多线程插件,那么您必须启动多线程就绪,这可以通过链接libpthread
来实现,或者通过添加-pthread
标记来编译和链接主可执行文件的行,从而实现更高的可移植性
这是gcc、glibc、libstdc++还是ld.so的问题
libstdc++
--GLIBC确实支持“突然多线程”执行,GCC根本不是运行时环境的一部分,而ld。因此
是GLIBC的一部分。我可以提供一些背景知识来解释为什么会出现segfault,但不幸的是没有解决方案
这似乎是libstdc++
的一个问题:从技术上讲,这个巨大的单片库依赖于libpthread
,但出于充分的理由,它们没有链接到libpthread
。现在,为了能够从根本不使用线程的程序加载libstdc++
,丢失的符号(例如pthread\u create
)必须在某个地方。所以libstdc++
将它们定义为弱符号
这些弱符号还用于在运行时检测是否实际加载了libpthread
。对于一个旧的ABI,甚至还有一个异常,如果没有加载pthread而不是调用一个弱定义的nullptr
,则会导致一个有意义的异常,这是我最不希望看到的
不幸的是,新ABI的运行时检查丢失了。相反,在编译调用\u M\u start\u thread
的代码时创建依赖项,并将指向pthread\u create
的指针传递到此函数中,从而为pthread\u create
创建了一个函数。不幸的是,该指针被丢弃,并且仍然使用较弱的nullptr
指针
现在,在链接/加载过程中,会出现一些问题,导致在有问题的情况下不会覆盖定义较弱的
pthread\u create
。我不确定在那里应用的确切解析规则-我假设它与加载libpthread
时已完全加载的libstdc++
有关。如果有任何补充答复能够澄清这一点,我将非常高兴。不幸的是,除了将主应用程序链接到-lpthread
或LD_PRELOAD=libpthread.so
(我不建议这样做)之外,似乎没有其他可行的方法来解决这个问题。问题在于libstdc++
- 对于C程序,这种情况不会发生李>
- 用LIbc++构建的C++程序也不会发生。李>
- 用LIPSTDC + +静态构建C++程序,这也不会发生。李>
- 使用libc++构建库时,即使调用程序是使用libstdc++动态构建的,也不会发生这种情况
- 当程序使用RTLD_GLOBAL打开库时,也不会发生这种情况
std::
类型的接口的情况。特别是,只导出C兼容接口的库应该是可以的
另一种解决方案是让您的库加载RTLD_GLOBAL(您可能需要将它分成两部分,一部分是主库,另一部分是一个小存根,它只加载带有RTLD_GLOBAL的主库)
同时,应该针对libstdc++提交一个bug,并等待修复。没有理由把它弄成那样
如果上述选项都不可行,那么唯一的解决方案似乎是在调用者和多线程模块之间实现完全隔离。使多线程模块成为一个单独的可执行文件,从插件中fork exec它,通过管道将参数/结果封送到它或从中封送出去
最后,调用者程序中总是存在预加载libpthread这一丑陋的解决方法。我可以用glibc/libstdc++重现分段错误,但libc++没有分段错误。>始终针对pthread链接可执行文件不是一个选项。相反,这是唯一的选择。别担心,即使插件不使用线程,也不会产生任何负面后果。>相反,这是唯一的选择。别担心,我会的
#include <iostream>
#include <dlfcn.h>
/** code from https://www.tldp.org/HOWTO/html_single/C++-dlopen/
*/
int main() {
std::cout << "C++ dlopen demo\n\n";
// open the library
std::cout << "Opening hello.so...\n";
void* handle = dlopen("./libhello.so", RTLD_LAZY);
if (!handle) {
std::cerr << "Cannot open library: " << dlerror() << '\n';
return 1;
}
// load the symbol
std::cout << "Loading symbol hello...\n";
typedef void (*hello_t)();
// reset errors
dlerror();
hello_t hello = (hello_t) dlsym(handle, "hello");
const char *dlsym_error = dlerror();
if (dlsym_error) {
std::cerr << "Cannot load symbol 'hello': " << dlsym_error <<
'\n';
dlclose(handle);
return 1;
}
// use it to do the calculation
std::cout << "Calling hello...\n";
hello();
// close the library
std::cout << "Closing library...\n";
dlclose(handle);
}
#!/bin/bash
echo "g++ -shared -fPIC -std=c++14 hello.cpp -o libhello.so -pthread"
g++ -shared -fPIC -std=c++14 hello.cpp -o libhello.so -pthread
echo "g++ example.cpp -o example1 -ldl"
g++ example.cpp -o example1 -ldl
echo "g++ example.cpp -o example2 -ldl -pthread"
g++ example.cpp -o example2 -ldl -pthread
echo "g++ example.cpp -o example3 -ldl -lhello -L ./"
g++ example.cpp -o example3 -ldl -lhello -L ./
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:$(pwd)
echo "===== example1 ====="
./example1
echo "===== end ====="
echo "===== example2 ====="
./example2
echo "===== end ====="
echo "===== example3 ====="
./example3
echo "===== end ====="
8111: symbol=_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_; lookup in file=/usr/lib/x86_64-linux-gnu/libstdc++.so.6 [0]
8111: binding file ./libhello.so [0] to /usr/lib/x86_64-linux-gnu/libstdc++.so.6 [0]: normal symbol `_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_' [GLIBCXX_3.4]
8111: symbol=pthread_create; lookup in file=./example1 [0]
8111: symbol=pthread_create; lookup in file=/lib/x86_64-linux-gnu/libdl.so.2 [0]
8111: symbol=pthread_create; lookup in file=/usr/lib/x86_64-linux-gnu/libstdc++.so.6 [0]
8111: symbol=pthread_create; lookup in file=/lib/x86_64-linux-gnu/libm.so.6 [0]
8111: symbol=pthread_create; lookup in file=/lib/x86_64-linux-gnu/libgcc_s.so.1 [0]
8111: symbol=pthread_create; lookup in file=/lib/x86_64-linux-gnu/libc.so.6 [0]
8111: symbol=pthread_create; lookup in file=/lib64/ld-linux-x86-64.so.2 [0]
8111: symbol=pthread_create; lookup in file=./libhello.so [0]
8111: symbol=pthread_create; lookup in file=/usr/lib/x86_64-linux-gnu/libstdc++.so.6 [0]
8111: symbol=pthread_create; lookup in file=/lib/x86_64-linux-gnu/libm.so.6 [0]
8111: symbol=pthread_create; lookup in file=/lib/x86_64-linux-gnu/libgcc_s.so.1 [0]
8111: symbol=pthread_create; lookup in file=/lib/x86_64-linux-gnu/libpthread.so.0 [0]
8111: binding file ./libhello.so [0] to /lib/x86_64-linux-gnu/libpthread.so.0 [0]: normal symbol `pthread_create' [GLIBC_2.2.5]
8111: symbol=_ZTVNSt6thread6_StateE; lookup in file=./example1 [0]
8111: symbol=_ZTVNSt6thread6_StateE; lookup in file=/lib/x86_64-linux-gnu/libdl.so.2 [0]
8111: symbol=_ZTVNSt6thread6_StateE; lookup in file=/usr/lib/x86_64-linux-gnu/libstdc++.so.6 [0]
8111: binding file ./libhello.so [0] to /usr/lib/x86_64-linux-gnu/libstdc++.so.6 [0]: normal symbol `_ZTVNSt6thread6_StateE' [GLIBCXX_3.4.22]
...
8111: binding file ./libhello.so [0] to ./libhello.so [0]: normal symbol `_ZNSt10_Head_baseILm0EPNSt6thread6_StateELb0EE7_M_headERS3_'
8111: symbol=_ZNSt6thread15_M_start_threadESt10unique_ptrINS_6_StateESt14default_deleteIS1_EEPFvvE; lookup in file=./example1 [0]
8111: symbol=_ZNSt6thread15_M_start_threadESt10unique_ptrINS_6_StateESt14default_deleteIS1_EEPFvvE; lookup in file=/lib/x86_64-linux-gnu/libdl.so.2 [0]
8111: symbol=_ZNSt6thread15_M_start_threadESt10unique_ptrINS_6_StateESt14default_deleteIS1_EEPFvvE; lookup in file=/usr/lib/x86_64-linux-gnu/libstdc++.so.6 [0]
8111: binding file ./libhello.so [0] to /usr/lib/x86_64-linux-gnu/libstdc++.so.6 [0]: normal symbol `_ZNSt6thread15_M_start_threadESt10unique_ptrINS_6_StateESt14default_deleteIS1_EEPFvvE' [GLIBCXX_3.4.22]
8111: symbol=pthread_create; lookup in file=./example1 [0]
8111: symbol=pthread_create; lookup in file=/lib/x86_64-linux-gnu/libdl.so.2 [0]
8111: symbol=pthread_create; lookup in file=/usr/lib/x86_64-linux-gnu/libstdc++.so.6 [0]
8111: symbol=pthread_create; lookup in file=/lib/x86_64-linux-gnu/libm.so.6 [0]
8111: symbol=pthread_create; lookup in file=/lib/x86_64-linux-gnu/libgcc_s.so.1 [0]
8111: symbol=pthread_create; lookup in file=/lib/x86_64-linux-gnu/libc.so.6 [0]
8111: symbol=pthread_create; lookup in file=/lib64/ld-linux-x86-64.so.2 [0]
./build.sh: line 18: 8111 Segmentation fault (core dumped) LD_DEBUG=all ./example1
===== end =====