Hierarchical ldd(1)
Asked Answered
P

4

59

Due to using Gentoo, it often happens that after an update programs are linked against old versions of libraries. Normally, revdep-rebuild helps resolving that, but this time it's a dependency on a python library, and python-updater won't pick it up.

Is there a "hierarchical" variant of ldd which shows me what shared library depends on which another shared library? Most of the time libraries and executables are linked only against a handful of other shared libraries, which in turn were linked against a handful, turning the library dependency into a big list. I want to know which dependency I've got to rebuild with the new version of another library that I upgraded.

Perpetuate answered 28/9, 2009 at 18:13 Comment(0)
L
26

If you are running Portage≥2.2 with FEATURES=preserve-libs, you should rarely ever need revdep-rebuild anymore as old .so.vers will be preserved as needed (though you still need to rebuild carefully, as stuff still goes kaboom when libA.so.0 wants libC.so.0 and libB.so.0 wants libC.so.1 and some binary wants both libA.so.0 and libB.so.0).


That being said, what ldd does is to get the dynamic linker to do load the executable or library as it usually would, but print out some info along the way. This is a recursive "binary needs library needs other library&hellip" search, because that's what the dynamic linker does.

I'm currently running Linux/ppc32; on Linux/x86, the dynamic linker is usually /lib/ld-linux.so.2, and on Linux/x86_64, the dynamic linker is usually /lib/ld-linux-x86-64.so.2. Here, I call it directly just to hammer in the point that all ldd is nothing more than a shell script that calls upon the dynamic linker to perform its magic.

$ /lib/ld.so.1 /sbin/badblocks
Usage: /sbin/badblocks [-b block_size] [-i input_file] [-o output_file] [-svwnf]
       [-c blocks_at_once] [-d delay_factor_between_reads] [-e max_bad_blocks]
       [-p num_passes] [-t test_pattern [-t test_pattern [...]]]
       device [last_block [first_block]]
$ LD_TRACE_LOADED_OBJECTS=1 /lib/ld.so.1 /sbin/badblocks
        linux-vdso32.so.1 =>  (0x00100000)
        libext2fs.so.2 => /lib/libext2fs.so.2 (0x0ffa8000)
        libcom_err.so.2 => /lib/libcom_err.so.2 (0x0ff84000)
        libc.so.6 => /lib/libc.so.6 (0x0fdfa000)
        libpthread.so.0 => /lib/libpthread.so.0 (0x0fdc0000)
        /lib/ld.so.1 (0x48000000)
$ LD_TRACE_LOADED_OBJECTS=1 /lib/ld.so.1 /lib/libcom_err.so.2
        linux-vdso32.so.1 =>  (0x00100000)
        libpthread.so.0 => /lib/libpthread.so.0 (0x6ffa2000)
        libc.so.6 => /lib/libc.so.6 (0x6fe18000)
        /lib/ld.so.1 (0x203ba000)
$ grep -l pthread /sbin/badblocks /lib/libcom_err.so.2
/lib/libcom_err.so.2

/sbin/badblocks doesn't list libpthread.so.0 as a library dependency, but it gets pulled in by libcom_err.so.2.

Is your problem that ldd doesn't output a nice-looking dependency tree? Use ldd -v.

$ LD_TRACE_LOADED_OBJECTS=1 LD_VERBOSE=1 /lib/ld.so.1 /sbin/badblocks
        linux-vdso32.so.1 =>  (0x00100000)
        libext2fs.so.2 => /lib/libext2fs.so.2 (0x0ffa8000)
        libcom_err.so.2 => /lib/libcom_err.so.2 (0x0ff84000)
        libc.so.6 => /lib/libc.so.6 (0x0fdfa000)
        libpthread.so.0 => /lib/libpthread.so.0 (0x0fdc0000)
        /lib/ld.so.1 (0x201f9000)

        Version information:
        /sbin/badblocks:
                libc.so.6 (GLIBC_2.2) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.4) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.1) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.0) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.3.4) => /lib/libc.so.6
        /lib/libext2fs.so.2:
                libc.so.6 (GLIBC_2.1.3) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.4) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.3) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.2) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.1) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.0) => /lib/libc.so.6
        /lib/libcom_err.so.2:
                ld.so.1 (GLIBC_2.3) => /lib/ld.so.1
                libpthread.so.0 (GLIBC_2.1) => /lib/libpthread.so.0
                libpthread.so.0 (GLIBC_2.0) => /lib/libpthread.so.0
                libc.so.6 (GLIBC_2.1.3) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.4) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.1) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.0) => /lib/libc.so.6
        /lib/libc.so.6:
                ld.so.1 (GLIBC_PRIVATE) => /lib/ld.so.1
                ld.so.1 (GLIBC_2.3) => /lib/ld.so.1
        /lib/libpthread.so.0:
                ld.so.1 (GLIBC_2.3) => /lib/ld.so.1
                ld.so.1 (GLIBC_2.1) => /lib/ld.so.1
                ld.so.1 (GLIBC_PRIVATE) => /lib/ld.so.1
                libc.so.6 (GLIBC_2.1.3) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.3.4) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.4) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.1) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.3.2) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.2) => /lib/libc.so.6
                libc.so.6 (GLIBC_PRIVATE) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.0) => /lib/libc.so.6

If you want, you can read the ELF headers directly instead of depending on the dynamic linker.

$ readelf -d /sbin/badblocks | grep NEEDED
 0x00000001 (NEEDED)                     Shared library: [libext2fs.so.2]
 0x00000001 (NEEDED)                     Shared library: [libcom_err.so.2]
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
$ readelf -d /lib/libcom_err.so.2 | grep NEEDED
 0x00000001 (NEEDED)                     Shared library: [libpthread.so.0]
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
 0x00000001 (NEEDED)                     Shared library: [ld.so.1]

You can also man ld.so for other cute tricks you can play with glibc's dynamic linker.

Laverne answered 29/9, 2009 at 3:58 Comment(1)
the tree section of ldd -v output shows only libraries with versioned symbolsSteffen
E
121

I see many interesting details but no direct answer to the question asked.

The 'hierarchical' version of ldd is lddtree (from app-misc/pax-utils):

$ lddtree /usr/bin/xmllint 
xmllint => /usr/bin/xmllint (interpreter => /lib64/ld-linux-x86-64.so.2)
    libreadline.so.6 => /lib64/libreadline.so.6
        libncurses.so.5 => /lib64/libncurses.so.5
            libdl.so.2 => /lib64/libdl.so.2
    libxml2.so.2 => /usr/lib64/libxml2.so.2
        libicui18n.so.49 => /usr/lib64/libicui18n.so.49
            libstdc++.so.6 => /usr/lib/gcc/x86_64-pc-linux-gnu/4.7.1/32/libstdc++.so.6
                ld-linux.so.2 => /lib64/ld-linux.so.2
            libgcc_s.so.1 => /usr/lib/gcc/x86_64-pc-linux-gnu/4.7.1/32/libgcc_s.so.1
        libicuuc.so.49 => /usr/lib64/libicuuc.so.49
        libicudata.so.49 => /usr/lib64/libicudata.so.49
        libz.so.1 => /lib64/libz.so.1
        liblzma.so.5 => /usr/lib64/liblzma.so.5
        libm.so.6 => /lib64/libm.so.6
    libpthread.so.0 => /lib64/libpthread.so.0
    libc.so.6 => /lib64/libc.so.6
Envision answered 18/8, 2012 at 7:58 Comment(4)
You, sir, are my hero. Thanks for mentioning lddtree, I've been looking for a tool like this for quite a while.Alethiaaletta
nice one, I didn't know about it :)Biblical
lddtree is here: github.com/gentoo/pax-utils/blob/master/lddtree.py (and there's also a fork at github.com/ncopa/lddtree written in sh instead of python).Dacoity
On ubuntu sudo apt install pax-utils to get lddtreeOwain
L
26

If you are running Portage≥2.2 with FEATURES=preserve-libs, you should rarely ever need revdep-rebuild anymore as old .so.vers will be preserved as needed (though you still need to rebuild carefully, as stuff still goes kaboom when libA.so.0 wants libC.so.0 and libB.so.0 wants libC.so.1 and some binary wants both libA.so.0 and libB.so.0).


That being said, what ldd does is to get the dynamic linker to do load the executable or library as it usually would, but print out some info along the way. This is a recursive "binary needs library needs other library&hellip" search, because that's what the dynamic linker does.

I'm currently running Linux/ppc32; on Linux/x86, the dynamic linker is usually /lib/ld-linux.so.2, and on Linux/x86_64, the dynamic linker is usually /lib/ld-linux-x86-64.so.2. Here, I call it directly just to hammer in the point that all ldd is nothing more than a shell script that calls upon the dynamic linker to perform its magic.

$ /lib/ld.so.1 /sbin/badblocks
Usage: /sbin/badblocks [-b block_size] [-i input_file] [-o output_file] [-svwnf]
       [-c blocks_at_once] [-d delay_factor_between_reads] [-e max_bad_blocks]
       [-p num_passes] [-t test_pattern [-t test_pattern [...]]]
       device [last_block [first_block]]
$ LD_TRACE_LOADED_OBJECTS=1 /lib/ld.so.1 /sbin/badblocks
        linux-vdso32.so.1 =>  (0x00100000)
        libext2fs.so.2 => /lib/libext2fs.so.2 (0x0ffa8000)
        libcom_err.so.2 => /lib/libcom_err.so.2 (0x0ff84000)
        libc.so.6 => /lib/libc.so.6 (0x0fdfa000)
        libpthread.so.0 => /lib/libpthread.so.0 (0x0fdc0000)
        /lib/ld.so.1 (0x48000000)
$ LD_TRACE_LOADED_OBJECTS=1 /lib/ld.so.1 /lib/libcom_err.so.2
        linux-vdso32.so.1 =>  (0x00100000)
        libpthread.so.0 => /lib/libpthread.so.0 (0x6ffa2000)
        libc.so.6 => /lib/libc.so.6 (0x6fe18000)
        /lib/ld.so.1 (0x203ba000)
$ grep -l pthread /sbin/badblocks /lib/libcom_err.so.2
/lib/libcom_err.so.2

/sbin/badblocks doesn't list libpthread.so.0 as a library dependency, but it gets pulled in by libcom_err.so.2.

Is your problem that ldd doesn't output a nice-looking dependency tree? Use ldd -v.

$ LD_TRACE_LOADED_OBJECTS=1 LD_VERBOSE=1 /lib/ld.so.1 /sbin/badblocks
        linux-vdso32.so.1 =>  (0x00100000)
        libext2fs.so.2 => /lib/libext2fs.so.2 (0x0ffa8000)
        libcom_err.so.2 => /lib/libcom_err.so.2 (0x0ff84000)
        libc.so.6 => /lib/libc.so.6 (0x0fdfa000)
        libpthread.so.0 => /lib/libpthread.so.0 (0x0fdc0000)
        /lib/ld.so.1 (0x201f9000)

        Version information:
        /sbin/badblocks:
                libc.so.6 (GLIBC_2.2) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.4) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.1) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.0) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.3.4) => /lib/libc.so.6
        /lib/libext2fs.so.2:
                libc.so.6 (GLIBC_2.1.3) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.4) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.3) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.2) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.1) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.0) => /lib/libc.so.6
        /lib/libcom_err.so.2:
                ld.so.1 (GLIBC_2.3) => /lib/ld.so.1
                libpthread.so.0 (GLIBC_2.1) => /lib/libpthread.so.0
                libpthread.so.0 (GLIBC_2.0) => /lib/libpthread.so.0
                libc.so.6 (GLIBC_2.1.3) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.4) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.1) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.0) => /lib/libc.so.6
        /lib/libc.so.6:
                ld.so.1 (GLIBC_PRIVATE) => /lib/ld.so.1
                ld.so.1 (GLIBC_2.3) => /lib/ld.so.1
        /lib/libpthread.so.0:
                ld.so.1 (GLIBC_2.3) => /lib/ld.so.1
                ld.so.1 (GLIBC_2.1) => /lib/ld.so.1
                ld.so.1 (GLIBC_PRIVATE) => /lib/ld.so.1
                libc.so.6 (GLIBC_2.1.3) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.3.4) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.4) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.1) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.3.2) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.2) => /lib/libc.so.6
                libc.so.6 (GLIBC_PRIVATE) => /lib/libc.so.6
                libc.so.6 (GLIBC_2.0) => /lib/libc.so.6

If you want, you can read the ELF headers directly instead of depending on the dynamic linker.

$ readelf -d /sbin/badblocks | grep NEEDED
 0x00000001 (NEEDED)                     Shared library: [libext2fs.so.2]
 0x00000001 (NEEDED)                     Shared library: [libcom_err.so.2]
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
$ readelf -d /lib/libcom_err.so.2 | grep NEEDED
 0x00000001 (NEEDED)                     Shared library: [libpthread.so.0]
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
 0x00000001 (NEEDED)                     Shared library: [ld.so.1]

You can also man ld.so for other cute tricks you can play with glibc's dynamic linker.

Laverne answered 29/9, 2009 at 3:58 Comment(1)
the tree section of ldd -v output shows only libraries with versioned symbolsSteffen
D
19

I needed something like this, so I wrote tldd, here it is showing its own library dependencies:

$ ./tldd ./tldd
./tldd
└─libstdc++.so.6 => /lib64/libstdc++.so.6 (0x0000003687c00000)
  ├─libm.so.6 => /lib64/libm.so.6 (0x0000003685000000)
  │ └─libc.so.6 => /lib64/libc.so.6 (0x0000003684c00000)
  │   └─ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x0000003684400000)
  └─libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x0000003686c00000)

You can also use ldd -v to see all dependencies of all shared libs, but it doesn't show a hierarchical tree.

Dacoity answered 29/1, 2014 at 1:29 Comment(5)
@erwin what prevented you getting it working?Dacoity
Thanks. For some reason I'm not getting email from gitlab for that project, but I'll fix it later this month.Dacoity
I just tried the latest update to tldd and it works great! Cool little utility!Melbourne
I saw my boss using it today and I've never heard of it before. So it looks like it's been a hit.Fruiter
For some libraries I am getting a slightly different dependency tree with tldd as compared to lddtree (pax-utils). Not sure which one is definitive.Alvarado
H
-2

I was also going to suggest "readelf -d" but also ensure you build with LDFLAGS="-Wl,--as-needed" if you don't already. This will make you hit this problem less often. Portage 2.2's preserve-libs is nice but I gather it was masked primarily because of it - it does have flaws.

Headmaster answered 18/8, 2012 at 7:32 Comment(1)
The flaws are minor nowadays. While at it, I think I was supposed to take a closer look at them, like a month ago or something :P.Hayse

© 2022 - 2024 — McMap. All rights reserved.