How to list library dependencies of a non-native binary?

LinuxGccShared LibrariesCross CompilingBinutils

Linux Problem Overview


When developing for native platform, I can use ldd to list all the shared libraries (.so files) a binary executable I build will try to load upon start-up. But when cross-compiling, I don't know how to get the same information. The ldd is not a normal binutils utility, like strip or ar, that can be built alongside gcc for cross compiling, but instead, it is a cryptic shell script that apparently can only run on native platform.

So, using the cross-target binutils tools, is there any way to get a list of the dynamically linked dependency for of a foreign binary?

Linux Solutions


Solution 1 - Linux

> is there any way to get a list of the dynamically linked dependency for of a foreign binary

You can list direct dependencies of a binary easily enough:

readelf -d a.out | grep NEEDED

 0x0000000000000001 (NEEDED)             Shared library: [librt.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]

I know of no way to recursively continue this to get the full list (as ldd does). You'll have to repeat the process for every NEEDED library by hand.

Solution 2 - Linux

You can do bash -x ldd /bin/ls to understand what ldd is doing. The ldd script is not that "cryptic". It basically runs

LD_TRACE_LOADED_OBJECTS=1 /lib64/ld-linux-x86-64.so.2 /bin/ls

so it uses the dynamic loader of the system (because the result of ldd depends upon your actual environment and system!). But you could examine with objdump -x /bin/ls the dynamic section of an executable, e.g.

% objdump -x /bin/ls
  /bin/ls:     file format elf64-x86-64
  /bin/ls
  architecture: i386:x86-64, flags 0x00000112:
  EXEC_P, HAS_SYMS, D_PAGED
  start address 0x00000000004046d4
  
  Program Header:
      PHDR off    0x0000000000000040 vaddr 0x0000000000400040 paddr 0x0000000000400040 align 2**3
           filesz 0x00000000000001c0 memsz 0x00000000000001c0 flags r-x
    INTERP off    0x0000000000000200 vaddr 0x0000000000400200 paddr 0x0000000000400200 align 2**0
           filesz 0x000000000000001c memsz 0x000000000000001c flags r--
      LOAD off    0x0000000000000000 vaddr 0x0000000000400000 paddr 0x0000000000400000 align 2**21
           filesz 0x0000000000019ef4 memsz 0x0000000000019ef4 flags r-x
      LOAD off    0x000000000001a000 vaddr 0x000000000061a000 paddr 0x000000000061a000 align 2**21
           filesz 0x000000000000077c memsz 0x0000000000001500 flags rw-
   DYNAMIC off    0x000000000001a028 vaddr 0x000000000061a028 paddr 0x000000000061a028 align 2**3
           filesz 0x00000000000001d0 memsz 0x00000000000001d0 flags rw-
      NOTE off    0x000000000000021c vaddr 0x000000000040021c paddr 0x000000000040021c align 2**2
           filesz 0x0000000000000044 memsz 0x0000000000000044 flags r--
  EH_FRAME off    0x0000000000017768 vaddr 0x0000000000417768 paddr 0x0000000000417768 align 2**2
           filesz 0x00000000000006fc memsz 0x00000000000006fc flags r--
     STACK off    0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**3
           filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-
  
  Dynamic Section:
    NEEDED               libselinux.so.1
    NEEDED               librt.so.1
    NEEDED               libacl.so.1
    NEEDED               libc.so.6
    INIT                 0x0000000000402148
    FINI                 0x00000000004125f8
    HASH                 0x0000000000400260
    GNU_HASH             0x00000000004005c0
    STRTAB               0x0000000000401100
    SYMTAB               0x0000000000400620
    STRSZ                0x00000000000004d7
    SYMENT               0x0000000000000018
    DEBUG                0x0000000000000000
    PLTGOT               0x000000000061a208
    PLTRELSZ             0x0000000000000990
    PLTREL               0x0000000000000007
    JMPREL               0x00000000004017b8
    RELA                 0x0000000000401740
    RELASZ               0x0000000000000078
    RELAENT              0x0000000000000018
    VERNEED              0x00000000004016c0
    VERNEEDNUM           0x0000000000000003
    VERSYM               0x00000000004015d8
  
  Version References:
    required from librt.so.1:
      0x09691a75 0x00 05 GLIBC_2.2.5
    required from libacl.so.1:
      0x05822452 0x00 06 ACL_1.2
      0x05822450 0x00 04 ACL_1.0
    required from libc.so.6:
      0x09691a75 0x00 03 GLIBC_2.2.5
      0x0d696913 0x00 02 GLIBC_2.3
  

Again, the actual dependency depends upon the system where your binary is run (e.g. because I could have an LD_LIBRARY_PATH with my own libc.so.6 somewhere, which would be a bad idea).

So you need a cross variant of objdump

Solution 3 - Linux

while in gdb, info shared is similar to ldd. It gives complete human readable runtime information of the executable.
readelf would miss path and other libraries information.
readelf is a very good tool for on the host study. Developer may choose that works.

