blob: e762bdafa59b4ab94477fcfec146fecf2cb991d7 [file] [log] [blame]
#!/usr/bin/perl -w
package arcsymbols;
use strict;
use warnings;
use List::Util qw[min max];
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw();
@EXPORT_OK = qw();
$VERSION = '0.01';
my $objdump = "/usr/local/ARC/gcc/bin/arc-linux-uclibc-objdump";
sub new {
my $package = shift;
my $bin_path = shift;
my $section_addrs_ref = shift;
my $self = {};
&load_symbols($self, $bin_path, $section_addrs_ref);
return bless($self, $package);
}
sub find_symbol {
my $self = shift;
my $addr = shift;
my $sym = $self->find_symbol_address_dumb($addr);
if ($sym) {
return $self->{symbols_ref}->{$sym};
} else {
return undef;
}
}
sub get_sorted_addrs_ref {
my $self = shift;
return $self->{sorted_symbol_keys_ref};
}
sub get_symbol_by_key {
my $self = shift;
my $addr = shift;
return $self->{symbols_ref}->{$addr};
}
sub find_section {
my $self = shift;
my $addr = shift;
foreach my $section_addr (@{$self->{sorted_section_keys_ref}}) {
my $offset = $addr - $section_addr;
my $section_size = $self->{sections_ref}->{$section_addr}->{size};
if ($offset >= 0 && $offset < $section_size) {
return $self->{sections_ref}->{$section_addr};
}
}
return undef;
}
sub fixfunc {
my ($self, $name, $offset, $size) = @_;
# find symbol, compare sizes; some functions are omitted in kallsyms
my $changed = 0;
my $sym = undef;
#warn sprintf "\t\t%s+%x/%x %s\n", $name, $offset, $size, $self->{bin_path};
if ($self->{symbol_names_ref}->{$name}) {
#warn sprintf "$name in $self->{bin_path}\n";
$sym = $self->{symbol_names_ref}->{$name};
my $objdsize = $sym->{size};
my $origsize = $size;
$size = $objdsize; # fix size regardless of oversize
my $reladdr = $sym->{addr};
if ($offset >= $size && $objdsize < $origsize) {
#warn sprintf "function: %s oversize (%x > %x) and nonzero offset (%x), addr %x+%x/%x (%x), rs %x\n", $name, $origsize, $objdsize, $offset, $reladdr, $offset, $size, $reladdr + $offset, $objdsize;
if ($self->{symbols_ref}->{$reladdr + $offset}) {
my $newname = $self->{symbols_ref}->{$reladdr + $offset}->{name};
my $newsize = $self->{symbols_ref}->{$reladdr + $offset}->{size};
warn sprintf "%s+0x%x/0x%x (realsize 0x%x) changed for %s+0x%x/0x%x, binary %s\n", $name, $offset, $size, $objdsize, $newname, 0, $newsize, $self->{bin_path};
#warn sprintf "found match: $name at %x\n", $reladdr + $offset;
$name = $newname;
$size = $newsize;
$offset = 0;
} else {
# create a new name to distinguish this, even though
# we don't know exactly what it is
$name = sprintf("%s+0x%x", $name, $objdsize);
}
}
$changed = 1;
}
$size = 1 if ($size == 0);
return ($name, $offset, $size, $changed == 1, $sym);
}
sub find_symbol_address_tree {
my ($self, $addr) = @_;
my $symbol_addr;
my @symbol_addrs = @{$self->{sorted_symbol_keys_ref}};
# binary search to find symbol
my $min = 0;
my $max = $#symbol_addrs;
while ($max >= $min) {
my $this = int(($max + $min) / 2);
my $sym = $symbol_addrs[$this];
#warn sprintf("\n%d %d %d %x %x", $min, $this, $max, $sym, $addr);
if ($sym == $addr) {
$symbol_addr = $sym;
#warn "$sym == $addr";
last;
} elsif ($sym > $addr) {
if ($max == $this) {
$max--;
} else {
$max = $this;
}
#warn sprintf("%x > %x", $sym, $addr);
} else {
my $symbol_size = $self->{symbols_ref}->{$sym}->{size};
my $offset = $addr - $sym;
if ($offset < $symbol_size) {
#warn "found, offset $offset < $symbol_size";
$symbol_addr = $sym;
last;
} else {
#warn sprintf("%x + %x < %x", $sym, $symbol_size, $addr);
if ($min == $this) {
$min++;
} else {
$min = $this;
}
}
}
}
return $symbol_addr;
}
sub find_symbol_address_dumb {
my ($self, $addr) = @_;
foreach my $sym (@{$self->{sorted_symbol_keys_ref}}) {
my $offset = $addr - $sym;
my $symbol_size = $self->{symbols_ref}->{$sym}->{size};
if ($offset >= 0 && $offset < $symbol_size) {
return $sym;
}
}
}
sub load_symbols {
my $self = shift;
my $bin_path = shift;
my $section_addrs_ref = shift;
my $is_module = defined($section_addrs_ref);
my %names;
my %data;
my %sections;
my %section_start_offsets;
die "Could not find binary: $bin_path\n" unless ( -e $bin_path );
foreach (`$objdump -ht $bin_path`) {
if (/^([\w\d]+)\s+\w?\s+\w?\s+([\.\w]+)\s+([\w\d]+)\s+([\.\w]+)\s*$/) {
my $addr = hex($1);
my $section = $2;
my $size = hex($3);
my $name = $4;
my $origaddr = $addr;
my $found = 1;
if ($is_module) {
if ($section eq $name) {
$section_start_offsets{$section} = $addr;
}
if ($section_addrs_ref->{$section}) {
$addr = $addr - $section_start_offsets{$section} + $section_addrs_ref->{$section};
} else {
$found = 0;
}
}
if ($found) {
if ($size > 0) {
$data{$addr}->{addr} = $addr;
$data{$addr}->{size} = $size;
$data{$addr}->{name} = $name;
$data{$addr}->{section} = $section;
#warn sprintf("parsed %x: $name $section $size\n", $addr);
}
$names{$name}->{addr} = $addr;
$names{$name}->{size} = $size;
$names{$name}->{section} = $section;
#warn sprintf ("Sym: 0x%x (orig 0x%x, section $section) $name\n", $addr, $origaddr);
}
}
elsif (/^\s*\d+\s+([\.\w]+)\s+([\d\w]+)\s+([\d\w]+)\s+([\d\w]+)\s+([\d\w]+)\s+\d*\**\d*\s*$/) {
my $addr = hex($3);
my $size = hex($2);
my $section = $1;
$sections{$addr}->{addr} = $addr;
$sections{$addr}->{size} = $size;
$sections{$addr}->{section} = $section;
}
}
my @sorted_symbol_keys = sort keys %data;
$self->{sorted_symbol_keys_ref} = \@sorted_symbol_keys;
$self->{symbols_ref} = \%data;
my @sorted_section_keys = sort keys %sections;
$self->{sorted_section_keys_ref} = \@sorted_section_keys;
$self->{sections_ref} = \%sections;
my @sorted_names = sort keys %names;
$self->{sorted_symbol_names_ref} = \@sorted_names;
$self->{symbol_names_ref} = \%names;
$self->{bin_path} = $bin_path;
}
1;