123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- #!/bin/sh
- # Copyright 2007-2013 Gentoo Foundation
- # Copyright 2007-2013 Mike Frysinger <vapier@gentoo.org>
- # Copyright 2014-2015 Natanael Copa <ncopa@alpinelinux.org>
- # Distributed under the terms of the GNU General Public License v2
- argv0=${0##*/}
- version=1.26
- : ${ROOT:=/}
- [ "${ROOT}" = "${ROOT%/}" ] && ROOT="${ROOT}/"
- [ "${ROOT}" = "${ROOT#/}" ] && ROOT="${PWD}/${ROOT}"
- usage() {
- cat >&2 <<-EOF
- Display ELF dependencies as a tree
- Usage: ${argv0} [options] <ELF file[s]>
- Options:
- -a Show all duplicated dependencies
- -x Run with debugging
- -R <root> Use this ROOT filesystem tree
- --no-auto-root Do not automatically prefix input ELFs with ROOT
- -l Display output in a flat format
- -h Show this help output
- -V Show version information
- EOF
- exit ${1:-0}
- }
- version() {
- exec echo "lddtree-${version}"
- }
- error() {
- echo "${argv0}: $*" 1>&2
- ret=1
- return 1
- }
- # functions for scanelf backend
- elf_rpath_scanelf() {
- scanelf -qF '#F%r' "$@"
- }
- elf_interp_scanelf() {
- scanelf -qF '#F%i' "$@"
- }
- elf_needed_scanelf() {
- scanelf -qF '#F%n' "$@"
- }
- elf_specs_scanelf() {
- # %a = machine (EM) type
- # %M = EI class
- # %D = endian
- # %I = osabi
- # With glibc, the NONE, SYSV, GNU, and LINUX OSABI's are compatible.
- # LINUX and GNU are the same thing, as are NONE and SYSV, so normalize
- # GNU & LINUX to NONE. #442024 #464380
- scanelf -BF '#F%a %M %D %I' "$1" | \
- sed -r 's: (LINUX|GNU)$: NONE:'
- }
- # functions for binutils backend
- elf_rpath_binutils() {
- objdump -x "$@" | awk '$1 == "RUNPATH" { if($2!="") {runpath=$2;} } $1 == "RPATH" { if($2!="") {rpath=$2;} } END { if(runpath!="") {print runpath;} else {print rpath;} }'
- }
- elf_interp_binutils() {
- # readelf -p .interp ouputs:
- #
- # String dump of section '.interp':
- # [ 0] /lib/ld-musl-x86_64.so.1
- #
- readelf -p .interp "$1" | sed -E -n '/\[\s*[0-9]\]/s/^\s*\[.*\]\s*(.*)/\1/p'
- }
- elf_needed_binutils() {
- objdump -x "$@" | awk '$1 == "NEEDED" { line=line sep $2 ; sep="," } END { print line }'
- }
- elf_specs_binutils() {
- # get Class, Data, Machine and OS/ABI.
- # the OS/ABI 'GNU', 'System V' and 'Linux' are compatible so normalize
- readelf -h "$1" \
- | awk -F: '$1 ~ /Class|Data|Machine|OS.ABI/ {gsub(/^ +/, "", $2); print $2}' \
- | sed -E -e 's/UNIX - (System V|Linux|GNU)/UNIX/' \
- | tr '\n' ' '
- }
- # elf wrapper functions
- elf_rpath() { elf_rpath_$BACKEND "$@"; }
- elf_interp() { elf_interp_$BACKEND "$@"; }
- elf_needed() { elf_needed_$BACKEND "$@"; }
- elf_specs() { elf_specs_$BACKEND "$1"; }
- unset lib_paths_fallback
- for p in ${ROOT}lib* ${ROOT}usr/lib* ${ROOT}usr/local/lib*; do
- lib_paths_fallback="${lib_paths_fallback}${lib_paths_fallback:+:}${p}"
- done
- c_ldso_paths_loaded='false'
- find_elf() {
- _find_elf=''
- local elf=$1 needed_by=$2
- if [ "${elf}" != "${elf##*/}" ] && [ -e "${elf}" ] ; then
- _find_elf=${elf}
- return 0
- else
- check_paths() {
- local elf="$1"
- local pathstr="$2"
- IFS=:
- set -- $pathstr
- unset IFS
- local path pe
- for path ; do
- : ${path:=${PWD}}
- if [ "${path#${ROOT}}" = "${path}" ]; then
- path="${ROOT}${path#/}"
- fi
- pe="${path%/}/${elf#/}"
- if [ -e "${pe}" ] ; then
- if [ "$(elf_specs "${pe}")" = "${elf_specs}" ] ; then
- _find_elf=${pe}
- return 0
- fi
- fi
- done
- return 1
- }
- if [ "${c_last_needed_by}" != "${needed_by}" ] ; then
- c_last_needed_by="${needed_by}"
- c_last_needed_by_rpaths=$(elf_rpath "${needed_by}" | \
- sed -e "s:[$]ORIGIN:${needed_by%/*}:")
- fi
- if [ -n "${c_last_needed_by_rpaths}" ]; then
- check_paths "${elf}" "${c_last_needed_by_rpaths}" && return 0
- fi
- if [ -n "${LD_LIBRARY_PATH}" ] ; then
- check_paths "${elf}" "${LD_LIBRARY_PATH}"
- fi
- if ! ${c_ldso_paths_loaded} ; then
- c_ldso_paths_loaded='true'
- c_ldso_paths=
- if [ -r ${ROOT}etc/ld.so.conf ] ; then
- read_ldso_conf() {
- local line p
- for p ; do
- # if the glob didnt match anything #360041,
- # or the files arent readable, skip it
- [ -r "${p}" ] || continue
- while read line ; do
- case ${line} in
- "#"*) ;;
- "include "*) read_ldso_conf ${line#* } ;;
- *) c_ldso_paths="$c_ldso_paths:${ROOT}${line#/}";;
- esac
- done <"${p}"
- done
- }
- # the 'include' command is relative
- local _oldpwd="$PWD"
- cd "$ROOT"etc >/dev/null
- read_ldso_conf "${ROOT}"etc/ld.so.conf
- cd "$_oldpwd"
- fi
- fi
- if [ -n "${c_ldso_paths}" ] ; then
- check_paths "${elf}" "${c_ldso_paths}" && return 0
- fi
- check_paths "${elf}" "${lib_paths_ldso:-${lib_paths_fallback}}" && return 0
- fi
- return 1
- }
- list_existing_file() {
- if [ -e "$1" ]; then
- echo "$1"
- else
- echo "$1: Not found." >&2
- fi
- }
- # echo all intermediate symlinks and return the resolved path in
- # global variable _resolv_links
- resolv_links() {
- _resolv_links="$1"
- _list_files="$2"
- local oldpwd="$PWD"
- [ "$_list_files" = yes ] && list_existing_file "${_resolv_links}"
- cd "${_resolv_links%/*}"
- while [ -L "$_resolv_links" ]; do
- _resolv_links=$(readlink "$_resolv_links")
- case "$_resolv_links" in
- /*) _resolv_links="${ROOT}${_resolv_links#/}"
- cd "${_resolv_links%/*}"
- ;;
- */*) cd "${_resolv_links%/*}"
- ;;
- esac
- _resolv_links=$(pwd -P)/${_resolv_links##*/}
- [ "$_list_files" = yes ] && list_existing_file "${_resolv_links}"
- done
- cd "$oldpwd"
- }
- show_elf() {
- local elf=$1 indent=$2 parent_elfs=$3
- local rlib lib libs
- local interp resolved
- find_elf "${elf}"
- resolved=${_find_elf}
- elf=${elf##*/}
- ${LIST} || printf "%${indent}s%s => " "" "${elf}"
- case ",${parent_elfs}," in
- *,${elf},*)
- ${LIST} || printf "!!! circular loop !!!\n" ""
- return
- ;;
- esac
- parent_elfs="${parent_elfs},${elf}"
- if ${LIST} ; then
- resolv_links "${resolved:-$1}" yes
- else
- resolv_links "${resolved:-$1}" no
- printf "${resolved:-not found}"
- fi
- resolved=${_resolv_links}
- if [ ${indent} -eq 0 ] ; then
- elf_specs=$(elf_specs "${resolved}")
- interp=$(elf_interp "${resolved}")
- # ignore interpreters that do not have absolute path
- [ "${interp#/}" = "${interp}" ] && interp=
- [ -n "${interp}" ] && interp="${ROOT}${interp#/}"
- if ${LIST} ; then
- [ -n "${interp}" ] && resolv_links "${interp}" yes
- else
- printf " (interpreter => ${interp:-none})"
- fi
- if [ -r "${interp}" ] ; then
- # Extract the default lib paths out of the ldso.
- lib_paths_ldso=$(
- strings "${interp}" | \
- sed -nr -e "/^\/.*lib/{s|^/?|${ROOT}|;s|/$||;s|/?:/?|:${ROOT}|g;p}" | \
- tr '\n' ':'
- )
- fi
- interp=${interp##*/}
- fi
- ${LIST} || printf "\n"
- [ -z "${resolved}" ] && return
- libs=$(elf_needed "${resolved}")
- local my_allhits
- if ! ${SHOW_ALL} ; then
- my_allhits="${allhits}"
- allhits="${allhits},${interp},${libs}"
- fi
- oifs="$IFS"
- IFS=,
- set -- ${libs}
- IFS="$oifs"
- for lib; do
- lib=${lib##*/}
- case ",${my_allhits}," in
- *,${lib},*) continue;;
- esac
- find_elf "${lib}" "${resolved}"
- rlib=${_find_elf}
- show_elf "${rlib:-${lib}}" $((indent + 4)) "${parent_elfs}"
- done
- }
- SHOW_ALL=false
- SET_X=false
- LIST=false
- AUTO_ROOT=true
- while getopts haxVR:l-: OPT ; do
- case ${OPT} in
- a) SHOW_ALL=true;;
- x) SET_X=true;;
- h) usage;;
- V) version;;
- R) ROOT="${OPTARG%/}/";;
- l) LIST=true;;
- -) # Long opts ftw.
- case ${OPTARG} in
- no-auto-root) AUTO_ROOT=false;;
- *) usage 1;;
- esac
- ;;
- ?) usage 1;;
- esac
- done
- shift $(( $OPTIND - 1))
- [ -z "$1" ] && usage 1
- ${SET_X} && set -x
- if which scanelf >/dev/null 2>&1; then
- BACKEND=scanelf
- elif which objdump >/dev/null 2>&1 && which readelf >/dev/null 2>&1; then
- BACKEND=binutils
- else
- error "This tool needs either scanelf or binutils (objdump and readelf)"
- exit 1
- fi
- ret=0
- for elf ; do
- unset lib_paths_ldso
- unset c_last_needed_by
- if ${AUTO_ROOT} && [ -z "${elf##/*}" ] ; then
- elf="${ROOT}${elf#/}"
- fi
- if [ ! -e "${elf}" ] ; then
- error "${elf}: file does not exist"
- elif [ ! -r "${elf}" ] ; then
- error "${elf}: file is not readable"
- elif [ -d "${elf}" ] ; then
- if $LIST; then
- echo ${elf}
- else
- error "${elf}: is a directory"
- fi
- else
- allhits=""
- [ "${elf##*/*}" = "${elf}" ] && elf="./${elf}"
- show_elf "${elf}" 0 ""
- fi
- done
- exit ${ret}
|