#!/usr/bin/perl # Purpose: # takes an input executable and determine which unresolved symbols are # provided by each shared library. # # Author: # Don Bahls # # External Executables: # /usr/bin/file # /usr/bin/nm # /usr/bin/objdump # /usr/bin/ldd # /usr/bin/c++filt # # Changes Log: # 2008-03-18 DMB Initial version # 2008-03-20 DMB Added print out of symbols that are not found # ############################################################################## use strict; use File::Basename; use Getopt::Std; my %opts; if ( ! &getopts('th', \%opts) ) { &usage(); exit(1); } if ( @ARGV == 0 ) { &usage(); exit(1); } my $exe; my $line; foreach $exe (@ARGV) { print "Unresolved symbols for '$exe'\n"; if ( ! -f $exe ) { print STDERR "Error: cannot find $exe! Skipping...\n\n"; next; } my $size=0; my $file=`/usr/bin/file $exe`; if ( $file =~ /ELF 32-bit/ ) { $size=32; } # this is a 32 bit exe elsif ( $file =~ /ELF 64-bit/ ) { $size=64; } # this is a 64 bit exe else { print STDERR "Error: unknown executable type for '$exe'! Skipping...\n\n"; next; } my %syms=(); my %version=(); my @nm=`/usr/bin/nm $exe`; # do an nm on $exe to get unresolved symbols foreach $line (@nm) { my $ws=$size/4; # size of address for reg exp if ( $line =~ /^.{$ws} (.) (.*)@@(.*)$/ ) { if ( $1 eq "U" or $1 eq "u" ) { $syms{$2}=$2; $version{$2}=$3; } } elsif ( $line =~ /^.{$ws} (.) (.+)$/ ) { if ( $1 eq "U" or $1 eq "u" ) { $syms{$2}=$2; $version{$2}=$3; } } } my @so=(); my @ldd=`/usr/bin/ldd $exe`; foreach $line (@ldd) { if ( $line =~ /\s(\S+)\s\(.*\)/ ) { push(@so,$1); } } my $s; my $lib; my $ver; my %count; foreach $lib (@so) { my @objdump=`/usr/bin/objdump -T $lib 2> /dev/null`; foreach $line (@objdump) { chomp($line); my @val=split(/\s+/,$line); if ( &check_symbol( $syms{$val[6]}, $version{$val[6]}, @val) ) { $count{$val[6]}++; my $warning=""; if ( $count{$val[6]} > 1 ) { $warning=' (WARNING duplicate symbol found!)'; } if ( $opts{t} eq "" ) { print " + " . $val[6] . " found in " . $lib . "$warning\n"; } else { my $filt=`/usr/bin/c++filt ${val[6]}`; chomp($filt); print " + " . $filt . " found in " . $lib . "$warning\n"; } } } } print "\n"; foreach $s (sort { $a cmp $b } keys %syms) { print " - Unresolved Symbol $s\n" if ( $count{$s} == 0 ); } print "\n"; } sub check_symbol() { # $sym symbol name # $ver version string # @in output from objdump # my ($sym, $ver, @in)=@_; my $textsym=($in[3] eq ".text"); my $symnonnull=($in[6] ne "" ); my $passlookup=($sym eq $in[6] ); my $passversion=($ver eq $in[5] || $in[5] eq "Base" && $ver eq "" ); return ($textsym && $symnonnull && $passlookup && $passversion); } sub usage() { my $bn=&basename($0); print STDERR "Usage: $bn executable1 ...\n"; print STDERR " -h show help dialogue\n"; print STDERR " -t attempt to translate c++ symbols\n"; }