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

qni6mghb  于 2023-10-16  发布在  Linux
关注(0)|答案(1)|浏览(85)

我的情况如下:我们的程序依赖于大量的共享(和静态)库。我想添加一个新的依赖项,这是一个静态库。程序编译时没有问题,但在运行时崩溃。事实证明,新的静态库定义了一个符号foo,它可以在许多共享库中找到。因此,链接器链接到共享库的foo,而不是新静态库的foo--然后在运行时调用了错误的foo,导致崩溃。
我创建了一个最小的例子来模拟这个:https://gitlab.com/luizromario/linker_example
在那里,我们有:

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

如果我先连接dynamic,然后连接static_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_new,然后连接dynamic,输出如下:

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

在这两种情况下,我都无法告诉链接器,对于可执行文件,它应该在static_new中查找print_thing,对于共享库,它应该在static_old中查找print_thing。即使我们已经将static_new的代码烘焙到可执行文件中(对吗?)和static_old的代码烘焙到共享库中,对于整个可执行文件,链接器只能链接到其中一个。
当然,我不能简单地链接到定义相同符号的两个不同库,但不幸的是,在真实的场景中,共享库是一个预构建的二进制文件,我不能再次构建。那么,在编译executable时,有没有一种方法可以告诉链接器不要在libdynamic中查找print_thing?或者某种方法从libdynamic中删除print_thing符号?
编辑:有人在这里描述了一个类似的问题:Linux/C++ shared libaries: Can I edit the sybol table, i.e. which symbols are exported?
我可能会尝试,但我真的不喜欢编辑共享库。
编辑2:经过一些尝试和错误,我设法将libdynamic.so二进制文件中的一个print_thing编辑为print_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

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

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

我仍然更愿意告诉链接器不要在libdynamic中查找print_thing
编辑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

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

0lvr5msh

0lvr5msh1#

查看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_new,然后链接到dynamic

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

注意:**连接顺序很重要。**先连接dynamic,然后连接static_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 ALL告诉链接器,对于每个链接库,排除链接库导出的每个符号。所以,在我的例子中,ld正在做的是:

  • executable链接到static_new
  • executable中调用print_thing将指向static_newprint_thing
  • 从结果executable中删除static_new导出的所有符号
  • executable二进制文件中不再提供print_thing
  • executable链接到dynamic
  • print_thing的调用肯定会指向dynamicprint_thing,因为我们在前面的步骤中已经排除了static_newprint_thing
  • 删除dynamic导出的所有符号
    秩序为何重要

链接dynamic首先失败,因为ld将执行以下操作:

  • executable链接到dynamic
  • 由于dynamic同时导出print_thing(从static_old)和do_thing,因此executable中对这些符号的引用都将指向烘焙到libdynamic.so中的代码
  • 从生成的executable中删除dynamic导出的所有符号
  • 没关系,print_thing已经链接了
  • executable链接到static_new
  • 什么都没有发生,print_thing已经在步骤1中定义。
  • 删除static_new导出的所有符号

不幸的是,由于链接顺序的问题,我仍然没有设法解决我原来的项目中的问题,但我关闭这个项目,因为链接的具体问题已经解决了。

相关问题