| 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.17 2012/11/15 20:35:04 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.17 2012/11/15 20:35:04 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}" "${p:-${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 |