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 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).