version `GLIBCXX_3.4.29' not found — the libstdc++ counterpart
$ ./tool
./tool: /usr/lib/x86_64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.29' not found
(required by ./tool)
This looks like the GLIBC symbol-version error and behaves the
same way, but the provider is different: GLIBCXX_* symbols come from libstdc++, the GCC C++
runtime, not from libc. That matters because the fixes are not interchangeable.
Reading the version number
GLIBCXX_3.4.29 encodes a GCC release: the 3.4 is the historical ABI baseline and the last
component tracks GCC versions.
GLIBCXX_ | Introduced in GCC |
|---|---|
| 3.4.19 | 4.8 |
| 3.4.22 | 5 |
| 3.4.25 | 8 |
| 3.4.28 | 9 |
| 3.4.29 | 11 |
| 3.4.30 | 12 |
| 3.4.32 | 13 |
| 3.4.33 | 14 |
So GLIBCXX_3.4.29 means “built with GCC 11 or newer”. A host whose libstdc++.so.6 came from GCC 9
tops out at 3.4.28 and cannot satisfy it.
Confirming the diagnosis
$ strings /usr/lib/x86_64-linux-gnu/libstdc++.so.6 | grep -E '^GLIBCXX_3\.4\.[0-9]+$' | sort -V | tail -3
GLIBCXX_3.4.28
GLIBCXX_3.4.29
GLIBCXX_3.4.30
$ readelf -V ./tool | grep GLIBCXX | sort -u | tail -3
0x0010: Name: GLIBCXX_3.4.32 Flags: none Version: 5
The first command lists what the host can provide; the second lists what the binary demands. Compare
the maxima. CXXABI_1.3.x errors work exactly the same way and come from the same library.
The legitimate fixes
1. Build against an older toolchain. The correct answer for anything you ship. Building inside the
oldest base image you support (or manylinux_2_28, or centos:7 for the truly conservative) caps the
required GLIBCXX_ version at what that GCC emits.
2. Link the C++ runtime statically. For C++ this is often the cleanest option, and unlike libc it carries none of the NSS problems:
g++ -static-libstdc++ -static-libgcc -O2 -o tool tool.cpp
The result still links against glibc dynamically, so it inherits the GLIBC constraints — but the
GLIBCXX/CXXABI requirement disappears because the runtime is inside the binary.
# check that it worked
readelf -d ./tool | grep -i stdc++ # expect no output
3. Ship the newer libstdc++.so.6 next to the binary.
patchelf --set-rpath '$ORIGIN/lib' ./tool
Unlike libc, replacing libstdc++ is comparatively safe: it is a normal shared library with a stable
SONAME, it does not own the process’s memory allocator or its threading primitives, and a newer
libstdc++ is backward compatible with older GLIBCXX_ symbols. This is exactly what many vendor
tools do. Two caveats: it must still match the host’s libc.so.6 ABI, and if the process also loads
another library that pulls in a different libstdc++, you get two copies in one process.
4. Use a newer base image. If the host is yours, upgrade it. The error is telling you the runtime is older than the compiler that produced the code.
What does not work
apt install libstdc++6— usually already installed, and the package does not jump major GCC versions within a stable release.- Copying
libstdc++.so.6into/usr/lib— affects every C++ program on the host, including the package manager, and mixing it with the system libc is how you getGLIBC_2.38 not foundin/usr/bin/apt. - Symlinking
libstdc++.so.6to a different major version — no such thing exists; the SONAME covers many GCC generations precisely so that a symlink is never needed.
Why this hits Python packages so often
A compiled Python extension (a wheel with .so files) is a C++ binary with the same version
requirements. When a wheel built against manylinux_2_28 is installed on a host whose libstdc++ is
from GCC 8, the import fails with GLIBCXX_3.4.29 and pip reports it as an ImportError — which
sends people looking at Python versions instead of at the C++ runtime. pip debug --verbose and
auditwheel show <wheel> are the tools that name the real constraint.
Related: wrong ELF class for the other “the library was found but rejected” failure, and checking hardening flags if you are reviewing the binary rather than fixing it.
Every command in this guide was run against the distribution named in the output. If your output differs, the mechanism section explains why — and the ELF Inspector shows the same fields without needing a shell.