To quote the ld.so man page,
The LD_ASSUME_KERNEL environment variable overrides the kernel version used by the dynamic linker to determine which library to load.
An ELF executable has its minimum compatible OS ABI version written into its .note.ABI-tag ELF section
at compile-time. The dynamic linker (e.g. /lib/ld-linux.so.2
) compares the kernel ABI to that version and errors out if the current environment isn’t sufficient.
Ulrich Drepper has a short write-up on the mechanism: http://www.akkadia.org/drepper/assumekernel.html
The ABI version is written into the .note.ABI-tag
ELF section. You can get the ABI version using objdump
and interpreting the hex manually, or you can install and use eu-readelf
, which will pretty-print the information for you.
Environment note
All work in this post was done on an Ubuntu Natty machine:
$ uname -a Linux kid-charlemagne 2.6.38-13-generic #53-Ubuntu SMP Mon Nov 28 19:33:45 UTC 2011 x86_64 x86_64 x86_64 GNU/Linux
Using objdump
$ objdump -s --section=.note.ABI-tag /bin/ls /bin/ls: file format elf64-x86-64 Contents of section .note.ABI-tag: 400254 04000000 10000000 01000000 474e5500 ............GNU. 400264 00000000 02000000 06000000 0f000000 ................
The Linux Standard Base Specification describes the format of the section:
The first 32-bit word of the desc field must be 0 (this signifies a Linux executable). The second, third, and fourth 32-bit words of the desc field contain the earliest compatible kernel version.
In our case, 2
, 6
, and f
mean our earliest compatible kernel version is 2.6.15.
Using eu-readelf
eu-readelf
is part of the elfutils
package on Ubuntu.
$ eu-readelf -n /bin/ls Note section [ 2] '.note.ABI-tag' of 32 bytes at offset 0x254: Owner Data size Type GNU 16 VERSION OS: Linux, ABI: 2.6.15 Note section [ 3] '.note.gnu.build-id' of 36 bytes at offset 0x274: Owner Data size Type GNU 20 GNU_BUILD_ID Build ID: 3e6f3159144281f709c3c5ffd41e376f53b47952
And we see that eu-readelf
agrees with our interpretation of the objdump
output.
ABI mismatch
What happens if your kernel ABI isn’t sufficient to run an executable?
Since the minimum compatible OS version for this ls
we’ve been examining is 2.5.15, let’s set LD_ASSUME_KERNEL
to a lower version number and find out:
$ export LD_ASSUME_KERNEL=2.5.14 $ ls ls: error while loading shared libraries: librt.so.1: cannot open shared object file: No such file or directory
This is not that helpful an error message, since it’s not true that librt.so.1
doesn’t exist:
$ ldd /bin/ls linux-vdso.so.1 => (0x00007fff653ff000) libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007fa3f87e7000) librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007fa3f85df000) libacl.so.1 => /lib/x86_64-linux-gnu/libacl.so.1 (0x00007fa3f83d6000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa3f8042000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fa3f7e3e000) /lib64/ld-linux-x86-64.so.2 (0x00007fa3f8a2f000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fa3f7c1f000) libattr.so.1 => /lib/x86_64-linux-gnu/libattr.so.1 (0x00007fa3f7a1a000)
tells us that it is in /lib/x86_64-linux-gnu/
.
We can see why this happens, though, in elf/dl-load.c
in the glibc source, where errno
is set to ENOENT
on version mismatch:
osversion = (abi_note[5] & 0xff) * 65536 + (abi_note[6] & 0xff) * 256 + (abi_note[7] & 0xff); (abi_note[4] != __ABI_TAG_OS || (GLRO(dl_osversion) && GLRO(dl_osversion) < osversion)) { close_and_out: __close (fd); __set_errno (ENOENT); fd = -1; }
A couple of notes on the various version variables in the above code snippet:
__ABI_TAG_OS
describes the platform (i.e. Linux, Solaris, FreeBSD) and is derived from data inabi-tags
. As mentioned in the LSB spec, Linux has an__ABI_TAG_OS
of 0, which is checked against the 5th word in the ABI note (which we verified was 0 in theobjdump
above).- In
sysdeps/unix/sysv/linux/dl-sysdep.c
, the dynamic linker attempts to setdl_osversion
based on the following sources:- a version given in the
PT_NOTE
ELF section for the “kernel-supplied DSO”. I’m not sure which DSO that glibc comment is specifying. - the uname system call
/proc/sys/kernel/osrelease
- a version given in the
Miscellaneous notes
- The assembly that writes the ABI version into the
.note.ABI-tag
ELF section lives incsu/abi-note.S
in the glibc source:.section ".note.ABI-tag", "a" .p2align 2 .long 1f - 0f /* name length */ .long 3f - 2f /* data length */ .long 1 /* note type */ 0: .asciz "GNU" /* vendor name */ 1: .p2align 2 2: .long __ABI_TAG_OS /* note data: the ABI tag */ .long __ABI_TAG_VERSION 3: .p2align 2 /* pad out section */
- A lot of shared libraries don’t have a
.note.ABI-tag
section. Here’s a small shell script to print out the OS version for those that do in the main shared library directories:for dir in /lib /lib64 /lib/x86_64-linux-gnu /lib64/x86_64-linux-gnu /usr/lib /usr/lib64 do for elt in `ls $dir/*.so*` do res=`eu-readelf -n $elt | grep "OS"`; echo $elt; if ! [ -z "$res" ]; then echo " "$res; fi done done
With almost no exception, all shared libraries with
.note.ABI-tag
sections are in/lib/x86_64-linux-gnu
and/lib64/x86_64-linux-gnu
(although not all shared libraries in those directories have an ABI tag). This is presumably on purpose, although I couldn’t find a document describing when you do or don’t have an ABI tag or why shared libraries in/lib
or/usr/lib
never have an ABI tag.The one exception was
/usr/lib/libvdpau_nvidia.so
//usr/lib64/libvdpau_nvidia.so
, which has the odd OS minimum version of 2.3.99. - I ran this script on RHEL 4 (2.6.9), Precise (3.2.0), and Fedora 16 (3.3.0) machines for comparison. The RHEL 4 shared libraries that have ABI tags all require 2.2.5, Precise required 2.6.24, and Fedora 16 required 2.6.32. I’m not sure what the relationship is between the minimum OS version written into ABI tags as built for a particular environment and the actual kernel ABI.
- The script caught a couple of
.so
files that were not actually shared libraries. They all in fact ended up being linker scripts. Here’s a partial list:/usr/lib/libc.so
, provided byglibc-devel
/usr/lib/libbfd.so
, provided bybinutils-devel
/usr/lib/libcurses.so
, provided byncurses-devel
/usr/lib/libfl.so
, provided byflex
/usr/lib/libpthread.so
, provided byglibc-devel
/usr/lib/libtermcap.so
, provided byncurses-devel
You can determine which package provides a file with
dpkg -S
ondpkg
-based sytems andrpm -qf
onrpm
-based systems.