#!/usr/bin/env bash # # lddsafe - Safely print shared library dependencies (similar to ldd). # Author: Ricardo Garcia Gonzalez. # Author: Ivan Mironov. # License: Public domain code. # # Better for grep and expected output of other tools. export LANG=C # Expand globs to nothing when no match is found. shopt -s nullglob # Print error and exit. die() { echo "ERROR: $1" 1>&2 exit 1 } # Find required program. findexec() { which "$1" >/dev/null 2>/dev/null || die "$1 not found" } # Check file exists and is readable. checkreadable() { [ -r "$1" ] || die "$1 missing or not readable" } # Check for bash version. [ -n "$BASH_VERSION" ] || die "Bash required" [ ${BASH_VERSINFO[0]} -ge 4 ] || die "Bash version 4 or later required" # Check needed programs. findexec objdump findexec readelf findexec dirname findexec sed # Check needed files. checkreadable /etc/ld.so.conf # Check arguments. [ "$1" == "-n" ] && recursive=0 || recursive=1 [ "$1" == "-n" ] && shift if [ $# -eq 0 ]; then echo "Usage: $( basename $0 ) [-n] FILE..." 2>&1 exit 1 fi for arg in "$@"; do checkreadable "$1" done # Recursively print the list of files included from /etc/ld.so.conf. ld_so_conf_deps() { echo "$1" dirname="$( dirname "$1" )" patterns="$( sed -n 's/^include[\ \t]\+\(.\+\)$/\1/p' "$1" )" set -o noglob for pattern in $patterns; do set +o noglob case $pattern in /*) for file in $pattern; do ld_so_conf_deps "$file" done ;; *) for file in $dirname/$pattern; do ld_so_conf_deps "$file" done ;; esac done set +o noglob } # Additional library directories. LD_LIBRARY_PATH_LIBS="${LD_LIBRARY_PATH//:/ }" MORELIBDIRS="$( sed '/^include[\ \t]/d' $( ld_so_conf_deps /etc/ld.so.conf ) )" # Search for a given library name. searchlib() { found=0 for libdir in $LIBDIRS; do path="$libdir"/"$1" if [ -r "$path" ]; then found=1 break fi done [ $found -eq 1 ] && echo "$path" } # Already visited libraries. declare -A VISITEDLIBS # Print dependency results, recursively. recursivedeps() { for lib in $( objdump -p "$1" | \ sed -n 's,^ *NEEDED \+\([^ ]\+\) *$,\1,p' ); do if [ ! "${VISITEDLIBS[$lib]}" ]; then VISITEDLIBS["$lib"]=1 file=`searchlib "$lib"` if [ "$file" ]; then echo " $lib => $file" [ $recursive -eq 1 ] && recursivedeps "$file" else echo " $lib => not found" fi fi done } # Search symbol names in library directories. for arg in "$@"; do # Print file name when more than one file given. [ $# -gt 1 ] && echo "${arg}:" # Set appropriate library search directories. class=$( readelf -h $arg 2>/dev/null | \ sed -n 's/^[ \t]*Class:[\ \t]\+\(.\+\)/\1/p' ) if [ -z "$class" ]; then echo "$arg: not an ELF file" 2>&1 continue fi if [ $class != ELF32 -a $class != ELF64 ]; then echo "$arg: unknown ELF format" 2>&1 continue fi if [ $class == ELF64 ]; then if [ -d /lib64 ]; then stdlibs="/lib64 /usr/lib64" else stdlibs="/lib /usr/lib" fi else # $class == ELF32 if [ -d /lib32 ]; then stdlibs="/lib32 /usr/lib32" else stdlibs="/lib /usr/lib" fi fi LIBDIRS="$LD_LIBRARY_PATH_LIBS $MORELIBDIRS $stdlibs" # Get a unique list of library dependencies for this argument. unset VISITEDLIBS declare -A VISITEDLIBS recursivedeps "$arg" done exit 0