#!/usr/bin/perl -w # elfgrep_fixup : Fixes output from multiple runs of elfgrep # Copyright (C) 2002 Dion Mendel # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. # elfgrep determines the position (if any) of object files inside a # target elf binary file. When run multiple times over many object # files, there may be collisions. (A collision is when multiple # object files overlap each other in the target file). This program # searches for these collisions, resolves any if possible, and # marks unresolveable collisions for manual resolution. # # Sample usage: # % for i in obj/*.o ; do # elfgrep -t $i the-binary # done | elfgrep_fixup - # % use Getopt::Long "GetOptions"; use FileHandle; use strict 'vars'; use vars '$VERSION', '$Verbose'; $VERSION = "1.0"; # version of this program $Verbose = 1; my @no_conflicts = (); my @multiples = (); my @conflicts = (); &parse_command_line_for_options(); &usage if (scalar @ARGV != 1); my $filename = $ARGV[0]; # read data and sort into the three lists &read_data($filename, \@conflicts, \@no_conflicts, \@multiples); # add all non intersecting multi object to the conflicts list. foreach my $obj (@multiples) { unless (&intersects($obj, \@no_conflicts)) { &add_obj_to_conflicts($obj, \@conflicts); } } # attempt to resolve conflicts &resolve_conflicts(\@conflicts, \@no_conflicts); my $fh = STDOUT; &display_output($fh, \@conflicts, \@no_conflicts); exit 0; ############################### Output Functions ############################## # ----------------------------------------------------------------------------- # Displays the results of this program. # Params: $fh - file handle to display output to. # \@conflicts - list of objects which conflict and need resolution # \@no_conflicts - list of objects which do not conflict # Returns: none sub display_output($\@\@) { my ($fh, $ref_conflicts, $ref_no_conflicts) = @_; my ($obj, $listref, @sorted); print $fh <<"_HERE"; # # -- This is an automatically generated file. -- # # This file contains the results of using elfgrep to search a binary file # for contained object file. Manual resolution of the conflicts at the # end of this file may be necessary. # _HERE # sort no conflicts list according to starting offset @sorted = sort { $a->{start} <=> $b->{start} } @$ref_no_conflicts; # display non conflicting matches foreach $obj (@sorted) { printf $fh ("%s - match at 0x%08x (0x%08x bytes)\n", $obj->{name}, $obj->{start}, $obj->{size}); } # display conflicting matches foreach $listref (@conflicts) { printf $fh ("\n# Possible conflict below requiring manual resolution:\n"); printf $fh ("# ----------------------------------------------------\n"); foreach $obj (@$listref) { printf $fh ("# %s - match at 0x%08x (0x%08x bytes)\n", $obj->{name}, $obj->{start}, $obj->{size}); } } } ################################ Obj Functions ################################ # ----------------------------------------------------------------------------- # Creates a new object. An object is a representation of an object file # (.o file). # an object in the list which intersects with the given object. # Params: $name - name of object file # $start - starting offset of the object file # $size - size of the object file # Returns: reference to an object sub new_obj($$$) { my ($name, $start, $size) = @_; return { name => $name, start => $start, size => $size }; } # ----------------------------------------------------------------------------- # Returns whether the two given objects intersect with each other or not. # Params: $obj1, $obj2 objects to test # Returns: 1 if the objects intersect, 0 otherwise. sub objs_intersect($$) { my ($r1, $r2) = @_; my ($a, $b); # order so that the start of $a is before the start of $b if ($r1->{start} < $r2->{start}) { ($a, $b) = ($r1, $r2); } else { ($a, $b) = ($r2, $r1); } # intersection occurs if the start of $b is before the end of $a return ($b->{start} < ($a->{start} + $a->{size})); } # ----------------------------------------------------------------------------- # For the given object and a list of objects, return the first index of # an object in the list which intersects with the given object. # Params: $obj - object to check # \@list - list of objects # Returns: index of intersecting object, or -1 if there are no intersections. sub intersection_index($\@) { my ($obj, $ref_list) = @_; my ($i, $index); $index = -1; for ($i = 0; $i < scalar @$ref_list; $i++) { if (&objs_intersect($obj, $ref_list->[$i])) { $index = $i; last; } } return $index; } # ----------------------------------------------------------------------------- # For the given object and a list of objects, returns whether the given object # intersects with any of the objects in the list. # Params: $obj - object to check # \@list - list of objects # Returns: 0 if no intersections, 1 if there exists an intersection. sub intersects($\@) { my ($obj, $ref_list) = @_; return (&intersection_index($obj, $ref_list) != -1); } ############################## Conflicts Functions ############################ # Note: The conflicts list is not a flat list. It is an fact a list of lists, # where the inner lists contain objects which conflict with each other. # ----------------------------------------------------------------------------- # Counts the number of occurances of objects with the given name, in the # conflicts list. # Params: $name - name to search for # \@conflicts - list of objects which conflict and need resolution # Returns: number of occurances of $name in the conflicts list sub count_name_in_conflicts($\@) { my ($name, $ref_conflicts) = @_; my ($count, $listref, $obj); $count = 0; foreach $listref (@$ref_conflicts) { foreach $obj (@$listref) { if ($obj->{name} eq $name) { $count++; } } } return $count; } # ----------------------------------------------------------------------------- # Attempts to automatically resolve conflicts. If any single objects in # the conflicts list to not interfere with any in the no_conflicts list, # then the single object can be moved to the no_conflicts list. A single # object is one that only occurs once in the conflicts list, and doesn't # intersect with any other objects in the conflicts list. # Params: \@conflicts - list of objects which conflict and need resolution # \@no_conflicts - list of objects which do not conflict # Returns: none sub resolve_conflicts(\@\@) { my ($ref_conflicts, $ref_no_conflicts) = @_; my ($i, $index, $listref, $name, $obj, @pending); # for each inner list in the conflicts list for ($i = 0; $i < scalar @$ref_conflicts; $i++) { $listref = $ref_conflicts->[$i]; # if the inner list contains just one object .. if (scalar @$listref == 1) { # .. and the object name only occurs once in the conflicts list .. $name = $listref->[0]->{name}; if (&count_name_in_conflicts($name, $ref_conflicts) == 1) { # .. then that object is a candidate for the no conflicts list. # unshift so that pending list contain indexes in descending order unshift @pending, $i; } } } # now attempt to resolve conflicts foreach $index (@pending) { # delete an inner list (which contains just the one object to resolve) $listref = splice(@$ref_conflicts, $index, 1); $obj = $listref->[0]; # add the single object to the no conflicts list &add_obj_to_no_conflicts($obj, $ref_conflicts, $ref_no_conflicts); } } # ----------------------------------------------------------------------------- # Returns whether the given object intersects an object in the conflicts list. # Params: $obj - object to test # \@conflicts - list of objects which conflict and need resolution # Returns: 0 if no intersections, 1 if there exists an intersection. sub obj_intersects_conflicts($\@) { my ($obj, $ref_conflicts) = @_; my ($i, $intersects, $collection_ref); $intersects = 0; $collection_ref = (); for ($i = 0; $i < scalar @$ref_conflicts; $i++) { $collection_ref = $ref_conflicts->[$i]; if (&intersects($obj, $collection_ref)) { $intersects = 1; last; } } return $intersects; } # ----------------------------------------------------------------------------- # Adds the given object to the conflicts list. # Params: $obj - object to add # \@conflicts - list of objects which conflict and need resolution # Returns: none sub add_obj_to_conflicts($\@) { my ($obj) = shift @_; my ($ref_conflicts) = @_; my ($i, $collection_ref, $intersects); # search for an inner list that the object intersects with. $intersects = 0; $collection_ref = (); for ($i = 0; $i < scalar @$ref_conflicts; $i++) { $collection_ref = $ref_conflicts->[$i]; if (&intersects($obj, $collection_ref)) { $intersects = 1; last; } } if ($intersects) { # add object to this inner list push @$collection_ref, $obj; } else { # create a new inner list containing the object push @$ref_conflicts, [$obj]; } } ############################ No Conflicts Functions ########################### # ----------------------------------------------------------------------------- # Request to add the given object to the no conflict list. If the object # can not be added to the no conflict list without causing a conflict, then # it is added to the conflict list instead. # Params: $obj - object to add # \@conflicts - list of objects which conflict and need resolution # \@no_conflicts - list of objects which do not conflict # Returns: none sub add_obj_to_no_conflicts($\@\@) { my ($obj) = shift @_; my ($ref_conflicts, $ref_no_conflicts) = @_; my ($intersection_index, $existing_obj); # determine if the object intersects with an object in the no conflict list $intersection_index = &intersection_index($obj, $ref_no_conflicts); if ($intersection_index != -1) { # add the two conflicting objects to the conflicts list $existing_obj = $ref_no_conflicts->[$intersection_index]; &add_obj_to_conflicts($existing_obj, $ref_conflicts); &add_obj_to_conflicts($obj, $ref_conflicts); # remove offending object from no conflict list splice(@$ref_no_conflicts, $intersection_index, 1); } else { # no intersection with any objects in no conflicts list # if object intersects with an object in the conflict list, # then add it to the conflict list as well (to be resolved later). if (&obj_intersects_conflicts($obj, $ref_conflicts)) { &add_obj_to_conflicts($obj, $ref_conflicts); } else { # no conflicts anywhere, so safe to add to the no conflicts list push @$ref_no_conflicts, $obj; } } } ############################# Read Data Functions ############################# # ----------------------------------------------------------------------------- # Reads data regarding matching object files, and sorts the data into # three lists. # Params: $filename - filename containing data to read or '-' for stdin # \@conflicts - list to store objects which conflict and need resolution # \@no_conflicts - list to store objects which do not conflict # \@multiples - list to store objects with multiple matches # Returns: none sub read_data($\@\@\@) { my ($filename) = shift @_; my ($ref_conflicts, $ref_no_conflicts, $ref_multiples) = @_; my ($fh, $line, $obj); # open file, or just use stdin if filename is '-' if ($filename eq "-") { $fh = STDIN; } else { $fh = new FileHandle(); open $fh, "< $filename" or die "could not open file `$filename': $!"; } # for each line while ($line = <$fh>) { next if $line =~ /no matches/; # skip lines with no matches if ($line =~ /(.*) - match at (0x.*) \((0x.*) bytes\)/) { # add single object to no conflicts list $obj = { name => $1, start => oct($2), size => oct($3) }; &add_obj_to_no_conflicts($obj, $ref_conflicts, $ref_no_conflicts); } elsif ($line =~ /(.*) - match \d+ at (0x.*) \((0x.*) bytes\)/) { # add multiple objects to multiples list $obj = { name => $1, start => oct($2), size => oct($3) }; push @$ref_multiples, $obj; } else { die "bad input `$line'"; } } # close file handle unless reading from stdin close $fh unless ($filename eq "-"); } ############################### Usage Functions ############################### # ----------------------------------------------------------------------------- # Parses the command line for any specified options. Sets the appropriate # option flags if options are specified. Prints usage info if invalid options # are given. # Returns: nothing sub parse_command_line_for_options() { my ($want_quiet) = 0; my ($want_version) = 0; my ($want_help) = 0; &GetOptions("q|quiet" => \$want_quiet, "V|version" => \$want_version, "h|help" => \$want_help, ); if ($want_version) { print "$0 $VERSION\n"; exit 0; } if ($want_help) { &usage(); } $Verbose = !$want_quiet; } # ----------------------------------------------------------------------------- # Prints a nice usage message to stdout, and then exits. sub usage() { print <<"_END"; $0 v${VERSION} A filter to process the output of multiple runs of elfgrep. All data chunks are checked for intersections, and any intersecting chunks are commented out for manual resolution. Usage: $0 [options] [file_name] filename is the name of the file containing data to process, or - for stdin Options: -V, --version outputs version information and exits -h, --help displays this help and exits _END exit 1; }