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 Answers
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:
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):
- Add the ARM one at the end of the line
- 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
:)
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