6

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.

Anthon
  • 79,293
Amit
  • 412
  • Instead of putting an answer in your question, you should answer your own question just like others. You can then even select that answer as the accepted answer. That way it is clear your question is answered, without having to read through all of it. (And you might get upvotes for both the question and the answer). – Anthon Aug 26 '13 at 08:30

3 Answers3

5

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
cuonglm
  • 153,898
  • This approach fails to find "/lib64/ld-linux-x86-64.so.2" - the last entry in the example stated in my question. – Amit Aug 02 '13 at 07:58
  • Updated. At first, I think you don't want to get the last entry, my mistake. – cuonglm Aug 02 '13 at 08:41
  • I finally adopted your solution. Looks like it will work fine for my case. Thanks again. – Amit Mar 03 '14 at 11:59
2

I know you said you don't want regexes, but the '=>' in the line makes it easy to parse. Even the verbose mode uses that, so you could probably even pass the verbose output through the one with the regex (with some tweaks). I've used just such a command as a list of loop variables (for i in ...) to setup a chroot environment.

So, I'll give you 2 ways of doing it, first with sed:

    ldd /path/to/binary | egrep -v 'linux-vdso|ld-linux-x86-64' | sed 's/.*\=> \(.*\) (.*/\1/'

Or, you can try to parse it by whitespace with cut something like:

    ldd /path/to/binary | egrep -v 'linux-vdso|ld-linux-x86-64' | cut -f 3 -d ' '

Both of these give me the following:

    ldd /bin/ls | egrep -v 'linux-vdso|ld-linux-x86-64' | cut -f 3 -d ' '
    /lib/libselinux.so.1
    /lib/librt.so.1
    /lib/libacl.so.1
    /lib/libc.so.6
    /lib/libdl.so.2
    /lib/libpthread.so.0 
    /lib/libattr.so.1
Joseph R.
  • 39,549
  • Similar to Gnouc's answer, this approach fails to find "/lib64/ld-linux-x86-64.so.2" - the last entry in the example stated in my question. – Amit Aug 02 '13 at 07:58
  • I purposely removed ld-linux with the grep because it's the dynamic linker library itself. Any dynamically linked binary needs that lib and I assumed it was understood, my bad. – user44345 Aug 10 '13 at 02:37
1

With GNU grep:

ldd /usr/sbin/lshw | grep -Po '/.*(?= \(0x)'

If you can guarantee that those path don't contain space characters, you can simplify it to:

ldd /usr/sbin/lshw | grep -o '/[^ ]*'

With sed:

ldd /usr/sbin/lshw | sed -n 's,[^/]*\(/.*\) (0x.*,\1,p'

The idea being to get everything from the first / to the last (0x