What Problem Does ldconfig Solve?
Before ldconfig existed, the dynamic linker had to search many directories every time a program was launched. Imagine a program depending on 10 shared libraries. On startup, the linker would open each directory in the search path and look for the right .so file. This was very slow, especially when dozens of directories were searched.
Second problem: as library versions are installed or removed, the soname symbolic links (like libdemo.so.1) can become stale — pointing to an old version or nothing at all.
ldconfig solves both problems in one tool.
1. Three Names Every Shared Library Has
Before we understand ldconfig, we must understand the three names a shared library uses. Each serves a different purpose.
| Name Type | Example | Purpose |
|---|---|---|
| Real Name | libdemo.so.1.0.1 |
Actual file on disk. Contains major + minor + patch version. |
| Soname | libdemo.so.1 |
Symlink → latest minor version. Embedded inside .so at build time. Used at run time. |
| Linker Name | libdemo.so |
Symlink → soname. Used by gcc -ldemo at compile time. |
| gcc -ldemo (compile time) |
→ | libdemo.so linker name (symlink) |
→ | libdemo.so.1 soname (symlink) |
→ | libdemo.so.1.0.1 real file on disk |
2. What ldconfig Does — Two Main Tasks
When you run ldconfig (usually as root), it performs exactly two tasks:
Scans standard library directories and writes /etc/ld.so.cache — a binary index of all libraries found. The dynamic linker reads this cache at program startup instead of walking directories manually. Result: very fast library loading.
Scans: directories in /etc/ld.so.conf, then /lib, then /usr/lib.
Reads the embedded soname from the latest minor version of each library and creates/updates the soname symbolic link in the same directory. Result: soname always points to the newest compatible version.
It finds soname by reading the ELF dynamic section of the .so file.
/etc/ld.so.conf to know which directories to scan. In many distros, this file uses an include /etc/ld.so.conf.d/*.conf directive so that packages can drop their own conf file in /etc/ld.so.conf.d/.Code Example 1 — Build a Shared Library and Run ldconfig
This full example walks through building a shared library with a soname, installing it, and running ldconfig to register it.
Step 1: Write the library source files
/* mod1.c — first module of our shared library */
#include <stdio.h>
void hello_from_mod1(void) {
printf("Hello from mod1! (libdemo version 1.0.1)\n");
}
int add_numbers(int a, int b) {
return a + b;
}
/* mod2.c — second module */
#include <stdio.h>
void hello_from_mod2(void) {
printf("Hello from mod2! (libdemo version 1.0.1)\n");
}
/* demo.h — public header */
#ifndef DEMO_H
#define DEMO_H
void hello_from_mod1(void);
void hello_from_mod2(void);
int add_numbers(int a, int b);
#endif
Step 2: Compile with -fPIC
# Compile each source file to Position Independent Code (PIC)
# -fPIC is REQUIRED for shared library code
# -g adds debug info, -Wall enables warnings
gcc -g -c -fPIC -Wall mod1.c mod2.c
-fPIC, the compiler generates code that does not assume a fixed address — it uses relative addressing. Without -fPIC, the linker would need to copy-relocate the code for each process, defeating the sharing purpose.Step 3: Link into a shared library with soname
# -shared: create a shared library (DSO)
# -Wl,-soname,libdemo.so.1 : embed soname "libdemo.so.1" inside the ELF file
# -o libdemo.so.1.0.1 : output file = real name
gcc -g -shared -Wl,-soname,libdemo.so.1 \
-o libdemo.so.1.0.1 \
mod1.o mod2.o
Step 4: Verify the embedded soname
# readelf shows the ELF dynamic section
# grep for SONAME entry
readelf -d libdemo.so.1.0.1 | grep SONAME
# Expected output:
# 0x000000000000000e (SONAME) Library soname: [libdemo.so.1]
Step 5: Install and run ldconfig
# Copy real file to /usr/local/lib
sudo cp libdemo.so.1.0.1 /usr/local/lib/
# Make sure /usr/local/lib is in /etc/ld.so.conf.d/
echo "/usr/local/lib" | sudo tee /etc/ld.so.conf.d/local.conf
# Run ldconfig (creates cache + soname symlink automatically)
sudo ldconfig -v | grep libdemo
# Expected output:
# libdemo.so.1 -> libdemo.so.1.0.1
# Verify symlink was created
ls -l /usr/local/lib/libdemo*
# lrwxrwxrwx libdemo.so.1 -> libdemo.so.1.0.1
# -rwxr-xr-x libdemo.so.1.0.1
# Manually create linker name (ldconfig does NOT do this)
sudo ln -s libdemo.so.1 /usr/local/lib/libdemo.so
libdemo.so.1). But the linker name symlink (libdemo.so) must be created manually. This is because the linker name is only used by the compiler (not the dynamic linker) and there can be only one, so automated management could be ambiguous.Step 6: Compile and run a test program
/* main.c — test program that uses libdemo */
#include "demo.h"
#include <stdio.h>
int main(void) {
hello_from_mod1();
hello_from_mod2();
printf("3 + 4 = %d\n", add_numbers(3, 4));
return 0;
}
# -ldemo tells linker to find libdemo.so (linker name)
# -L/usr/local/lib tells the compiler where to find it
gcc -o demo_prog main.c -ldemo -L/usr/local/lib
# Run the program
./demo_prog
# Hello from mod1! (libdemo version 1.0.1)
# Hello from mod2! (libdemo version 1.0.1)
# 3 + 4 = 7
3. ldconfig Command-Line Options
| Option | What It Does | When to Use |
|---|---|---|
-v |
Verbose: prints each library it processes | Debugging — confirm which libraries were found |
-p |
Print current contents of /etc/ld.so.cache | Check what the cache currently knows about |
-N |
Skip rebuilding the cache | Only update soname symlinks, not the cache |
-X |
Skip updating soname symlinks | Only rebuild the cache |
-n dir |
Process ONLY libs in named dir; no cache update | Private/local libraries during development |
Code Example 2 — Two Major Versions Side by Side
This example installs libdemo.so.1.0.1 and libdemo.so.2.0.0 simultaneously and shows how ldconfig creates separate soname symlinks for each.
# Suppose we already have libdemo.so.1.0.1 in /usr/lib
# Now we build a new MAJOR version (breaking change)
gcc -g -c -fPIC -Wall mod1_v2.c mod2_v2.c
# Note: soname now says libdemo.so.2
gcc -g -shared -Wl,-soname,libdemo.so.2 \
-o libdemo.so.2.0.0 \
mod1_v2.o mod2_v2.o
# Install both versions
sudo mv libdemo.so.1.0.1 libdemo.so.2.0.0 /usr/lib/
# Run ldconfig — it finds BOTH and creates BOTH soname symlinks
sudo ldconfig -v | grep libdemo
# libdemo.so.1 -> libdemo.so.1.0.1 (changed)
# libdemo.so.2 -> libdemo.so.2.0.0 (changed)
# Verify in /usr/lib
ls -l /usr/lib/libdemo*
# lrwxrwxrwx libdemo.so.1 -> libdemo.so.1.0.1
# -rwxr-xr-x libdemo.so.1.0.1
# lrwxrwxrwx libdemo.so.2 -> libdemo.so.2.0.0
# -rwxr-xr-x libdemo.so.2.0.0
# Create the linker name pointing to the NEW version
sudo ln -s libdemo.so.2 /usr/lib/libdemo.so
# New programs compiled with -ldemo will link against v2
# Old programs that embedded soname "libdemo.so.1" still
# find libdemo.so.1 -> libdemo.so.1.0.1 and work fine!
libdemo.so.1 stored in its ELF file as the needed library. When it runs, the dynamic linker looks up libdemo.so.1 in the cache and finds libdemo.so.1.0.1. The v2 library is completely invisible to it. Both versions coexist peacefully.Code Example 3 — Private Library with ldconfig -n
When you are developing a private library (not installed in a system directory), you can still use ldconfig to create soname symlinks automatically using the -n option.
/* Build and use a private library in current directory */
# Step 1: Compile modules
gcc -g -c -fPIC -Wall mod1.c mod2.c
# Step 2: Create shared library with soname
gcc -g -shared -Wl,-soname,libdemo.so.1 \
-o libdemo.so.1.0.1 \
mod1.o mod2.o
# Step 3: Use ldconfig -n to create soname symlink only in this dir
# -n: process only libs in specified directory
# -v: verbose
/sbin/ldconfig -nv .
# Output:
# .:
# libdemo.so.1 -> libdemo.so.1.0.1
# Check result
ls -l libdemo.so*
# lrwxrwxrwx libdemo.so.1 -> libdemo.so.1.0.1
# -rwxr-xr-x libdemo.so.1.0.1
# Step 4: Manually create linker name
ln -s libdemo.so.1 libdemo.so
# Step 5: Compile test program, using current dir as lib search path
gcc -o demo_prog main.c -ldemo -L.
# Step 6: Tell dynamic linker to find our library in current dir
export LD_LIBRARY_PATH=.
./demo_prog
# Output:
# Hello from mod1!
# Hello from mod2!
/sbin may not be in $PATH. Always use /sbin/ldconfig explicitly when running as a regular user.4. LD_LIBRARY_PATH — Runtime Library Search Path
The dynamic linker (ld.so or ld-linux.so) searches for shared libraries at run time in this order:
| Priority | Location | Notes |
|---|---|---|
| 1 | RPATH (embedded in ELF) |
Baked in at link time with -Wl,-rpath,/path |
| 2 | LD_LIBRARY_PATH |
Environment variable; ignored for setuid programs |
| 3 | /etc/ld.so.cache |
Built by ldconfig — primary source for installed libraries |
| 4 | /lib, /usr/lib |
Default system directories (fallback) |
# Check what library a program needs
ldd ./demo_prog
# linux-vdso.so.1 (0x00007fff...)
# libdemo.so.1 => /usr/local/lib/libdemo.so.1.0.1 (0x00007f...)
# libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f...)
# View cache contents
ldconfig -p | grep libdemo
# libdemo.so.1 (libc6,x86-64) => /usr/local/lib/libdemo.so.1.0.1
Interview Questions & Answers
/etc/ld.so.conf plus /lib and /usr/lib, and builds/updates the binary cache file /etc/ld.so.cache. This allows the dynamic linker to find libraries quickly without traversing directories at runtime. (2) It reads the embedded soname of the latest minor version of each library and creates or updates the soname symbolic link in the same directory.libdemo.so.1.0.1. Contains major, minor, and patch version.Soname: A symbolic link like
libdemo.so.1, pointing to the latest minor version of a major release. Embedded inside the ELF file at build time. Used by the dynamic linker at run time.Linker name: A symlink like
libdemo.so pointing to the soname. Used only at compile time when you write gcc -ldemo./etc/ld.so.cache is a binary file maintained by ldconfig that maps library sonames to the full path of their corresponding real files. The dynamic linker reads this file at program startup to quickly locate shared libraries without scanning directories. This dramatically speeds up library loading, especially for systems with many directories in the search path.-n option. It instructs ldconfig to process only the directories given on the command line and to update soname symlinks there, without touching the system-wide /etc/ld.so.cache. Example: /sbin/ldconfig -nv . creates soname symlinks in the current directory.libdemo.so.1). The linker name (libdemo.so) must be created manually using ln -s libdemo.so.1 libdemo.so. The rationale is that the linker name is used only by developers at compile time, and there can only be one “active” linker name at a time, so automated management could be ambiguous in multi-version scenarios./etc/ld.so.cache. The dynamic linker won’t know about it, so programs that depend on it will fail to start with an error like: error while loading shared libraries: libXYZ.so.1: cannot open shared object file. The soname symlink also won’t be updated to point to the new version.LD_LIBRARY_PATH is an environment variable that adds directories to the dynamic linker’s search path at run time. It is useful for testing private libraries. However, in production, it is problematic: (1) It applies to all programs run from that shell, potentially loading wrong library versions. (2) It is ignored for setuid/setgid programs for security reasons. (3) It overrides system library resolution, which can cause subtle version conflicts. The preferred approach is to install libraries in standard directories and use ldconfig.ldd ./program lists all shared libraries required by the executable, along with the path where each will be found. Example output: libdemo.so.1 => /usr/local/lib/libdemo.so.1.0.1. Warning: never use ldd on untrusted binaries — it actually executes the program’s dynamic linker, which could trigger malicious code in constructors.-Wl,-soname,libXYZ.so.N passes the -soname option to the underlying linker (ld) via GCC. It embeds the given soname string into the ELF dynamic section (SONAME tag) of the shared library file. When the dynamic linker loads a program, it reads this SONAME tag to know which soname symlink to resolve. Without this, the library would not have a proper soname and versioning would not work correctly.Continue to Part 2
Learn when a library change is backward-compatible and when it requires a new major version.
