SONAME and Library Symlinks: Package an ABI Upgrade Without Breaking Old Consumers
Package the runtime name recorded in each consumer’s DT_NEEDED entries, and keep it aligned with the library’s DT_SONAME and actual ABI. The implementation filename, runtime SONAME and development linker name have different jobs. Pointing an old major-version name at an incompatible new library does not create compatibility.
This guide pins GNU BFD Binutils 2.44 and Linux man-pages 6.12 loader behavior for ordinary Linux ELF shared libraries. An executed host fixture installed two synthetic ABI majors side by side and changed one compatible implementation behind its major-version link. No live root filesystem, package database or target device was modified.
Draw the three-name contract
Consider a hypothetical implementation file libcodec.so.1.0 containing DT_SONAME equal to libcodec.so.1. A development linker name, commonly libcodec.so, can select an implementation while building a consumer. The resulting consumer normally records the shared object’s SONAME rather than the full implementation filename used on the link command.
The Binutils 2.44 linker reference defines this SONAME behavior. Inspect the built files rather than inferring it from a filename convention. A library lacking a SONAME can produce a different dependency string, and an explicit path containing slashes has distinct loader treatment.
Keep the identities in a ledger: implementation file and digest, embedded SONAME, available runtime filename/link, and every consumer’s NEEDED string. That exposes packaging gaps that a successful build against staging can hide.

Read the ELF metadata before adjusting links
The readelf documentation describes dynamic-section inspection. These read-only templates inspect the intended artifacts:
readelf -d libcodec.so.1.0
readelf -d old-consumer
readelf -d new-consumer
Check SONAME in the provider and NEEDED in each consumer. Also confirm architecture, class and the actual loader used by the image. A library with the expected name but the wrong machine ABI is not an admissible provider.
Then resolve the final image’s runtime links. A link named libcodec.so.1 pointing to an absent libcodec.so.1.0 is a packaging defect even when another implementation exists nearby. A development link named libcodec.so alone does not satisfy a consumer asking for libcodec.so.1.
The Linux loader reference distinguishes dependency strings containing slashes from names searched through configured paths. Keep test dependencies in the intended form; a build that accidentally records a temporary pathname can bypass the naming contract you thought you were testing.
Decide compatibility before assigning the major
Retain a runtime ABI identity only when the new implementation preserves the supported consumers’ contract. That includes calling conventions, public data layouts, symbol availability, ownership behavior and documented semantics. The SONAME itself does not test any of those properties.
If an interface changes incompatibly, assign a distinct ABI identity and rebuild its consumers accordingly. An old function name retaining a new prototype is especially dangerous because the loader can resolve it while calls remain invalid. Do not use an alias symlink as a substitute for a conversion layer or compatible implementation.
For a compatible implementation update, retaining the major SONAME allows old consumers to request the same runtime identity. Record why compatibility is justified and test the oldest supported consumer. Adding an optional symbol can be compatible for old consumers while a new consumer using that symbol still requires the newer implementation.
Co-install incompatible generations deliberately
| Consumer | Required runtime identity | Installed implementation |
|---|---|---|
| Old application | libcodec.so.1 | Compatible ABI-1 implementation. |
| New application | libcodec.so.2 | ABI-2 implementation. |
| New build using a development linker name | Determined by the provider selected at link time | Verify the output’s actual NEEDED entry. |
Co-installation permits different consumers to retain distinct dependencies, but it needs package ownership and image-space planning. Do not let two package install steps overwrite the same development link unpredictably. Select the intended development interface explicitly for each build configuration.
Also inspect plugins and rarely used helpers. The main application’s NEEDED list is not a complete inventory of runtime loading. A plugin loaded later can still require the old major, and deleting that provider after a boot-only test can break field diagnostics.
Executed two-consumer fixture
GCC 14.2.0 and BFD ld 2.44 built ABI 1 with codec_value returning seven and ABI 2 with a distinct codec_value_v2 function. The first consumer called the old API; the second called the new API. Each implementation carried its corresponding major SONAME.
Even though the link commands named the implementation files directly, inspection showed that the old consumer required libcodec.so.1 and the new consumer required libcodec.so.2. Both ran successfully with the two runtime major links in one owned fixture directory.
A second ABI-1 implementation retained codec_value and added an unused extra function. The fixture changed only the libcodec.so.1 link from the first ABI-1 file to that compatible candidate, then reran both consumers successfully. This demonstrates the synthetic naming and selection behavior, not a general proof that arbitrary library changes are compatible.
Test deployment states beyond a working directory
For the target image, test both consumers from the final packaged filesystem with the intended loader environment. Include a deliberately missing runtime-major link as a negative packaging case, a candidate compatible update, and coexistence during the supported transition period. Use trusted test binaries only.
Keep update atomicity separate from SONAME semantics. A complete compatible image can still be deployed badly if files or links are replaced in an unsafe order. Qualify the product’s approved update mechanism and preserve rollback requirements; this article does not prescribe replacing libraries in a running production filesystem.
Save the consumer-to-SONAME map, provider hashes, resolved links and runtime outcomes. If the failure names an old major, first determine which consumer still requests it and why. Renaming the new library until the loader stops complaining can hide an ABI mismatch that will surface later as corrupted state rather than a clear missing-file error.