【问题标题】:Ld magically overrides statically linked symbolsLd 神奇地覆盖了静态链接的符号
【发布时间】:2011-11-04 07:48:30
【问题描述】:

这几天我们都在处理非常奇怪的问题。

我什至无法理解它是如何发生的 - 当第三方 (MATLAB) 程序使用我们的共享库时,它会以某种方式用它自己的符号覆盖我们的一些符号(准确地说是增强)。这些符号是静态链接的并且 (!!) 本地的。

这是交易 - 我们使用 boost 1.47,MATLAB 有 boost 1.40。目前,从我们的库调用到它们的 boost(正则表达式)时,库调用 segfaults。

所以,这就是魔法:

  • 我们没有库依赖,ldd:
linux-vdso.so.1 => (0x00007fff4abff000) libpthread.so.0 => /lib/libpthread.so.0 (0x00007f1a3fd65000) libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007f1a3fa51000) libm.so.6 => /lib/libm.so.6 (0x00007f1a3f7cd000) libgomp.so.1 => /usr/lib/libgomp.so.1 (0x00007f1a3f5bf000) libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x00007f1a3f3a8000) libc.so.6 => /lib/libc.so.6 (0x00007f1a3f024000) /lib64/ld-linux-x86-64.so.2 (0x00007f1a414f9000) librt.so.1 => /lib/librt.so.1 (0x00007f1a3ee1c000)
  • 没有从我们的库 nm 导出 Cxx 符号(我们的公共符号是 POC C 以实现二进制兼容性):
nm -g --defined-only libmysharedlib.so addr1 T OurCSymbol1 addr2 T OurCSymbol2 addr3 T OurCSymbol3 ...
  • 不过,它还是使用了他们的助推器。如何? Stacktrace(路径切割):
[0] 0x00007f21fddbb0a9 bin/libmwfl.so+00454825 fl::sysdep::linux::unwind_stack(void const**, unsigned long, unsigned long, fl::diag::thread_context const&)+000009 [1] 0x00007f21fdd74111 bin/glnxa64/libmwfl.so+00164113 fl::diag::stacktrace_base::capture(fl::diag::thread_context const&,无符号长)+000161 [2] 0x00007f21fdd7d42d bin/glnxa64/libmwfl.so+00201773 [3] 0x00007f21fdd7d6b4 bin/glnxa64/libmwfl.so+00202420 fl::diag::terminate_log(char const*, fl::diag::thread_context const&, bool)+000100 [4] 0x00007f21fce525a7 bin/glnxa64/libmwmcr.so+00365991 [5] 0x00007f21fb9eb8f0 lib/libpthread.so.0+00063728 [6] 0x00007f21f3e939a9 libboost_regex.so.1.40.0+00342441 boost::re_detail::perl_matcher, std::allocator > >, boost::regex_traits > >::match_all_states()+000073 [7] 0x00007f21f3eb6546 bin/glnxa64/libboost_regex.so.1.40.0+00484678 boost::re_detail::perl_matcher, std::allocator > >, boost::regex_traits > >::match_imp()+000758 [8] 0x00007f21c04ad595 lib/libmysharedlib.so+04855189 bool boost::regex_match, std::allocator >>, char, boost::regex_traits >>(__gnu_cxx::__normal_iterator, __gnu_cxx::__normal_iterator, boost::match_results, std: :allocator > > >&, boost::basic_regex > > const&, boost::regex_constants::_match_flags)+000245 [9] 0x00007f21c04a71c7 lib/libmysharedlib.so+04829639 myfunc2()+000183 [10] 0x00007f21c01b41e3 lib/libmysharedlib.so+01737187 myfunc1()+000307

众所周知,MATLAB 仅使用 RTLD_NOW 标志进行 dlopen。

人们,请和我一起思考。 现在我迫切地不想解决这个问题,而是简单地理解 ld&elf 的行为。

编辑: 额外的小问题:我是如何理解的,没有特殊的链接器选项,linux .so 库中的符号永远不会通过地址链接?所以即使是静态链接的局部符号也在运行时解析?

