Case 05: Missing SONAME¶
| Field | Value |
|---|---|
| Verdict | ๐ข COMPATIBLE |
| Category | Quality (Compatible) |
| Platforms | Linux |
| Flags | Bad practice |
Detected ChangeKinds |
soname_missing |
| Source files | browse on GitHub |
Category: ELF/Linker | Verdict: ๐ก BAD PRACTICE
What breaks¶
Without a SONAME, the dynamic linker records the bare filename (libfoo.so) in
DT_NEEDED entries of every consumer. If you later ship libfoo.so.1, existing
binaries won't find it. SONAME is how Linux implements library versioning.
Why the check catches it¶
readelf -d on a well-built library shows (SONAME) Library soname: [libfoo.so.1].
Its absence means the library was linked without -Wl,-soname.
Build comparison¶
| good.c (with SONAME) | bad.c (without) |
|---|---|
gcc -shared -fPIC good.c -o libfoo.so -Wl,-soname,libfoo.so.1 |
gcc -shared -fPIC bad.c -o libfoo.so |
readelf -d โ (SONAME) libfoo.so.1 |
readelf -d โ (no SONAME entry) |
Reproduce manually¶
gcc -shared -fPIC good.c -o libgood.so -Wl,-soname,libfoo.so.1
gcc -shared -fPIC bad.c -o libbad.so
readelf -d libgood.so | grep SONAME # โ present
readelf -d libbad.so | grep SONAME # โ empty
How to fix¶
Always pass -Wl,-soname,libname.so.MAJOR when building a shared library intended
for system installation.
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.
Real Failure Demo¶
Severity: BAD PRACTICE
Scenario: build app against bad.so (no SONAME) vs good.so (with SONAME). Runtime works either way โ the issue is packaging and future versioning.
# Build both variants
gcc -shared -fPIC -g bad.c -o libbad.so
gcc -shared -fPIC -g good.c -o libgood.so -Wl,-soname,libfoo.so.1
# Check SONAME presence
readelf -d libgood.so | grep SONAME # โ (SONAME) Library soname: [libfoo.so.1]
readelf -d libbad.so | grep SONAME # โ (empty โ no SONAME)
# Build bad variant (no SONAME)
cp libbad.so libfoo.so
gcc -g app.c -L. -Wl,-rpath,. -lfoo -o app-bad
./app-bad
# โ foo() = 0 (works at runtime)
readelf -d app-bad | grep NEEDED
# โ (NEEDED) Shared library: [libfoo.so] โ bare filename, no SONAME
# Build good variant (with SONAME) for comparison
cp libgood.so libfoo.so
gcc -g app.c -L. -Wl,-rpath,. -lfoo -o app-good
readelf -d app-good | grep NEEDED
# โ (NEEDED) Shared library: [libfoo.so.1] โ SONAME baked in
#
# Without SONAME, ldconfig cannot create the libfoo.so.1 symlink.
# Any binary linked against libbad.so will look for "libfoo.so" forever.
Why BAD PRACTICE: The runtime works, but without a SONAME the dynamic linker
embeds the bare filename in DT_NEEDED. If you later ship libfoo.so.1, existing
binaries won't find it and packaging tools (ldconfig, dpkg) can't manage the symlink tree.
References¶
Source files¶
See also: Examples overview ยท All COMPATIBLE cases ยท Category: Quality (Compatible).