I believe that the main gripe I now have with shared libraries (AKA Dynamic Shared Objects -- DSOs) is the fact that they truly aim at solving two mutually exclusive problems: give vendors a flexibility to patch systems "live" and also protect end-users from experiencing failures of the unsuspecting applications which don't want to be patched.
One of the tools for protecting the endusers is, of course, versioning of the symbols in DSOs introduced by Sun more than 10 years ago. And even what Sun did was somewhat of an overkill, but the GNU crowd decided to go the whole nine yard as far as complexity is concerned when they decided to "augment" Sun's versioning strategy with a couple of things of their own.
Of course the best of it is: "The second GNU extension is to allow multiple versions of the same function to appear in a given shared library."
Why do I care? Well, primarily because the following doesn't really work as expected on Linux:
int pthread_cond_signal(pthread_cond_t *cond)
{
/* Snitch on pthread_cond_signal */
sym = dlsym(RTLD_NEXT, "pthread_cond_signal");
return sym(cond);
};
In fact it breaks. Horribly! Why ? Well, because pthread_cond_signal
happens to be a versioned symbol with the previous version still
available in glibc (and in libpthread.so, but that's a different story):
$ nm /lib/libc.so.6 | grep pthread_cond_signal 000cb780 t __pthread_cond_signal 000cb780 t __pthread_cond_signal_2_0 000cb780 T pthread_cond_signal@GLIBC_2.0 000cb780 T pthread_cond_signal@@GLIBC_2.3.2And regardless of the fact that the default one is supposed to be the GLIBC_2.3.2 one when I call dlsym() I get the older guy. Of course the older guys now has problems working with a cond. variable initialized by the unitercepted (2.3.2) pthread_cond_init and the whole thing goes kaboom.
Which means that in order for my code to work not only do I have to now version my symbols in order to intercept only what's needed but I also have to do a funny dance around dl[v]sym.
Versioning my own symbols was a bit of a challenge as well. Don't get me wrong -- the Sun way of writing linker map files worked quite nicely, but I really wanted to experience some of that magical world of GNU asm:
__asm__(".symver old_foo,foo@@VERS_2.0");
Suffice it to say, that the following example broke:
$ cat test.c
void old_foo() {}
__asm__(".symver old_foo,foo@@VERS_2.0");
$ gcc -shared -fPIC -o test.so test.c
/usr/lib/gcc/i586-suse-linux/bin/ld:
test.so: undefined versioned symbol name foo@@VERS_2.0
/usr/lib/gcc/i586-suse-linux/bin/ld: failed to set dynamic
section sizes: Bad value
collect2: ld returned 1 exit status
and it took me a while to realize that the claim they make:
"This was done mainly to reduce the burden on the library maintainer."
is a bit further from realiaty than I expected -- you still need
the mapfile!Oh well, yet another day, taming glibc.