Case 47: Inline Method Moved Out-of-Line¶
| Field | Value |
|---|---|
| Verdict | ๐ข COMPATIBLE |
| Category | Addition (Compatible) |
| Platforms | Linux, macOS, Windows |
| Flags | โ |
Detected ChangeKinds |
func_added |
| Source files | examples/case47_inline_to_outlined/ |
Category: Compatible | Verdict: ๐ข COMPATIBLE
Verdict and consumer impact¶
Calculator::add() is defined inline in v1's header, so it has no
exported symbol โ every consumer gets its own inlined copy baked into
their binary at compile time. In v2, add() is moved out-of-line into
the .cpp file, so the library now exports _ZN10Calculator3addEii.
Consumers compiled against v1 keep using their own inlined copy (the new
export is simply unused by them); consumers compiled against v2 call the
newly-exported symbol. Nothing that worked before stops working โ this is
a pure addition.
Old/new diff¶
| old/lib.hpp | new/lib.hpp |
|---|---|
inline int add(int a, int b) { return a + b; } (no exported symbol) |
int add(int a, int b); โ body in lib.cpp, symbol exported |
abicheck command¶
g++ -shared -fPIC -g -std=c++17 old/lib.cpp -Iold -o libcalc_v1.so
g++ -shared -fPIC -g -std=c++17 new/lib.cpp -Inew -o libcalc_v2.so
abicheck compare libcalc_v1.so libcalc_v2.so
Expected abicheck finding¶
Verdict: COMPATIBLE (exit 0)
Additions:
- func_added: New public function: Calculator::add(int, int)
> New function available; existing binaries are unaffected.
Minimum evidence¶
min_evidence: L0 โ the exported-symbol table alone is enough: add's
mangled symbol is absent from v1's .dynsym and present in v2's. No debug
info or headers needed to see that a new public symbol appeared.
Why abicheck catches it¶
abicheck diffs the two libraries' exported-symbol sets directly from ELF;
a demangled symbol present only in the new snapshot is reported as
func_added, a compatible addition, since no existing symbol changed or
disappeared.
Runtime failure demonstration¶
No observable effect on existing binaries โ add()/multiply()/subtract()
return identical results both before and after the swap, whether the app
is compiled against v1's inline body or v2's exported symbol.
# Build old library + app (compiled against v1's inline add())
g++ -shared -fPIC -g -std=c++17 old/lib.cpp -Iold -o libcalc.so
g++ -g -std=c++17 app.cpp -Iold -L. -lcalc -Wl,-rpath,. -o app
./app
# โ add=5 multiply=20 subtract=8
# โ expected: 5 20 8
# Swap in new library (no recompile)
g++ -shared -fPIC -g -std=c++17 new/lib.cpp -Inew -o libcalc.so
./app
# โ add=5 multiply=20 subtract=8 (identical โ app used its own inlined add())
# Confirm the symbol table difference directly:
nm -D libcalc_v1.so | grep add # โ (nothing โ add() was inlined, no symbol)
nm -D libcalc_v2.so | grep add # โ T _ZN10Calculator3addEii
Safe redesign¶
This transition is already safe for the binary-vs-binary case above, but
it does introduce a build-coordination hazard that a pure library diff
can't see: a consumer compiled against v2's header (declaration only,
no inline body) but linked against v1's .so (which never exported
add โ it was inline) gets a hard link error,
undefined reference to Calculator::add(int, int). That's not an ABI
break in the usual sense โ no existing binary stops working โ but it
does mean v2's header and v2's .so must ship together:
| Consumer built against | Runtime .so |
Result |
|---|---|---|
| v1 header (inline, no symbol) | v1 .so |
works โ caller uses its own inlined copy |
| v1 header (inline, no symbol) | v2 .so (symbol exported) |
works โ caller's inlined copy is used; the new export is unused |
| v2 header (declaration only) | v2 .so (symbol exported) |
works โ caller resolves the exported symbol |
| v2 header (declaration only) | v1 .so (no symbol) |
link failure โ add was never exported by v1 |
Only the last row fails, and it requires a specific build mismatch (new
headers paired with an old library) that a coordinated release naturally
avoids. See case16 (inline_to_non_inline) for the same mechanism spelled
out end to end with a free function.
Cross-tool comparison¶
References¶
Source files¶
CMakeLists.txtapp.cpp
See also: Examples overview ยท All COMPATIBLE cases ยท Category: Addition (Compatible).