Finding the absolute path of shared libraries used by an executable

dynamic-linking

Considering lshw as a sample program, here is what ldd gives:

$ ldd /usr/sbin/lshw    

linux-vdso.so.1 =>  (0x00007fff8bdaf000)
libresolv.so.2 => /lib64/libresolv.so.2 (0x000000360e400000)
libsqlite3.so.0 => /lib64/libsqlite3.so.0 (0x0000003631600000)
libstdc++.so.6 => /lib64/libstdc++.so.6 (0x000000360ec00000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x000000360d800000)
libc.so.6 => /lib64/libc.so.6 (0x000000360c000000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x000000360cc00000)
libdl.so.2 => /lib64/libdl.so.2 (0x000000360c800000)
libm.so.6 => /lib64/libm.so.6 (0x000000360c400000)
/lib64/ld-linux-x86-64.so.2 (0x000000360bc00000)

If I want to find the absolute locations of the shared libraries (and want to exclude linux-vdso.so.1 for obvious reasons), how do I go about it? Using
awk with regular expressions seems brittle here. ldd has a verbose flag (-v) which does print the complete paths of the shared libraries, but it is not very friendly for machine reading.

Is there another way to go about this? If someone knows of a system call to do so, I am fine with that too.

PS: For a bit of context, I want to run this program in chroot jail, so I need to make sure that the shared libraries are all available. Compiling it statically works without all this drama, but that is a path I am looking to avoid.

Update:

I think I may have found something which may be more suitable while reading Michael Kerrisk's book, "The Linux Programming Interface". If I run a program as $ LD_DEBUG=libs lshw, it outputs various helpful information. For example:

$ LD_DEBUG=libs lshw 
     32058:     find library=libresolv.so.2 [0]; searching
     32058:      search cache=/etc/ld.so.cache
     32058:       trying file=/lib64/libresolv.so.2
     32058:
     32058:     find library=libstdc++.so.6 [0]; searching
     32058:      search cache=/etc/ld.so.cache
     32058:       trying file=/lib64/libstdc++.so.6
     32058:
     32058:     find library=libgcc_s.so.1 [0]; searching
     32058:      search cache=/etc/ld.so.cache
     32058:       trying file=/lib64/libgcc_s.so.1
     32058:
     32058:     find library=libc.so.6 [0]; searching
     32058:      search cache=/etc/ld.so.cache
     32058:       trying file=/lib64/libc.so.6
     32058:
     32058:     find library=libm.so.6 [0]; searching
     32058:      search cache=/etc/ld.so.cache
     32058:       trying file=/lib64/libm.so.6
     32058:
     32058:
     32058:     prelink checking: ok
     32058:
     32058:     calling init: /lib64/ld-linux-x86-64.so.2
     32058:
     32058:
     32058:     calling init: /lib64/libc.so.6
     32058:
     32058:
     32058:     calling init: /lib64/libm.so.6
     32058:
     32058:
     32058:     calling init: /lib64/libgcc_s.so.1
     32058:
     32058:
     32058:     calling init: /lib64/libstdc++.so.6
     32058:
     32058:
     32058:     calling init: /lib64/libresolv.so.2


     <more output>

I think, if I look for the 'calling init' lines, I will have the shared library paths it is initializing before starting to execute the program.

Best Answer

You can try awk without using regex:

ldd /bin/ls | awk 'NF == 4 {print $3}; NF == 2 {print $1}'

Output:

/lib/x86_64-linux-gnu/libselinux.so.1
/lib/x86_64-linux-gnu/librt.so.1
/lib/x86_64-linux-gnu/libacl.so.1
/lib/x86_64-linux-gnu/libc.so.6
/lib/x86_64-linux-gnu/libdl.so.2
/lib64/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/libpthread.so.0
/lib/x86_64-linux-gnu/libattr.so.1