1. Overview

In Linux, binary executables usually load shared libraries at runtime. Sometimes, we’d like to have an overview of which libraries are going to be loaded when a program starts.

In this tutorial, we’ll take a look at several ways to list all shared libraries used by a program.

2. Short Introduction to Libraries

In programming, a library is a collection of pre-compiled pieces of code. A library can be reused in different programs. 

In Linux, libraries can be categorized into:

  • Static libraries: bound to a program statically at compile time
  • Shared libraries: loaded when a program launches and loaded into memory at runtime

Next, let’s see how to list the shared libraries of a program.

3. Using the ldd Command

The ldd utility is a shell script. It outputs the shared libraries required by a program. The syntax to use this command is pretty straight forward:

ldd [option]... file...

Let’s have a look which shared libraries are needed for the Vim editor:

$ ldd /usr/bin/vim
	linux-vdso.so.1 (0x00007ffc75fb1000)
	libgtk-3.so.0 => /usr/lib/libgtk-3.so.0 (0x00007fa4dcb5e000)
	libgdk-3.so.0 => /usr/lib/libgdk-3.so.0 (0x00007fa4dca64000)	
	libXau.so.6 => /usr/lib/libXau.so.6 (0x00007fa4db7a9000)
        ....
	liblzma.so.5 => /usr/lib/liblzma.so.5 (0x00007fa4db63f000)
	liblz4.so.1 => /usr/lib/liblz4.so.1 (0x00007fa4db61d000)
	libgcrypt.so.20 => /usr/lib/libgcrypt.so.20 (0x00007fa4db4ff000)
	libgpg-error.so.0 => /usr/lib/libgpg-error.so.0 (0x00007fa4db4d8000)

The ldd command is pretty handy to list the shared libraries of a program.

However, we should use it with caution, as the ldd utility may execute the program to get the list of the shared libraries. We should never run the ldd command on untrusted executables.

4. Using the objdump and grep Commands

The objdump command is a member of the package GNU Binutils. It displays information from object files. To list the required shared libraries of a program, we combine it with grep:

objdump -p File | grep 'NEEDED'

Let’s list Vim’s shared libraries using the objdump command:

$ objdump -p /usr/bin/vim | grep 'NEEDED'
  NEEDED               libgtk-3.so.0
  NEEDED               libgdk-3.so.0
  NEEDED               libpangocairo-1.0.so.0
  NEEDED               libpango-1.0.so.0
  NEEDED               libcairo.so.2
  NEEDED               libgdk_pixbuf-2.0.so.0
  NEEDED               libgio-2.0.so.0
  NEEDED               libgobject-2.0.so.0
  NEEDED               libglib-2.0.so.0
  NEEDED               libSM.so.6
  NEEDED               libICE.so.6
  NEEDED               libXt.so.6
  NEEDED               libX11.so.6
  NEEDED               libm.so.6
  NEEDED               libncursesw.so.6
  NEEDED               libcanberra.so.0
  NEEDED               libacl.so.1
  NEEDED               libgpm.so.2
  NEEDED               libdl.so.2
  NEEDED               libpthread.so.0
  NEEDED               libc.so.6

If we compare the output above to the output of the ldd command, we’ll see that the ldd command lists many more libraries than the objdump command. For example, liblz4.so.1 and libgcrypt.so.20 are in the output of the ldd command, but they don’t show up in the objdump‘s output.

This is because the objdump command is dumping what the program itself lists as libraries. However, the ldd utility is listing which libraries ld.so would load. It follows the graph so that we can see what would be loaded by those libraries at runtime.

Therefore, compared to the objdump command, ldd gives a much better picture of what needs to be available at runtime.

5. Reading the /proc/<pid>/maps File

If the program is already running, we can also get the list of loaded shared libraries by reading the file /proc/<PID>/maps.

In this file, each row describes a region of contiguous virtual memory in a process or thread. If the process has loaded a shared library, the library will show up in this file.

Again, let’s take the Vim editor as an example to get the shared libraries it loaded.

We can get the PID of the running vim process using the pgrep command:

$ pgrep vim
179015

The /proc/179015/maps file contains:

$ cat /proc/179015/maps 
...
7f2cb67c3000-7f2cb67c6000 r--p 00000000 08:13 3810274                    /usr/lib/libnss_files-2.31.so
7f2cb67c6000-7f2cb67cd000 r-xp 00003000 08:13 3810274                    /usr/lib/libnss_files-2.31.so
..
7f2cb6a89000-7f2cb6a8a000 r--p 00002000 08:13 3810903                    /usr/lib/libutil-2.31.so
7f2cb6a8a000-7f2cb6a8b000 r--p 00002000 08:13 3810903                    /usr/lib/libutil-2.31.so
...
7f2cb9802000-7f2cb9803000 rw-p 00000000 00:00 0 
7ffe77658000-7ffe7767a000 rw-p 00000000 00:00 0                          [stack]
7ffe776c8000-7ffe776cc000 r--p 00000000 00:00 0                          [vvar]
7ffe776cc000-7ffe776ce000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0                  [vsyscall]

Through the output above, we see that the loaded shared libraries are listed in the last column.

However, the last column contains some other values we don’t need. Also, there are many duplicates. We can filter the data and remove the duplicates using an awk one-liner to get a clean list of shared libraries:

$ awk '$NF!~/\.so/{next} {$0=$NF} !a[$0]++' /proc/179015/maps     
...
/usr/lib/libpython3.8.so.1.0
/usr/lib/libgpg-error.so.0.29.0
/usr/lib/libgcrypt.so.20.2.5
/usr/lib/liblz4.so.1.9.2
/usr/lib/liblzma.so.5.2.5
/usr/lib/libsystemd.so.0.28.0
/usr/lib/libogg.so.0.8.4
/usr/lib/libvorbis.so.0.4.8
/usr/lib/libblkid.so.1.1.0
/usr/lib/libXdmcp.so.6.0.0
/usr/lib/libXau.so.6.0.0
/usr/lib/libdatrie.so.1.3.5
...

Let’s understand how the awk one-liner works:

  • $NF !~ /\.so/{next} – If the last column doesn’t contain “.so“, we ignore it
  • {$0=$NF} – If the last column contains a shared library, we replace the line by the last column, which is the filename of the library

!a[$0]++ is an awk trick to remove duplicate lines:

  • When a line with value “foo” comes the first time to awk, awk creates an associative array element: a[“foo”] with the default value: 0
  • a[“foo”]++ returns the original value 0 and then increments its value by 1, so the expression returns 0 and then we have a[“foo”]=1
  • !a[“foo”]++ will become !0, it is evaluated as true, thus, triggers the default action: print the current line
  • When a line with “foo” comes again, the array element exists already, a[“foo”]++ will return and hold 2
  • !a[“foo”]++ this time will become !1, therefore we have false: do nothing. In this way, duplicated lines get printed only once

6. Conclusion

In this article, we’ve discussed three different ways to list shared libraries of a program.

The ldd command is the most straightforward one to show the shared libraries of a program. However, we must keep in mind that we should never use it on untrusted executables.

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments