Skip to content

Case 05: Missing SONAME

Field Value
Verdict 🟢 COMPATIBLE
Category Quality (Compatible)
Classification Rule
Platforms Linux
Flags Bad practice
Detected ChangeKinds soname_missing
Source files catalog/cases/case05_soname/
Rule family soname
Subject ELF export and linker metadata

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

  • new/
  • old/
  • CMakeLists.txt
  • app.c

See also: Compatibility Catalog · All COMPATIBLE cases · Category: Quality (Compatible) · Rule: SONAME missing · Subject: ELF export and linker metadata.