What is Shared Library Versioning With SONAME in Linux-Embedded Linux Course In Hyderabad

Shared Library Versioning With SONAME

How GNU/Linux lets two incompatible versions of the same shared library coexist on one target — without breaking the programs that already depend on it.

9 min read
Beginner friendly
Toolchain series, Lecture 12
shared library versioning
soname
symbolic links
ABI compatibility
free linux development course
free embedded linux course

In the previous lecture you built libep_mathutil.so and linked a demo program against it. But what happens on a real target when that library gets a bug-fix update — or worse, an update that breaks its API? That’s exactly what shared library versioning exists to solve, and it’s a topic every engineer on this free linux development course needs to understand before shipping an image with shared libraries on it.

This lecture explains the release version, the interface number, and the SONAME mechanism that lets GNU/Linux run two incompatible copies of the same library side by side on one filesystem — and shows you how to build and inspect that setup yourself.

What You Will Learn

  • Why shared libraries need a versioning scheme at all
  • The difference between a release version and an interface (SONAME) number
  • How to build a versioned shared library with a cross toolchain
  • How the runtime linker uses SONAME to pick the right library at load time
  • How two incompatible library versions can coexist on the same target

Prerequisites

  • Lecture 11, “Static vs Dynamic Linking Explained”
  • A working cross toolchain and basic familiarity with -shared and -fPIC

Two Kinds of Library Updates

Once a shared library ships on real targets, updates to it fall into two very different buckets. A backwards-compatible update fixes bugs or adds new functions without touching any existing function’s signature or behavior — every program already linked against the old version keeps working unmodified. A breaking update changes or removes something programs already depend on — anything still using the old interface can misbehave or crash if it accidentally loads the new file.

GNU/Linux solves this with a two-part version scheme baked into every shared library’s filename and metadata.

Release Version vs Interface Number

The release version is a plain string appended to the library’s real filename — for example libep_logger.so.1.2.0. It changes every time you ship any update at all, compatible or not.

The interface number (also called the SONAME, short for “shared object name”) only changes when the library’s ABI actually breaks compatibility. It’s embedded inside the library binary itself at build time and formatted as <library name>.so.<interface number>.

Anatomy of a Versioned Shared Library
libep_logger.so -> symlink used at build/link time
libep_logger.so.1 -> SONAME symlink, used by the runtime linker
libep_logger.so.1.2.0 -> the real file, release version 1.2.0

Building a Versioned Shared Library

Say you maintain an original small logging helper, ep_logger, with one function:

// ep_logger.c  (release 1.0.0)
#include <stdio.h>
void ep_log_message(const char *msg) {
    printf("[ep_logger] %s\n", msg);
}

To build it as a properly versioned shared object, pass the SONAME to the linker with -Wl,-soname and name the output file with the full release version:

$ ${CROSS_COMPILE}gcc -fPIC -c ep_logger.c
$ ${CROSS_COMPILE}gcc -shared -Wl,-soname,libep_logger.so.1 \
    -o libep_logger.so.1.0.0 ep_logger.o

$ ln -sf libep_logger.so.1.0.0 libep_logger.so.1
$ ln -sf libep_logger.so.1     libep_logger.so

Confirm the embedded SONAME with readelf, exactly as you’d check it on any library already installed on a target:

$ ${CROSS_COMPILE}readelf -a libep_logger.so.1.0.0 | grep SONAME
0x000000000000000e (SONAME)   Library soname: [libep_logger.so.1]

A program linked with -lep_logger requests libep_logger.so.1 at runtime — not the unversioned .so symlink, and not the full release filename. That single indirection is what makes the whole scheme work.

What Happens When You Break the ABI

Now suppose version 2.0.0 of ep_logger changes ep_log_message‘s signature to take a severity level, breaking every program built against 1.x. You bump the interface number, not just the release string:

$ ${CROSS_COMPILE}gcc -shared -Wl,-soname,libep_logger.so.2 \
    -o libep_logger.so.2.0.0 ep_logger.o

$ ln -sf libep_logger.so.2.0.0 libep_logger.so.2
$ ln -sf libep_logger.so.2     libep_logger.so

The unversioned symlink now points at the new SONAME, so any new build using -lep_logger picks up version 2 and will hit a compile error if it still calls the old signature — exactly what you want, because it forces the developer to fix the call rather than silently misbehaving at runtime. Meanwhile, any program already deployed and linked against SONAME libep_logger.so.1 keeps loading the 1.x file and keeps working, because both SONAMEs can exist on the target filesystem at once:

