Case 05: Missing SONAME¶
| Field | Value |
|---|---|
| Verdict | ๐ข COMPATIBLE |
| Category | Quality (Compatible) |
| Platforms | Linux |
| Flags | Bad practice |
Detected ChangeKinds |
soname_missing |
| Source files | examples/case05_soname/ |
Category: ELF/Linker | Verdict: โ COMPATIBLE (bad practice)
Verdict and consumer impact¶
Nothing breaks today: foo() behaves identically in both versions and any
binary linked against either .so runs fine right now. The finding is a
packaging/deployment risk, not an ABI break โ old's build omits -Wl,-soname,
so consumers record the bare filename libfoo.so in their DT_NEEDED entry
instead of a real SONAME. If this library is later shipped as libfoo.so.1
with symlink-based versioning, ldconfig and existing binaries have nothing
to resolve against.
Old/new diff¶
| old/lib.c | new/lib.c |
|---|---|
int foo(void) { return 0; } |
int foo(void) { return 0; } |
Identical source. The change is entirely in the link step: old is linked
with no -Wl,-soname; new is linked with -Wl,-soname,libv2.so.
abicheck command¶
gcc -shared -fPIC -g old/lib.c -o libfoo_v1.so
gcc -shared -fPIC -g new/lib.c -o libfoo_v2.so -Wl,-soname,libv2.so
abicheck compare libfoo_v1.so libfoo_v2.so
Expected abicheck finding¶
Verdict: COMPATIBLE (exit 0)
Quality Issues:
- soname_missing: Old library has no SONAME (bad practice โ packaging/
ldconfig will fail); new library correctly defines SONAME 'libv2.so'
Minimum evidence¶
min_evidence: L0 โ SONAME presence is an ELF dynamic-section fact
(DT_SONAME), readable straight from the binary's .dynamic section. No
debug info or headers needed.
Why abicheck catches it¶
abicheck reads each library's DT_SONAME entry (or its absence) directly
from the ELF dynamic section as part of its L0 platform metadata pass, and
flags a library that ships without one as a quality issue rather than an ABI
change โ the exported symbols and their signatures are untouched, so there's
nothing for the breaking-change detectors to report.
Runtime failure demonstration¶
Severity: INFORMATIONAL โ no observable effect on existing binaries.
Scenario: compile app against old (no SONAME) vs new (SONAME libv2.so).
# 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
# โ foo() = 0
# Swap in new library (no recompile)
gcc -shared -fPIC -g new/lib.c -o libfoo.so -Wl,-soname,libv2.so
./app
# โ foo() = 0 (same โ no breakage)
readelf -d libfoo.so | grep SONAME
# โ (SONAME) Library soname: [libv2.so]
readelf -d app | grep NEEDED
# โ (NEEDED) Shared library: [libfoo.so] โ baked in at link time from
# the *old* library's bare filename; unaffected by the new SONAME
Why INFORMATIONAL: the missing SONAME doesn't break anything at the
moment app was linked โ DT_NEEDED is fixed to whatever name the linker
saw at build time either way. The risk only materializes later, when the
library is repackaged with a real version symlink (libfoo.so.1 โ
libfoo.so) and ldconfig/packaging tooling need DT_SONAME to manage
that symlink tree.
Safe redesign¶
Always pass -Wl,-soname,libname.so.MAJOR when building a shared library
intended for system installation, so consumers link against the versioned
SONAME instead of a bare filename.
Real-world example: many in-tree/vendored libraries built with simple
Makefiles omit SONAME. Debian packaging policy enforces SONAME presence
and will reject packages without it.
Cross-tool comparison¶
readelf -d is the lower-level way to spot this directly:
readelf -d libfoo_v1.so | grep SONAME # โ (empty โ no SONAME)
readelf -d libfoo_v2.so | grep SONAME # โ (SONAME) Library soname: [libv2.so]
References¶
Source files¶
CMakeLists.txtapp.c
See also: Examples overview ยท All COMPATIBLE cases ยท Category: Quality (Compatible).