- how is a shared library file called by two different processes in Linux?
- 3 Answers 3
- How to Show All Shared Libraries Used by Executables in Linux?
- 1. Overview
- 2. Short Introduction to Libraries
- 3. Using the ldd Command
- 4. Using the objdump and grep Commands
- 5. Using the readelf Command
- 6. Reading the /proc//maps File
- 7. Conclusion
- How to see the currently loaded shared objects in Linux?
how is a shared library file called by two different processes in Linux?
In Linux, I have a shared library file called foo.so When I execute 2 different process p1, p2 that both use foo.so. Does this foo.so get overlapped by those 2 process?
3 Answers 3
On Unix-based systems (includes Linux), the code segment (.text) may be shared among multiple processes because it’s immutable. Is this overlapping you mention?
Basically, each shared library that contains static data (such as global variables) has a Global Offset Table (GOT). On shared libraries, all references to static data (think of global vars) occur via GOT (they’re indirect). So even if the code segment is shared among multiple processes, each process has its exclusive mapping of other segments of the shared library, including the respective GOT, whose entries are relocated accordingly.
In short, only code is shared among processes, not data. However, I think constants may be an exception depending on compilation flags.
I also recommend chapter 10, Dynamic Linking and Loading, from the following book: Linkers and Loaders.
To emphasize this point, a unixy system can either share or not share the dynamic library, but from the application’s point of view, there is no observable difference between either implementation. Nearly all unix-like systems do share the code between processes because it’s easy to do and a great way to conserve ram at virtually no cost. The rare exceptions are paranoid operating systems on hardware with weak (or no) MMU, such that a shared text could allow one process to corrupt another.
The code for the shared library is copied (or more accurately, mapped) into memory by the operating system.
Then the OS gives each of the processes access to that one copy in memory.
It’s possible that each of the processes will «see» the copy as being at a different memory address than the other. This is resolved by the CPU’s memory management unit.
It can get more complicated than this, but that’s basically how things work in Linux and other Unix-related operating systems like Mac OS X.
The two processes are using the same physical address of the code segment of the shared library, but their virtual address is different for the two processes. Virtual memory is helping as implementing this feature here.
According to the book, Computer Systems: A Programmer’s Perspective, in chapter 9.5 VM as a tool for Memory Management
However, in some instances it is desirable for processes to share code and data. For example, every process must call the same operating system kernel code, and every C program makes calls to routines in the standard C library such as printf. Rather than including separate copies of the kernel and standard C library in each process, the operating system can arrange for multiple processes to share a single copy of this code by mapping the appropriate virtual pages in different processes to the same physical pages.
How to Show All Shared Libraries Used by Executables in Linux?
The Kubernetes ecosystem is huge and quite complex, so it’s easy to forget about costs when trying out all of the exciting tools.
To avoid overspending on your Kubernetes cluster, definitely have a look at the free K8s cost monitoring tool from the automation platform CAST AI. You can view your costs in real time, allocate them, calculate burn rates for projects, spot anomalies or spikes, and get insightful reports you can share with your team.
Connect your cluster and start monitoring your K8s costs right away:
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:
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//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:
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/ !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/ – If the last column doesn’t contain “.so“, we ignore it
- – If the last column contains a shared library, we replace the line by the last column, which is the filename of the library
- 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.
How to see the currently loaded shared objects in Linux?
You can do both with lsof . To see what processes have a library open or mapped do:
and to see what files (including shared libraries) a process has open and/or mapped, do:
That only helps if you know WHICH instance of a .so file is loaded. Is it possible to list all th e .so files actually used by an app to find the disk path to the one I need?
Another way to see what’s loaded in a process is by looking at the /proc/PID/maps file. This shows everything mapped into your address space, including shared objects mapped in.
Worked fine on my embedded ARM platform. While the BusyBox implementation of lsof did not have the needed functionality.
sudo grep libcairo.so /proc/*/maps
is a nice way to explore all /proc/PID/maps mentioned by Rich at once. Sample output:
/proc/8390/maps:7f0a9afae000-7f0a9b0bc000 r-xp 00000000 fc:00 274690 /usr/lib/x86_64-linux-gnu/libcairo.so.2.11400.6 /proc/8390/maps:7f0a9b0bc000-7f0a9b2bc000 ---p 0010e000 fc:00 274690 /usr/lib/x86_64-linux-gnu/libcairo.so.2.11400.6 /proc/8390/maps:7f0a9b2bc000-7f0a9b2bf000 r--p 0010e000 fc:00 274690 /usr/lib/x86_64-linux-gnu/libcairo.so.2.11400.6 /proc/8390/maps:7f0a9b2bf000-7f0a9b2c0000 rw-p 00111000 fc:00 274690 /usr/lib/x86_64-linux-gnu/libcairo.so.2.11400.6 /proc/8466/maps:7f0a9afae000-7f0a9b0bc000 r-xp 00000000 fc:00 274690 /usr/lib/x86_64-linux-gnu/libcairo.so.2.11400.6 /proc/8466/maps:7f0a9b0bc000-7f0a9b2bc000 ---p 0010e000 fc:00 274690 /usr/lib/x86_64-linux-gnu/libcairo.so.2.11400.6 /proc/8466/maps:7f0a9b2bc000-7f0a9b2bf000 r--p 0010e000 fc:00 274690 /usr/lib/x86_64-linux-gnu/libcairo.so.2.11400.6 /proc/8466/maps:7f0a9b2bf000-7f0a9b2c0000 rw-p 00111000 fc:00 274690 /usr/lib/x86_64-linux-gnu/libcairo.so.2.11400.6
Further awk and bash-fu can refine the output further.
This method also shows libraries opened with dlopen , tested with this minimal setup hacked up with a sleep(1000) on Ubuntu 18.04.