Installing Shared Libraries Standard Directories · ldconfig · /etc/ld.so.conf

 

Installing Shared Libraries
Chapter 41 — Fundamentals of Shared Libraries (TLPI)
Standard Directories · ldconfig · /etc/ld.so.conf · System-Wide Installation
📂
Std Directories
/usr/lib, /lib, /usr/local/lib
🔧
ldconfig
automates soname links
📋
/etc/ld.so.conf
custom search paths
🎓
10 Interview Q
with answers

🔑 Key Terms in This Tutorial
ldconfig /usr/lib /lib /usr/local/lib /etc/ld.so.conf /etc/ld.so.cache LD_LIBRARY_PATH superuser privilege soname link update ldd rpath dynamic linker

Why Proper Installation Matters

During development, using LD_LIBRARY_PATH to find shared libraries is convenient. But in production environments this approach has serious problems:

  • Security risk: A malicious LD_LIBRARY_PATH entry could make programs load attacker-controlled libraries
  • Maintenance nightmare: Every user and every script must set the variable correctly
  • No system-wide sharing: Other programs can’t benefit from the library
  • Ignored by setuid/setgid programs: The dynamic linker ignores LD_LIBRARY_PATH for security when running privileged programs

The correct approach for production is to install libraries in standard system directories and run ldconfig to update the linker cache.

1. Standard Shared Library Directories
/usr/lib /lib /usr/local/lib /etc/ld.so.conf

Linux defines several standard directories where shared libraries should be installed. The dynamic linker automatically searches these at runtime, so no LD_LIBRARY_PATH is needed once a library is installed correctly.

