In my last post I gave a brief overview of the mechanism used internally by glibc for versioning symbols within shared libraries. As an addendum to my previous article I would like to discuss a simple way to force linking against older glibc symbols. Why would you do this you may ask? Well, suppose you have several GNU/Linux systems with varying glibc installs across them but you want to deliver a binary that would be compatible across them. One option is to statically link your binary, my vote
, the other is to link to an older symbol within the shared library. I would like to mention that using an older symbol has the obvious drawback, possible advantage, of using something that was deprecated for a reason in the past (i.e. broken, behavior changes, performed poorly, new arch support, etc.). The reason I say it is possibly advantageous to link against an older symbol may be because it is known to behave in a desired way, broken or not! I present the following merely for education purposes, so use with care.
To force linking against a particular symbol you need to use the same .symver pseudo-op that is used for defining versioned symbols in the first place. In the following example I make use of glibc’s realpath, but want to make sure it is linked against an older 2.2.5 version.
#include <limits.h> #include <stdlib.h> #include <stdio.h> __asm__(".symver realpath,realpath@GLIBC_2.2.5"); int main() { char* unresolved = "/lib64"; char resolved[PATH_MAX+1]; if(!realpath(unresolved, resolved)) { return 1; } printf("%s\n", resolved); return 0; }
If you were to use objdump on the resulting binary you would see that it is indeed using realpath@GLIBC_2.2.5! Also note that other symbols have been resolved to their defaults so you need to make sure you add a .symver pseudo-op for each symbol you want to force to an older version.
0000000000000000 F *UND* 0000000000000000 realpath@GLIBC_2.2.5 ... 0000000000000000 F *UND* 0000000000000000 __stack_chk_fail@@GLIBC_2.4
The .symver pseudo-op can be used this way to force any symbol to be linked against an older one so long as it is valid. To ease linking against older glibc versions I’ve provided a simple header which can be used to force linking against the minimum glibc version for a give x86 architecture. The minimum versions I am using were taken from shlib-versions file of the glibc git tree.
#ifndef __GLIBC_COMPAT_SYMBOL_H__ #define __GLIBC_COMPAT_SYMBOL_H__ 1 /** * add other architecures below */ #ifdef __amd64__ #define GLIBC_COMPAT_SYMBOL(FFF) __asm__(".symver " #FFF "," #FFF "@GLIBC_2.2.5"); #else #define GLIBC_COMPAT_SYMBOL(FFF) __asm__(".symver " #FFF "," #FFF "@GLIBC_2.0"); #endif /*__amd64__*/ #endif /*__GLIBC_COMPAT_SYMBOL_H__*/
To use the glibc compatible header with the realpath example above, we merely use the GLIBC_COMPAT_SYMBOL macro with the appropriate symbol:
GLIBC_COMPAT_SYMBOL(realpath)