Recent from talks
Nothing was collected or created yet.
Ldd (Unix)
View on Wikipedialdd (List Dynamic Dependencies) is a *nix utility that prints the shared libraries required by each program or shared library specified on the command line.[1] It was developed by Roland McGrath and Ulrich Drepper.[2] If some shared library is missing for any program, that program won't come up.
Security
[edit]ldd is a shell script that executes the program given as argument, and shouldn't be used with untrusted binaries. The ldd manual page suggests to use the following command using the objdump and grep utilities as alternative:[3]
user@home ~/ $ objdump -p /path/program | grep NEEDED
Usage examples
[edit]user@home ~/ $ ldd /usr/bin/mp3blaster
linux-vdso.so.1 => (0x00007fff8fdff000)
libsidplay.so.1 => /usr/lib/libsidplay.so.1 (0x00007f4ea98ec000)
libvorbisfile.so.3 => /usr/lib/libvorbisfile.so.3 (0x00007f4ea96e4000)
libvorbis.so.0 => /usr/lib/libvorbis.so.0 (0x00007f4ea94b6000)
libncurses.so.5 => /lib/libncurses.so.5 (0x00007f4ea9273000)
libpthread.so.0 => /lib/libpthread.so.0 (0x00007f4ea9056000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007f4ea8d41000)
libm.so.6 => /lib/libm.so.6 (0x00007f4ea8abe000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x00007f4ea88a7000)
libc.so.6 => /lib/libc.so.6 (0x00007f4ea8523000)
libogg.so.0 => /usr/lib/libogg.so.0 (0x00007f4ea831c000)
libdl.so.2 => /lib/libdl.so.2 (0x00007f4ea8118000)
/lib64/ld-linux-x86-64.so.2 (0x00007f4ea9b59000)
user@home ~/ $ ldd /usr/lib/i386-linux-gnu/libstdc++.so.6.0.20
linux-gate.so.1 (0xb7733000)
libm.so.6 => /lib/i386-linux-gnu/i686/cmov/libm.so.6 (0xb75da000)
libc.so.6 => /lib/i386-linux-gnu/i686/cmov/libc.so.6 (0xb742f000)
/lib/ld-linux.so.2 (0xb7734000)
libgcc_s.so.1 => /lib/i386-linux-gnu/libgcc_s.so.1 (0xb7411000)
References
[edit]- ^ "ldd(1) – Linux man page". die.net. Retrieved December 28, 2011.
- ^ "ldd Source Code". stuff.mit.edu. Retrieved March 26, 2014.
- ^ "ldd(1): print shared library dependencies - Linux man page". linux.die.net. Retrieved 2020-11-18.
Ldd (Unix)
View on Grokipedialdd is a command-line utility available in Unix-like operating systems, including Linux, FreeBSD, and Solaris, that displays the shared library dependencies required by an executable file or shared object.[1][2][3] It operates by invoking the system's dynamic linker—such as ld.so on Linux or rtld on FreeBSD—to trace and list the loaded shared objects, including their paths and memory addresses, without fully executing the target program.[1][2] This tool is essential for debugging linkage issues, verifying library dependencies, and optimizing software deployment in environments relying on dynamic linking.[3]
The command originated in SunOS 4.0 and has been adapted across various Unix variants, with implementations that may differ in options and behavior.[2] For instance, on Linux, ldd sets the environment variable LD_TRACE_LOADED_OBJECTS=1 to enable tracing and supports options like --verbose for symbol versioning details or --unused to identify unneeded libraries.[1] In FreeBSD, it uses format strings via -f for customized output and includes an -a flag to show all indirect dependencies.[2] Solaris versions provide extensive flags, such as -r for checking relocations or -U for unreferenced dependencies, emphasizing analysis of lazy and deferred bindings.[3] Despite these variations, the core purpose remains consistent: to resolve and report the full dependency tree at runtime.[1][2][3]
A key security consideration with ldd is its potential to execute arbitrary code if run on untrusted binaries, as it launches the dynamic linker on the target file; safer alternatives include using objdump to inspect needed libraries statically.[1] It does not support older formats like a.out shared libraries and may fail unpredictably on certain legacy executables.[1] For running processes, related tools like pldd provide dependency information without the risks associated with ldd.[1][3] Overall, ldd facilitates efficient management of dynamic linking, a foundational aspect of modern Unix software architecture.[1][2][3]
Overview
Purpose
ldd is a command-line utility in Unix-like operating systems that displays the shared libraries, or dynamic dependencies, required by a specified executable file or shared object.[1] This tool examines the binary's dependency information to list libraries such aslibc.so and libm.so, along with their resolved paths in the filesystem.[3]
The primary role of ldd is to assist in debugging linking issues by revealing potential problems like missing libraries or incorrect resolutions. It simulates the dynamic linking process performed by the runtime linker, such as ld.so, to report dependencies as they would be loaded during program execution.[1] However, while it avoids executing the main program code, ldd invokes the dynamic linker on the target file, which can pose security risks when inspecting untrusted binaries; safer static alternatives like objdump are recommended for such cases.[1] ldd reports only dynamic dependencies and provides no information for statically linked executables. It enables developers and system administrators to verify library usage, ensuring that all necessary components are available for runtime execution.[4] Furthermore, ldd provides insights into an application's runtime requirements, aiding in tasks such as packaging, deployment, and cross-platform compatibility assessments.[5]
ldd debuted in SunOS 4.0 in 1988 alongside support for dynamic linking and has since been adapted for various object file formats, including ELF, which became standard in SVR4 during the early 1990s.[6]
History and Availability
The ldd utility traces its origins to the late 1980s, coinciding with the advent of dynamic linking in Unix systems. Dynamic shared libraries were introduced in SunOS 4.0 in late 1988, and ldd debuted alongside this feature to list dependencies via the runtime linker. Similar capabilities emerged in SVR4 Unix around 1989, where the runtime linker provided diagnostics for shared library dependencies, predating Linux-specific implementations.[6] In Linux, ldd appeared in early distributions around 1992 as part of the GNU C Library (glibc), enabling developers to inspect dynamic dependencies in the burgeoning ecosystem. Its implementation evolved distinctly across platforms: on Linux, it is a shell script in /usr/bin/ldd that sets the LD_TRACE_LOADED_OBJECTS environment variable to trace library loading without executing the target fully. In contrast, BSD variants like FreeBSD, NetBSD, and OpenBSD implement ldd as a compiled binary utility for efficiency. Variations persist in systems such as Solaris (using ld.so.1 for diagnostics) and AIX (focused on XCOFF dependencies).[7][8][3][9] ldd is widely available on Unix-like systems, including Linux (via glibc), FreeBSD, NetBSD, OpenBSD, Solaris, AIX, and QNX. On macOS, equivalent functionality is offered by otool -L, which displays dynamic library dependencies. It is not native to Windows but can be emulated using Cygwin's ldd, which invokes the Windows loader for DLL analysis. The underlying dynamic linking diagnostics were standardized in POSIX.1-2008 via interfaces like dlopen and dlsym, though ldd as a specific command is not required by the standard. As of November 2025, ldd's core functionality remains consistent across major distributions, such as Ubuntu 24.04 and Fedora 43, with no significant changes to its dependency-listing behavior.[1][8][3][9][10][11][12][13]Operation
How It Works
Theldd utility operates by invoking the system's dynamic linker/loader, such as ld-linux.so on Linux systems, to simulate the library loading process without executing the target program. Specifically, ldd is implemented as a shell script that sets the environment variable LD_TRACE_LOADED_OBJECTS=1 and passes the target executable or shared object as an argument to the dynamic linker.[14] This variable instructs the linker to trace and report the shared libraries it loads, printing their resolved paths to standard output while halting before transferring control to the program.[14] On Linux, the linker (ld-linux.so) is itself a shared object typically located in /lib or /lib64, and ldd ensures it uses the appropriate architecture-specific version.[15]
The process begins with the dynamic linker parsing the Executable and Linkable Format (ELF) file of the target object to access its dynamic section. It examines the DT_NEEDED entries, which are tags listing the names of required shared libraries as strings.[16] Next, the linker resolves these dependencies by searching for the libraries according to its standard algorithm: first checking the RPATH or RUNPATH embedded in the ELF file, then the directories specified in the LD_LIBRARY_PATH environment variable (if set), followed by paths in /etc/ld.so.conf (and its included files), and finally default system paths like /lib and /usr/lib.[14] If a library is found, its full path is reported; otherwise, ldd indicates that the dependency is not found, potentially due to version mismatches or absent installations.[14]
This resolution handles recursive dependencies by iteratively applying the search algorithm to each newly identified library, ensuring all transitive dependencies are traced and listed.[17] The linker supports versioned libraries, such as libc.so.6, by matching the soname (shared object name) specified in DT_NEEDED against available versions in the search paths, prioritizing the highest compatible version.[14]
On non-Linux Unix-like systems, such as FreeBSD, ldd similarly invokes the runtime linker (rtld) with tracing enabled via LD_TRACE_LOADED_OBJECTS=1, producing output that includes resolved library paths and, in verbose modes, load addresses in memory.[8] This approach maintains portability across systems using ELF, though the exact linker binary and configuration files may vary (e.g., FreeBSD uses /etc/libmap.conf for additional path mapping).[8]
Unlike static analysis tools such as [objdump](/page/Objdump), which merely extract and display the raw DT_NEEDED tags from the ELF dynamic section without resolution, ldd performs a runtime-like simulation to verify actual library availability and paths based on the current environment. This dynamic resolution can reveal issues like missing libraries or path conflicts that static inspection overlooks.
Syntax and Options
The basic syntax of theldd command is ldd [options] file..., where each file argument specifies an executable program or shared library (typically a .so file) whose dynamic dependencies are to be examined.[1][8][3] The command supports multiple files, processing them sequentially and reporting dependencies for each.[1] If a specified file is not a dynamic executable—such as a static binary or non-ELF object—ldd issues an error message, for example, "not a dynamic executable".[1]
On Linux systems using the GNU C Library (glibc), several standard options provide additional control over the output and analysis. The -v or --verbose option enables detailed output, including symbol versioning details for each dependency.[1] The -u or --unused option lists unused direct dependencies, helping identify superfluous libraries (available since glibc 2.3.4).[1] For relocation checking, -d or --data-relocs performs data relocations and reports any missing objects (ELF files only), while -r or --function-relocs extends this to function relocations as well.[1] Additionally, --version prints the version of ldd itself, and --help displays usage information.[1]
The output format of ldd generally consists of lines showing each required shared library in the form library.so => /full/path/to/library.so (0xload_address), where the load address is a hexadecimal memory location; unresolved libraries appear as library.so => not found.[1][3]
Options vary significantly across Unix-like platforms due to differences in dynamic linking implementations. On FreeBSD, the command offers limited flags: -a to list all objects needed by each loaded object (including indirect ones), and -f format (usable up to twice) to customize the output using format strings defined by the runtime linker rtld.[8] Solaris provides a broader set, including -d to verify immediate (data symbol) references, -r to check both immediate and lazy references, -U to identify unreferenced dependencies, and -u to show unused objects, along with options like -v for verbose dependency and version details.[3] In contrast, AIX's ldd accepts no options and simply lists dependencies for valid XCOFF executables or libraries, outputting paths prefixed with "needs:".[9] QNX provides options such as -d for data relocations, -r for function relocations, -u for unused dependencies, -v for verbose output, and -V for the version, producing a list of required shared objects with paths and load addresses.[10]
Security Considerations
Potential Risks
The primary security risk associated with theldd utility stems from its reliance on the dynamic linker to inspect dependencies, which can partially execute the target binary. When ldd invokes the dynamic linker (typically ld.so) with the LD_TRACE_LOADED_OBJECTS=1 environment variable set, the linker loads the binary and its libraries into memory, potentially triggering initialization routines in the .init and .fini sections of the executable or shared objects.[1][18] These constructors execute code automatically upon loading, allowing malicious binaries to run arbitrary instructions, such as those designed to exploit environment variables or perform unauthorized actions.[1]
Historical vulnerabilities have highlighted these dangers, notably CVE-2009-5064 (disputed by the glibc project) in glibc versions before 2.27, where the upstream ldd script could execute the target program directly without preventing code execution.[19][20] This issue was addressed in glibc 2.27 by modifying the ldd script to invoke the dynamic linker properly and avoid direct program execution.[20]
Unlike purely static analysis tools like [objdump](/page/Objdump), ldd can induce side effects during dependency resolution, including file system accesses for library paths, potential network connections if libraries initiate them, or even privilege escalation when run with elevated permissions like root.[1] If the LD_PRELOAD environment variable is set to point to a malicious library, the dynamic linker's loading process during ldd execution may incorporate it, amplifying the risk of code injection before tracing begins.[21]
As of November 2025, while glibc implementations from version 2.27 onward incorporate safer invocation methods to avoid full program execution, the utility remains inherently risky for untrusted files, particularly on legacy systems predating these patches, where direct binary execution or unmitigated constructor runs are possible.[1][20] Recent vulnerabilities, such as CVE-2025-4802 in glibc 2.27 to 2.38, further underscore risks from manipulated environment variables like LD_LIBRARY_PATH in dynamic loading scenarios.[22] The official manual explicitly advises against using ldd on untrusted executables due to the persistent potential for arbitrary code execution.[1]
Mitigation Strategies
To mitigate the security risks associated with usingldd, which executes the target binary to trace its dynamic library dependencies, it is recommended to run the command as a non-root user whenever possible. This practice limits the potential damage from any malicious code triggered during execution, as the process lacks elevated privileges to access sensitive system resources or escalate attacks.[1]
For enhanced isolation, employ chroot environments or containerization tools such as Docker to confine the execution of ldd on untrusted binaries. Chroot changes the apparent root directory for the process, restricting access to only the specified filesystem subset, while Docker provides additional layers of namespace isolation, preventing the binary from interacting with the host system beyond controlled boundaries. These methods ensure that any anomalous actions during library loading are contained without broader system impact.
Alternative safe modes include setting the LD_BIND_NOW=1 environment variable before invoking ldd, which forces the dynamic linker to resolve all symbols immediately rather than using lazy binding. This reduces the window for exploits that manipulate unresolved symbols during runtime, enhancing security without altering the tool's core functionality. Additionally, the dynamic linker's --verify option—invoked directly via /lib64/ld-linux-x86-64.so.2 --verify <binary>—can check if a binary is dynamically linked and compatible without full execution or listing dependencies, serving as a preliminary validation step.[14]
As a non-executing alternative, use static analyzers like readelf -d from the binutils package to inspect the ELF dynamic section and extract DT_NEEDED entries, which list required shared libraries without running the binary. For deeper auditing prior to ldd usage, tools such as Valgrind (for memory and execution tracing) or strace (for system call monitoring) can be applied in a controlled environment to profile the binary's behavior, identifying potential issues like unexpected file accesses or injections.
On systems supporting mandatory access controls, enforce SELinux or AppArmor policies to restrict actions initiated by ldd-executed processes, such as blocking unauthorized file reads, network connections, or privilege transitions. For instance, SELinux targeted policies can confine the dynamic linker's domain to prevent escalation, while AppArmor profiles limit path access for specific binaries. Complement these with regular updates to glibc and ld.so, which patch known vulnerabilities like CVE-2023-4911 (Looney Tunables) and CVE-2025-4802 that could be exploited via dynamic loading mechanisms used by ldd.[23][22]
For ongoing monitoring, integrate ldd runs with auditd, the Linux Audit Daemon, to log system calls and detect anomalous behavior such as unexpected opens or executes during dependency resolution. Auditd rules targeting execve or openat events for the dynamic linker can flag deviations, enabling forensic analysis and rapid response to potential compromises.[24]
Practical Usage
Basic Examples
One common basic use of theldd command is to examine the shared library dependencies of a standard executable, such as /bin/ls, which helps identify the core system libraries required for its operation.[1]
For example, running ldd /bin/ls on a typical Linux system might produce output similar to the following:
linux-vdso.so.1 (0x00007ffcc3563000)
libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f24ed8b5000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f24ed4c4000)
libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007f24ed252000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f24ed04e000)
/lib64/ld-linux-x86-64.so.2 (0x00007f24edaf7000)
linux-vdso.so.1 (0x00007ffcc3563000)
libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f24ed8b5000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f24ed4c4000)
libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007f24ed252000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f24ed04e000)
/lib64/ld-linux-x86-64.so.2 (0x00007f24edaf7000)
linux-vdso.so.1), standard libraries like the GNU C library (libc.so.6), and the dynamic linker (ld-linux-x86-64.so.2), along with their resolved file paths and load addresses in memory.[1] If a required library is missing from the system, the output indicates this clearly, such as libxyz.so => not found, allowing quick detection of linking issues without executing the program.[1]
On FreeBSD, the output for ldd /bin/ls is simpler, typically showing:
/usr/lib/libc.so.7
/libexec/ld-elf.so.1
/usr/lib/libc.so.7
/libexec/ld-elf.so.1
libc.so.7) and the runtime linker (ld-elf.so.1).[2]
Another straightforward application is checking dependencies for a shared library file itself, such as the C library at /lib/x86_64-linux-gnu/libc.so.6 on Linux, which reveals its own minimal requirements and any versioned paths.[1]
For instance, ldd /lib/x86_64-linux-gnu/libc.so.6 typically yields:
linux-vdso.so.1 (0x00007fff...)
/lib64/ld-linux-x86-64.so.2 (0x00007f...)
linux-vdso.so.1 (0x00007fff...)
/lib64/ld-linux-x86-64.so.2 (0x00007f...)
ldd recursively traces the libraries loaded for the specified shared object, including version information where applicable.[1] This is particularly useful for verifying linkages in build systems or scripts, where automated checks ensure all dependencies are resolvable before deployment.
For statically linked binaries, ldd reports "not a dynamic executable," confirming no shared library dependencies exist, as all code is compiled directly into the file.[1] Common elements in ldd output across these cases include full resolved paths to libraries (using =>), hexadecimal memory addresses for loaded modules, and indicators for unresolved dependencies.[1]
Advanced Applications
In advanced debugging scenarios on Linux and Solaris, theldd -u option identifies unused shared libraries in a binary, enabling developers to strip unnecessary dependencies and reduce executable size during optimization passes.[1][3] For instance, running ldd -u /bin/program lists direct dependencies that are not referenced, allowing targeted removal via tools like strip or relinking. Combining this with ldd -r performs both data and function relocations, revealing unresolved symbols or relocation errors that might cause runtime failures, particularly useful when diagnosing linking issues in complex applications.[1][3] On FreeBSD, the -a option can be used to show all indirect dependencies for similar analysis.[2]
In containerization workflows on Linux, ldd aids optimization by mapping out the exact shared library dependencies of an application, facilitating the creation of minimal images with only required components. This is especially effective in lightweight distributions like Alpine Linux, which uses the musl libc implementation to minimize footprint; by analyzing ldd output, builders can copy just the necessary libraries into the image, avoiding bloated layers from full package installations.[25] For example, ldd myapp might reveal dependencies like libc.musl-x86_64.so.1, guiding the inclusion of a slim musl-based runtime to keep images under 10 MB while maintaining functionality.
For integration in automated environments, ldd can be scripted to validate dependencies across multiple files, such as in build or deployment pipelines. A common bash loop like for file in *.so; do ldd "$file" | [grep](/page/Grep) "not found" && echo "Missing deps for $file"; done scans shared objects for unresolved libraries, flagging issues early in batch processing.[1] This approach ensures completeness without manual inspection, integrating seamlessly with version control hooks or automation tools.
In cross-compilation setups, such as building ARM binaries on an x86 host, dependencies can be inspected using static analysis tools like readelf -d or [objdump](/page/Objdump) -x on the target binary to list required libraries (NEEDED entries), relative to a sysroot directory mimicking the target architecture's library environment.[26] Alternatively, custom scripts can invoke the cross-compiled dynamic linker with the sysroot to simulate resolution, helping verify that all foreign dependencies are available without executing on the host.[27]
Troubleshooting advanced configurations often involves ldd to detect deviations in library loading, such as rpath-embedded paths overriding defaults or LD_LIBRARY_PATH influencing resolution. The output displays actual loaded locations, revealing if custom paths are prioritized incorrectly.[1] In CI/CD pipelines, this capability supports reproducible builds by scripting ldd checks to confirm consistent dependency resolution across environments, as implemented in tools like GitHub Actions workflows for container validation.[28]