使用-Bsymbolic-functions会有什么缺点吗?

32

我最近在GNU ld中发现了链接器选项“-Bsymbolic-functions”:

-Bsymbolic
  When creating a shared library, bind references to global symbols to the 
  definition within the shared library, if any. Normally, it is possible 
  for a program linked against a shared library to override the definition 
  within the shared library. 

  This option is only meaningful on ELF platforms which support shared libraries.

-Bsymbolic-functions
  When creating a shared library, bind references to global function symbols 
  to the definition within the shared library, if any.  

  This option is only meaningful on ELF platforms which support shared libraries.

这似乎是GCC选项-fvisibility=hidden的相反,它不是防止导出引用函数到其他共享对象,而是防止库内引用该函数与不同共享对象的导出函数绑定。我了解到-Bsymbolic-functions会阻止为函数创建PLT条目,这是一个很好的副作用。

  1. 但我想知道是否有更精细的控制方式,例如覆盖库中单个函数定义的-Bsymbolic

  2. 我应该注意使用-Bsymbolic-functions时是否存在任何问题? 我计划仅使用它,因为-Bsymbolic会破坏异常,我想这将使得对typeinfo对象的引用无法统一。

谢谢!


3
你是我在漫长的链接错误中美好、愉悦的结局。我有点喜欢你。 - Alex Reece
5个回答

29

回答自己的问题,因为我刚刚为它赢得了一个Tumbleweed徽章...然后我随后发现

但是我想知道是否有更细粒度的控制,比如覆盖库中单个函数定义的-Bsymbolic

是的,有一个选项--dynamic-list正好可以做到这一点

我是否应该注意使用-Bsymbolic-functions的任何缺陷?我计划仅使用它,因为我认为-Bsymbolic将破坏异常(它将使对typeinfo对象的引用不统一)。

我仔细研究了它,似乎没有问题。 Libstdc ++库显然这样做了,或者至少考虑过它们只需要添加--dynamic-list-cpp-new,以仍然统一operator new(防止多个分配器/解分配器在程序中混合但我会认为这样的程序已经损坏了)。 Ubuntu默认使用它或曾经使用它,并且似乎会与某些软件包发生冲突。 但总的来说,我希望它能很好地工作。


一个陷阱是,在当前发布的gold版本中,同时使用--dynamic-list-Bsymbolic*是有问题的(在bfd ld中可以正常运行),请参见https://sourceware.org/bugzilla/show_bug.cgi?id=13577 - thakis
抱歉提出这个愚蠢的建议(我在这方面不够能力):你看过这个吗?(它们似乎描述了更多的副作用,尽管可能与符号数据有关,而不是-Bsymbolic-functions。) - Sasha

4
您可以将其视为一种“加固”选项,因为它确保您对库函数的调用最终都会到达那里。但我发现一个问题是一些项目测试套件。
例如,libvirt测试套件希望调用刚构建的libvirt0.so,但也要模拟从那里进行的某些调用。
由于在构建中使用了-Bsymbolic-functions,这会破坏测试,因为调用原始函数而不是模拟函数。
示例回溯 良好情况:
#0  virHostCPUGetThreadsPerSubcore (arch=VIR_ARCH_PPC64) at ../../../tests/virhostcpumock.c:30
#1  0x00007ffff7c1e4c4 in virHostCPUGetInfoPopulateLinux (cpuinfo=<optimized out>, arch=VIR_ARCH_PPC64, cpus=0x7fffffffdf38, mhz=<optimized out>, nodes=0x7fffffffdf40, sockets=0x7fffffffdf44, cores=0x7fffffffdf48, threads=0x7fffffffdf4c)
    at ../../../src/util/virhostcpu.c:661                                           
#2  0x0000555555557e6f in linuxTestCompareFiles (outputfile=0x55555558f150 "/build/libvirt-OUKR8i/libvirt-4.10.0/tests/virhostcpudata/linux-ppc64-subcores2.expected", arch=VIR_ARCH_PPC64,·
    cpuinfofile=0x5555555a3f10 "/build/libvirt-OUKR8i/libvirt-4.10.0/tests/virhostcpudata/linux-ppc64-subcores2.cpuinfo") at ../../../tests/virhostcputest.c:44
#3  linuxTestHostCPU (opaque=<optimized out>) at ../../../tests/virhostcputest.c:189
#4  0x000055555555914d in virTestRun (title=0x55555555c0a1 "subcores2", body=0x555555557cc0 <linuxTestHostCPU>, data=0x7fffffffe0c0) at ../../../tests/testutils.c:176
#5  0x000055555555781a in mymain () at ../../../tests/virhostcputest.c:263          
#6  0x0000555555559df4 in virTestMain (argc=1, argv=0x7fffffffe2c8, func=0x5555555577b0 <mymain>) at ../../../tests/testutils.c:1114
#7  0x00007ffff79bb09b in __libc_start_main (main=0x5555555576a0 <main>, argc=1, argv=0x7fffffffe2c8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe2b8) at ../csu/libc-start.c:308
#8  0x00005555555576ea in _start () at ../../../tests/virhostcputest.c:278 

糟糕的情况:

#0  virHostCPUGetThreadsPerSubcore (arch=arch@entry=VIR_ARCH_PPC64) at ../../../src/util/virhostcpu.c:1119
#1  0x00007ffff7c27e04 in virHostCPUGetInfoPopulateLinux (cpuinfo=<optimized out>, arch=VIR_ARCH_PPC64, cpus=0x7fffffffdea8, mhz=<optimized out>, nodes=0x7fffffffdeb0, sockets=0x7fffffffdeb4, cores=0x7fffffffdeb8, threads=0x7fffffffdebc)
    at ../../../src/util/virhostcpu.c:661                                           
#2  0x0000555555557e6f in linuxTestCompareFiles (outputfile=0x5555555a5c30 "/build/libvirt-4biJ7f/libvirt-4.10.0/tests/virhostcpudata/linux-ppc64-subcores2.expected", arch=VIR_ARCH_PPC64,·
    cpuinfofile=0x55555558fd20 "/build/libvirt-4biJ7f/libvirt-4.10.0/tests/virhostcpudata/linux-ppc64-subcores2.cpuinfo") at ../../../tests/virhostcputest.c:44
#3  linuxTestHostCPU (opaque=<optimized out>) at ../../../tests/virhostcputest.c:189
#4  0x000055555555914d in virTestRun (title=0x55555555c0a1 "subcores2", body=0x555555557cc0 <linuxTestHostCPU>, data=0x7fffffffe030) at ../../../tests/testutils.c:176
#5  0x000055555555781a in mymain () at ../../../tests/virhostcputest.c:263          
#6  0x0000555555559df4 in virTestMain (argc=1, argv=0x7fffffffe238, func=0x5555555577b0 <mymain>) at ../../../tests/testutils.c:1114
#7  0x00007ffff79b009b in __libc_start_main () from /lib/x86_64-linux-gnu/libc.so.6
#8  0x00005555555576ea in _start () at ../../../tests/virhostcputest.c:278 

比较这两个函数virHostCPUGetThreadsPerSubcore的源代码,你会看到它们之间的差异。

我见过另一种情况:

由于原始问题是关于潜在缺点的,所以我认为值得提及这些相关问题的常见类别。


4
我最近和SUSE的一位工具链专家讨论了这个问题。以下是他的评论:
-Bsymbolic-functions是一个来自旧世界的东西,不再存在。它完全绕过了ELF可以提供的所有内容,包括可见性。当您使用它时,所有内容都是本地绑定的。换句话说:不要使用它:)没有人应该使用-Bsymbolic-functions,对于大多数用途来说,它是一个太大的锤子。”

-Bsymbolic-functions与库版本控制(--version-script)有什么关系?

-Bsymbolic-functions会覆盖链接器脚本、GCC属性或任何其他地方有关符号可见性或其他任何内容的信息。它始终将所有内容绑定为本地,无论您在命令行、额外文件或目标文件中添加了什么其他内容。(是的,--dynamic-list=是一个错误的尝试,试图修复一些问题并使-Bsymbolic*更加友好)。因此,它优先于链接器脚本。它是一个巨大的锤子:)”
“更加精确地说:-Bsymbolic-functions不完全等同于链接器脚本全局/本地,这可能是人们有时仍在使用它的原因。虽然-Bsymbolic-functions会将引用定义绑定到本地(如链接器脚本中的local:),但它也会保留它们的导出(如global:所示)。在ELF语言中,这有点像PROTECTED可见性。不幸的是,目前无法通过符号版本脚本表达它,只能通过GCC的__attribute__(visibility)来实现。因此,当人们尝试获取本地绑定的速度优势(在库加载时少一些符号查找),同时仍然从共享库中导出所有函数时,他们经常会先发现-Bsymbolic-functions“做我想要的”,而没有意识到它会在后面制造问题。”

0

不建议使用-Bsymbolic-functions构建glibc。这是我得到的结果:

Core was generated by `/home/lano1106/dev/packages/glibc/repos/core-i686/src/glibc-build/elf/ld-linux                                                               .'.
Program terminated with signal 11, Segmentation fault.
#0  0x400a3e90 in _int_free ()
   from /home/lano1106/dev/packages/glibc/repos/core-i686/src/glibc-build/libc.so.6
(gdb) where
#0  0x400a3e90 in _int_free ()
   from /home/lano1106/dev/packages/glibc/repos/core-i686/src/glibc-build/libc.so.6
#1  0x4016b94b in __libc_dlsym ()
   from /home/lano1106/dev/packages/glibc/repos/core-i686/src/glibc-build/libc.so.6
#2  0x4004c2c7 in __gconv_find_shlib ()
   from /home/lano1106/dev/packages/glibc/repos/core-i686/src/glibc-build/libc.so.6
#3  0x40042320 in find_derivation ()
   from /home/lano1106/dev/packages/glibc/repos/core-i686/src/glibc-build/libc.so.6
#4  0x40042889 in __gconv_find_transform ()
   from /home/lano1106/dev/packages/glibc/repos/core-i686/src/glibc-build/libc.so.6
#5  0x400d6f00 in __wcsmbs_load_conv ()
   from /home/lano1106/dev/packages/glibc/repos/core-i686/src/glibc-build/libc.so.6
#6  0x400c86f6 in mbrtowc ()
   from /home/lano1106/dev/packages/glibc/repos/core-i686/src/glibc-build/libc.so.6
#7  0x08048914 in ?? ()
#8  0x00000000 in ?? ()

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接