LD_DEBUG β€” Monitoring the Dynamic Linker

 

LD_DEBUG β€” Monitoring the Dynamic Linker
Chapter 42.6 β€” The Linux Programming Interface | EmbeddedPathashala
πŸ“Œ Topic
Dynamic Linker Debug
πŸ§ͺ Examples
3 Code Demos
🎯 Level
Beginner–Intermediate
πŸ“– Source
TLPI Ch 42.6

Key Concepts
LD_DEBUG LD_DEBUG_OUTPUT libs keyword symbols keyword bindings keyword versions keyword Dynamic linker tracing Library search paths

Why Do We Need LD_DEBUG?

When a shared library fails to load, or a program crashes because a symbol cannot be found, you need to know: Where exactly is the dynamic linker searching? Which library is it picking? Which version is it binding to?

LD_DEBUG is a built-in diagnostic tool in the Linux dynamic linker (ld.so). By setting it to specific keywords, you get detailed trace output about exactly what the linker is doing β€” library search paths, symbol lookups, relocation processing, version binding, and more.

It requires zero changes to your source code or binary. You just prefix your command with the environment variable and read the output.

LD_DEBUG=libs ./prog β†’ ld.so (dynamic linker)
Traces library searches
β†’ Debug output to stderr
or LD_DEBUG_OUTPUT file

All LD_DEBUG Keywords β€” Reference Table

Run LD_DEBUG=help date to see all options (the program itself won’t execute):

$ LD_DEBUG=help date
Valid options for the LD_DEBUG environment variable are:

  libs       display library search paths
  reloc      display relocation processing
  files      display progress for input file
  symbols    display symbol table processing
  bindings   display information about symbol binding
  versions   display version dependencies
  all        all previous options combined
  statistics display relocation statistics
  unused     determine unused DSOs
  help       display this help message and exit

To direct the debugging output into a file instead of standard output
a filename can be specified using the LD_DEBUG_OUTPUT environment variable.
Keyword What it shows When to use
libs Library search paths and which file was selected Library not found errors, wrong version loaded
symbols Symbol lookup process for each needed symbol “undefined symbol” errors at runtime
bindings Which library each symbol binding resolves to Debugging function interposition / wrong function called
versions Version dependency checks for each library Symbol version mismatch errors
reloc Relocation entries being processed Low-level linker debugging
files Progress for each input file (library) being loaded Seeing load order of all libraries
unused Libraries loaded but never actually used Removing unnecessary link dependencies
statistics Relocation statistics (counts, timing) Performance analysis of library loading
all All of the above combined Full diagnostic dump

Example 1 β€” Using LD_DEBUG=libs to Trace Library Search

This is the most commonly used option. It shows every directory the dynamic linker checks when looking for a library, and which file it eventually loads.

# See where libm.so is being searched and found
LD_DEBUG=libs ls 2>&1 | head -30

# Sample output:
#      7543:   find library=libselinux.so.1 [0]; searching
#      7543:    search cache=/etc/ld.so.cache
#      7543:     /lib/x86_64-linux-gnu/libselinux.so.1
#      7543:     trying file=/lib/x86_64-linux-gnu/libselinux.so.1
#      7543:
#      7543:   find library=libc.so.6 [0]; searching
#      7543:    search cache=/etc/ld.so.cache
#      7543:     /lib/x86_64-linux-gnu/libc.so.6
#      7543:     trying file=/lib/x86_64-linux-gnu/libc.so.6
# More targeted: check library loading for your own program
LD_DEBUG=libs LD_LIBRARY_PATH=. ./prog 2>&1

# This tells you:
# 1. Which directories were searched (RPATH, LD_LIBRARY_PATH, /etc/ld.so.cache, default dirs)
# 2. Which .so file was actually opened
# 3. Which libraries were NOT found (look for "not found" in output)
Tip: The number on the left (e.g., 7543:) is the process ID (PID). This helps when multiple processes are running and their debug output is interleaved.

Example 2 β€” Using LD_DEBUG=bindings to See Symbol Resolution

When you have multiple libraries that define the same function (e.g., when using LD_PRELOAD), bindings tells you exactly which library each call resolved to.

# Using bindings to verify LD_PRELOAD is working correctly
LD_DEBUG=bindings LD_PRELOAD=libalt.so LD_LIBRARY_PATH=. ./prog 2>&1 | grep "x1\|x2"

# Sample output:
#  12345:    binding file ./prog [0] to libalt.so [0]: normal symbol `x1'
#  12345:    binding file ./prog [0] to libdemo.so [0]: normal symbol `x2'
# Checking which malloc() a program uses (useful after LD_PRELOAD)
LD_DEBUG=bindings LD_PRELOAD=./libmtrace.so ./prog 2>&1 | grep malloc

# Output shows malloc bound to your libmtrace.so (preloaded) not libc
#  99:    binding file ./prog to ./libmtrace.so: normal symbol `malloc'

Example 3 β€” Redirect Output to File and Combine Keywords

Debug output goes to stderr by default. For long programs, this floods your terminal. Use LD_DEBUG_OUTPUT to redirect it to a file. You can also combine multiple keywords.

# Save all debug output to a file (pid is appended automatically)
LD_DEBUG=libs LD_DEBUG_OUTPUT=/tmp/linker_debug ./prog