Solution 4 - Linux

this helped me

objdump -p /path/to/program | grep NEEDED 

Solution 5 - Linux

Sorry to make a zombie thread, but I am working on some things for my OpenWRT router and wanted this to check some dependencies to see if I had enough space on my jffs2 partition to copy over just e2fsck. Short answer: nope.avi.

Anyhoo, I made a little script that uses the accepted answer plus some (probably overly verbose) grep calls and with a little hand waving and some unicorn tears (no worries, they were happy tears!) I managed to put together the following script that gives you all the dependencies. I'm sure there's lots of room for improvement especially RE: the loops and recursion, as well as the fact that it's all bashisms all the time (i.e. indexed arrays) but this is at least nominally working for me:

#!/bin/bash

declare -a search_path
declare -a found_deps

find_dependencies() {
	local file="$1"
	local -a deps
	local -a deps_to_process

	deps=( $( readelf -d "$file" | grep "NEEDED" | \
		grep -o -E "\[[^]]*\]" | grep -o -E "[^][]*" ) )

	local add_this_dep=true

	# always assume we've found $file and add it to the $found_deps list
	# if it's not there
	for found_dep in "${found_deps[@]}"
	do
		if [ "$found_dep" = "$(basename $file)" ]
		then
			add_this_dep=false
			break
		fi
	done

	# if $add_this_dep is true, go ahead and add to the found_deps list
	if $add_this_dep
	then
		found_deps+=("$(basename $file)")
	fi

	# for every dependency found by readelf (no path)
	for dep in "${deps[@]}"
	do
		local process_dep=true

		# if the basename of the file passed into the function is
		# this dep, skip processing altogether
		if [ "$dep" = "$(basename $file)" ]
		then
			break
		else
			# otherwise, if it's one of the 'found deps' don't process it
			for found_dep in "${found_deps[@]}"
			do
				if [ "$dep" = "$found_dep" ]
				then
					process_dep=false
					break
				fi
			done

			# it wasn't one of the 'found deps' so add 
			# it to the found_deps list
			if $process_dep
			then
				found_deps+=($dep)
			fi
		fi

		# if we are supposed to process this dep
		if $process_dep
		then
			local file_path=

			# check each search path for a file named $dep underneath it
			for dir in $search_path
			do
				file_path=$( find "$dir" -name "$dep" | head -n 1 )

				# if the $file_path is not empty, then we found
				# a copy of it, so break out of the loop
				if [ -n "$file_path" ]; then break; fi;
			done

			# if the $file_path is not empty, then we found a copy
			# of the file, place it the list of deps to be processed
			if [ -n "$file_path" ]
			then
				deps_to_process+=($file_path)
			fi
		fi
	done

	# now, go through all of our $deps_to_process (with path)
	# and run "find_dependencies" on them
	for dep_to_process in "${deps_to_process[@]}"
	do
		find_dependencies "$dep_to_process"
	done
}

argc=$#

if [ $argc -eq 0 ]
then
	printf '%s: prints dependencies of a (potentially) non-native elf executable, recursively\n'
	printf '\n'
	printf 'usage:\n'
	printf '\t%s <non-native elf executable> [ --supress-header ] [ <path> ... ]\n' "$(basename $0)"
	printf '\twhere\n'
	printf '\t\t<non-native elf executable> is the name of a file to find the dependencies of.\n'
	printf '\t\t[ <path> ... ] is an optional list of directories under which to search for libraries.\n'
	printf '\t\t[ --supress-header ] is an optional flag that prints nothing other than the list of files to stdout.\n'
	printf '\t\t\t(without the parameter a banner is sent to stderr)'
	printf '\n'
else
	file="$1"
	shift 1

	show_header=true

	if [ "$1" = "--supress-header" ]; then show_header=false; shift 1; fi;

	if $show_header
	then
		printf ' -------------------------------------------------------------------------------------------------------------\n' 1>&2
		printf '  ldd-nonnative: find all dependencies of a (potentially) non-native binary %s\n' "$file" 1>&2
		printf ' -------------------------------------------------------------------------------------------------------------\n' 1>&2
	fi

	search_path="$@"
	find_dependencies $file

	printf '\t%s\n' "${found_deps[@]}"
fi

# ❤ copyheart, shelleybutterfly, 2014
# love is never subject to the low; please copy and share with love :)

# contact information:
# [email protected]

# I hereby dedicate this software to the public domain in all jurisdictions
# where possible. In other jurisdictions, I license it to you under your
# choice of permissive license, as defined by the Open Source Institute (OSI),
# found at URL http://opensource.org. Should such a license be unavailable in
# your jurisdiection, you may use any copyleft open source license, again as
# defined by OSI; and if that too is unavailable, then you are licensed this
# software under the least restrictive possible terms allowed in your
# jurisdiction.

