4

The ldd program reports some missing shared libraries that my shared library needs. (I built it on a different machine with Red Hat Linux 7 and want to run it on another with Red Hat Linux 6.)

In the "version information" section below, there are three entries for libc.so.6; each one has a different version in parentheses after it (GLIBC_2.14, GLIBC_2.4, and GLIBC_2.2.5). The first of those does not have an associated shared library installed on the machine.

I'm new to Linux and do not understand how to interpret this output, let alone solve the problem. Below is the command and the output.

>ldd -v libAtlasUtilsPB.so
./libAtlasUtilsPB.so: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by ./libAtlasUtilsPB.so)
./libAtlasUtilsPB.so: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.18' not found (required by ./libAtlasUtilsPB.so)
./libAtlasUtilsPB.so: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.15' not found (required by ./libAtlasUtilsPB.so)
    linux-vdso.so.1 =>  (0x00007fffa3dff000)
    librt.so.1 => /lib64/librt.so.1 (0x00007fea7a7b2000)
    libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00007fea7a4ab000)
    libm.so.6 => /lib64/libm.so.6 (0x00007fea7a227000)
    libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fea7a011000)
    libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fea79df3000)
    libc.so.6 => /lib64/libc.so.6 (0x00007fea79a5f000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fea7ad17000)

    Version information:
    ./libAtlasUtilsPB.so:
            libgcc_s.so.1 (GCC_3.0) => /lib64/libgcc_s.so.1
            libm.so.6 (GLIBC_2.2.5) => /lib64/libm.so.6
            libpthread.so.0 (GLIBC_2.2.5) => /lib64/libpthread.so.0
            libc.so.6 (GLIBC_2.14) => not found
            libc.so.6 (GLIBC_2.4) => /lib64/libc.so.6
            libc.so.6 (GLIBC_2.2.5) => /lib64/libc.so.6
            libstdc++.so.6 (GLIBCXX_3.4.18) => not found
            libstdc++.so.6 (GLIBCXX_3.4.15) => not found
            libstdc++.so.6 (GLIBCXX_3.4.9) => /usr/lib64/libstdc++.so.6
            libstdc++.so.6 (GLIBCXX_3.4.11) => /usr/lib64/libstdc++.so.6
            libstdc++.so.6 (CXXABI_1.3) => /usr/lib64/libstdc++.so.6
            libstdc++.so.6 (GLIBCXX_3.4) => /usr/lib64/libstdc++.so.6
5

ldd lists the shared libraries which its argument requires. With the -v option it lists all available information, including version symbols.

Lines of the form

linux-vdso.so.1 =>  (0x00007fffa3dff000)
librt.so.1 => /lib64/librt.so.1 (0x00007fea7a7b2000)
libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00007fea7a4ab000)

list the libraries required by libAtlasUtilsPB.so, and how they’re resolved; in your case all the required libraries are found. linux-vdso.so.1 is special, it’s a virtual library provided by the kernel.

Lines of the form

libgcc_s.so.1 (GCC_3.0) => /lib64/libgcc_s.so.1
libm.so.6 (GLIBC_2.2.5) => /lib64/libm.so.6
libpthread.so.0 (GLIBC_2.2.5) => /lib64/libpthread.so.0

list the version symbols required by libAtlasUtilsPB.so, and again how they’re resolved. This is where ldd fails to resolve items, which it indicates both by indicating “not found” here and by printing error messages at the start of its output.

GLIBC_..., GLIBCXX_... etc. are version symbols, which are used in some libraries (including the GNU C library and the GCC libraries) to identify required versions and to manage backward compatibility. A binary (executable or library) will usually end up requiring multiple versions, based on the symbols it really uses from the target library. To satisfy the requirements of a given binary, you need to provide a library which supports all the required versions — i.e. a library matching at least the highest version symbol in the list of requirements.

The reason multiple versions can end up being required, is that each imported object (function etc.) can have a version, and a given binary can link against multiple versions across all the functions it uses. For example, picking an example with two versions on my system, /lib/x86_64-linux-gnu/libgcc_s.so.1, ldd -v shows

/lib/x86_64-linux-gnu/libgcc_s.so.1:
        libc.so.6 (GLIBC_2.14) => /lib/x86_64-linux-gnu/libc.so.6
        libc.so.6 (GLIBC_2.2.5) => /lib/x86_64-linux-gnu/libc.so.6

libgcc_s.so.1 needs two version symbols from libc.so.6. To figure out why, we need to look at the undefined symbols it contains — these are the symbols it needs to import:

$ objdump -T /lib/x86_64-linux-gnu/libgcc_s.so.1|grep -F '*UND*'
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 dl_iterate_phdr
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 free
0000000000000000  w   D  *UND*  0000000000000000              __pthread_key_create
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 abort
0000000000000000  w   D  *UND*  0000000000000000              _ITM_deregisterTMCloneTable
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 strlen
0000000000000000  w   D  *UND*  0000000000000000              pthread_getspecific
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 memset
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 calloc
0000000000000000  w   D  *UND*  0000000000000000              pthread_key_create
0000000000000000  w   D  *UND*  0000000000000000              __gmon_start__
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.14  memcpy
0000000000000000  w   D  *UND*  0000000000000000              pthread_once
0000000000000000  w   DF *UND*  0000000000000000  GLIBC_2.2.5 pthread_mutex_unlock
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 malloc
0000000000000000  w   D  *UND*  0000000000000000              pthread_setspecific
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 realloc
0000000000000000  w   D  *UND*  0000000000000000              _Jv_RegisterClasses
0000000000000000  w   D  *UND*  0000000000000000              _ITM_registerTMCloneTable
0000000000000000  w   DF *UND*  0000000000000000  GLIBC_2.2.5 __cxa_finalize
0000000000000000  w   DF *UND*  0000000000000000  GLIBC_2.2.5 pthread_mutex_lock

This lists a number of versioned symbols (those with GLIBC_2.2.5 or GLIBC_2.14 in the second-last column), and we can see here that the versions match those which we saw with ldd. Why do we end up with multiple versions symbols? This is where backwards compatibility comes in. Version 2.2.5 is the baseline for symbol versioning in the GNU C library; any function which was present in version 2.2.5 carries that version. In version 2.13 of the GNU C library, the behaviour of memcpy was changed; this broke a number of programs, so in version 2.14, a backwards-compatible version was put in place with the optional GLIBC_2.2.5 version tag, and a new version added with the GLIBC_2.14 version tag. Thus old binaries built with earlier versions of the C library use the old version of memcpy, and binaries built with version 2.14 and later use the new version of memcpy.

The same story applies to libstdc++.so.6, but with many more changes with updated version numbers; the libstdc++ manual provides details.

In your case, you need libstdc++.so.6 matching GLIBCXX_3.4.18, i.e. the version provided by GCC 4.8.0 or later, and version 2.14 of the GNU C library.

You can’t easily build binaries on RHEL 7 and run them on RHEL 6 (that’s why the major version is different). You’d be better off rebuilding your program on RHEL 6...

  • 1
    Thanks for the good explanation. However, I also don't understand why there are three version symbols required for libc.so.6 (GLIBC_2.14, GLIBC_2.4 and GLIBC_2.2.5), and six for libstd++.so.6. If I compile all the cpp files with the same compiler version, shouldn't there be just one version symbol required for each library? – Tones Jul 27 '18 at 7:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.