# The file will be named /tmp/linker_debug.PID (e.g., /tmp/linker_debug.12345)
ls /tmp/linker_debug.*

# Open the file
less /tmp/linker_debug.12345
# Combine multiple keywords with comma separation
LD_DEBUG=libs,symbols,bindings ./prog 2>&1 | head -50
# Real debugging scenario: "undefined symbol" at runtime
# Step 1: check which libraries load
LD_DEBUG=libs ./prog 2>&1 | grep "find library"

# Step 2: check if the symbol exists in the expected library
nm -D /path/to/libfoo.so | grep my_missing_function

# Step 3: check what version your program needs
readelf -d ./prog | grep NEEDED
ldd ./prog

# Step 4: if library is found but symbol isn't, check symbol lookup
LD_DEBUG=symbols ./prog 2>&1 | grep my_missing_function
# Check for unused libraries (helps reduce binary size / load time)
LD_DEBUG=unused ./prog 2>&1

# Output example:
#  12345:   unused DSO: /lib/x86_64-linux-gnu/libm.so.6
# β†’ This means -lm is in your link command but no math functions were called
# β†’ You can remove -lm from your Makefile
Output destination: LD_DEBUG output always goes to stderr (file descriptor 2), not stdout. To capture it alongside normal output, use 2>&1 to merge stderr into stdout, or redirect with 2>debug.log.

Practical Debugging Workflow β€” “library not found” Problem

Here is a realistic step-by-step workflow for diagnosing a library loading failure:

/* Step 0: The problem β€” program fails to start */
$ ./prog
./prog: error while loading shared libraries: libfoo.so.1:
        cannot open shared object file: No such file or directory

/* Step 1: Check what libraries the program needs */
$ ldd ./prog
    linux-vdso.so.1 (0x00007ffc...)
    libfoo.so.1 => not found        ← problem here
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6

/* Step 2: Find where libfoo.so.1 actually is */
$ find /usr /lib /opt -name "libfoo.so*" 2>/dev/null
/opt/myapp/lib/libfoo.so.1

/* Step 3: See what paths the linker searches */
$ LD_DEBUG=libs ./prog 2>&1 | grep "find library=libfoo"
     find library=libfoo.so.1 [0]; searching
      search cache=/etc/ld.so.cache
      search path=/usr/lib          (from RPATH)
      trying file=/usr/lib/libfoo.so.1  (not found)
      ...

/* Step 4a: Quick fix β€” add path via LD_LIBRARY_PATH */
$ LD_LIBRARY_PATH=/opt/myapp/lib ./prog

/* Step 4b: Permanent fix β€” add to ldconfig */
$ echo "/opt/myapp/lib" > /etc/ld.so.conf.d/myapp.conf
$ ldconfig   # rebuilds /etc/ld.so.cache

/* Verify fix */
$ ldd ./prog
    libfoo.so.1 => /opt/myapp/lib/libfoo.so.1  ← now found

🎯 Interview Questions & Answers
Q1. What is LD_DEBUG and what is it used for?
LD_DEBUG is an environment variable that enables diagnostic tracing in the Linux dynamic linker. By setting it to keywords like libs, symbols, or bindings, you get detailed output about library search paths, symbol lookups, and function bindings. It requires no changes to the binary or source code.
Q2. Which LD_DEBUG keyword would you use to find out why a library cannot be found?
LD_DEBUG=libs. This shows every directory the dynamic linker searches when looking for a library, and whether the file was found or not. Combined with ldd, this quickly identifies missing libraries and where to place them.
Q3. How do you redirect LD_DEBUG output to a file instead of stderr?
Set LD_DEBUG_OUTPUT=/path/to/file. The dynamic linker will write its debug output to that file (with the process PID appended as a suffix, e.g., debug.12345) instead of printing to stderr.
Q4. What does LD_DEBUG=bindings show?
It shows which library each symbol (function or variable) is bound to at runtime. This is useful for verifying that LD_PRELOAD is working (your override is being called), or for diagnosing cases where the wrong version of a function is being used.
Q5. What is the LD_DEBUG=unused keyword used for?
It identifies shared libraries (DSOs) that were loaded by the dynamic linker but none of whose symbols were actually used by the program. This helps in reducing unnecessary link dependencies β€” you can remove the -lfoo flag from your Makefile if libfoo.so appears as unused.
Q6. In LD_DEBUG output, what does the number at the start of each line represent?
It is the process ID (PID) of the process generating the output. This is helpful when multiple processes run simultaneously and their debug output is interleaved β€” you can filter by PID to see only the output from a specific process.
Q7. What is the difference between LD_DEBUG=symbols and LD_DEBUG=bindings?
symbols shows the process of looking up each required symbol β€” which libraries are searched and in what order. bindings shows the final result of each binding β€” “symbol X in program Y was bound to library Z”. For most debugging, bindings is more concise and actionable.

πŸ“‹ Chapter 42 β€” Complete Summary

This chapter covered four advanced features of Linux shared libraries:

Section Topic Key Tool / Feature
42.3 Symbol Versioning .symver, version script, @/@@ binding
42.4 Init & Fini Functions __attribute__((constructor/destructor))
42.5 Preloading Libraries LD_PRELOAD, /etc/ld.so.preload, RTLD_NEXT
42.6 Dynamic Linker Monitoring LD_DEBUG=libs/symbols/bindings/unused

Leave a Reply

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