SunOS : List dynamic libraries loaded by a running process or core

Both SunOS and Linux provides ldd which provides the list of shared objects or dynamic libraries that are required for the executable to run. It will tell which libraries will be required and from where they will be loaded when that executable is started. This is controlled by LD_LIBRARY_PATH and/or compilation flags. Here I will address how to find which objects were really picked when executable was started because what you see and what may be happening are two different things.

Take for example a running process that starts to misbehave or even crash. For troubleshooting it is a must that real dynamic libraries that were loaded by the application are found. ldd on the executable lists what it is supposed to use (based on our current environment) but not what that running process is currently using or it was using when it was running (if crashed). Maybe environment was setup differently when that executable was started and process loaded shared libraries from a different location. So ldd on the same binary when it started may be different than what it is currently, baffling the troubleshooter.

SunOS provides pldd (from the suite of /proc utilities) which lists the dynamic libraries that were loaded in reality when the process in question was started (or during its lifetime). pldd can also be used on a corefile to list the dynamic libraries that were used by that process. You simply run pldd with process Id or the the core to get the list of dynamic libraries that the process is using or was using before crash.

Lets see an example. I create a temporary directory (tmp) and copied all dependencies for “bash” in there. Following displays from where I copied the dependent libraries.

sandbox:~> ldd /opt/csw/bin/bash
        libcurses.so.1 =>        /lib/libcurses.so.1
        libintl.so.3 =>  /opt/csw/lib/i386/libintl.so.3
        libiconv.so.2 =>         /opt/csw/lib/pentium_pro/libiconv.so.2
        libc.so.1 =>     /lib/libc.so.1
        libsocket.so.1 =>        /lib/libsocket.so.1
        libnsl.so.1 =>   /lib/libnsl.so.1
        libdl.so.1 =>    /lib/libdl.so.1
        libmp.so.2 =>    /lib/libmp.so.2
        libmd.so.1 =>    /lib/libmd.so.1
        libscf.so.1 =>   /lib/libscf.so.1
        libdoor.so.1 =>  /lib/libdoor.so.1
        libuutil.so.1 =>         /lib/libuutil.so.1
        libgen.so.1 =>   /lib/libgen.so.1
        libm.so.2 =>     /lib/libm.so.2

Now lets set our LD_LIBRARY_PATH to include our temporary directory for fulfilling the dependencies and start the bash. Running pldd $$ ($$ = pid of current shell) shows that dependencies for this current shell are loaded from temporary directory as opposed to the system directory.

sandbox:~> LD_LIBRARY_PATH=$HOME/tmp/:$LD_LIBRARY_PATH /opt/csw/bin/bash
sandbox:~> pldd $$
306:    /opt/csw/bin/bash
/home/unixite/tmp/libcurses.so.1
/home/unixite/tmp/libintl.so.3
/home/unixite/tmp/libiconv.so.2
/home/unixite/tmp/libc.so.1
/home/unixite/tmp/libsocket.so.1
/home/unixite/tmp/libnsl.so.1
/home/unixite/tmp/libdl.so.1
/usr/lib/locale/en_US.UTF-8/en_US.UTF-8.so.3
/usr/lib/locale/common/methods_unicode.so.3
/usr/lib/straddr.so.2
sandbox:~>

Now if we think that maybe pldd picked up the libraries from temporary location because it itself was executed in the same shell (with LD_LIBRARY_PATH including temporary location), lets start an independent shell which does not have this LD_LIBRARY_PATH and run the pldd. On that shell pldd clearly indicates that all shared libraries are loaded from temporary location for the bash process in above example (see matching process ID) and not from the real location which is listed by ldd.

sandbox:~> pldd 306
306:    /opt/csw/bin/bash
/home/unixite/tmp/libcurses.so.1
/home/unixite/tmp/libintl.so.3
/home/unixite/tmp/libiconv.so.2
/home/unixite/tmp/libc.so.1
/home/unixite/tmp/libsocket.so.1
/home/unixite/tmp/libnsl.so.1
/home/unixite/tmp/libdl.so.1
/usr/lib/locale/en_US.UTF-8/en_US.UTF-8.so.3
/usr/lib/locale/common/methods_unicode.so.3
/usr/lib/straddr.so.2
sandbox:~> ldd /opt/csw/bin/bash
        libcurses.so.1 =>        /lib/libcurses.so.1
        libintl.so.3 =>  /opt/csw/lib/i386/libintl.so.3
        libiconv.so.2 =>         /opt/csw/lib/pentium_pro/libiconv.so.2
        libc.so.1 =>     /lib/libc.so.1
        libsocket.so.1 =>        /lib/libsocket.so.1
        libnsl.so.1 =>   /lib/libnsl.so.1
        libdl.so.1 =>    /lib/libdl.so.1
        libmp.so.2 =>    /lib/libmp.so.2
        libmd.so.1 =>    /lib/libmd.so.1
        libscf.so.1 =>   /lib/libscf.so.1
        libdoor.so.1 =>  /lib/libdoor.so.1
        libuutil.so.1 =>         /lib/libuutil.so.1
        libgen.so.1 =>   /lib/libgen.so.1
        libm.so.2 =>     /lib/libm.so.2
sandbox:~>

So in summary ldd can provide the dynamic library location for a binary, but pldd can provide which libraries were really used.

OS Interoperability

pldd exists only on SunOS and not on Linux (citation needed).