54
votes

I have created a cross compiled arm executable. I want to find the library dependency of the executable. I am using ubuntu natty and installed arm-linux-gnueabi tool chain, which does not contain ldd. Is there a tool available to view arm executables library dependancy in linux.

6

6 Answers

75
votes

This is a bit of a kluge, but it's the best solution I could find, and it really works quite well for basic use - just save this script as "arm-none-linux-gnueabi-ldd" with your other cross tools.

#!/bin/sh
arm-none-linux-gnueabi-readelf -a $1 | grep "Shared library:"
17
votes

You could also use objdump and in order to just dump and search the header fraction of the binary. This may save you some milliseconds...

#!/bin/sh  
arm-none-linux-gnueabi-objdump -x $1 | grep NEEDED
8
votes

Here is another option, you can set LD_TRACE_LOADED_OBJECTS environment variable to any value, say 1, and then you just run the executable, the output should be its dynamic dependencies.

4
votes

I found this and hope it can help a lot of people who are still searching for a true ldd solution. ldd is just a script with support of ld-linux library. So why not making your own ldd script? First you need to find ld-linux library in your system, which should be available. In my case it is /lib/ld-linux-armhf.so.3 so I put this into RTLDLIST as you can see in the script below.

And put this script in your device or board and you can have ldd which is very convenient to find dependents libraries for an executable.

Good luck!

#! /bin/bash
# Copyright (C) 1996-2011, 2012 Free Software Foundation, Inc.
# This file is part of the GNU C Library.

# The GNU C Library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.

# The GNU C Library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.

# You should have received a copy of the GNU Lesser General Public
# License along with the GNU C Library; if not, write to the Free
# Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
# 02111-1307 USA.


# This is the `ldd' command, which lists what shared libraries are
# used by given dynamically-linked executables.  It works by invoking the
# run-time dynamic linker as a command and setting the environment
# variable LD_TRACE_LOADED_OBJECTS to a non-empty value.

# We should be able to find the translation right at the beginning.
TEXTDOMAIN=libc
TEXTDOMAINDIR=/usr/share/locale

RTLDLIST="/lib/ld-linux.so.2 /lib64/ld-linux-x86-64.so.2 /lib/ld-linux-armhf.so.3"
warn=
bind_now=
verbose=

while test $# -gt 0; do
  case "$1" in
  --vers | --versi | --versio | --version)
    echo 'ldd (Ubuntu EGLIBC 2.15-0ubuntu10.3) 2.15'
    printf $"Copyright (C) %s Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
" "2012"
    printf $"Written by %s and %s.
" "Roland McGrath" "Ulrich Drepper"
    exit 0
    ;;
  --h | --he | --hel | --help)
    printf $"Usage: ldd [OPTION]... FILE...
      --help              print this help and exit
      --version           print version information and exit
  -d, --data-relocs       process data relocations
  -r, --function-relocs   process data and function relocations
  -u, --unused            print unused direct dependencies
  -v, --verbose           print all information
"
    printf $"For bug reporting instructions, please see:
%s.
" "<http://www.debian.org/Bugs/>"
    exit 0
    ;;
  -d | --d | --da | --dat | --data | --data- | --data-r | --data-re | \
  --data-rel | --data-relo | --data-reloc | --data-relocs)
    warn=yes
    shift
    ;;
  -r | --f | --fu | --fun | --func | --funct | --functi | --functio | \
  --function | --function- | --function-r | --function-re | --function-rel | \
  --function-relo | --function-reloc | --function-relocs)
    warn=yes
    bind_now=yes
    shift
    ;;
  -v | --verb | --verbo | --verbos | --verbose)
    verbose=yes
    shift
    ;;
  -u | --u | --un | --unu | --unus | --unuse | --unused)
    unused=yes
    shift
    ;;
  --v | --ve | --ver)
    echo >&2 $"ldd: option \`$1' is ambiguous"
    exit 1
    ;;
  --)       # Stop option processing.
    shift; break
    ;;
  -*)
    echo >&2 'ldd:' $"unrecognized option" "\`$1'"
    echo >&2 $"Try \`ldd --help' for more information."
    exit 1
    ;;
  *)
    break
    ;;
  esac
done

nonelf ()
{
  # Maybe extra code for non-ELF binaries.
  return 1;
}

add_env="LD_TRACE_LOADED_OBJECTS=1 LD_WARN=$warn LD_BIND_NOW=$bind_now"
add_env="$add_env LD_LIBRARY_VERSION=\$verify_out"
add_env="$add_env LD_VERBOSE=$verbose"
if test "$unused" = yes; then
  add_env="$add_env LD_DEBUG=\"$LD_DEBUG${LD_DEBUG:+,}unused\""
fi

# The following use of cat is needed to make ldd work in SELinux
# environments where the executed program might not have permissions
# to write to the console/tty.  But only bash 3.x supports the pipefail
# option, and we don't bother to handle the case for older bash versions.
if x=`set -o` && test "$x" != "${x#*pipefail}" && set -o pipefail ; then
  try_trace() {
    eval $add_env '"$@"' | cat
  }
else
  try_trace() {
    eval $add_env '"$@"'
  }
fi

case $# in
0)
  echo >&2 'ldd:' $"missing file arguments"
  echo >&2 $"Try \`ldd --help' for more information."
  exit 1
  ;;
1)
  single_file=t
  ;;
*)
  single_file=f
  ;;
esac

result=0
for file do
  # We don't list the file name when there is only one.
  test $single_file = t || echo "${file}:"
  case $file in
  */*) :
       ;;
  *) file=./$file
     ;;
  esac
  if test ! -e "$file"; then
    echo "ldd: ${file}:" $"No such file or directory" >&2
    result=1
  elif test ! -f "$file"; then
    echo "ldd: ${file}:" $"not regular file" >&2
    result=1
  elif test -r "$file"; then
    RTLD=
    ret=1
    for rtld in ${RTLDLIST}; do
      if test -x $rtld; then
    verify_out=`${rtld} --verify "$file"`
    ret=$?
    case $ret in
    [02]) RTLD=${rtld}; break;;
    esac
      fi
    done
    case $ret in
    0|2)
      try_trace "$RTLD" "$file" || result=1
      ;;
    1|126)
      # This can be a non-ELF binary or no binary at all.
      nonelf "$file" || {
    echo $" not a dynamic executable"
    result=1
      }
      ;;
    *)
      echo 'ldd:' ${RTLD} $"exited with unknown exit code" "($ret)" >&2
      exit 1
      ;;
    esac
  else
    echo 'ldd:' $"error: you do not have read permission for" "\`$file'" >&2
    result=1
  fi
done

exit $result
# Local Variables:
#  mode:ksh
# End:
2
votes

I found a solution when running into [Sourceware.Bugzilla]: Bug 16628 - Segfault after a binary without pthread dlopen()s a library linked with pthread while working on a project (which involved porting some C++ code on ARM).

I'm using Ubtu 16.04, and for ARM cross compile tools I installed g++-4.9-arm-linux-gnueabihf (and dependencies, and also forced me to uninstall g++-multilib (and dependencies)). For testing (running) the executables, I installed QEMU (qemu-user-static).

According to [man7]: LDD(1) (which is just a shell (bash) script):

In the usual case, ldd invokes the standard dynamic linker (see ld.so(8)) with the LD_TRACE_LOADED_OBJECTS environment variable set to 1.

I'm going to exemplify on the data from the above bug report.

issue16417.cpp:

#include <string>
#include <iostream>


extern char **environ;


int main() {
    for (char **it = environ; *it != nullptr; ++it) {
        std::string str(*it);
        std::cout << "export " << str.substr(0, str.find('=')) << std::endl;
    }
}

build_issue16417.sh (removed flags unsupported by the compiler):

#!/bin/bash

_BUILD_TOOL_BASE_NAME=arm-linux-gnueabihf-g++-4.9
_FILE_BASE_NAME=issue16417

${_BUILD_TOOL_BASE_NAME} -pthread -std=gnu++0x -pedantic -ggdb -O2 -pipe -mtls-dialect=gnu2 -ftree-loop-distribution -ftree-vectorize -fmerge-all-constants -fira-loop-pressure -pedantic -c -o ${_FILE_BASE_NAME}.o ${_FILE_BASE_NAME}.cpp

${_BUILD_TOOL_BASE_NAME} -pthread -std=gnu++0x -pedantic -ggdb -O2 -pipe -mtls-dialect=gnu2 -ftree-loop-distribution -ftree-vectorize -fmerge-all-constants -fira-loop-pressure -pedantic -Wl,-O1 -Wl,--sort-common -Wl,--enable-new-dtags -Wl,--gc-sections -Wl,--hash-style=gnu -o ${_FILE_BASE_NAME}.${_BUILD_TOOL_BASE_NAME} ${_FILE_BASE_NAME}.o  -Wl,--as-needed -pthread -Wl,-fuse-ld=gold

Output:

[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q006150000]> ~/sopr.sh
*** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***
[prompt]>
[prompt]> uname -a
Linux cfati-ubtu16x64-0 4.15.0-39-generic #42~16.04.1-Ubuntu SMP Wed Oct 24 17:09:54 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
[prompt]> which arm-linux-gnueabihf-g++-4.9
/usr/bin/arm-linux-gnueabihf-g++-4.9
[prompt]> ldd /usr/bin/arm-linux-gnueabihf-g++-4.9
        linux-vdso.so.1 =>  (0x00007ffdf55c2000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f59b6c62000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f59b702c000)
[prompt]> dpkg -S /usr/bin/arm-linux-gnueabihf-g++-4.9
g++-4.9-arm-linux-gnueabihf: /usr/bin/arm-linux-gnueabihf-g++-4.9
[prompt]>
[prompt]> ls
build_issue16417_g++4.9.sh  issue16417.cpp
[prompt]> ./build_issue16417_g++4.9.sh
[prompt]> ls
build_issue16417_g++4.9.sh  issue16417.arm-linux-gnueabihf-g++-4.9  issue16417.cpp  issue16417.o
[prompt]> file ./issue16417.arm-linux-gnueabihf-g++-4.9
./issue16417.arm-linux-gnueabihf-g++-4.9: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=17e0f3d6ceaa13df4ac031e6581074757abee36c, not stripped
[prompt]> echo ${_QEMU_CMD}
/usr/bin/qemu-arm-static -L /usr/arm-linux-gnueabihf
[prompt]> ${_QEMU_CMD} ./issue16417.arm-linux-gnueabihf-g++-4.9
qemu: uncaught target signal 11 (Segmentation fault) - core dumped
Segmentation fault (core dumped)
[prompt]>
[prompt]> ldd ./issue16417.arm-linux-gnueabihf-g++-4.9
        not a dynamic executable

As expected, (regular) ldd doesn't work for ARM binaries.

In the ldd script, there's a line (no other one references .sos) somewhere at the beginning:

RTLDLIST="/lib/ld-linux.so.2 /lib64/ld-linux-x86-64.so.2 /libx32/ld-linux-x32.so.2"

As seen, it doesn't have the correct ld.so (so far there are only the i686 and x86_64 ones), so it can't handle ARM binaries. To make things work, either (note that for each option, sudo is required):

  1. Add the ARM one at the end of the line
  2. Create a copy of ldd, and modify that version (replace existing .sos with the right one)

I chose the 2nd variant. But where is ARM ld.so? As you probably guessed, it can be obtained from ${_QEMU_CMD} env var (after all, qemu needs it as well). On my machine it's: /usr/arm-linux-gnueabihf/lib/ld-2.23.so (libc6-armhf-cross required, but that should be installed by now). So, I copied ldd to arm-linux-gnueabihf-ldd, and replaced the above line with:

_LIB_PATH=/usr/arm-linux-gnueabihf/lib

RTLDLIST="${_LIB_PATH}/ld-2.23.so"

# @TODO - cfati: Not a fan of the line below (I think it's considered bad practice), but without it, it can't find the libraries locations.
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${_LIB_PATH}

Also posting the diff format (check [SO]: Run/Debug a Django application's UnitTests from the mouse right click context menu in PyCharm Community Edition? (@CristiFati's answer) (Patching utrunner section) for how to apply it):

[prompt]> diff --binary -uN /usr/bin/ldd /usr/bin/arm-linux-gnueabihf-ldd
--- /usr/bin/ldd        2018-01-15 04:49:13.000000000 +0200
+++ /usr/bin/arm-linux-gnueabihf-ldd    2018-12-04 19:34:19.499001000 +0200
@@ -26,7 +26,13 @@
 TEXTDOMAIN=libc
 TEXTDOMAINDIR=/usr/share/locale

-RTLDLIST="/lib/ld-linux.so.2 /lib64/ld-linux-x86-64.so.2 /libx32/ld-linux-x32.so.2"
+_LIB_PATH=/usr/arm-linux-gnueabihf/lib
+
+RTLDLIST="${_LIB_PATH}/ld-2.23.so"
+
+# @TODO - cfati: Not a fan of the line below (I think it's considered bad practice), but without it, it can't find the libraries locations.
+export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${_LIB_PATH}
+
 warn=
 bind_now=
 verbose=

Try running the "new" ldd on the ARM executable, and voilà:

[prompt]> arm-linux-gnueabihf-ldd ./issue16417.arm-linux-gnueabihf-g++-4.9
        libstdc++.so.6 => /usr/arm-linux-gnueabihf/lib/libstdc++.so.6 (0xf66c6000)
        libc.so.6 => /usr/arm-linux-gnueabihf/lib/libc.so.6 (0xf65da000)
        /lib/ld-linux-armhf.so.3 => /usr/arm-linux-gnueabihf/lib/ld-2.23.so (0xf6fd7000)
        libm.so.6 => /usr/arm-linux-gnueabihf/lib/libm.so.6 (0xf6562000)
        libgcc_s.so.1 => /usr/arm-linux-gnueabihf/lib/libgcc_s.so.1 (0xf6538000)

Note: What comes next, is not part of the question / answer.
Anyway, replacing --as-needed by --no-as-needed in build_issue16417.sh, yields:

[prompt]> vi build_issue16417_g++4.9.sh
[prompt]> ./build_issue16417_g++4.9.sh
[prompt]> ${_QEMU_CMD} ./issue16417.arm-linux-gnueabihf-g++-4.9
export LC_NAME
export LC_TIME
export LESSCLOSE
export XDG_RUNTIME_DIR
export LESSOPEN
export SSH_CONNECTION
export XDG_DATA_DIRS
export LOGNAME
export HOME
export SHLVL
export PAPERSIZE
export LC_MEASUREMENT
export LANG
export PWD
export LC_IDENTIFICATION
export QT_QPA_PLATFORMTHEME
export PATH
export MAIL
export LC_TELEPHONE
export LS_COLORS
export USER
export SSH_TTY
export OLDPWD
export LC_NUMERIC
export SSH_CLIENT
export SHELL
export TERM
export ANDROID_HOME
export LC_MONETARY
export XDG_SESSION_ID
export LC_ADDRESS
export LC_PAPER

:)

1
votes

As already said, 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