File indexing completed on 2024-10-06 08:10:26

0001 #!/bin/bash
0002 
0003 # Copyright 2013 Dominik Seichter <domseichter@googlemail.com>
0004 # 
0005 # This program is free software; you can redistribute it and/or
0006 # modify it under the terms of the GNU General Public License as
0007 # published by the Free Software Foundation; either version 2 of 
0008 # the License, or (at your option) any later version.
0009 # 
0010 # This program is distributed in the hope that it will be useful,
0011 # but WITHOUT ANY WARRANTY; without even the implied warranty of
0012 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013 # GNU General Public License for more details.
0014 # 
0015 # You should have received a copy of the GNU General Public License
0016 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
0017  
0018 # This is the maximum depth to which dependencies are resolved
0019 MAXDEPTH=14
0020 
0021 TMPFILE=
0022 READELFTMPFILE=
0023 LDDTMPFILE=
0024 
0025 cleanup() {
0026         for _v in TMPFILE READELFTMPFILE LDDTMPFILE; do
0027                 [ -n "${!_v}" ] && rm -f -- "${!_v}"
0028         done
0029 }
0030 
0031 trap cleanup EXIT
0032  
0033 # analyze a given file on its
0034 # dependecies using ldd and write
0035 # the results to a given temporary file
0036 #
0037 # Usage: analyze [OUTPUTFILE] [INPUTFILE]
0038 analyze()
0039 {
0040     local OUT="$1"
0041     local IN="$2"
0042     local NAME=$(basename "$IN")
0043  
0044     for i in $LIST
0045     do
0046         if [ X"$i" == X"$NAME" ];
0047         then
0048             # This file was already parsed
0049             return
0050         fi
0051     done
0052     # Put the file in the list of all files
0053     LIST="$LIST $NAME"
0054  
0055     DEPTH=$(($DEPTH + 1))
0056     if [ $DEPTH -ge $MAXDEPTH ];
0057         then
0058         echo "MAXDEPTH of $MAXDEPTH reached at file $IN."
0059         echo "Continuing with next file..."
0060         DEPTH=$(($DEPTH - 1))
0061         return
0062     fi
0063  
0064     echo "Parsing file:              $IN"
0065  
0066     $READELF "$IN" > "$READELFTMPFILE" 2>&1
0067     ELFRET=$?
0068  
0069     if [ $ELFRET != 0 ];
0070         then
0071         echo "ERROR: ELF reader returned error code $RET"
0072         echo "ERROR:"
0073         cat -v -- "$TMPFILE"
0074         echo "Aborting..."
0075         exit 1
0076     fi
0077  
0078     DEPENDENCIES=$(cat "$READELFTMPFILE" | grep NEEDED | awk '{if (substr($NF,1,1) == "[") print substr($NF, 2, length($NF) - 2); else print $NF}')
0079  
0080     for DEP in $DEPENDENCIES;
0081     do
0082         if [ -n "$DEP" ];
0083         then
0084  
0085             # XXX Change ldd to something more secure?
0086             ldd "$IN" > "$LDDTMPFILE" 2>&1
0087             LDDRET=$?
0088  
0089             if [ $LDDRET != 0 ];
0090                 then
0091                 echo "ERROR: ldd returned error code $RET"
0092                 echo "ERROR:"
0093                 cat -v -- "$TMPFILE"
0094                 echo "Aborting..."
0095                 exit 1
0096             fi
0097  
0098             DEPPATH=$(grep -- "$DEP" "$LDDTMPFILE" | awk '{print $3}')
0099             if [ -n "$DEPPATH" ];
0100             then
0101                 echo "  \"$NAME\" -> \"$DEP\";" >> $OUT
0102                 analyze "$OUT" "$DEPPATH"
0103             fi
0104         fi
0105     done
0106  
0107     DEPTH=$(($DEPTH - 1))
0108 }
0109  
0110 ########################################
0111 # main                                 #
0112 ########################################
0113  
0114 if [ $# != 2 ];
0115     then
0116     echo "Usage:"
0117     echo "  $0 [filename] [outputimage]"
0118     echo ""
0119     echo "This tools analyses a shared library or an executable"
0120     echo "and generates a dependency graph as an image."
0121     echo ""
0122     echo "GraphViz must be installed for this tool to work."
0123     echo ""
0124     exit 1
0125 fi
0126  
0127 DEPTH=0
0128 INPUT="$1"
0129 OUTPUT="$2"
0130 TMPFILE="$(mktemp -t)"
0131 LDDTMPFILE="$(mktemp -t)"
0132 READELFTMPFILE="$(mktemp -t)"
0133 LIST=""
0134  
0135 if [ ! -e "$INPUT" ];
0136     then
0137     echo "ERROR: File not found: $INPUT"
0138     echo "Aborting..."
0139     exit 2
0140 fi
0141  
0142 # Use either readelf or dump
0143 # Linux has readelf, Solaris has dump
0144 READELF=$(type readelf 2> /dev/null)
0145 if [ $? != 0 ]; then
0146   READELF=$(type dump 2> /dev/null)
0147   if [ $? != 0 ]; then
0148     echo Unable to find ELF reader
0149     exit 1
0150   fi
0151   READELF="dump -Lv"
0152 else
0153   READELF="readelf -d"
0154 fi
0155  
0156  
0157  
0158 echo "Analyzing dependencies of: $INPUT"
0159 echo "Creating output as:        $OUTPUT"
0160 echo ""
0161  
0162 echo "digraph DependencyTree {" > "$TMPFILE"
0163 echo "  \"$(basename $INPUT)\" [shape=box];" >> "$TMPFILE"
0164 analyze "$TMPFILE" "$INPUT"
0165 echo "}" >> "$TMPFILE"
0166  
0167 #cat $TMPFILE # output generated dotfile for debugging purposses
0168 dot -Tpng "$TMPFILE" "-o$OUTPUT"
0169  
0170 exit 0