158

I want to find out the list of dynamic libraries a binary loads when run (With their full paths). I am using CentOS 6.0. How to do this?

0

9 Answers 9

156

You can do this with ldd command:

NAME
       ldd - print shared library dependencies

SYNOPSIS
       ldd [OPTION]...  FILE...

DESCRIPTION
       ldd  prints  the  shared  libraries  required by each program or shared
       library specified on the command line.
....

Example:

$ ldd /bin/ls
    linux-vdso.so.1 =>  (0x00007fff87ffe000)
    libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007ff0510c1000)
    librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007ff050eb9000)
    libacl.so.1 => /lib/x86_64-linux-gnu/libacl.so.1 (0x00007ff050cb0000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff0508f0000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007ff0506ec000)
    /lib64/ld-linux-x86-64.so.2 (0x00007ff0512f7000)
    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007ff0504ce000)
    libattr.so.1 => /lib/x86_64-linux-gnu/libattr.so.1 (0x00007ff0502c9000)
8
  • 2
    Any idea of what would be a macOS equivalent of this? No lld on darwin, it appears, nor can I find it via homebrew.
    – mz2
    Oct 23, 2016 at 14:05
  • 18
    On macOS: otool -L <path-to-binary> Mar 17, 2017 at 22:23
  • 11
    be aware that this may execute the binary. So if the binary is untrusted, it may be better to not use ldd. See man page. Aug 19, 2019 at 2:57
  • 4
    To follow @PaulRooney 's comment: In particular, the man page suggests using objdump -p /path/to/program | grep NEEDED as an alternative to ldd if you do not trust the executable. Apr 4, 2020 at 21:56
  • 1
    lddtree from pax-utils is better when you want to see the hierarchical tree of dependencies including dependencies' dependencies; plain ldd shows only the immediate dependencies.
    – legends2k
    Oct 18, 2021 at 10:57
111

readelf -d $executable | grep 'NEEDED'

Can be used if you can't run the executable, e.g. if it was cross compiled, or if you don't trust it:

In the usual case, ldd invokes the standard dynamic linker (see ld.so(8)) with the LD_TRACE_LOADED_OBJECTS environment variable set to 1, which causes the linker to display the library dependencies. Be aware, however, that in some circumstances, some versions of ldd may attempt to obtain the dependency information by directly executing the program. Thus, you should never employ ldd on an untrusted executable, since this may result in the execution of arbitrary code.

Example:

readelf -d /bin/ls | grep 'NEEDED'

Sample ouptut:

 0x0000000000000001 (NEEDED)             Shared library: [libselinux.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libacl.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]

Note that libraries can depend on other libraries, so now you need to find the dependencies.

A naive approach that often works is:

$ locate libselinux.so.1
/lib/i386-linux-gnu/libselinux.so.1
/lib/x86_64-linux-gnu/libselinux.so.1
/mnt/debootstrap/lib/x86_64-linux-gnu/libselinux.so.1

but the more precise method is to understand the ldd search path / cache. I think ldconfig is the way to go.

Choose one, and repeat:

readelf -d /lib/x86_64-linux-gnu/libselinux.so.1 | grep 'NEEDED'

Sample output:

0x0000000000000001 (NEEDED)             Shared library: [libpcre.so.3]
0x0000000000000001 (NEEDED)             Shared library: [libdl.so.2]
0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
0x0000000000000001 (NEEDED)             Shared library: [ld-linux-x86-64.so.2]

And so on.

See also:

/proc/<pid>/maps for running processes

Mentioned by Basile, this is useful to find all the libraries currently being used by running executables. E.g.:

sudo awk '/\.so/{print $6}' /proc/1/maps | sort -u

shows all currently loaded dynamic dependencies of init (PID 1):

