| 1 |
#!/bin/bash
|
| 2 |
# Copyright 2007-2012 Gentoo Foundation
|
| 3 |
# Copyright 2007-2012 Mike Frysinger <vapier@gentoo.org>
|
| 4 |
# Distributed under the terms of the GNU General Public License v2
|
| 5 |
# $Header: /var/cvsroot/gentoo-projects/pax-utils/lddtree.sh,v 1.16 2012/11/13 01:09:06 vapier Exp $
|
| 6 |
|
| 7 |
argv0=${0##*/}
|
| 8 |
|
| 9 |
: ${ROOT:=/}
|
| 10 |
[[ ${ROOT} != */ ]] && ROOT="${ROOT}/"
|
| 11 |
[[ ${ROOT} != /* ]] && ROOT="${PWD}${ROOT}"
|
| 12 |
|
| 13 |
usage() {
|
| 14 |
cat <<-EOF
|
| 15 |
Display ELF dependencies as a tree
|
| 16 |
|
| 17 |
Usage: ${argv0} [options] <ELF file[s]>
|
| 18 |
|
| 19 |
Options:
|
| 20 |
-a Show all duplicated dependencies
|
| 21 |
-x Run with debugging
|
| 22 |
-R <root> Use this ROOT filesystem tree
|
| 23 |
-l Display output in a flat format
|
| 24 |
-h Show this help output
|
| 25 |
-V Show version information
|
| 26 |
EOF
|
| 27 |
exit ${1:-0}
|
| 28 |
}
|
| 29 |
|
| 30 |
version() {
|
| 31 |
local id='$Id: lddtree.sh,v 1.16 2012/11/13 01:09:06 vapier Exp $'
|
| 32 |
id=${id##*,v }
|
| 33 |
exec echo "lddtree-${id% * Exp*}"
|
| 34 |
}
|
| 35 |
|
| 36 |
error() {
|
| 37 |
echo "${argv0}: $*" 1>&2
|
| 38 |
ret=1
|
| 39 |
return 1
|
| 40 |
}
|
| 41 |
|
| 42 |
elf_specs() {
|
| 43 |
# With glibc, the NONE, SYSV, and LINUX OSABI's are compatible.
|
| 44 |
# NONE and SYSV are the same thing, so normalize LINUX to NONE. #442024
|
| 45 |
scanelf -BF '#F%a %M %D %I' "$1" | \
|
| 46 |
sed 's: LINUX$: NONE:'
|
| 47 |
}
|
| 48 |
|
| 49 |
lib_paths_fallback="${ROOT}lib* ${ROOT}usr/lib* ${ROOT}usr/local/lib*"
|
| 50 |
c_ldso_paths_loaded='false'
|
| 51 |
find_elf() {
|
| 52 |
_find_elf=''
|
| 53 |
|
| 54 |
local elf=$1 needed_by=$2
|
| 55 |
if [[ ${elf} == */* ]] && [[ -e ${elf} ]] ; then
|
| 56 |
_find_elf=${elf}
|
| 57 |
return 0
|
| 58 |
else
|
| 59 |
check_paths() {
|
| 60 |
local elf=$1 ; shift
|
| 61 |
local path pe
|
| 62 |
for path ; do
|
| 63 |
pe="${path%/}/${elf#/}"
|
| 64 |
if [[ -e ${pe} ]] ; then
|
| 65 |
if [[ $(elf_specs "${pe}") == "${elf_specs}" ]] ; then
|
| 66 |
_find_elf=${pe}
|
| 67 |
return 0
|
| 68 |
fi
|
| 69 |
fi
|
| 70 |
done
|
| 71 |
return 1
|
| 72 |
}
|
| 73 |
|
| 74 |
if [[ ${c_last_needed_by} != ${needed_by} ]] ; then
|
| 75 |
c_last_needed_by=${needed_by}
|
| 76 |
c_last_needed_by_rpaths=$(scanelf -qF '#F%r' "${needed_by}" | \
|
| 77 |
sed -e 's|:| |g' -e "s:[$]ORIGIN:${needed_by%/*}:")
|
| 78 |
fi
|
| 79 |
check_paths "${elf}" ${c_last_needed_by_rpaths} && return 0
|
| 80 |
|
| 81 |
if [[ -n ${LD_LIBRARY_PATH} ]] ; then
|
| 82 |
# Need to handle empty paths as $PWD,
|
| 83 |
# and handle spaces in between the colons
|
| 84 |
local p path=${LD_LIBRARY_PATH}
|
| 85 |
while : ; do
|
| 86 |
p=${path%%:*}
|
| 87 |
check_paths "${elf}" "${path:-${PWD}}" && return 0
|
| 88 |
[[ ${path} == *:* ]] || break
|
| 89 |
path=${path#*:}
|
| 90 |
done
|
| 91 |
fi
|
| 92 |
|
| 93 |
if ! ${c_ldso_paths_loaded} ; then
|
| 94 |
c_ldso_paths_loaded='true'
|
| 95 |
c_ldso_paths=()
|
| 96 |
if [[ -r ${ROOT}etc/ld.so.conf ]] ; then
|
| 97 |
read_ldso_conf() {
|
| 98 |
local line p
|
| 99 |
for p ; do
|
| 100 |
# if the glob didnt match anything #360041,
|
| 101 |
# or the files arent readable, skip it
|
| 102 |
[[ -r ${p} ]] || continue
|
| 103 |
while read line ; do
|
| 104 |
case ${line} in
|
| 105 |
"#"*) ;;
|
| 106 |
"include "*) read_ldso_conf ${line#* } ;;
|
| 107 |
*) c_ldso_paths+=( "${ROOT}${line#/}" ) ;;
|
| 108 |
esac
|
| 109 |
done <"${p}"
|
| 110 |
done
|
| 111 |
}
|
| 112 |
# the 'include' command is relative
|
| 113 |
pushd "${ROOT}"etc >/dev/null
|
| 114 |
read_ldso_conf "${ROOT}"etc/ld.so.conf
|
| 115 |
popd >/dev/null
|
| 116 |
fi
|
| 117 |
fi
|
| 118 |
if [[ ${#c_ldso_paths[@]} -gt 0 ]] ; then
|
| 119 |
check_paths "${elf}" "${c_ldso_paths[@]}" && return 0
|
| 120 |
fi
|
| 121 |
|
| 122 |
check_paths "${elf}" ${lib_paths_ldso:-${lib_paths_fallback}} && return 0
|
| 123 |
fi
|
| 124 |
return 1
|
| 125 |
}
|
| 126 |
|
| 127 |
show_elf() {
|
| 128 |
local elf=$1 indent=$2 parent_elfs=$3
|
| 129 |
local rlib lib libs
|
| 130 |
local interp resolved
|
| 131 |
find_elf "${elf}"
|
| 132 |
resolved=${_find_elf}
|
| 133 |
elf=${elf##*/}
|
| 134 |
|
| 135 |
${LIST} || printf "%${indent}s%s => " "" "${elf}"
|
| 136 |
if [[ ,${parent_elfs}, == *,${elf},* ]] ; then
|
| 137 |
${LIST} || printf "!!! circular loop !!!\n" ""
|
| 138 |
return
|
| 139 |
fi
|
| 140 |
parent_elfs="${parent_elfs},${elf}"
|
| 141 |
if ${LIST} ; then
|
| 142 |
echo "${resolved:-$1}"
|
| 143 |
else
|
| 144 |
printf "${resolved:-not found}"
|
| 145 |
fi
|
| 146 |
if [[ ${indent} -eq 0 ]] ; then
|
| 147 |
elf_specs=$(elf_specs "${resolved}")
|
| 148 |
interp=$(scanelf -qF '#F%i' "${resolved}")
|
| 149 |
[[ -n ${interp} ]] && interp="${ROOT}${interp#/}"
|
| 150 |
|
| 151 |
if ${LIST} ; then
|
| 152 |
[[ -n ${interp} ]] && echo "${interp}"
|
| 153 |
else
|
| 154 |
printf " (interpreter => ${interp:-none})"
|
| 155 |
fi
|
| 156 |
if [[ -r ${interp} ]] ; then
|
| 157 |
# Extract the default lib paths out of the ldso.
|
| 158 |
lib_paths_ldso=$(
|
| 159 |
strings "${interp}" | \
|
| 160 |
sed -nr -e "/^\/.*lib/{s|^/?|${ROOT}|;s|/$||;s|/?:/?|\n${ROOT}|g;p}"
|
| 161 |
)
|
| 162 |
fi
|
| 163 |
interp=${interp##*/}
|
| 164 |
fi
|
| 165 |
${LIST} || printf "\n"
|
| 166 |
|
| 167 |
[[ -z ${resolved} ]] && return
|
| 168 |
|
| 169 |
libs=$(scanelf -qF '#F%n' "${resolved}")
|
| 170 |
|
| 171 |
local my_allhits
|
| 172 |
if ! ${SHOW_ALL} ; then
|
| 173 |
my_allhits="${allhits}"
|
| 174 |
allhits="${allhits},${interp},${libs}"
|
| 175 |
fi
|
| 176 |
|
| 177 |
for lib in ${libs//,/ } ; do
|
| 178 |
lib=${lib##*/}
|
| 179 |
[[ ,${my_allhits}, == *,${lib},* ]] && continue
|
| 180 |
find_elf "${lib}" "${resolved}"
|
| 181 |
rlib=${_find_elf}
|
| 182 |
show_elf "${rlib:-${lib}}" $((indent + 4)) "${parent_elfs}"
|
| 183 |
done
|
| 184 |
}
|
| 185 |
|
| 186 |
# XXX: internal hack
|
| 187 |
if [[ $1 != "/../..source.lddtree" ]] ; then
|
| 188 |
|
| 189 |
SHOW_ALL=false
|
| 190 |
SET_X=false
|
| 191 |
LIST=false
|
| 192 |
|
| 193 |
while getopts haxVR:l OPT ; do
|
| 194 |
case ${OPT} in
|
| 195 |
a) SHOW_ALL=true;;
|
| 196 |
x) SET_X=true;;
|
| 197 |
h) usage;;
|
| 198 |
V) version;;
|
| 199 |
R) ROOT="${OPTARG%/}/";;
|
| 200 |
l) LIST=true;;
|
| 201 |
?) usage 1;;
|
| 202 |
esac
|
| 203 |
done
|
| 204 |
shift $((OPTIND - 1))
|
| 205 |
[[ -z $1 ]] && usage 1
|
| 206 |
|
| 207 |
${SET_X} && set -x
|
| 208 |
|
| 209 |
ret=0
|
| 210 |
for elf ; do
|
| 211 |
unset lib_paths_ldso
|
| 212 |
unset c_last_needed_by
|
| 213 |
if [[ ! -e ${elf} ]] ; then
|
| 214 |
error "${elf}: file does not exist"
|
| 215 |
elif [[ ! -r ${elf} ]] ; then
|
| 216 |
error "${elf}: file is not readable"
|
| 217 |
elif [[ -d ${elf} ]] ; then
|
| 218 |
error "${elf}: is a directory"
|
| 219 |
else
|
| 220 |
allhits=""
|
| 221 |
[[ ${elf} != */* ]] && elf="./${elf}"
|
| 222 |
show_elf "${elf}" 0 ""
|
| 223 |
fi
|
| 224 |
done
|
| 225 |
exit ${ret}
|
| 226 |
|
| 227 |
fi
|