$ ls -l /usr/lib/libep_logger*
lrwxrwxrwx 1 root root      20 Aug 12 10:02 libep_logger.so -> libep_logger.so.2
lrwxrwxrwx 1 root root      22 Aug 12 10:02 libep_logger.so.1 -> libep_logger.so.1.0.0
lrwxrwxrwx 1 root root      22 Aug 12 10:02 libep_logger.so.2 -> libep_logger.so.2.0.0
-rwxr-xr-x 1 root root   14328 Aug 12 09:40 libep_logger.so.1.0.0
-rwxr-xr-x 1 root root   14896 Aug 12 10:00 libep_logger.so.2.0.0
Two Incompatible Versions, One Filesystem
Old program (built vs SONAME .so.1) –NEEDED–> libep_logger.so.1 –> libep_logger.so.1.0.0
New program (built vs SONAME .so.2) –NEEDED–> libep_logger.so.2 –> libep_logger.so.2.0.0
libep_logger.so (dev symlink) ———————————–> points at the newest SONAME

The Four Files You’ll See On a Real Target

File Purpose
libep_logger.a Static archive, used only at static-link time
libep_logger.so Dev-time symlink to the current SONAME, used when linking new builds
libep_logger.so.N SONAME symlink, what running binaries actually request
libep_logger.so.N.n.n The real, versioned shared object file

Common Mistakes and Troubleshooting

  • Forgetting -Wl,-soname: without it, your .so has no embedded interface number, so the runtime linker falls back to the exact filename — any future rename breaks every program that used it.
  • Bumping the SONAME for a compatible change: this needlessly forces every dependent program to be rebuilt — only bump the interface number when you actually break the ABI.
  • Stale symlinks after an update: if libep_logger.so.1 isn’t relinked to point at the newest 1.x release file, programs keep loading an old build even after you “updated” the library — always run your package manager’s ldconfig equivalent after installing new library files.
  • Confusing the dev symlink with the SONAME symlink: only the unversioned .so symlink is meant for build-time linking; shipping only that file to a target and skipping the SONAME symlink will break any binary trying to load by SONAME.

Best Practices

  • Follow semantic versioning discipline: bump the interface number only for breaking ABI changes, keep it stable for bug fixes and additions.
  • Always set -Wl,-soname explicitly rather than relying on defaults, so the embedded interface number is exactly what you intend.
  • Performance: SONAME resolution is a cheap symlink lookup done once at load time — it has no measurable runtime cost.
  • Security: versioned SONAMEs let you patch a vulnerable library in place (rebuild the same SONAME, same interface) and have every dependent process pick up the fix on next start, with zero rebuilds required elsewhere.

Summary and Key Takeaways

  • Shared libraries carry two version numbers: a release version in the filename, and an interface number (SONAME) embedded in the binary.
  • Programs link against the SONAME, not the exact release filename — that indirection is what allows safe, independent library updates.
  • Bump the SONAME only for breaking ABI changes; keep it stable for compatible bug fixes.
  • Two incompatible versions of the same library can coexist on one target, each served to the programs that need it.
  • -Wl,-soname at build time and a matching symlink chain on the target are what make the whole scheme work in practice.

SONAME versioning is what makes it safe to update one library on a fielded embedded device without rebuilding and redeploying every application that depends on it — a genuinely load-bearing piece of how Linux systems stay maintainable in the field. With static, dynamic, and versioned linking now covered, you have the full picture of how your cross toolchain wires an application to its dependencies from source to a running target.

FAQ: Shared Library Versioning

What is a SONAME in Linux?

The SONAME is the interface number embedded inside a shared library at build time, formatted as library-name.so.N. It’s what the runtime linker actually resolves when loading a dependency, rather than the exact release filename.

Why does a shared library need three different filenames?

The dev symlink (.so) is used at build time, the SONAME symlink (.so.N) is what running binaries request, and the versioned file (.so.N.n.n) is the real code — separating them lets updates happen without breaking already-built binaries.

When should I bump the SONAME interface number?

Only when you make a breaking, backwards-incompatible change to the library’s ABI. Bug fixes and additive, compatible changes should keep the same SONAME.

Can two versions of the same shared library exist on one target?

Yes — as long as they have different SONAMEs, both can be installed simultaneously, and each dependent program loads whichever SONAME it was originally linked against.

How do I set the SONAME when building a shared library?

Pass -Wl,-soname,<name>.so.<N> to gcc at link time, then create the symlink chain from the unversioned .so down to the real release-versioned file.

What tool shows me a library’s embedded SONAME?

readelf -a <library> | grep SONAME, using your toolchain-prefixed readelf, prints the interface number recorded inside the binary.

Continue the Free Embedded Linux Course

This lecture is part of EmbeddedPathashala’s free embedded linux course on cross toolchains — built for engineers who want the “why,” not just the command to copy-paste.

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *