Skip to content

Instantly share code, notes, and snippets.

@ernstki
Last active April 8, 2024 12:12
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save ernstki/593076d2d164ca51d4a6c0a15731846f to your computer and use it in GitHub Desktop.
Save ernstki/593076d2d164ca51d4a6c0a15731846f to your computer and use it in GitHub Desktop.
List the ABI versions of all detected libc and libstdc++'s (GNU/Linux only)

Linker error messages related to libc and libstdc++

We run into the dreaded

/usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.18' not found

error messages a lot around here. This article is an attempt to explain what's going on with that.

Background

The GNU C library (libc.so) and standard C++ library (libstdc++.so) are dynamically loaded by most of the programs on a Unix system at runtime.

When you're talking about binaries that have their library dependencies "baked in," it's the job of the regular old linker (ld) to resolve a program's library dependencies at compile time (e.g., "static" dependencies). If a binary has been compiled to use "shared objects" (external dependencies that are not baked into the binary, but rather loaded upon execution), it's the job of the dynamic linker (ld.so) to resolve those dependencies at runtime. A "shared object" on Unix is like a DLL on Windows.

For example, for a binary that was compiled against a fictional shared object /path/to/some/lib.so, the dynamic linker will yield error messages like

unable to load shared object /path/to/some/lib.so

when that required shared object (at the required ABI version) cannot be located in any of the following

  • the system-wide linker search path, defined in /etc/ld.so.conf (you must be root to modify this), or
  • the linker runtime search path specified in LD_LIBRARY_PATH

Mucking around with LD_LIBRARY_PATH is frowned upon for a variety of reasons, but we still do it sometimes because it seems easier than the much preferred alternative (appending to the binary's run-time linker path with a compiler flag, as in: LDFLAGS='-L/my/strange/path/lib -Wl,-rpath /my/strange/path/lib').[1]

It's also objectively easier to specify a "priority" for various runtime library paths in this way. For example, many modulefiles provided in an HPC cluster environment will manipulate the LD_LIBRARY_PATH in order to make the libstdc++.so (and other libraries) specific to one version of GCC available with priority over other system-default libraries.

An example of a linker error

Here's a real-world example of a linker error, which you would get in the R shell when correct libstdc++ isn't available

library(DESeq2)

The resulting error message is this:

Error in dyn.load(file, DLLpath = DLLpath, ...) :
  unable to load shared object '/usr/local/R/3.2.0/lib64/R/library/Rcpp/libs/Rcpp.so':
  /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.20' not found (required by /usr/local/R/3.2.0/lib64/R/library/Rcpp/libs/Rcpp.so)
Error: package ‘Rcpp’ could not be loaded

The solution to this particular linker error is to module load gcc/4.8.0 (or higher) either before or after module load R, then trying again.

Reporting which versions of the GLIBC or GLIBCXX ABI are available

The libcversions.sh included as a part of this gist reports which libc and libstdc++ ABI versions are available to running programs. It does this by inspecting the output of ldconfig as well as the value of LD_LIBRARY_PATH, which is usually modified by modulefiles (e.g., when you module load gcc/4.8.5).

The authoritative reference for which GCC version goes with which ABI (Application Binary Interface) version is the "ABI Policy and Guidelines" section of the official documentation for the GNU C++ Library.[2] You can get a rough idea which version of GCC you'll need to module load by looking at that page and matching up the GLIBCXX version from the linker error you got with the GCC version that provides it.

If you have any doubts, load a GCC module that seems to be in the neighborhood, then grepthe output of the libcversions.sh script, above, for the exact GLIBCXX_X.Y.Z version number you need.

References

  1. Should I set LD_LIBRARY_PATH? (the short answer is "never")
  2. ABI Policy and Guidelines - GNU C++ Library documentation, gcc.gnu.org

Author

Kevin Ernst (ernstki -at- mail.uc.edu)

License

Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License.

The associated libcversions.sh script is provided under the MIT license.

#!/bin/bash
##############################################################################
##
## List the ABI versions of all detected libc and libstdc++'s
##
## Author: Kevin Ernst <ernstki -at- mail.uc.edu>
## Date: 5 February 2018
## License: MIT
## Source: https://gist.github.com/ernstki/593076d2d164ca51d4a6c0a15731846f
##
## See also:
## ---------
## 1. https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html
##
##############################################################################
# Print copies of character "$2" for length of string $1
print_x_for_length_of() {
for (( i=0; i<${#2}; i++ )); do echo -n "$1"; done
}
print_n_of() {
for (( i=0; i<$1; i++ )); do echo -n "$2"; done
}
indent_and_ul() {
print_n_of 2 ' '
echo -ne "$1\n"
print_n_of 2 ' '
print_x_for_length_of '-' "$1"
echo
}
echo "Library paths found in /etc/ld.so.conf (system linker config)"
echo "============================================================="
echo
for lib in $( ldconfig -p | awk -F'=>' '$2 ~ /libc\.|libstdc/{print $2}' ); do
indent_and_ul "$lib supports the following ABI versions:"
objdump -x "$lib" | grep -P '\sGLIBC(..)?_\d+\.\d+(\.\d+)?$' \
| cut -d' ' -f4 | sed 's/\(.*\)/ \1/' | column -c78
echo
done
echo
echo "Library paths found in LD_LIBRARY_PATH (dynamic linker runtime path)"
echo "===================================================================="
echo
for libdir in $( echo $LD_LIBRARY_PATH | tr : '\n' ); do
if [[ ! -d $libdir ]]; then
echo " WARNING: skipping '$libdir'; not found or not a directory" >&2
continue
fi
pushd "$libdir" >/dev/null
# see: http://mywiki.wooledge.org/BashPitfalls#for_i_in_.24.28ls_.2A.mp3.29
while IFS= read -r -d '' lib; do
indent_and_ul "$libdir/$lib supports the following ABI versions:"
objdump -x "$lib" | grep -P '\sGLIBC(..)?_\d+\.\d+(\.\d+)?$' \
| cut -d' ' -f4 \
| sed 's/\(.*\)/ \1/' | column -c78
echo
done < <( find * -maxdepth 0 -type f \
-regex ".*lib\(std\)?c\(\+\+\)?\.so\.[.0-9]+$" -print0 )
popd >/dev/null
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment