如何告诉链接者不要在链接的共享库中查找某些符号?

How to tell linker not to look for certain symbols in a linked shared library?

提问人:Romário 提问时间:9/29/2023 最后编辑:Romário 更新时间:10/3/2023 访问量:82

问:

我的情况如下:我们的程序依赖于大量共享(和静态)库。我想添加一个新的依赖项,这是一个静态库。程序编译没有问题,但在运行时崩溃。事实证明,新的静态库定义了一个符号,该符号在许多共享库之一中可用。因此,链接器链接到共享库而不是新的静态库 - 然后在运行时调用错误,导致崩溃。foofoofoofoo

我创建了一个最小的示例来模拟这一点:https://gitlab.com/luizromario/linker_example

在那里,我们有:

  • 一个名为 的库,包含一个打印libstatic_oldprint_thing()old static lib
  • 一个名为 的库,包含一个打印libstatic_newprint_thing()new static lib
  • 一个名为 的共享库,它链接到 。它包含一个函数,该函数:libdynamiclibstatic_olddo_things()
    • 打印以下消息:about to print thing from dynamic lib (should print "old static lib"):
    • 调用print_thing()
  • 链接到 libdynamic 和 libstatic_new 的可执行文件。它:executable
    • 指纹:about to do things from executable
    • 调用do_things()
    • 指纹:about to print thing from executable (should print "new static lib"):
    • 调用print_thing()

如果我先链接,那么,这是输出:dynamicstatic_new

about to do things from executable
about to print thing from dynamic lib (should print "old static lib"): old static lib
about to print thing from executable (should print "new static lib"): old static lib

如果我先链接,那么,这是输出:static_newdynamic

about to do things from executable
about to print thing from dynamic lib (should print "old static lib"): new static lib
about to print thing from executable (should print "new static lib"): new static lib

在这两种情况下,我都无法告诉链接器,对于可执行文件,它应该查找 in,对于共享库,它应该查找 .即使我们有 baked 到 executable 中的代码(对吧?)和 baked 到 shared lib 中的代码,链接器也只能链接到整个可执行文件中的任何一个。print_thingstatic_newprint_thingstatic_oldstatic_newstatic_old

当然,我根本无法链接到定义相同符号的两个不同库,但不幸的是,在实际场景中,共享库是一个预构建的二进制文件,我无法再次构建。那么,在编译的时候,有没有办法告诉链接器不要在里面找呢?或者以某种方式从中删除符号?executableprint_thinglibdynamicprint_thinglibdynamic


编辑:其他人在这里描述了类似的问题:Linux/C++ 共享库:我可以编辑 sybol 表,即导出哪些符号吗?

我可能会尝试这样做,但我真的不想编辑共享库。


编辑 2:经过一些反复试验,我设法将二进制文件中的一个 s 编辑成,这成功了:print_thinglibdynamic.soprint_thong

about to do things from executable
about to print thing from dynamic lib (should print "old static lib"): old static lib
about to print thing from executable (should print "new static lib"): new static lib

不过,这真的很不可靠,我不能简单地找到并替换二进制文件中的所有 s,因为二进制文件中还有另一个我无法编辑(否则执行失败):print_thingprint_thing

about to do things from executable
./executable: symbol lookup error: /home/c/luizromario/local/linker_example/build/libdynamic.so: undefined symbol: print_thong

我仍然更愿意告诉链接器不要在里面寻找print_thinglibdynamic


编辑3:我可能越来越接近解决方案。

我发现了链接器选项并像这样使用它:--exclude-libs

target_link_options(dynamic PRIVATE "-Wl,--exclude-libs,libstatic_old.a")

最终结果正是我想要的:

about to do things from executable
about to print thing from dynamic lib (should print "old static lib"): old static lib
about to print thing from executable (should print "new static lib"): new static lib

不幸的是,我需要一个非侵入性的解决方案,因为正如我所提到的,我不能依赖能够重新编译动态库

Linux GCC CMake 链接器

评论

1赞 Basile Starynkevitch 9/30/2023
我建议(如果涉及的库是开源的)更改冲突的名称并重建其中的大部分。也许还可以在他们的 github 存储库上打开一个问题
0赞 Romário 9/30/2023
不幸的是,由于几个原因,这行不通,但主要是因为有几十个这样的动态库,有些是开源的,有些不是。尝试重建一切是不切实际的。
1赞 ash 10/2/2023
您可以在代码中使用动态库加载来显式链接到共享库代码,然后让加载器获取静态库 defs。然而,这两个库似乎重叠了相当多的......

答:

1赞 Romário 10/3/2023 #1

查看手册,我注意到以下选项:ld

   --exclude-libs lib,lib,...
       Specifies a list of archive libraries from which symbols should not
       be automatically exported.  The library names may be delimited by
       commas or colons.  Specifying "--exclude-libs ALL" excludes symbols
       in all archive libraries from automatic export.

我设法用它来做我想做的事。这很棘手,但它工作足够可靠,我不需要篡改 libdynamic.so

  1. 将链接选项添加到可执行文件:--exclude-libs
target_link_options(executable PRIVATE "-Wl,--exclude-libs,ALL")
  1. 首先链接到,然后链接到 。static_newdynamic
target_link_libraries(executable static_new dynamic)

做!

about to do things from executable
about to print thing from dynamic lib (should print "old static lib"): old static lib
about to print thing from executable (should print "new static lib"): new static lib

注意:链接顺序很重要。先链接后会使执行失败:dynamicstatic_new

about to do things from executable
about to print thing from dynamic lib (should print "old static lib"): old static lib
about to print thing from executable (should print "new static lib"): old static lib

这是怎么回事

据我了解,告诉链接器对于每个链接库,排除链接库导出的每个符号。所以,在我的例子中,正在做的是:--exclude-libs ALLld

  1. 链接到executablestatic_new
    • 对 in 的调用将指向 的print_thingexecutablestatic_newprint_thing
  2. 从结果中排除导出的所有符号static_newexecutable
    • print_thing在二进制文件中不再可用executable
  3. 链接到executabledynamic
    • 调用 肯定会指向 的 ,因为我们在上一步中已经排除了 的print_thingdynamicprint_thingstatic_newprint_thing
  4. 排除导出的所有符号dynamic

为什么订单很重要

首先链接失败,因为随后将执行以下操作:dynamicld

  1. 链接到executabledynamic
    • 由于 export (from ) 和 ,因此对这些符号的引用都将指向烘焙到dynamicprint_thingstatic_olddo_thingexecutablelibdynamic.so
  2. 从结果中排除导出的所有符号dynamicexecutable
    • 没关系,已经链接了print_thing
  3. 链接到executablestatic_new
    • 什么也没发生,在步骤 1 中已经定义了。print_thing
  4. 排除导出的所有符号static_new

不幸的是,由于链接排序问题,我仍然无法解决我原始项目中的问题,但我正在关闭它,因为链接的具体问题已经解决。