The
documentation
on ld's symbol versioning syntax is a little bit vague on
"dependencies", which it talks about but doesn't give many details on.
Let's construct a small example:
``$ cat foo.c``
#include <stdio.h>
#ifndef VERSION_2
void foo(int f) {
printf("version 1 called\n");
}
#else
void foo_v1(int f) {
printf("version 1 called\n");
}
__asm__(".symver foo_v1,foo@VERSION_1");
void foo_v2(int f) {
printf("version 2 called\n");
}
/* i.e. foo_v2 is really foo@VERSION_2
* @@ means this is the default version
*/
__asm__(".symver foo_v2,foo@@VERSION_2");
#endif
``$ cat 1.ver``
VERSION_1 {
global:
foo;
local:
*;
};
``$ cat 2.ver``
VERSION_1 {
local:
*;
};
VERSION_2 {
foo;
} VERSION_1;
``$ cat main.c``
#include <stdio.h>
void foo(int);
int main(void) {
foo(100);
return 0;
}
``$ cat Makefile``
all: v1 v2
libfoo.so.1 : foo.c
gcc -shared -fPIC -o libfoo.so.1 -Wl,--soname='libfoo.so.1' -Wl,--version-script=1.ver foo.c
libfoo.so.2 : foo.c
gcc -shared -fPIC -DVERSION_2 -o libfoo.so.2 -Wl,--soname='libfoo.so.2' -Wl,--version-script=2.ver foo.c
v1: main.c libfoo.so.1
ln -sf libfoo.so.1 libfoo.so
gcc -Wall -o v1 -lfoo -L. -Wl,-rpath=. main.c
v2: main.c libfoo.so.2
ln -sf libfoo.so.2 libfoo.so
gcc -Wall -o v2 -lfoo -L. -Wl,-rpath=. main.c
.PHONY: clean
clean:
rm -f libfoo* v1 v2
``$ ./v1``
version 1 called
``$ ./v2``
version 2 called
In words, we create two libraries; a version 1 and a version 2, where we
provide a new version of foo in the version 2 library. The
soname is set in the libraries, so v1 and v2 can distinguish
the correct library to use.
In the updated 2.ver version, we say that VERSION_2 depends on
VERSION_1. So, the question is, what does this mean? Does it have
any effect?
We can examine the version descriptors in the library and see that there
is indeed a relationship recorded there.
``$ readelf --version-info ./libfoo.so.2``
[...]
Version definition section '.gnu.version_d' contains 3 entries:
Addr: 0x0000000000000264 Offset: 0x000264 Link: 5 (.dynstr)
000000: Rev: 1 Flags: BASE Index: 1 Cnt: 1 Name: libfoo.so.2
0x001c: Rev: 1 Flags: none Index: 2 Cnt: 1 Name: VERSION_1
0x0038: Rev: 1 Flags: none Index: 3 Cnt: 2 Name: VERSION_2
0x0054: Parent 1: VERSION_1
Looking at the
specification
we can see that each version definition has a vd_aux field which
is a linked list of, essentially, strings that give "the version or
dependency name". This is a little vague for a specification, however
it appears to mean that the first entry is the name of the version
specification, and any following elements are your dependencies. At
least, this is how readelf interprets it when it shows you the
"Parent" field in the output above.
This implies something that the ld documentation doesn't mention; in
that you may list multiple dependencies for a version node. That does
work, and readelf will just report more parents if you try it.
So the question is, what does this dependency actually do? Well, as far
as I can tell, nothing really. The dynamic loader doesn't look at the
dependency information; and doesn't have any need to — it is looking to
resolve something specific, foo@VERSION_2 for example, and doesn't
really care that VERSION_1 even exists.
ld does enforce the dependency, in that if you specify a dependent
node but leave it out or accidentally erase it, the link will fail.
However, it doesn't really convey anything other than its intrinsic
documenation value.