【问题标题】:std::thread weak when using -static-libstdc++, thus causing crash at runtime使用 -static-libstdc++ 时 std::thread 弱,从而导致运行时崩溃
【发布时间】:2018-07-25 19:25:43
【问题描述】:

我需要构建一个可移植的共享对象,它是 Linux 上另一个软件的插件。我对该主题进行了一些阅读,得出的结论是,我应该使用相当旧的 glibc(以提供与旧系统的兼容性)构建一个 sysrooted gcc(如果重要,gcc 5.4.0),链接到@987654321 @ 和 -static-libgcc 从而到达了一个点,我有一些东西只依赖于主机 glibc 和一些其他的小东西,它们将永远存在。

现在,我做了所有这些,现在我遇到了一个奇怪的崩溃 - 分段错误发生在代码调用 std::thread 的地方,而 gdb 实际上显示堆栈帧在 libstdc++.so.6 内(应该在哪里不是,我的共享对象的ldd 也没有列出libstdc++.so)。崩溃时的栈顶是:

#0  0x0000000000000000 in ?? ()
#1  0x00007ffff79075e3 in std::thread::_M_start_thread(std::shared_ptr<std::thread::_Impl_base>, void (*)()) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 # THIS SHOULD NOT BE HERE RIGHT?
#2  0x00007ffff5a25a5c in std::thread::thread<void (ReferenceAnalytics::*)(std::timed_mutex&), ReferenceAnalytics*&, std::reference_wrapper<std::timed_mutex> >
    (this=0x7fffffffcf40, __f=
    @0x7fffffffcf60: (void (ReferenceAnalytics::*)(ReferenceAnalytics * const, std::timed_mutex &)) 0x7ffff5a1750c <ReferenceAnalytics::WorkerThreadMethod(std::timed_mutex&)>)
    at /home/developer/Toolchains/x86_64-unknown-linux-gnu/x86_64-unknown-linux-gnu/include/c++/5.4.0/thread:137 # Looks like my toolchain

所以,我做了一些阅读,然后使用 nm 发现我的共享对象具有所有 std::thread 东西,如 ctor、dtor、swap ......定义为弱符号(我假设这会导致冲突如果加载插件的主机使用动态libstdc++,然后我的呼叫被路由到那里并且所有地狱都崩溃了,是这样吗?)。

我对谷歌搜索和阅读的进一步尝试没有给我一个答案,我该如何控制这一点,因为在我的 sysrooted gcc 中强制将 std::thread 的东西解析为静态 libstdc++

此外,我创建了一个小型可执行文件,它只在我的共享对象上执行dlopen,然后调用一个内部构造线程的方法 - 如果可执行文件也是使用-static-libstdc++ 构建的,那么一切都很好,如果不是,崩溃发生。所以我假设我关于std::thread 的弱符号被解析到主机libstdc++ 的理论是正确的,但是如何解决这个问题呢?

【问题讨论】:

    标签: c++ linux shared-libraries libstdc++ gcc5


    【解决方案1】:

    如果您在不隐藏 libstdc++ 符号的情况下将 DSO 与 libstdc++ 静态链接,并且主程序也与 libstdc++ 链接,则当 DSO 打开时,主程序中的符号定义将插入/抢占 DSO 中的定义dlopen.

    但是,由于主程序没有链接到 libpthread,进程映像中的系统 libstdc++ DSO 发现 libpthread 符号不可用(空),因此禁用了线程支持。但是,您的 DSO 需要线程支持,但无法从系统 libstdc++ 中获取。

    作为一种直接的解决方法,您可以在 DSO 中隐藏所有静态链接的 libstdc++ 符号。然后不会发生任何插入,并且您的 DSO 将实际使用 DSO 本身中的 libstdc++ 副本,这已经确定该进程中不应有任何线程支持。

    但这可能无法解决您的所有问题,因为通过dlopen 延迟加载 libpthread 有其问题。我们在这里修复了一个错误:

    但是您的发行版可能没有该修复程序,我预计还会有其他问题,其中之一是:这里实际上需要 libstdc++ 的第二个静态链接副本,因为系统 libstdc++ 已在没有线程支持的情况下加载(因为libpthread 的符号绑定时未加载,导致您观察到崩溃),因此您不能使用它来创建线程。它还激活了优化,使库不是线程安全的(避免原子指令和类似的东西)。

    【讨论】:

    • 好的,谢谢,这很有意义,我会试试看。至少仍然是一个赞成票。我是不是太偏执了,也许我应该放弃使用静态 libstdc++ 的想法? stdlibc++ 是否向后兼容,并且使用带有 gcc 5.4.0 的工具链适用于现代发行版?
    • 我更新了答案,提到由于 libpthread 的延迟加载,您实际上需要 libstdc++ 的第二个副本。
    • GCC 5 对于广泛的发行版支持来说太新了,而 libdstdc++ 向后兼容,但不向前兼容,因此您需要在最旧的受支持发行版上构建。一些工具链使用混合链接模型,其中仅静态链接较新的 libstdc++ 位,以便应用程序使用系统编译器中的旧的、未更改的系统 libstdc++ 运行。然而,更难(阅读:截至目前尚未解决)的问题是 libpthread 的 dlopen 在之前没有加载 libpthread 的进程中,并且期望 pthread_create 可以工作。
    • 非常感谢您阐明了这一点:)现在至少我觉得我明白发生了什么。
    • 非常感谢,现在看来一切正常。还有一件事,我用链接脚本实现了隐藏,但是我看了很多文章,说-fvisibility=hidden更胜一筹。但是,如果我理解正确,-fvisibility=hidden(我在编译我的源代码时已经在使用它)不会控制来自存档的符号的可见性(比如在这种情况下libstdc++.a),因此链接器脚本是唯一的方法,对吧?
    猜你喜欢
    • 2020-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-03
    • 1970-01-01
    相关资源
    最近更新 更多