I'd like to know what exactly ldd
does. Does it print only libraries from DT_NEEDED
structures of .dynamic
section? This is not a complete list of libraries that are required to resolve all undefined symbols of ldd
's input library, as far as I know. In this case, what is the use of ldd
at all?
Or does it really list all the libraries the ldd
's input library actually depends on?
This is not a question about whether ldd shows dependencies of dependencies - this is a question about whether libraries reported by ldd resolve all the undefined symbols of ldd's input library.
Does ldd
print only libraries from DT_NEEDED
structures of .dynamic
section?
No, that is what readelf --dynamic
does.
what is the use of ldd
at all?
ldd
shows what libraries the runtime linker ld.so
loads when starting your executable or loading a shared library. This is a recursive process, e.g. an executable needs a shared library (DT_NEEDED
), so that library gets loaded. Then it proceeds to load the dependencies of the loaded library (DT_NEEDED
) and so on.
You don't necessarily need ldd
, you can just set LD_DEBUG=all
environment variable to make ld.so
print that information and more. See man ld.so
for more information.
Each loaded executable or shared library expose their defined exported dynamic symbols as a lookup scope (a hash table). Lookup scopes form a list. When resolving an undefined symbol ld.so
walks the lookup scopes and finds the first one that defines the symbol and resolve the symbol reference. If ld.so
reaches the end of lookup scopes it reports the symbol as unresolved.
There is no correspondence between the unresolved symbol name and an executable/shared library it is supposed to come from. ld.so
loads all shared libraries from DT_NEEDED
sections recursively, builds that list of lookup scopes and then looks for unresolved symbols in there.
How To Write Shared Libraries by U. Drepper explains this in full detail.
There is no way to derive the list of libraries needed to resolve undefined symbols in the shared object from the shared object itself. Such a list may or may not exist. It is easy to create a library with an undefined symbol that cannot be resolved by any existing library in the world.
# cat test.c
extern void foo99988776543quzzu();
void test() {
foo99988776543quzzu();
}
# gcc -fPIC -shared -o libtest.so test.c
Here we have created a library with an undefined symbol that no other library in the world can satisfy — until we build one.
# cat foo.c
void foo99988776543quzzu() {}
# gcc -fPIC -shared -o libfoo.so foo.c
No magic in the world can help ldd libtest.so
find libfoo.so. But it is easy to build a loadable, runnable program from libtest.so and libfoo.so.
# cat main.c
extern void test();
int main() { test(); }
# gcc main.c -lfoo -ltest -L. -Wl,-rpath=.
# ./a.out
ldd does not attempt to produce an impossible list of librarirs required to resolve undefined symbols. It does exactly what it says on the tin:
ldd prints the shared objects (shared libraries) required by each program or shared object specified on the command line.
The word "required" here does not mean "required to resolve undefined symbols". As said, a list of objects required to resolve undefined symbols cannot be produced. Instead, "required" means the set of dynamic dependencies, a.k.a. "shared objects required by DT_NEEDED recursively", as detailed in ldd(1) and ld.so(8).
what is the use of ldd at all?
DT_NEEDED sections contain sonames. ldd collects these sonames, recursively, and maps them to file paths using information in DT_RUNPATH, DT_RPATH, LD_LIBRARY_PATH, /etc/ld.so.conf, and whatever places we're searching this week. So the output of ldd
contains a list of file paths for shared objects that will be loaded when the shared object on ldd
command line is loaded. Here's an example:
#ldd ./test
linux-vdso.so.1 (0x00007fff0593f000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fe7e6776000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe7e6385000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fe7e5fe7000)
/lib64/ld-linux-x86-64.so.2 (0x00007fe7e6d01000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fe7e5dcf000)
This is quite a bunch of useful information even for a most casual of observers. We see that test
appears to be a 64-bit C++ program for x86 Linux built with a relatively recent version of gcc
or a compatible compiler, for example. We also see that it has no third party dependencies.
On the other hand,
# ldd /usr/bin/kdiff3
linux-vdso.so.1 (0x00007ffeeed79000)
libkparts.so.4 => /usr/lib/libkparts.so.4 (0x00007f801a14d000)
libkio.so.5 => /usr/lib/libkio.so.5 (0x00007f8019c9b000)
libkdeui.so.5 => /usr/lib/libkdeui.so.5 (0x00007f8019637000)
libkdecore.so.5 => /usr/lib/libkdecore.so.5 (0x00007f801916b000)
libQtCore.so.4 => /usr/lib/x86_64-linux-gnu/libQtCore.so.4 (0x00007f8018c79000)
libQtGui.so.4 => /usr/lib/x86_64-linux-gnu/libQtGui.so.4 (0x00007f8017f84000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f8017bfb000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f801785d000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f801746c000)
libQtXml.so.4 => /usr/lib/x86_64-linux-gnu/libQtXml.so.4 (0x00007f8017226000)
libQtNetwork.so.4 => /usr/lib/x86_64-linux-gnu/libQtNetwork.so.4 (0x00007f8016ed1000)
libQtSvg.so.4 => /usr/lib/x86_64-linux-gnu/libQtSvg.so.4 (0x00007f8016c78000)
libX11.so.6 => /usr/lib/x86_64-linux-gnu/libX11.so.6 (0x00007f8016940000)
... many more lines ...
lists a lot of dependencies. If it fails to load, we should be able to use the list to figure out the reason. For example, any line that says => not found
would be very helpful.
Do libraries reported by ldd resolve all undefined references of an input library?
No. A shared library may be linked containing undefined references
(and this is commonplace). So it may be linked containing undefined references that
will not be resolved by any of its (recursive) DSO dependencies, or indeed any DSO or object file that exists.
foo.c
#include <stdio.h>
extern void bar(void);
void foo(void)
{
puts(__func__);
bar();
}
Make shared library:
$ gcc -shared -o libfoo.so foo.c
ldd libfoo.so
lists recursively the DSO dependencies of libfoo.so
:
$ ldd libfoo.so
linux-vdso.so.1 (0x00007ffc30bf5000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd19209b000)
/lib64/ld-linux-x86-64.so.2 (0x00007fd19268e000)
None of them resolves the undefined reference to bar
.