#!/usr/bin/perl # mfdecode.pl Decode mudflap reports # # $Author: deece $ # $Date: 2003/04/10 06:36:47 $ Note: this time is UTC # $Revision: 1.1.2.3 $ # $Source: /cvsroot/c/top/unix/home/mfdecode.pl,v $ # $Name: $ # Config $gdb = '/usr/bin/gdb'; $tmpDir = '/tmp'; %cache; %dllCache; $doneExecutable; # Convert a hex address in a dll to a line number # Param 1: the filename # Param 2: the offset in hex sub convertDLL { my $filename = $_[0]; my $function = $_[1]; my $fnOffset = $_[2]; my $offset = $_[3]; my $tmp = time(); if (-r $filename) { # Construct the command file open(COMMAND, ">$tmpDir/mfdecode.$tmp") or die("Could not open $tmpDir/mfdecode.$tmp for writing."); print(COMMAND "info address $function\nquit\n"); close(COMMAND); my $data = `echo 'quit' | $gdb -q -x $tmpDir/mfdecode.$tmp $filename 2> /dev/null`; unlink("$tmpDir/mfdecode.$tmp"); chomp($data); # get the function address if (($data =~ /is a function at address (0x[0-9a-f]+)\./) || !$function) { my $dstFnOffset; if ($fnOffset) { $dstFnOffset = sprintf('0x%x', hex($fnOffset) + hex($1)); } elsif ($dllCache{$filename}) { $dstFnOffset = sprintf('0x%x', hex($offset) - $dllCache{$filename}); } else { return convertExecutable($filename, $offset); } # Cache the base address of the dll if (!$dllCache{$filename}) { $dllCache{$filename} = hex($offset) - hex($dstFnOffset); } # Construct the command file open(COMMAND, ">$tmpDir/mfdecode.$tmp") or die("Could not open $tmpDir/mfdecode.$tmp for writing."); print(COMMAND "set listsize 0\n"); print(COMMAND "list *$dstFnOffset\n"); print(COMMAND "quit\n"); close(COMMAND); my $data = `echo 'quit' | $gdb -q -x $tmpDir/mfdecode.$tmp $filename 2> /dev/null`; unlink("$tmpDir/mfdecode.$tmp"); if ($data =~ /\((.+)\)/) { if ($1 eq 'gdb') { return 'No source file for address'; } return $1; } return 'Invalid gdb result'; } else { # Couldn't find the function, just use the offset return convertDLL($filename, '', '', $offset); } } return "** File '$filename' not readable **"; } # Convert a hex address in an executable to a line number # Param 1: the filename # Param 2: the offset in hex sub convertExecutable { my $filename = $_[0]; my $offset = $_[1]; my $tmp = time(); if ($doneExecutable) { return 'Could not resolve symbol'; } $doneExecutable = 1; if (-r $filename) { # Construct the command file open(COMMAND, ">$tmpDir/mfdecode.$tmp") or die("Could not open $tmpDir/mfdecode.$tmp for writing."); print(COMMAND "set listsize 0\nlist *$offset\nquit\n"); close(COMMAND); my $data = `echo 'quit' | $gdb -q -x $tmpDir/mfdecode.$tmp $filename 2> /dev/null`; unlink("$tmpDir/mfdecode.$tmp"); if ($data =~ /\((.+)\)/) { if ($1 eq 'gdb') { # No info from GDB, perhaps its a dll? return convertDLL($filename, '', '', $offset); } return $1; } return 'Invalid gdb result'; } return "** File '$filename' not readable **"; } # Program main while ($line = ) { $doneExecutable = 0; chomp($line); # Flush the cache if we're starting a new report # if ($line =~ /^mudflap violation 1 /) { # %cache = (); # %dllCache = (); # } # Skip the mudflap line if ($line =~ /libmudflap[.]so/) { print $line, "\n"; } # DLLs elsif ($line =~ /^([^(]+)\((.+)\+(.+)\) \[(0x.+)\]$/) { $file = $1; $function = $2; $fnOffset = $3; $offset = $4; chomp($file); $file =~ s/\s*//; if ($cache{$line}) { $lineNum = $cache{$line}; } else { $lineNum = convertDLL($file, $function, $fnOffset, $offset); $cache{$line} = $lineNum; } print $line; print " {$lineNum}\n"; } # Executables elsif ($line =~ /^(.+) \[(0x.+)\]$/) { $file = $1; $offset = $2; chomp($file); $file =~ s/\s*//; if ($cache{$line}) { $lineNum = $cache{$line}; } else { $lineNum = convertExecutable($file, $offset); if ($lineNum ne 'Could not resolve symbol') { $cache{$line} = $lineNum; } } print $line; print " {$lineNum}\n"; } # Default else { print $line, "\n"; } }