Case 22: Method Const Qualifier Changed¶
| Field | Value |
|---|---|
| Verdict | ๐ด BREAKING |
| Category | Breaking |
| Platforms | Linux, macOS, Windows |
| Flags | ABI break, API break |
Detected ChangeKinds |
func_cv_changed |
| Source files | examples/case22_method_const_changed/ |
Category: Breaking | Verdict: ๐ด BREAKING
Verdict and consumer impact¶
In the Itanium C++ ABI, const qualification on a member function is
encoded directly into the mangled symbol name: Widget::get() const mangles
to _ZNK6Widget3getEv (the K marks const); dropping const mangles to
_ZN6Widget3getEv โ a completely different symbol. Old binaries import
_ZNK6Widget3getEv; new's library exports only _ZN6Widget3getEv. The
dynamic linker cannot resolve the reference โ an unresolved-symbol error at
load or first-call time, not a subtle miscalculation.
Old/new diff¶
| old/lib.h | new/lib.h |
|---|---|
class Widget { public: void get() const; }; |
class Widget { public: void get(); }; |
abicheck command¶
g++ -shared -fPIC -std=c++17 -g old/lib.cpp -Iold -o libfoo_v1.so
g++ -shared -fPIC -std=c++17 -g new/lib.cpp -Inew -o libfoo_v2.so
abicheck compare libfoo_v1.so libfoo_v2.so
Expected abicheck finding¶
Verdict: BREAKING (exit 4)
- func_removed_elf_only: Elf_only function removed: Widget::get() const
> Exported function symbol removed from the binary; old binaries that
link or dlsym() it can fail even without header evidence.
Additions:
- func_added: New public function: Widget::get()
> New function available; existing binaries are unaffected.
Minimum evidence¶
min_evidence: L1 โ because const is baked into the Itanium mangled name
itself, the exported-symbol table already shows two unrelated symbols
(_ZNK6Widget3getEv gone, _ZN6Widget3getEv new) โ this is detectable from
ELF/DWARF symbol evidence alone, no public headers required. Headers (L2)
sharpen the finding from a generic removed+added pair into a named
CV-qualifier change โ see below โ but aren't needed to reach the BREAKING
verdict.
Why abicheck catches it¶
At L1, abicheck's exported-symbol diff sees _ZNK6Widget3getEv present only
in old and _ZN6Widget3getEv present only in new, with no other
correlating evidence to link them, so it reports a straightforward
func_removed_elf_only + func_added pair โ already BREAKING, since a
symbol old binaries depend on is gone. With header AST evidence (L2), the
same two functions are recognized as the same declaration source
(Widget::get, old/lib.h:3) with a changed const qualifier, so the
finding gets renamed to the more specific func_cv_changed:
abicheck compare libfoo_v1.so libfoo_v2.so \
--header old=old/lib.h --header new=new/lib.h --ast-frontend clang
- func_cv_changed: CV qualifier changed: get (const=True volatile=False ->
const=False volatile=False)
> const/volatile on 'this' changes the mangled name; old binaries link to
the wrong symbol.
Runtime failure demonstration¶
Severity: CRITICAL
Scenario: app compiled against old header calls get() const
(_ZNK6Widget3getEv). new's library exports only _ZN6Widget3getEv โ
undefined symbol at runtime.
# Build old library + app
g++ -shared -fPIC -std=c++17 -g old/lib.cpp -Iold -o libwidget.so
g++ -std=c++17 -g app.cpp -Iold -L. -lwidget -Wl,-rpath,. -o app
./app
# โ get() const called
# โ exit: 0
# Swap in new library (const removed)
g++ -shared -fPIC -std=c++17 -g new/lib.cpp -Inew -o libwidget.so
./app
# โ ./app: symbol lookup error: ./app: undefined symbol: _ZNK6Widget3getEv
# โ exit: 127
Why CRITICAL: const is part of the C++ mangled name (K in
_ZNK...). Removing it produces a completely different symbol. Every
pre-built binary that calls widget.get() on a const reference fails to
load โ a hard runtime crash, not a silent miscalculation.
Safe redesign¶
Keep the old const-qualified overload and add the new non-const one alongside it if the semantics genuinely need to differ; if the const qualifier was simply wrong, ship both overloads during a transition period so binaries built against either one keep resolving.
Real-world example: const-correctness cleanups that touch an
already-shipped public method signature are a classic source of this โ
every mangled-name change from a const fix is, by ABI rules,
indistinguishable from removing one function and adding an unrelated one.
References¶
Source files¶
CMakeLists.txtapp.cpp
See also: Examples overview ยท All BREAKING cases ยท Category: Breaking.