Keyboard Shortcuts

Ctrl+K
Open search
Esc
Close search / dialog
↑
Back to top

Shared Libraries

20 minadvancedModule 7: Dynamic LinkingContinues from dynamic-section

Why This Matters

Shared libraries are the reason your system doesn't need a separate copy of printf() in every program. Instead, one copy of libc.so lives on disk, and every program shares it at runtime. This saves disk space, memory, and makes updating libraries possible without recompiling every program.

Understanding shared libraries explains why you see ".so" files, why "library not found" errors happen, and how versioning keeps old programs working.

A Simple Model

A shared library is a position-independent ELF executable that can be loaded at any address. It's built with three key concepts:

This three-level naming lets you update a library (bump 1.2.3 to 1.2.4) without breaking programs linked against soname "libfoo.so.1".

The Actual Detail

Building a shared library:

# Compile with -fPIC (position-independent code)
gcc -fPIC -c foo.c -o foo.o
gcc -fPIC -c bar.c -o bar.o

# Create the shared library
gcc -shared -o libfoo.so.1.2.3 foo.o bar.o

# Create versioned symlinks
ln -s libfoo.so.1.2.3 libfoo.so.1    # soname
ln -s libfoo.so.1 libfoo.so          # linker name

# The soname is embedded in the library
readelf -d libfoo.so.1.2.3 | grep SONAME
# 0x000000000000000e (SONAME)    Library soname: [libfoo.so.1]

At compile time, the linker reads the soname from the library and stores it in your program's .dynamic section:

gcc -o myprog myprog.c -L. -lfoo
readelf -d myprog | grep NEEDED
# 0x0000000000000001 (NEEDED)  Shared library: [libfoo.so.1]
# Note: it says libfoo.so.1, NOT libfoo.so.1.2.3

At runtime, ld.so searches for "libfoo.so.1" and follows the symlink chain to find the actual file.

A Real Example

$ ls -la /usr/lib/x86_64-linux-gnu/libc.so*
lrwxrwxrwx 1 root root 14 libc.so.6 symlink libc.so.6

$ readelf -d /usr/lib/x86_64-linux-gnu/libc.so.6 | grep SONAME
 0x000000000000000e (SONAME)  Library soname: [libc.so.6]

# The linker name (for gcc -lc) is a separate file:
$ file /usr/lib/x86_64-linux-gnu/libc.so
/usr/lib/x86_64-linux-gnu/libc.so: ASCII text

$ cat /usr/lib/x86_64-linux-gnu/libc.so
                   
OUTPUT_FORMAT(elf64-x86-64)
GROUP ( /usr/lib/x86_64-linux-gnu/libc.so.6
        /usr/lib/x86_64-linux-gnu/libc_nonshared.a
        AS_NEEDED ( /usr/lib/x86_64-linux-gnu/libdl.so.2 ) )

On Debian/Ubuntu, the linker name is actually a text script that specifies which real libraries to link. This is how the linker resolves -lc to the correct libc version.

Try It

# See the soname of any shared library
readelf -d /usr/lib/x86_64-linux-gnu/libc.so.6 | grep SONAME

# Build your own shared library
# mylib.c: int my_func(int x) returns x * 2
gcc -fPIC -shared -Wl,-soname,libmylib.so.1 -o libmylib.so.1.0 mylib.c
ln -s libmylib.so.1.0 libmylib.so.1
ln -s libmylib.so.1 libmylib.so

# Verify soname
readelf -d libmylib.so.1.0 | grep SONAME

# See library search order
ldconfig -p | head -10

Check Your Understanding

Why does ELF use a three-level naming scheme (real name, soname, linker name)?

Apply It

You upgrade libfoo.so.1.2.3 to libfoo.so.1.2.4. Do you need to recompile programs that use libfoo?

Connect

Shared libraries are found and loaded by the dynamic linker (ld.so). The next concept covers how ld.so searches for libraries, processes relocations, and sets up the runtime environment.

← Swipe to navigate →