Case 104: libstdc++ dual-ABI flip (glibcxx_dual_abi_flip_detected)¶
| Field | Value |
|---|---|
| Verdict | ๐ด BREAKING |
| Category | Breaking |
| Platforms | Linux |
| Flags | ABI break, Bad practice |
Detected ChangeKinds |
glibcxx_dual_abi_flip_detected, func_removed |
| Source files | examples/case104_glibcxx_dual_abi_flip/ |
Category: breaking | Verdict: BREAKING
What breaks¶
GNU libstdc++ ships two incompatible ABIs for std::string (and
std::list), selected at compile time by the _GLIBCXX_USE_CXX11_ABI macro.
With =1 (the modern default) std::string mangles as
std::__cxx11::basic_string<...>; with =0 it mangles as the legacy
std::basic_string<...>. The two are not link-compatible.
Here the source is byte-for-byte identical between v1 and v2. Only the macro
flips โ v1 builds with _GLIBCXX_USE_CXX11_ABI=0, v2 with =1. Every
exported function that takes or returns std::string gets a different mangled
name, so a caller linked against one build cannot resolve the symbols of the
other: the dynamic linker fails at load, or โ worse โ a partially-rebuilt
process silently pairs a legacy-ABI string with a cxx11-ABI string and corrupts
memory.
Why abicheck catches it¶
A naive symbol diff would report dozens of unrelated func_removed /
func_added / type_removed churn entries. abicheck recognises the
signature of a dual-ABI flip: a large set of public symbols disappears while an
equally large set appears, and a significant fraction of the churned mangled
names carry the __cxx11 marker. It collapses that pattern into a single
glibcxx_dual_abi_flip_detected diagnostic (alongside the underlying
func_removed/func_added evidence), and the overall verdict is BREAKING.
Code diff¶
The sources are identical; the build configuration differs:
# v1 build
-g++ -shared -fPIC -g -D_GLIBCXX_USE_CXX11_ABI=0 -o libv1.so v1.cpp
# v2 build
+g++ -shared -fPIC -g -D_GLIBCXX_USE_CXX11_ABI=1 -o libv2.so v2.cpp
Symbol-level effect:
-_Z4joinRKSsS0_ # std::string (legacy ABI)
+_Z4joinRKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEES5_ # std::__cxx11::string
Reproduce manually¶
g++ -shared -fPIC -g -D_GLIBCXX_USE_CXX11_ABI=0 -o libv1.so v1.cpp
g++ -shared -fPIC -g -D_GLIBCXX_USE_CXX11_ABI=1 -o libv2.so v2.cpp
# Hundreds of mangled-name differences, all driven by the ABI tag
nm -D --defined-only libv1.so | c++filt | sort > v1.syms
nm -D --defined-only libv2.so | c++filt | sort > v2.syms
diff v1.syms v2.syms | head
# abicheck collapses it into one dual-ABI diagnostic + BREAKING verdict
python -m abicheck compare libv1.so libv2.so
How to fix¶
Pick one _GLIBCXX_USE_CXX11_ABI value and use it consistently across the
library, all of its dependencies, and every consumer that exchanges
std::string/std::list across the boundary. Never expose libstdc++ container
types on a public ABI surface unless the dual-ABI setting is part of the
documented build contract; prefer a C ABI or opaque handles at boundaries that
must survive a toolchain change.
Source files¶
CMakeLists.txtapp.cppv1.cppv2.cpp
See also: Examples overview ยท All BREAKING cases ยท Category: Breaking.