#!/usr/bin/env perl use strict; use IPC::Open2; if ($ARGV[0] eq '--version') { print "scandeps version 1.0\n"; print "Copright (c) 2005 Hongli Lai\n"; print "Licensed under the GNU General Public License v2.\n"; exit; } elsif ($ARGV[0] eq '--help') { print "Usage: scandeps [FILES...]\n"; print "This script scans ELF binaries' library dependancies and prints a nice report\n" . "of the dependancies.\n"; exit; } # First, generate a list of binaries. Either scan user-specified files, # or scan all files in the current directory (including subfolders). my @files; if (@ARGV > 0) { # Scan user-specified files @files = @ARGV; } else { # Scan directory recursively @files = scandir("."); } # Now get the dependancies for all binaries # %fileDeps: hash which contains a list of dependancies. The filename is the key. # %depFiles: hash which contains a list of files. The dependancy is the key. my (%fileDeps, %depFiles); getdeps(\%fileDeps, \%depFiles, \@files); print "Common dependancies:\n"; foreach my $dep (keys %depFiles) { if (@{$depFiles{$dep}} == @files) { print " $dep\n"; } } print "\n"; print "All dependancies:\n"; my @keys = keys %depFiles; @keys = sort { @{$depFiles{$b}} - @{$depFiles{$a}} } @keys; foreach my $dep (@keys) { my $num = scalar(@{$depFiles{$dep}}); my $word = ($num > 1) ? "files" : "file"; printf " %-40s (used by %3d $word)\n", $dep, $num; } print "\n"; print "Dependancies and associated binaries:\n"; foreach my $dep (@keys) { print " $dep:\n"; if (@{$depFiles{$dep}} == @files) { print " All files depend on this library.\n"; } else { foreach my $file (@{$depFiles{$dep}}) { print " $file\n"; } } print "\n"; } ################################## sub fatal { print STDERR $_[0]; exit 1; } sub isELF { my ($f, $data); if (!open($f, $_[0])) { print STDERR "Warning: cannot open file '$_[0]' for reading.\n"; return 0; } binmode $f; sysread $f, $data, 4; close $f; return $data eq "\177ELF"; } sub scandir { my $d; if (!opendir($d, $_[0])) { print STDERR "Warning: cannot open directory '$_[0]' for reading.\n"; return; } my @files; foreach my $file (readdir($d)) { next if ($file eq "." || $file eq ".."); $file = "$_[0]/$file"; if (-d $file) { # Recurse into subdirectory push @files, scandir("$file"); } elsif (-f $file && -x $file && isELF($file)) { push @files, $file; } } closedir($d); return @files; } sub getdeps { my ($fileDeps, $depFiles, $files) = @_; my ($r, $w); if (!open2($r, $w, 'objdump', '-p', @{$files})) { fatal("Cannot communicate with objdump.\n"); } close $w; my $currentFile; foreach my $line (<$r>) { if ($line =~ /^(.+):[ \t]+file format /) { $currentFile = $1; $currentFile =~ s/^.\///; } elsif ($line =~ /NEEDED[ \t]+(.+)$/) { $fileDeps->{$currentFile} = [] if (!exists $fileDeps->{$currentFile}); push @{$fileDeps->{$currentFile}}, $1; $depFiles->{$1} = [] if (!exists $depFiles->{$1}); push @{$depFiles->{$1}}, $currentFile; } } close $r; }