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打开库时,也不会发生这种情况
因此,一个解决方案是切换到libc++。显然,这只适用于从不导出依赖于任何
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      =====