📁
/usr/lib
The primary directory for most standard system libraries. Libraries installed here are available system-wide to all users. Requires root privilege to write. This is where packages like libreadline, libssl, libpthread, etc. are typically found.
🔒
/lib
Contains libraries needed during system startup — before /usr is mounted. This includes critical libraries like libc.so, libm.so, and ld-linux.so. On modern systemd systems, /lib is often a symlink to /usr/lib. Requires root privilege.
🧪
/usr/local/lib
For nonstandard, experimental, or locally-compiled libraries. Useful when /usr/lib is a network mount shared among multiple machines but you need a library only on one specific machine. Also safe for custom/vendor libraries that shouldn’t mix with distro-managed libraries. Requires root privilege.
📋
/etc/ld.so.conf (custom directories)
Any directory listed in /etc/ld.so.conf (or files included by it) is also searched by the dynamic linker. You can add your own custom directory here and run ldconfig to make the system aware of libraries in that directory. This is how many third-party packages (e.g., CUDA, Oracle) install their libraries.
# View current ld.so.conf on your system:
$ cat /etc/ld.so.conf
include /etc/ld.so.conf.d/*.conf

# Many systems use the include-directory pattern:
$ ls /etc/ld.so.conf.d/
cuda.conf        libc.conf        x86_64-linux-gnu.conf

$ cat /etc/ld.so.conf.d/cuda.conf
/usr/local/cuda/lib64
/usr/local/cuda/lib

# After editing ld.so.conf, always run ldconfig to update the cache:

2. Step-by-Step: Installing a Library in /usr/lib
sudo/su mv to /usr/lib create symlinks ldconfig

Installing our libdemo library system-wide requires root privileges since /usr/lib is not writable by regular users.

Installation Process Flow:
1
Become root (su or sudo)
2
Copy/move the real name file to /usr/lib
3
Create soname symlink in /usr/lib
4
Create linker name symlink in /usr/lib
5
Run ldconfig to update /etc/ld.so.cache
# Complete installation sequence for libdemo in /usr/lib

# Step 1: Become root
$ su
Password:

# Step 2: Move the real-name file to /usr/lib
# mv preserves the file's permissions and avoids a copy+delete
# (use cp -p if you want to keep the original for other uses)
# mv libdemo.so.1.0.1 /usr/lib/
# libdemo.so.1.0.1 is now at /usr/lib/libdemo.so.1.0.1

# Step 3: Create the soname symbolic link (relative, in same directory)
# ln -s libdemo.so.1.0.1 /usr/lib/libdemo.so.1

# Step 4: Create the linker name symbolic link
# ln -s libdemo.so.1 /usr/lib/libdemo.so

# Step 5: Run ldconfig to update the dynamic linker cache
# ldconfig

# Verify the installation:
# ls -l /usr/lib/libdemo.so*
# lrwxrwxrwx libdemo.so      -> libdemo.so.1
# lrwxrwxrwx libdemo.so.1    -> libdemo.so.1.0.1
# -rwxr-xr-x libdemo.so.1.0.1

# Now run the program WITHOUT LD_LIBRARY_PATH — it works!
$ exit    # back to normal user
$ ./prog
Called mod1-x1
Called mod2-x2
✅ After proper installation: The program finds the library automatically. No LD_LIBRARY_PATH needed. The dynamic linker checks /etc/ld.so.cache (built by ldconfig) first, then the standard directories.

3. Understanding ldconfig
ldconfig purpose /etc/ld.so.cache automatic soname management ldconfig -v ldconfig -p

ldconfig is a system utility that does two important things:

  • Scans library directories (standard dirs + those in /etc/ld.so.conf) and finds all shared libraries
  • Creates/updates soname symbolic links to point to the most recent minor version of each major library version
  • Rebuilds /etc/ld.so.cache — a binary cache file that the dynamic linker uses for fast library lookup at runtime

How ldconfig works internally:

ldconfig scans
/usr/lib, /lib,
/usr/local/lib,
+ /etc/ld.so.conf dirs
Finds real name files
libdemo.so.1.0.1
libdemo.so.1.0.2
libdemo.so.2.0.0
Updates soname links
libdemo.so.1→1.0.2
libdemo.so.2→2.0.0
Writes /etc/ld.so.cache
# --- Using ldconfig ---

# Basic usage (must be root) - scan dirs and rebuild cache:
$ sudo ldconfig

# Verbose mode - shows which soname links are being created:
$ sudo ldconfig -v
/usr/lib:
        libdemo.so.1 -> libdemo.so.1.0.2
        libreadline.so.8 -> libreadline.so.8.2
        libssl.so.3 -> libssl.so.3.0.8
        ...

# List all libraries currently in the cache:
$ ldconfig -p
        libdemo.so.1 (libc6,x86-64) => /usr/lib/libdemo.so.1
        libreadline.so.8 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libreadline.so.8.2
        libc.so.6 (libc6,x86-64) => /lib/x86_64-linux-gnu/libc.so.6
        ...

# Search for a specific library in the cache:
$ ldconfig -p | grep libdemo
        libdemo.so.1 (libc6,x86-64) => /usr/lib/libdemo.so.1
📌 Important: You must run ldconfig after every new library installation or after updating existing ones. Otherwise, the dynamic linker may not find the new library or soname links may be stale. Many package managers (apt, yum) run ldconfig automatically via post-install scripts.
# Example: ldconfig automatically creates soname links
# Suppose you've installed THREE versions of libdemo:
# /usr/lib/libdemo.so.1.0.1   (older)
# /usr/lib/libdemo.so.1.0.2   (newer, bugfix)
# /usr/lib/libdemo.so.2.0.0   (new major version)

# After running ldconfig:
$ sudo ldconfig -v 2>&1 | grep libdemo
        libdemo.so.1 -> libdemo.so.1.0.2   # latest of major 1
        libdemo.so.2 -> libdemo.so.2.0.0   # latest of major 2

# ldconfig correctly creates TWO soname links:
# - libdemo.so.1 -> libdemo.so.1.0.2 (NOT 1.0.1 — picks the newest minor)
# - libdemo.so.2 -> libdemo.so.2.0.0

# This is the key power of ldconfig: it automatically maintains soname links!

4. Installing in a Custom Directory via /etc/ld.so.conf
/etc/ld.so.conf.d/ custom path ldconfig after edit

If you want to install a library outside the standard directories (e.g., in /opt/myapp/lib), you can register that directory in /etc/ld.so.conf so ldconfig and the dynamic linker know to search there.

# Example: Installing libdemo in /opt/myapp/lib

# Step 1: Create the directory and install the library file
$ sudo mkdir -p /opt/myapp/lib
$ sudo cp libdemo.so.1.0.1 /opt/myapp/lib/
$ sudo ln -s libdemo.so.1.0.1 /opt/myapp/lib/libdemo.so.1
$ sudo ln -s libdemo.so.1 /opt/myapp/lib/libdemo.so

# Step 2: Register the directory in ldconfig config
# Method A: Add directly to /etc/ld.so.conf
$ echo "/opt/myapp/lib" | sudo tee -a /etc/ld.so.conf

# Method B (preferred): Create a separate .conf file in ld.so.conf.d/
$ echo "/opt/myapp/lib" | sudo tee /etc/ld.so.conf.d/myapp.conf

# Step 3: Rebuild the linker cache
$ sudo ldconfig

# Step 4: Verify it's in the cache
$ ldconfig -p | grep libdemo
        libdemo.so.1 (libc6,x86-64) => /opt/myapp/lib/libdemo.so.1

# Step 5: Now programs can link and run against the library
# without LD_LIBRARY_PATH:
$ gcc -o prog prog.c -ldemo
$ ./prog            # works without LD_LIBRARY_PATH!
Called mod1-x1
Called mod2-x2
📌 Best Practice: Use a separate file in /etc/ld.so.conf.d/ (e.g., myapp.conf) rather than editing /etc/ld.so.conf directly. This keeps configurations modular and is the approach used by most package managers.

5. Library Search Order & Troubleshooting Tools
search order ldd command readelf -d rpath

The dynamic linker searches in this order:

Priority Location Notes
1st DT_RPATH in ELF binary Hardcoded paths embedded at link time via -Wl,-rpath,/path. Older method.
2nd LD_LIBRARY_PATH Environment variable. Ignored for setuid/setgid programs. For development only.
3rd DT_RUNPATH in ELF binary Newer version of rpath. Set with -Wl,-rpath,/path -Wl,--enable-new-dtags.
4th /etc/ld.so.cache Built by ldconfig from standard dirs + /etc/ld.so.conf. Primary production lookup.
5th /lib and /usr/lib Hardcoded fallback if cache lookup fails.
# --- Diagnostic Tool 1: ldd ---
# ldd shows which shared libraries a program depends on
# and where the dynamic linker will find each one

$ ldd prog
        linux-vdso.so.1 (0x00007ffd3e9de000)
        libdemo.so.1 => /usr/lib/libdemo.so.1 (0x00007f3e5a200000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f3e5a000000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f3e5a450000)

# If a library is NOT found, ldd shows:
#       libdemo.so.1 => not found    ← this means install failed or ldconfig not run

# --- Diagnostic Tool 2: readelf -d ---
# Shows the NEEDED entries (sonames) and RPATH/RUNPATH embedded in the binary

$ readelf -d prog | grep -E '(NEEDED|RPATH|RUNPATH|SONAME)'
 0x0000000000000001 (NEEDED)  Shared library: [libdemo.so.1]
 0x0000000000000001 (NEEDED)  Shared library: [libc.so.6]

# --- Diagnostic Tool 3: objdump -p ---
$ objdump -p prog | grep NEEDED
  NEEDED               libdemo.so.1
  NEEDED               libc.so.6

# --- Using rpath (embed library path in binary) ---
# For deployment where you can't touch /etc/ld.so.conf:
$ gcc -g -Wall -o prog prog.c -L/opt/myapp/lib -ldemo \
      -Wl,-rpath,/opt/myapp/lib
# prog will look in /opt/myapp/lib at runtime, always — no ldconfig needed
$ ./prog
Called mod1-x1
# --- Common Installation Mistakes & Fixes ---

# Problem 1: "cannot open shared object file: No such file or directory"
# Fix: Check if ldconfig has been run after installation
$ sudo ldconfig
$ ldd prog   # verify library is found now

# Problem 2: Wrong version loaded at runtime
# Fix: Check soname symlinks and ldconfig cache
$ ldconfig -p | grep libdemo
$ ls -la /usr/lib/libdemo.so*

# Problem 3: Library installed but gcc can't find it at link time
# Fix: Make sure the LINKER NAME symlink exists (libdemo.so, not just libdemo.so.1)
$ ls /usr/lib/libdemo.so   # must exist!
# If missing: sudo ln -s libdemo.so.1 /usr/lib/libdemo.so

# Problem 4: Installed in /usr/local/lib but not found
# Fix: /usr/local/lib must be in ldconfig config
$ cat /etc/ld.so.conf.d/*.conf | grep local
# If missing:
$ echo "/usr/local/lib" | sudo tee /etc/ld.so.conf.d/local.conf
$ sudo ldconfig

6. Complete Production Installation Script
#!/bin/bash
# install_shared_lib.sh — Production installation of libdemo
# Run as root or with sudo

set -e  # Exit on any error

LIB_NAME="libdemo"
MAJOR="1"
MINOR="0"
PATCH="2"
REAL_NAME="${LIB_NAME}.so.${MAJOR}.${MINOR}.${PATCH}"
SONAME="${LIB_NAME}.so.${MAJOR}"
LINKER_NAME="${LIB_NAME}.so"
INSTALL_DIR="/usr/lib"

echo "=== Installing ${REAL_NAME} to ${INSTALL_DIR} ==="

# Step 1: Copy the library file
if [ ! -f "./${REAL_NAME}" ]; then
    echo "ERROR: ${REAL_NAME} not found in current directory"
    exit 1
fi

cp -p "./${REAL_NAME}" "${INSTALL_DIR}/"
chmod 755 "${INSTALL_DIR}/${REAL_NAME}"
echo "[OK] Copied ${REAL_NAME} to ${INSTALL_DIR}"

# Step 2: Create soname symlink
ln -sf "${REAL_NAME}" "${INSTALL_DIR}/${SONAME}"
echo "[OK] Created ${SONAME} -> ${REAL_NAME}"

# Step 3: Create linker name symlink (points to soname, not real name)
ln -sf "${SONAME}" "${INSTALL_DIR}/${LINKER_NAME}"
echo "[OK] Created ${LINKER_NAME} -> ${SONAME}"

# Step 4: Run ldconfig to update the cache
ldconfig
echo "[OK] ldconfig cache updated"

# Step 5: Verify
echo ""
echo "=== Verification ==="
ls -la "${INSTALL_DIR}/${LIB_NAME}.so"*
echo ""
ldconfig -p | grep "${LIB_NAME}"
echo ""
echo "=== Installation complete! ===" 

🎯 Interview Questions — Installing Shared Libraries

Q1: Why should LD_LIBRARY_PATH not be used in production applications?

Several reasons: (1) Security risk — a user could set LD_LIBRARY_PATH to point to a malicious library, causing privilege escalation if combined with setuid programs; (2) The dynamic linker ignores LD_LIBRARY_PATH for setuid/setgid processes; (3) Every user and script would need to set it correctly; (4) It makes deployment complex and error-prone. Libraries should be installed in standard directories instead.
Q2: What is ldconfig and what does it do?

ldconfig is a system utility that (1) scans standard library directories and directories listed in /etc/ld.so.conf, (2) creates and updates soname symbolic links to point to the most recent minor version, and (3) rebuilds the /etc/ld.so.cache binary cache file used by the dynamic linker for fast library lookups at runtime.
Q3: What is /etc/ld.so.cache and why is it used?

It is a binary cache file that maps library sonames to their actual file paths on disk. The dynamic linker reads this cache at runtime instead of scanning all library directories every time a program starts. This makes startup much faster. It is built and updated by running ldconfig.
Q4: What is the difference between /lib and /usr/lib on a traditional Linux system?

/lib contains libraries needed during early system boot — before /usr is mounted. This includes libc.so, the dynamic linker itself (ld-linux.so), and kernel interface libraries. /usr/lib contains the bulk of system libraries and is available after /usr is mounted. On modern systemd-based systems, /lib is often a symlink to /usr/lib.
Q5: How do you add a custom library directory so it’s searched at runtime?

Add the directory path to /etc/ld.so.conf (or create a new .conf file in /etc/ld.so.conf.d/), then run sudo ldconfig to rebuild the cache. Alternatively, embed the path in the binary using rpath (-Wl,-rpath,/custom/path) at link time.
Q6: What is the dynamic linker search order for finding shared libraries?

In order: (1) DT_RPATH embedded in ELF (old rpath); (2) LD_LIBRARY_PATH environment variable; (3) DT_RUNPATH embedded in ELF (new rpath); (4) /etc/ld.so.cache (built by ldconfig); (5) /lib and /usr/lib as hardcoded fallback.
Q7: How do you check which shared libraries a program depends on and whether they can be found?

Use ldd <binary>. It prints each required soname, the resolved file path, and the load address. Lines showing => not found indicate a missing or improperly installed library. You can also use readelf -d <binary> | grep NEEDED to see the raw soname list without resolution.
Q8: What is rpath and when should it be used?

RPATH is a path (or colon-separated list of paths) embedded inside the ELF binary at link time using -Wl,-rpath,/some/path. The dynamic linker searches these paths first. It’s useful for self-contained application deployments (e.g., in /opt) where you can’t modify system ldconfig configuration. Avoid using rpath pointing to development directories in production.
Q9: After installing a new version of a shared library, what must you do before programs can use it?

Run sudo ldconfig. This updates the soname symbolic links to point to the newest minor version and rebuilds /etc/ld.so.cache. Without this step, the dynamic linker may continue using the old version or fail to find the library entirely.
Q10: What is the difference between the linker name symlink and the soname symlink, and which one does ldconfig manage?

The soname symlink (e.g., libfoo.so.1) is managed by ldconfig — it automatically updates it to point to the most recent minor version of each major version. The linker name symlink (e.g., libfoo.so) is NOT managed by ldconfig; it must be created and maintained manually by the developer/package installer. However, since the linker name usually points to the soname (not the real name), ldconfig changes are indirectly reflected in the linker name.

📖 Chapter 41 — Shared Libraries Series Complete!
You’ve covered naming, creation, and installation of shared libraries

← Part 1: Library Names ← Part 2: Creating Libraries Back to Course Index →

Leave a Reply

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