/lib/x86_64-linux-gnu/ld-2.23.so
/lib/x86_64-linux-gnu/libapparmor.so.1.4.0
/lib/x86_64-linux-gnu/libaudit.so.1.0.0
/lib/x86_64-linux-gnu/libblkid.so.1.1.0
/lib/x86_64-linux-gnu/libc-2.23.so
/lib/x86_64-linux-gnu/libcap.so.2.24
/lib/x86_64-linux-gnu/libdl-2.23.so
/lib/x86_64-linux-gnu/libkmod.so.2.3.0
/lib/x86_64-linux-gnu/libmount.so.1.1.0
/lib/x86_64-linux-gnu/libpam.so.0.83.1
/lib/x86_64-linux-gnu/libpcre.so.3.13.2
/lib/x86_64-linux-gnu/libpthread-2.23.so
/lib/x86_64-linux-gnu/librt-2.23.so
/lib/x86_64-linux-gnu/libseccomp.so.2.2.3
/lib/x86_64-linux-gnu/libselinux.so.1
/lib/x86_64-linux-gnu/libuuid.so.1.3.0

This method also shows libraries opened with dlopen, tested with this minimal setup hacked up with a sleep(1000) on Ubuntu 18.04.

See also: How to see the currently loaded shared objects in Linux? | Super User

3
  • 4
    Nice part about readelf method is that it works on cross-binaries (ex: armhf on amd64) as well
    – Ghostrider
    Aug 20, 2019 at 2:21
  • 2
    +1 for deep diving into elf binary itself rather than relying on linker or other tools. Feb 27, 2020 at 16:58
  • 1
    Great, readelf worked on shared libs too.
    – SAMPro
    Jan 11, 2022 at 5:46
27

ldd and lsof show the libraries loaded either directly or at a given moment. They do not account for libraries loaded via dlopen (or discarded by dlclose). You can get a better picture of this using strace, e.g.,

strace -e trace=open myprogram

(since dlopen ultimately calls open - though you may of course have a system using different names for 64-bit opens...).

Example:

strace -e trace=open date

shows me this:

open("/etc/ld.so.cache", O_RDONLY)      = 3
open("/lib/x86_64-linux-gnu/librt.so.1", O_RDONLY) = 3
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY) = 3
open("/lib/x86_64-linux-gnu/libpthread.so.0", O_RDONLY) = 3
open("/usr/lib/locale/locale-archive", O_RDONLY) = 3
open("/etc/localtime", O_RDONLY)        = 3
Wed Apr 12 04:56:32 EDT 2017

from which one could grep the ".so" names to just see shared objects.

2
15

lsof also can show you which libraries are being used for one particular process.

i.e.

$ pidof nginx
6920 6919

$ lsof -p 6919|grep mem
nginx   6919 root  mem    REG               0,64    65960     43 /lib64/libnss_files-2.12.so
nginx   6919 root  mem    REG               0,64    19536     36 /lib64/libdl-2.12.so
nginx   6919 root  mem    REG               0,64    10312   1875 /lib64/libfreebl3.so
nginx   6919 root  mem    REG               0,64  1923352     38 /lib64/libc-2.12.so
nginx   6919 root  mem    REG               0,64    88600   1034 /lib64/libz.so.1.2.3
nginx   6919 root  mem    REG               0,64  1967392   1927 /usr/lib64/libcrypto.so.1.0.1e
nginx   6919 root  mem    REG               0,64   183080   1898 /lib64/libpcre.so.0.0.1
nginx   6919 root  mem    REG               0,64    40400   1217 /lib64/libcrypt-2.12.so
nginx   6919 root  mem    REG               0,64   142688     77 /lib64/libpthread-2.12.so
nginx   6919 root  mem    REG               0,64   154664     31 /lib64/ld-2.12.so
3

For mass query:

  1. create a small script (useslib) and put in in the PATH (orspecify a full path in the command below)

    #! /bin/bash
    ldd $1 | grep -q $2
    exit $?
    
  2. Use it in a find command, for instance:

    find /usr/bin/ -executable -type f -exec useslib {} libgtk-x11-2.0 \; -print
    

(libgtk-x11-2.0 seems to be the gtk2 lib)

3

For a process of pid 1234, you could also read the /proc/1234/maps (textual) pseudo-file (read proc(5)...) or use pmap(1)

This gives the virtual address space of that process, hence the files (including shared libraries, even dlopen(3)-ed one) which are memory mapped

(of course, use ps aux or pgrep(1) to find the processes running some given program)

