0

I need to programmatically inspect the library dependencies of a given executable. Is there a better way than running the ldd (or objdump) commands and parsing their output? Is there an API available which gives the same results as ldd ?

Saurabh Nanda
  • 6,373
  • 5
  • 31
  • 60

1 Answers1

1

I need to programmatically inspect the library dependencies of a given executable.

I am going to assume that you are using an ELF system (probably Linux).

Dynamic library dependencies of an executable or a shared library are encoded as a table on Elf{32_,64}_Dyn entries in the PT_DYNAMIC segment of the library or executable. The ldd (indirectly, but that's an implementation detail) interprets these entries and then uses various details of system configuration and/or LD_LIBRARY_PATH environment variable to locate the needed libraries.

You can print the contents of PT_DYNAMIC with readelf -d a.out. For example:

$ readelf -d /bin/date

Dynamic section at offset 0x19df8 contains 26 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000c (INIT)               0x3000
 0x000000000000000d (FINI)               0x12780
 0x0000000000000019 (INIT_ARRAY)         0x1a250
 0x000000000000001b (INIT_ARRAYSZ)       8 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x1a258
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x000000006ffffef5 (GNU_HASH)           0x308
 0x0000000000000005 (STRTAB)             0xb38
 0x0000000000000006 (SYMTAB)             0x358
 0x000000000000000a (STRSZ)              946 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000015 (DEBUG)              0x0
 0x0000000000000003 (PLTGOT)             0x1b000
 0x0000000000000002 (PLTRELSZ)           1656 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x2118
 0x0000000000000007 (RELA)               0x1008
 0x0000000000000008 (RELASZ)             4368 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000006ffffffb (FLAGS_1)            Flags: PIE
 0x000000006ffffffe (VERNEED)            0xf98
 0x000000006fffffff (VERNEEDNUM)         1
 0x000000006ffffff0 (VERSYM)             0xeea
 0x000000006ffffff9 (RELACOUNT)          170
 0x0000000000000000 (NULL)               0x0

This tells you that the only library needed for this binary is libc.so.6 (the NEEDED entry).

If your real question is "what other libraries does this ELF binary require", then that is pretty easy to obtain: just look for DT_NEEDED entries in the dynamic symbol table. Doing this programmatically is rather easy:

  1. Locate the table of program headers (the ELF file header .e_phoff tells you where it starts).
  2. Iterate over them to find the one with PT_DYNAMIC .p_type.
  3. That segment contains a set of fixed sized Elf{32,64}_Dyn records.
  4. Iterate over them, looking for ones with .d_tag == DT_NEEDED.

Voila.

P.S. There is a bit of a complication: the strings, such as libc.so.6 are not part of the PT_DYNAMIC. But there is a pointer to where they are in the .d_tag == DT_STRTAB entry. See this answer for example code.

Employed Russian
  • 199,314
  • 34
  • 295
  • 362
  • Hi, thanks for your answer. It has gotten me one step closer to me ideal solution, but I'm not there yet. I'm assuming that you're suggesting to parse the ELF binary and not literally use the `readelf` tool, right? I'm doing this in Haskell, and I found https://hackage.haskell.org/package/elf-0.30/docs/Data-Elf.html which does the job of parsing an ELF binary. – Saurabh Nanda Mar 29 '20 at 15:30
  • a parsed ELF binary is represented by this Haskell record - https://hackage.haskell.org/package/elf-0.30/docs/Data-Elf.html#t:Elf - and each section in the binary is represented by https://hackage.haskell.org/package/elf-0.30/docs/Data-Elf.html#t:ElfSegment . I can find the segment with `elfSegmentType==PT_DYNAMIC`, but what next? It seems like the data for all elf-segments is represented by an opaque `ByteString`. Am I right in concluding that the library doesn't parse the data of each segment (depending upon the segment-type)? – Saurabh Nanda Mar 29 '20 at 15:33
  • @SaurabhNanda: Correct: I am suggesting parsing the data directly. Also correct: it doesn't look like Data-Elf is looking into the contents of the segments. You'll have to write code to do so (but that is quite easy). – Employed Russian Mar 29 '20 at 15:42
  • Do you know of a simple document that explains the format of the `PT_DYNAMIC` section? Also, as you've mentioned in your answer, this section will not contain the literal strings, but references to strings. Does it look like https://hackage.haskell.org/package/elf-0.30/docs/Data-Elf.html#v:parseSymbolTables is already parsing all such strings? – Saurabh Nanda Mar 29 '20 at 15:46
  • @SaurabhNanda This answer *told* you the format of `PT_DYNAMIC`: it's a set of fixed-sized ElfXX_Dyn records. The strings are just literal ASCII strings packed one after the other in one of the `PT_LOAD` segments. You'll have to find that load segment by decoding `PT_DYNAMIC` first. – Employed Russian Mar 29 '20 at 15:54
  • Thanks. Let me try to work something out. Your answer address my original question. I'll need to figure out how to get this working in Haskell. Thank you for the detailed answer and being kind enough to address my follow-up questions. – Saurabh Nanda Mar 29 '20 at 16:00