Skip to content

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.txt
  • app.c

See also: Examples overview ยท All COMPATIBLE cases ยท Category: Quality (Compatible).