【问题讨论】:

    标签: c++ ld dlopen


    【解决方案1】:

    查看 ld-Bsymbolic 选项。

    如果指定了-Bsymbolic,那么在创建共享 对象 ld 将尝试将对全局符号的引用绑定到定义 共享库中。默认是延迟绑定到运行时。

    举个例子可能会更清楚。

    假设example.o 包含对定义在 global.o,

    $ nm example.o | grep ' U'
         U _GLOBAL_OFFSET_TABLE_
         U globalfn
    $ nm global.o | grep ' T'
    00000000 T globalfn
    

    两个共享对象normal.sosymbolic.so 构建为 如下:

    $ cc -fPIC -c example.c
    $ cc -c global.c
    $ rm -f archive.a; ar cr archive.a global.o
    $ ld -shared -o normal.so example.o archive.a
    $ ld -Bsymbolic -shared -o symbolic.so example.o archive.a
    

    反汇编normal.so 的代码显示调用 globalfn实际上是在遍历过程联动表,并且 因此调用的最终目的地是在运行时确定的。

    $ objdump --disassemble normal.so
    ...snip...
    00000194 <example>:
    ...snip...
     1a6:   e8 d9 ff ff ff          call   184 <globalfn@plt>
    ...snip...
    $ readelf -r normal.so
    
    Relocation section '.rel.plt' at offset 0x16c contains 1 entries:
    Offset     Info    Type            Sym.Value  Sym. Name
    00001244  00000207 R_386_JUMP_SLOT   000001b8   globalfn
    

    而在symbolic.so 中,调用总是调用 globalfn 在共享对象中。

    $ objdump --disassemble symbolic.so
    ...snip...
    0000016c <shared>:
    ...snip...
     17e:   e8 0d 00 00 00          call   190 <globalfn>
    ...snip...
    $ readelf -r symbolic.so
    
    There are no relocations in this file.
    

    【讨论】:

    • 看起来像一个解决方案,我会检查它))谢谢。直接的问题 - 使用 -Bsymbolic 是否有可能覆盖 MATLAB 符号并导致“对称”段错误?作为信息,我们库中的所有增强符号都是“局部弱”。
    • @ALOR -Bsymbolic 在链接时完成它的工作,并且只改变“对象内”引用的解析方式。我已经扩展了我的答案以澄清这一点。
    • 您还应该考虑使用链接器版本脚本来完全隐藏libmysharedlib.so 中的所有符号(尤其是所有 Boost 符号),MatLab 实际需要的除外。这样做将加快程序的运行时加载速度消除您的库影响稍后加载的其他库的可能性。
    【解决方案2】:

    这是交易 - 我们使用 boost 1.47,MATLAB 有 boost 1.40。目前,从我们的库调用到它们的 boost (regex) 时,库调用段错误。

    您正在调用未定义的行为,这是一种“医生,我这样做会很痛”的情况。 Matlab 可执行文件已经包含类boost::re_detail::perl_matcher&lt; elided &gt; 的外部函数。当 Matlab 加载您的共享库时,动态链接器会看到您的共享库以与现有定义冲突的方式定义了那些完全相同的符号。未定义的行为。

    解决方案是构建一个与 Matlab 一起使用的库版本,该版本使用与 Matlab 相同版本的 Boost。

    【讨论】:

    • 我同意。然而,问题是为什么在地狱里他甚至会触及地方符号? LD 文档还不清楚。如果我现在理解正确,没有 RTLD_DEEPBIND 本地符号可能会被全局符号覆盖?
    • 是什么让您认为共享库中的那些符号是“本地的”?至少其中一些不是。
    • nm 让我这么想 :) 只有我们的 public-C 接口函数是全局的 - 库是用 -fvisibility=hidden 编译的,接口函数有 gcc 属性 visibility=default
    • @ALOR:尝试使用nm -u libmysharedlib.so | c++filt。你的函数myfunc2()使用模板函数boost::regex_match;它就在您的堆栈跟踪中。这个小模板函数通过创建一个boost::re_detail::perl_matcher 对象然后调用该对象的match 方法来完成它的工作。最终你会遇到不小的代码并且它不是内联的。它是编译后的 boost 库的一部分。
    猜你喜欢
    • 2012-10-16
    • 2014-05-29
    • 1970-01-01
    • 2021-10-21
    • 2020-04-11
    • 2014-01-11
    • 1970-01-01
    • 2011-07-12
    相关资源
    最近更新 更多