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 libpython3.7m.so.1.0
NEEDED libcrypt.so.2
NEEDED libpthread.so.0
NEEDED libdl.so.2
NEEDED libutil.so.1
NEEDED libm.so.6
NEEDED libselinux.so.1
NEEDED libtinfo.so.6
NEEDED libacl.so.1
NEEDED libgpm.so.2
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. Using the readelf Command
Like objdump, the readelf command is part of GNU Binutils. It displays information about ELF format object files, performing a similar function to objdump.
Let’s check vim’s shared libraries using the readelf command, filtered by grep:
$ readelf --dynamic /usr/bin/vim | grep NEEDED
0x0000000000000001 (NEEDED) Shared library: [libpython3.7m.so.1.0]
0x0000000000000001 (NEEDED) Shared library: [libcrypt.so.2]
0x0000000000000001 (NEEDED) Shared library: [libpthread.so.0]
0x0000000000000001 (NEEDED) Shared library: [libdl.so.2]
0x0000000000000001 (NEEDED) Shared library: [libutil.so.1]
0x0000000000000001 (NEEDED) Shared library: [libm.so.6]
0x0000000000000001 (NEEDED) Shared library: [libselinux.so.1]
0x0000000000000001 (NEEDED) Shared library: [libtinfo.so.6]
0x0000000000000001 (NEEDED) Shared library: [libacl.so.1]
0x0000000000000001 (NEEDED) Shared library: [libgpm.so.2]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
From the output above, we can see the shared libraries of vim.
6. Reading the /proc//maps File
If the program is already running, we can also get the list of loaded shared libraries by reading the file /proc/
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 1 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
7. Conclusion
In this article, we’ve discussed 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. The readelf command is a better choice compared to the rest since we deal with “ELF” format on Linux. However, we must keep in mind that we should never use it on untrusted executables.