2

It is possible use pmap.

For example, start a process: $ watch date

Get pid: $ ps -ef | grep watch

Show memory map: $ pmap <pid>

Show with full path: $ pmap <pid> -p

$ pmap 72770
72770:   watch date
00005613a32c9000     20K r-x-- watch
00005613a34cd000      4K r---- watch
00005613a34ce000      4K rw--- watch
00005613a4f6a000    264K rw---   [ anon ]
00007f2f3a7d5000 204616K r---- locale-archive
00007f2f46fa7000   1748K r-x-- libc-2.27.so
00007f2f4715c000   2048K ----- libc-2.27.so
00007f2f4735c000     16K r---- libc-2.27.so
00007f2f47360000      8K rw--- libc-2.27.so
00007f2f47362000     16K rw---   [ anon ]
00007f2f47366000     12K r-x-- libdl-2.27.so
00007f2f47369000   2044K ----- libdl-2.27.so
00007f2f47568000      4K r---- libdl-2.27.so
00007f2f47569000      4K rw--- libdl-2.27.so
00007f2f4756a000    160K r-x-- libtinfo.so.6.1
00007f2f47592000   2048K ----- libtinfo.so.6.1
00007f2f47792000     16K r---- libtinfo.so.6.1
00007f2f47796000      4K rw--- libtinfo.so.6.1
00007f2f47797000    232K r-x-- libncursesw.so.6.1
00007f2f477d1000   2048K ----- libncursesw.so.6.1
00007f2f479d1000      4K r---- libncursesw.so.6.1
00007f2f479d2000      4K rw--- libncursesw.so.6.1
00007f2f479d3000    148K r-x-- ld-2.27.so
00007f2f47bdb000     20K rw---   [ anon ]
00007f2f47bf1000     28K r--s- gconv-modules.cache
00007f2f47bf8000      4K r---- ld-2.27.so
00007f2f47bf9000      4K rw--- ld-2.27.so
00007f2f47bfa000      4K rw---   [ anon ]
00007ffd39404000    136K rw---   [ stack ]
00007ffd3959b000     12K r----   [ anon ]
00007ffd3959e000      8K r-x--   [ anon ]
ffffffffff600000      4K r-x--   [ anon ]
 total           215692K
$ pmap 72770 -p
72770:   watch date
00005613a32c9000     20K r-x-- /usr/bin/watch
00005613a34cd000      4K r---- /usr/bin/watch
00005613a34ce000      4K rw--- /usr/bin/watch
00005613a4f6a000    264K rw---   [ anon ]
00007f2f3a7d5000 204616K r---- /usr/lib/locale/locale-archive
00007f2f46fa7000   1748K r-x-- /usr/lib64/libc-2.27.so
00007f2f4715c000   2048K ----- /usr/lib64/libc-2.27.so
00007f2f4735c000     16K r---- /usr/lib64/libc-2.27.so
00007f2f47360000      8K rw--- /usr/lib64/libc-2.27.so
00007f2f47362000     16K rw---   [ anon ]
00007f2f47366000     12K r-x-- /usr/lib64/libdl-2.27.so
00007f2f47369000   2044K ----- /usr/lib64/libdl-2.27.so
00007f2f47568000      4K r---- /usr/lib64/libdl-2.27.so
00007f2f47569000      4K rw--- /usr/lib64/libdl-2.27.so
00007f2f4756a000    160K r-x-- /usr/lib64/libtinfo.so.6.1
00007f2f47592000   2048K ----- /usr/lib64/libtinfo.so.6.1
00007f2f47792000     16K r---- /usr/lib64/libtinfo.so.6.1
00007f2f47796000      4K rw--- /usr/lib64/libtinfo.so.6.1
00007f2f47797000    232K r-x-- /usr/lib64/libncursesw.so.6.1
00007f2f477d1000   2048K ----- /usr/lib64/libncursesw.so.6.1
00007f2f479d1000      4K r---- /usr/lib64/libncursesw.so.6.1
00007f2f479d2000      4K rw--- /usr/lib64/libncursesw.so.6.1
00007f2f479d3000    148K r-x-- /usr/lib64/ld-2.27.so
00007f2f47bdb000     20K rw---   [ anon ]
00007f2f47bf1000     28K r--s- /usr/lib64/gconv/gconv-modules.cache
00007f2f47bf8000      4K r---- /usr/lib64/ld-2.27.so
00007f2f47bf9000      4K rw--- /usr/lib64/ld-2.27.so
00007f2f47bfa000      4K rw---   [ anon ]
00007ffd39404000    136K rw---   [ stack ]
00007ffd3959b000     12K r----   [ anon ]
00007ffd3959e000      8K r-x--   [ anon ]
ffffffffff600000      4K r-x--   [ anon ]
 total           215692K
