Is there a downside to using -Bsymbolic-functions?
Asked Answered
T

5

34

I recently discovered the linker option "-Bsymbolic-functions" in GNU ld:

-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.

This seems to be the inverse of the GCC option -fvisibility=hidden, in that instead of preventing the export of the referenced function to other shared objects, it prevents library-internal references to that function from being bound to an an exported function of a different shared object. I informed myself that -Bsymbolic-functions will prevent the creation of PLT entries for the functions, which is a nice side effect.

  1. But I was wondering whether there is perhaps a finer-grained control over this, like overwriting -Bsymbolic for individual function definitions of a library.

  2. Should I be aware of any pitfalls of using -Bsymbolic-functions? I plan to only use that, because the -Bsymbolic will break exceptions, I think (it will make it so that references to typeinfo objects are not unified, I think).

Thanks!

Triturate answered 27/8, 2011 at 19:52 Comment(1)
You were the glorious, happy resolution to hours of linker hell for me. I kinda love you.Parasitic
B
5

I recently discussed this this with one of the toolchain experts at SUSE. Here are his remarks:

"-Bsymbolic-functions is a thing from an old world which doesn't exist anymore. It completely bypasses everything about what ELF can provide, including visibility. When you're using it, everything is bound locally. IOW: don't use it :) Noone should use -Bsymbolic-functions, it's a too big hammer for most purposes."

How does -Bsymbolic-functions relate to library versioning (--version-script) ?

"-Bsymbolic-functions overrides anything, from linker scripts, from GCC attributes or anywhere, about symbol visibilities or anything. It makes everything bind local, always, irrespective of anything else that you might have added on command lines, or extra files, or object files. (And yes, --dynamic-list= was a mis-guided attempt to fix some of that and make -Bsymbolic* somewhat more friendly). So, yes, it takes precendence over linker script. It's a big hammer :) "

"To be extra precise: -Bsymbolic-functions is not quite that same as linker script global/local, which is probably a reason why people still use it sometimes. While -Bsymbolic-functions does bind references to definitions locally (like local: in linker scripts), it also keeps them exported (like the global: ones). In ELF speak that would be somewhat like PROTECTED visibility. Unfortunately that can't be expressed in a symbol version script right now, only via GCCs __attribute__(visibility). So, when people try to get the speed advantage of local binding (fewer symbol lookups at library load time), while still exporting all their functions from the shared lib, they unfortunately often end up first finding that -Bsymbolic-functions "does what I want", without realizing that it creates problems down the line."

Boneblack answered 21/3, 2022 at 14:40 Comment(0)
T
29

Answering my own question because I just earned a Tumbleweed badge for it... and I found out subsequently

But I was wondering whether there is perhaps a finer-grained control over this, like overwriting -Bsymbolic for individual function definitions of a library.

Yes, there is the option --dynamic-list which does exactly that

Should I be aware of any pitfalls of using -Bsymbolic-functions? I plan to only use that, because the -Bsymbolic will break exceptions, I think (it will make it so that references to typeinfo objects are not unified, I think).

I looked more into it, and it seems there is no issue. The libstdc++ library apparently does it or at least did consider it and they only had to add --dynamic-list-cpp-new to still have operator new unified (to prevent issues with multiple allocator / deallocators mixing up in a program but I would argue such programs are broken anyway). Ubuntu uses it or used it by default, and it seems it causes conflicts with some packages. But overall it should work nicely I expect.

Triturate answered 3/9, 2011 at 20:12 Comment(2)
One pitfall is that using --dynamic-list and -Bsymbolic* at the same time is broken in currently released versions of gold (it's fine in bfd ld), see sourceware.org/bugzilla/show_bug.cgi?id=13577Haemato
Sorry for stupid suggestion (I'm not competent in this): did you saw this? (They seem to describe more side-effects, although maybe related to symbolic data, not to -Bsymbolic-functions.)Liana
B
5

I recently discussed this this with one of the toolchain experts at SUSE. Here are his remarks:

"-Bsymbolic-functions is a thing from an old world which doesn't exist anymore. It completely bypasses everything about what ELF can provide, including visibility. When you're using it, everything is bound locally. IOW: don't use it :) Noone should use -Bsymbolic-functions, it's a too big hammer for most purposes."

How does -Bsymbolic-functions relate to library versioning (--version-script) ?

"-Bsymbolic-functions overrides anything, from linker scripts, from GCC attributes or anywhere, about symbol visibilities or anything. It makes everything bind local, always, irrespective of anything else that you might have added on command lines, or extra files, or object files. (And yes, --dynamic-list= was a mis-guided attempt to fix some of that and make -Bsymbolic* somewhat more friendly). So, yes, it takes precendence over linker script. It's a big hammer :) "

"To be extra precise: -Bsymbolic-functions is not quite that same as linker script global/local, which is probably a reason why people still use it sometimes. While -Bsymbolic-functions does bind references to definitions locally (like local: in linker scripts), it also keeps them exported (like the global: ones). In ELF speak that would be somewhat like PROTECTED visibility. Unfortunately that can't be expressed in a symbol version script right now, only via GCCs __attribute__(visibility). So, when people try to get the speed advantage of local binding (fewer symbol lookups at library load time), while still exporting all their functions from the shared lib, they unfortunately often end up first finding that -Bsymbolic-functions "does what I want", without realizing that it creates problems down the line."

Boneblack answered 21/3, 2022 at 14:40 Comment(0)
C
4

Well you could say it is a "hardening" option as it ensures your calls to in-library functions surely end up there. But one issue that I found is some projects test-suites.

For example the libvirt test-suite would want to call into the just built libvirt0.so but also mock some of the calls that will be done from there.

Due to -Bsymbolic-functions being used on the build that breaks the test as the original and not the mocked function is called.

Example backtraces Good case:

#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 

Bad case:

#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 

Compare the source for virHostCPUGetThreadsPerSubcore in those two and you will see the difference.

Another case I have seen are:

Since the original question was about potential drawbacks I thought it is worth to mention those somewhat common category of related issues as well.

Countermeasure answered 9/1, 2019 at 9:0 Comment(0)
C
0

There are cases with side effects. A documented one: https://bugs.launchpad.net/ubuntu/+source/xfe/+bug/644645 I would also like to figure out more about it, because I have such a case right now.

Canella answered 22/12, 2013 at 12:17 Comment(0)
S
0

building glibc with -Bsymbolic-functions is not recommended neither. Here is the result I got:

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 ?? ()
Smail answered 8/1, 2014 at 19:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.