Some time ago I wrote a description of the
-Bsymbolic
linker flag which could do with some further explanation. The
original
article is a good starting place.
One interesting point that I didn't go into was the potential for
code optimisation
-Bsymbolic brings about. I'm not sure if I
missed that at the time, or the toolchain changed, both are probably
equally likely!
Let me recap the example...
ianw@jj:/tmp/bsymbolic$ cat Makefile
all: test test-bsymbolic
clean:
rm -f *.so test testsym
liboverride.so : liboverride.c
$(CC) -Wall -O2 -shared -fPIC -o liboverride.so $<
libtest.so : libtest.c
$(CC) -Wall -O2 -shared -fPIC -o libtest.so $<
libtest-bsymbolic.so : libtest.c
$(CC) -Wall -O2 -shared -fPIC -Wl,-Bsymbolic -o $@ $<
test : test.c libtest.so liboverride.so
$(CC) -Wall -O2 -L. -Wl,-rpath=. -ltest -o $@ $<
test-bsymbolic : test.c libtest-bsymbolic.so liboverride.so
$(CC) -Wall -O2 -L. -Wl,-rpath=. -ltest-bsymbolic -o $@ $<
$ cat liboverride.c
#include <stdio.h>
int foo(void)
printf("override foo called\n");
return 0;
$ cat libtest.c
#include <stdio.h>
int foo(void)
printf("libtest foo called\n");
return 1;
int test_foo(void)
return foo();
$ cat test.c
#include <stdio.h>
int test_foo(void);
int main(void)
printf("%d\n", test_foo());
return 0;
In words;
libtest.so provides
test_foo(), which
calls
foo() to do the actual work.
libtest-bsymbolic.so is simply built with the flag in
question,
-Bsymbolic.
liboverride.so provides the
alternative version of
foo() designed to override the
original via a
LD_PRELOAD of the library.
test is built against
libtest.so,
test-bsymbolic against
libtest-bsymbolic.so.
Running the examples, we can see that the
LD_PRELOAD does
not override the symbol in the library built with
-Bsymbolic.
$ ./test
libtest foo called
1
$ ./test-bsymbolic
libtest foo called
1
$ LD_PRELOAD=liboverride.so ./test
override foo called
0
$ LD_PRELOAD=liboverride.so ./test-bsymbolic
libtest foo called
1
There are a couple of things going on here. Firstly, you can see
that the SYMBOLIC flag is set in the dynamic section, leading to the
dynamic linker behaviour I explained in the
original
article:
ianw@jj:/tmp/bsymbolic$ readelf --dynamic ./libtest-bsymbolic.so
Dynamic section at offset 0x550 contains 22 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libc.so.6]
0x00000010 (SYMBOLIC) 0x0
...
However, there is also an effect on generated code. Have a look at
the PLTs:
$ objdump --disassemble-all ./libtest.so
Disassembly of section .plt:
[... blah ...]
0000039c <foo>:
39c: ff a3 10 00 00 00 jmp *0x10(%ebx)
3a2: 68 08 00 00 00 push $0x8
3a7: e9 d0 ff ff ff jmp 37c <_init+0x30>
$ objdump --disassemble-all ./libtest-bsymbolic.so
Disassembly of section .plt:
00000374 <__gmon_start__@plt-0x10>:
374: ff b3 04 00 00 00 pushl 0x4(%ebx)
37a: ff a3 08 00 00 00 jmp *0x8(%ebx)
380: 00 00 add %al,(%eax)
...
00000384 <__gmon_start__@plt>:
384: ff a3 0c 00 00 00 jmp *0xc(%ebx)
38a: 68 00 00 00 00 push $0x0
38f: e9 e0 ff ff ff jmp 374 <_init+0x30>
00000394 <puts>:
394: ff a3 10 00 00 00 jmp *0x10(%ebx)
39a: 68 08 00 00 00 push $0x8
39f: e9 d0 ff ff ff jmp 374 <_init+0x30>
000003a4 <__cxa_finalize@plt>:
3a4: ff a3 14 00 00 00 jmp *0x14(%ebx)
3aa: 68 10 00 00 00 push $0x10
3af: e9 c0 ff ff ff jmp 374 <_init+0x30>
Notice the difference? There is no PLT entry for
foo()
when
-Bsymbolic is used.
Effectively, the toolchain has noticed that
foo() can
never be overridden and optimised out the PLT call for it. This is
analogous to using "hidden" attributes for symbols, which I have
detailed in another article on
symbol
visiblity attributes (which also goes into PLT's, if the above
meant nothing to you).
So
-Bsymbolic does have some more side-effects than just
setting a flag to tell the dynamic linker about binding rules -- it
can actually result in optimised code. However, I'm still struggling
to find good use-cases for
-Bsymbolic that can't be better
done with Version scripts and visibility attributes. I would
certainly recommend using these methods if at all possible.
Thanks to Ryan Lortie for comments on the original article.