1

i don't know how widespread the -p option to ldconfig(8), but the following sh/awk script, using the readelf(1) suggestion, and ldconfig's -p, might be of help if you want to avoid any security issues with ldd(1), but also see the path of the dynamic libraries. (NB: this does not do the transitive closure of inclusion, as it were, so you only see the libraries called out directly by the a.out.)

#!/usr/bin/env bash

needs=`readelf -d $1 | \
  awk '$2=="(NEEDED)" {
      gsub("[][]", "", $5); 
      hits[$5]=$5
  }
  END {
      for (i in hits) {
          printf(sprintf("%s ", i))
      }
  }'`

ldconfig -p | \
    awk -vneeds="$needs" \
       'BEGIN{
          split(needs,aneeds)
        }

        { 
          if (!arr[$1]) 
            arr[$1] = $NF
        }

        END { 
          for (i in aneeds) { 
            need = aneeds[i]; 
            if (arr[need]) {
              print need "\t" arr[need]
            } else {
               print "unfulfilled:", need
            }
          }
       }'

for example:

{1001} ./foo.sh a.out
libc.so.6   /usr/lib/libc.so.6
libX11.so.6 /usr/lib/libX11.so.6
libgdk-3.so.0   /usr/lib/libgdk-3.so.0
libgcr-base-3.so.1  /usr/lib/libgcr-base-3.so.1
libjavascriptcoregtk-4.0.so.18  /usr/local/lib/libjavascriptcoregtk-4.0.so.18
libatk-1.0.so.0 /usr/lib/libatk-1.0.so.0
libharfbuzz.so.0    /usr/lib/libharfbuzz.so.0
libsoup-2.4.so.1    /usr/lib/libsoup-2.4.so.1
libgdk_pixbuf-2.0.so.0  /usr/lib/libgdk_pixbuf-2.0.so.0
libgck-1.so.0   /usr/lib/libgck-1.so.0
libgthread-2.0.so.0 /usr/lib/libgthread-2.0.so.0
libgcr-ui-3.so.1    /usr/lib/libgcr-ui-3.so.1
libglib-2.0.so.0    /usr/lib/libglib-2.0.so.0
libgobject-2.0.so.0 /usr/lib/libgobject-2.0.so.0
libcairo-gobject.so.2   /usr/lib/libcairo-gobject.so.2
libpango-1.0.so.0   /usr/lib/libpango-1.0.so.0
libz.so.1   /usr/lib/libz.so.1
libgio-2.0.so.0 /usr/lib/libgio-2.0.so.0
libpangocairo-1.0.so.0  /usr/lib/libpangocairo-1.0.so.0
libp11-kit.so.0 /usr/lib/libp11-kit.so.0
libcairo.so.2   /usr/lib/libcairo.so.2
libgtk-3.so.0   /usr/lib/libgtk-3.so.0
libwebkit2gtk-4.0.so.37 /usr/local/lib/libwebkit2gtk-4.0.so.37
1

I found lddtree from PaX-utils (package name pax-utils on most distros) to be more useful; it prints a hierarchical tree of dependencies (including dependencies' dependencies). This is better than ldd which only lists the binary's immediate dependencies.

lddtree $(which ls)

ls => /usr/bin/ls (interpreter => /lib64/ld-linux-x86-64.so.2)
    libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1
        libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0
            libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2
        ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6

lddtree by default skips duplicate dependencies; if you also want that pass -a; be ready to tolerate the redundancy and verbosity.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy