In this tutorial, we’ll be covering how we can have multiple glibc libraries on a Linux machine. We’ll start by discussing the possibility of having multiple libraries and the issues that may arise with it. Then, we’ll look at a couple of ways of using multiple versions of glibc on the same machine.
2. Different Versions of a Shared Library on Linux
It’s possible to have installations of multiple versions of the same shared libraries on a Linux machine. The linker, during program compilation, looks for a soft link that links to a specific version of a library. For instance, in the case of glibc, the libglib-2.0.so link under the /usr/lib directory will point to a particular version of glibc.
On Linux, the convention for naming shared libraries is lib<name>.so.<major>.<minor>.<patch>. The variables in the convention have different meanings:
- name is the shared object (shared library) filename
- major is the API version of the library — major upgrades possibly introduce backward incompatibility
- minor is the minor version of the API version, which indicates new features
- patch signifies patches for bugs
So, in a way, we can have multiple versions of glibc on our machine and have a libglib-2.0.so link to a specific version. The linker will look for the soname field in the shared object and embed it in the resulting binary. The soname field specifies the filename for our target shared library. Therefore, during runtime, the dynamic linker will look for the shared library specified in the soname field.
For that reason, we’ll be able to use different glibc versions in our program. One caveat of this approach is that we can’t use multiple versions of the same library in our program at the same time. Nevertheless, we can use static linking if different versions of the same library are an absolute necessity.
3. Specifying a Different glibc to g++
glibc is not a standalone file that we can link in our programs. It depends on many other modules. One of them is ld-linux. The ld tool or linkage editor is a helper utility that combines multiple object files and other resources into a single output file. Therefore, we’d need to specify the correct version of ld-linux to g++ along with glibc.
Now, if we have a different version of glibc and the appropriate version of ld-linux, we can compile our source code with our targeted glibc library:
$ g++ <main-file> -o <output-name> -Wl,--rpath=/path/glibc -Wl,--dynamic-linker=/path/glibc/ld-linux.so.2
Let’s break it down:
- We build our file with g++ and specified the output name with the -o option
- -Wl will turn on long warnings for linking the glibc library
- –rpath option tells the runtime loader to look for the required libraries in the /path/glibc directory
- –dynamic-linker option provides the correct path to ld-linux.so.2 in the resulting binary
Alternatively, we could also export the LD_LIBRARY_PATH to include the path to our glibc directory before running our application. However, any new child processes launched by the program should also use the newer glib as specified in this variable. Otherwise, if they’re not compiled with the newer glibc or if the child process environment doesn’t inherit the parent’s environment. Thus, we should stick with the –rpath option and avoid using the environment variable.
4. Using patchelf
This approach works well when we have the source code of our program. On the other hand, it gets a bit complicated to re-link the binaries because we have to do a lot of manual work, such as creating a chroot environment. For that reason, we can use patchelf to save us time and headaches.
patchelf is a utility that we can use to easily modify the rpath and the dynamic linker of an ELF executable. ELF stands for Executable and Linkable Format. It’s the standard file format for binaries and libraries in Linux. These include .so, .o, and executable files that have no specific extensions.
4.1. Installing patchelf
Some distros come with a patchelf pre-installed. However, if it’s not installed, we can install it from the official repository of our distribution using a package manager.
On Ubuntu and Debian derivatives:
# apt install patchelf
For RHEL and Fedora:
# yum install patchelf
# pacman -S patchelf
Once patchelf is installed, let’s verify it:
$ patchelf --version patchelf 0.14.3
Using patchelf is pretty straightforward. The basic syntax for the patchelf command is:
$ patchelf [OPERATION] [OPTIONS] <ELF File>
We’ll go over the different CRUD operations for rpath.
First, we’ll add an rpath to our binary executable for our preferred glibc:
$ patchelf --add-rpath /path/glibc-older my_prog
Similarly, we can update the rpath with the –set-rpath option. This might break the program, so use it with caution:
$ patchelf --set-rpath "/path/glibc-older:/path/libsdl:/path/libgl" my_prog
To remove an existing rpath:
$ patchelf --remove-rpath /path/glibc-older my_prog
We can also update the dynamic linker with —set-interpreter:
$ patchelf --set-interpreter /path/glibc-older/ld-linux/ld-linux.so.2
Now, let’s say we want to use the latest existing glibc library located in the /usr/lib directory. We can simply update the soname of our ELF file:
$ patchelf --set-soname libglib-2.0.so.0.7000.2
In this article, we saw how we could have multiple versions of the same libraries on a single host. We went through a couple of ways to use multiple glibc on our machine with g++ and patchelf.