【问题标题】:Unexpected call to a function from dynamically loaded library从动态加载的库中意外调用函数
【发布时间】:2021-10-18 20:44:04
【问题描述】:

在执行 thread sanitizer 测试期间,我注意到一些让我有点担心的事情(可能更多是因为缺乏理解)。

下面的调用堆栈部分表示线程清理器检测到的数据竞争:

Previous write of size 8 at 0x7b4400020080 by thread T4 (mutexes: write M258529629327854840):

#0 malloc <null> (libtsan.so.0+0x2cbe2)
...
#11 std::thread::_Invoker<std::tuple<void (MyCustomWorker::*)(), MyCustomWorker*> >::operator()() <null> (myApp+0x11de952)
#12 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (MyCustomWorker::*)(), MyCustomWorker*> > >::_M_run() <null> (myApp+0x11de524)
#13 execute_native_thread_routine <null> (liboatpp.so+0x3e4d3e)

这个堆栈有趣的部分不是数据竞争本身,而是调用堆栈的底部:

#13 execute_native_thread_routine <null> (liboatpp.so+0x3e4d3e)

二进制文件使用 oatpp 库,但发生竞争的代码与库完全无关。据我了解,这行说:

  • std::thread 库的函数execute_native_thread_routineliboatpp.so 调用
  • MyCustomWorker 运行的线程是由liboatpp.so 代码创建的

因此,出于某种原因,我的二进制调用 std::thread 通过 liboatpp.so 运行,而不是我自己预期的那样。我已经(或考虑过)的:

  1. liboatpp 的符号列表,用于搜索在链接 MyApp 期间可能查找的任何“thread”相关符号。所有符号似乎都包含与 oatpp 相关的名称。 nm -gD liboatpp.so | grep thread | grep run的结果:
00000000003c7054 W _ZNSt6thread11_State_implINS_8_InvokerISt5tupleIJMN5oatpp3web6server13HttpProcessor4TaskEFvvES7_EEEEE6_M_runEv
...
(similar oatpp specific entries)
  1. 通过执行:ltrace -l liboatpp.so ./MyApp | grep threadliboatpp.so 中搜索对线程相关函数的调用,但没有发现任何有趣的东西。我希望找到一些负责该问题的电话。

  2. 我还检查了导出LD_DEBUG=all 后动态链接器的输出,但也没有发现任何有趣的东西。在这里,我希望在 MyApp 中找到未解析的符号,以便在 liboatpp.so 中找到与线程相关的函数。没有观察到这样的事情。

已编辑:

libstdc++.a 绝对没有有意链接到 liboatpp.so。我确认它没有链接的证据是,如果我在 libstdc++.a (nm -g /opt/rh/devtoolset-8/root/usr/lib/gcc/x86_64-redhat-linux/8/libstdc++.a | grep thread) 中采用以下定义的符号之一:

0000000000000000 T _ZNSt6thread20hardware_concurrencyEv
0000000000000000 T _ZNSt6thread4joinEv
0000000000000000 T _ZNSt6thread6detachEv

然后在liboattp中搜索nm -gD liboatpp.so | grep thread:

        U _ZNSt6thread6detachEv

但它被发现为未定义的符号。

Oatpp 链接命令如下:

c++ -fPIC   -shared -Wl,-soname,liboatpp.so -o liboatpp.so CMakeFiles/oatpp.dir/oatpp/algorithm/CRC.cpp.o (...) -lpthread -latomic

【问题讨论】:

  • 我不知道 liboatpp 是什么,但从堆栈跟踪看来,这个库确实创建了一个线程。为何如此令人惊讶?
  • 这对我来说并不奇怪。 Oat++ 是一个“网络框架”,可能会创建线程。这并不出人意料。也许我错过了你的观点? (顺便说一句,Oat++ 网站似乎建议您将其构建为 static 库并链接到它:oatpp.io/docs/installation/unix-linux/…。)
  • @yzt 感谢您指出这一点,我一定会用适当的标志重建 Oat++。
  • 你的“证据”证明了什么都没有libstdc++.a 由许多 .o 文件组成,并且并非所有文件都最终包含在 liboatpp.so 中。显示您如何链接liboatpp.so可能是一个好的开始。
  • nm 输出过滤为全局符号(-g 标志)在这里会适得其反。

标签: c++ linux gcc linker dynamic-linking


【解决方案1】:

execute_native_thread_routine() 似乎是 GCC 的 libstdc++ 的一部分。

目前尚不清楚您是如何将liboatpp.so 链接到其中的,但您可能会将部分libstdc++.a 链接到其中。

假设发生了这样的情况,liboatpp.sos 版本的execute_native_thread_routine() 被作为任何std::thread 创建的一部分被调用也就不足为奇了——链接器将使用此例程的第一个可用定义,而这只是恰好在liboatpp.so。如果您没有将libstdc++.a 链接到liboatpp.so,则链接器将使用libstdc++.so.6s 版本。

【讨论】:

    猜你喜欢
    • 2015-08-07
    • 1970-01-01
    • 2018-06-04
    • 2011-07-28
    • 2020-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-26
    相关资源
    最近更新 更多