Case 03: Compatible Addition (New Export)¶
| Field | Value |
|---|---|
| Verdict | ๐ข COMPATIBLE |
| Category | Addition (Compatible) |
| Platforms | Linux, macOS, Windows |
| Flags | โ |
Detected ChangeKinds |
func_added |
| Source files | examples/case03_compat_addition/ |
Category: Symbol API | Verdict: โ COMPATIBLE
Verdict and consumer impact¶
Nothing breaks in existing binaries โ they never referenced get_build().
Adding a new exported function is the textbook backward-compatible library
change: old consumers keep working unmodified, and new consumers can start
using get_build() once they recompile against the new headers.
Old/new diff¶
| old/lib.c | new/lib.c |
|---|---|
int get_version(void) { return 1; } |
int get_version(void) { return 1; } |
| (nothing) | int get_build(void) { return 42; } |
abicheck command¶
gcc -shared -fPIC -g old/lib.c -o libv1.so
gcc -shared -fPIC -g new/lib.c -o libv2.so
abicheck compare libv1.so libv2.so
Expected abicheck finding¶
Verdict: COMPATIBLE (exit 0)
Additions:
- func_added: New public function: get_build
> New function available; existing binaries are unaffected.
Minimum evidence¶
min_evidence: L0 โ the exported-symbol table alone is enough: get_build
is present in v2's .dynsym and absent from v1's, with no removed or
changed symbols. No debug info or headers needed; -g above is only there
so the Runtime failure demonstration below can build a matching app.
Why abicheck catches it¶
The dynamic symbol table is authoritative L0 evidence โ abicheck diffs the
exported-symbol sets directly. A symbol present only on the new side, with
no matching removal or signature change, is classified func_added and
placed in the compatible/additions bucket rather than the breaking one.
Runtime failure demonstration¶
Severity: INFORMATIONAL โ no observable effect.
Scenario: compile app against old, swap in new .so without recompile.
# Build old library + app
gcc -shared -fPIC -g old/lib.c -o libfoo.so
gcc -g app.c -L. -lfoo -Wl,-rpath,. -o app
./app
# โ get_version() = 1
# Swap in new library (no recompile)
gcc -shared -fPIC -g new/lib.c -o libfoo.so
./app
# โ get_version() = 1 (same โ no breakage)
Why INFORMATIONAL: adding new exports is backward-compatible; existing binaries continue to work unchanged because they never referenced the new symbol.
Safe redesign¶
No fix needed โ this is the correct way to extend a library API. Just keep the SONAME unchanged for compatible additions and bump it on breaking changes.
Real-world example: glibc regularly adds new symbols (e.g.,
reallocarray, explicit_bzero) to minor releases without bumping the
SONAME, relying on this compatible-addition guarantee.
Cross-tool comparison¶
abidw --out-file v1.xml libfoo_v1.so
abidw --out-file v2.xml libfoo_v2.so
abidiff v1.xml v2.xml
echo "exit: $?" # โ 4 (abidiff's convention: any ABI change, including a
# compatible addition, sets exit-bit 2; the absence
# of bit 3 (value 8) means no breaking change)
References¶
Source files¶
CMakeLists.txtapp.c
See also: Examples overview ยท All COMPATIBLE cases ยท Category: Addition (Compatible).