# I request but do not require that you give credit to me, shelleybutterfly,
# which I will accept in cases of licensing other than the public domain as
# valuable consideration for your use of my software. (I love to know that 
# my name is plastered all over some obscure piece of code that's at least
# appreciated by those who do see it, and I consider that more than enough
# consideration. :D) This software is provided completely as-is, and I take
# absolutely no responsibility for any damages that it may cause. It has
# not been fully tested and should be considered pre-alpha release quality,
# (that is to say, it is likely unstable and unsafe to use without your own
# validation to ensure that it meets some or any your needs without: among
# other things: melting your computer, calling your grandma at midnight,
# telling your girlfriend she's fat, and throwing your computer in the
# dishwasher to make sure it's clean, and you take full responsibility for
# doing your own testing to ensure that it suits your needs, and operates 
# properly int the conditions under which you intend to use it.

# Should you not be willing to take this risk, it is highly recommended
# that you do not use this software at all, ever, and that you instead find
# a real commercial piece of software, or other warranted piece of software,
# as I can not and do not and shall not provide any warranty of fitness for
# any purpose whatsoever, even to scrub your toilet, and it's provided with
# the understanding that it will be used primarily as an educational tool
# rather than any sort of production code. I disclaim any responsibility for
# anything that may happen to you due to your use of software, even if it
# causes huge zits, a rash under your boobs that wont go away, or a burning
# sensation when you pee. Sorry, **especially** for a burning sensation when
# you pee.

# Your use of this software implies acceptance of these terms as well as 
# any painful urination or other issues it may cause in your life.

# [deep breath] 

# my love to you all; I hope this software was useful to you in some way; if
# you end up using it despite the dire warnings against doing so, feel free
# to drop me a note at [email protected], as I imagine it
# will be rare enough to make my day, every time. ♥

So, there ya go. Hope it helps someone. Egads it's taken me a long time to even get good enough with shell scripting to be able to pull something like this off, nevermind the fact that it took me much longer than it probably should have, so please forgive what has likely shaken some of you script gurus out there to the very core of your being. :)

Solution 6 - Linux

A bit late for this addition but someone might benefit/clarify. Doesn't the -A flag to readelf give the same result as ldd?

    $ readelf -AW /bin/vi

Library list section '.gnu.liblist' contains 8 entries:
     Library              Time Stamp          Checksum   Version Flags
  0: libselinux.so.1      2011-07-25T08:02:58 0x17a7d5f7 0       0
  1: libtermcap.so.2      2011-07-25T08:02:59 0x29ae9ff7 0       0
  2: libacl.so.1          2011-07-25T08:02:58 0x60748842 0       0
  3: libc.so.6            2011-07-25T08:02:58 0x0c2c7eeb 0       0
  4: libdl.so.2           2011-07-25T08:02:58 0xdfbfc467 0       0
  5: libsepol.so.1        2011-07-25T08:02:58 0x857499cb 0       0
  6: /lib64/ld-linux-x86-64.so.2 2011-07-25T08:02:58 0x9e6549b7 0       0
  7: libattr.so.1         2011-07-25T08:02:58 0x862f1546 0       0

The only info missing here seems to be the full path where those libraries are located.

On the other hand, the tools mentioned so far are only useful once you have an installation known to work. My most common problems are:

  1. Installing a program (mostly via rpm) that then fails to start or crashes during start up. Somehow I think this is related to library incompatibilities but I haven't found an easy way of checking these thing before installing a program (nor indeed after)
  2. In an attemtp to overcome (1), sometimes I have resorted to downloading the sources and compiling locally. The typical configure script is partially helpful because it tells you which libraries you are missing. However, it fails to tell you what the lowest version of such libraries are required

Can anyone shed some light on these issues? BTW, I have tried reading installation instructions and release notes but they are almost always nowhere near sufficient.

A beefy example may put everything into context, so please try compiling Cinelerra.

Solution 7 - Linux

By design ldd can only been executed on target. However, it is possible to mimic ldd behavior using readelf. A script called `xldd' was developed in crosstool-ng project. An independent version of this script is available here:

https://gist.github.com/jerome-pouiller/c403786c1394f53f44a3b61214489e6f

Solution 8 - Linux

To list shared libraries dependency of a non-native binary, you can try the following tool: http://www.mathembedded.com/component/k2/item/1-cross-ldd.html

I use it on SH4 and MIPS. As reported in other answer, you can achieve the same using readelf output and a recursive loop, but I have never try by myself since cross-ldd exist.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionlvellaView Question on Stackoverflow
Solution 1 - LinuxEmployed RussianView Answer on Stackoverflow
Solution 2 - LinuxBasile StarynkevitchView Answer on Stackoverflow
Solution 3 - LinuxAjayKumarBasuthkarView Answer on Stackoverflow
Solution 4 - LinuxcaptainchhalaView Answer on Stackoverflow
Solution 5 - LinuxshelleybutterflyView Answer on Stackoverflow
Solution 6 - Linuxquiet-rangerView Answer on Stackoverflow
Solution 7 - LinuxJérôme PouillerView Answer on Stackoverflow
Solution 8 - LinuxPatrice TisserandView Answer on Stackoverflow