Explaining LD_ASSUME_KERNEL

Ulrich Drepper, 2004-5-12

It is frightening how something as simple as the mechanism behind LD_ASSUME_KERNEL can create that much confusion. So here are a few details which should enable everybody to investigate the issue closer by her/himself.

  1. First, LD_ASSUME_KERNEL is handled by the dynamic linker.

  2. The behavior of a specific value of LD_ASSUME_KERNEL is not hardcoded in the dynamic linker.

  3. Every DSO (Dynamic Shared Object, aka shared library) can tell the dynamic linker in glibc which minimum OS ABI version is needed. NB: dynamic linkers other than glibc's do not have this feature. The information about the minimum OS ABI version is encoded in a ELF note section usually named .note.ABI-tag. This note section must be referenced by a PT_NOTE entry in the DSO's program header.

    To examine the content of a DSO's note section use the readelf program from elfutils (the version from binutils is not capable enough). On Red Hat system the binary is called eu-readelf. Using it on a Fedora Core 2 system shows the following:

    $ eu-readelf -n /lib/tls/libc-2.3.3.so
    
    Note segment of 32 bytes at offset 0x174:
      Owner          Data size  Type
      GNU                   16  VERSION
        OS: Linux, ABI: 2.4.20
    

    This means the /lib/tls/libc-2.3.3.so DSO requires at least OS ABI version 2.4.20.

  4. The specific ABI version requirements on a RHL9, and Fedora Core 1 and 2 system are as follows (NB: this implies IA-32 is the architecture):

    This means no LD_ASSUME_KERNEL setting requesting versions earlier than 2.2.5 will work at all. Versions from 2.2.5 to 2.4.0 will use the DSOs in /lib, versions from 2.4.1 to 2.4.19 will use the DSOs in /lib/i686, versions 2.4.20 and younger will use the DSOs in /lib/tls.

    For the Red Hat releases this layout was chosen to provide the maximum amount of backward compatibility for broken applications (correctly written applications have no problems anyway). The code in /lib consists of the very early LinuxThreads code which had fixed size threads which could not be placed by the application. The version in /lib/i686 is the LinuxThreads code which does away with this limitation (aka floating stacks). The code in /lib/tls is the new NPTL POSIX thread library.

  5. The fact that a DSO has a more stringent version requirement does not mean it is automatically chosen. The dynamic only rejects loading DSOs based on the version information, it does not look for the best fit. The above description is true because the dynamic linker always looks in the directories /lib/tls, /lib/i686, and /lib in this order. Why this is the case is complicated and not explained here. Read the dynamic linker source code.

  6. For architectures other than IA-32 the situation is similar, but less complicated. In RHEL3 only two versions of glibc are needed, one using LinuxThreads and one using NPTL. The very old LinuxThreads code is not needed.