2016-12-21 15:08:47 +00:00
|
|
|
#!/usr/bin/env perl
|
|
|
|
use warnings;
|
|
|
|
use strict;
|
|
|
|
#
|
|
|
|
#
|
|
|
|
#
|
|
|
|
|
|
|
|
package Symbols;
|
|
|
|
use warnings;
|
|
|
|
use strict;
|
|
|
|
|
|
|
|
sub new($) {
|
|
|
|
my ($class) = @_;
|
|
|
|
my $self = {};
|
|
|
|
bless $self, $class;
|
|
|
|
|
|
|
|
return $self;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub add {
|
|
|
|
my $self = shift;
|
|
|
|
my $addr = shift;
|
|
|
|
my $name = shift;
|
|
|
|
|
2016-12-22 02:52:18 +00:00
|
|
|
die("bad symbol add $addr $name") if (!defined($name) or !defined($addr));
|
2016-12-21 15:30:02 +00:00
|
|
|
|
|
|
|
# dont overwrite an existing name
|
|
|
|
if (!defined($self->{a2n}{$addr})) {
|
|
|
|
$self->{a2n}{$addr} = $name;
|
|
|
|
}
|
2016-12-21 15:08:47 +00:00
|
|
|
return $self;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub lookup_addr {
|
|
|
|
my $self = shift;
|
|
|
|
my $addr = shift;
|
|
|
|
|
|
|
|
return $self->{a2n}{$addr};
|
|
|
|
}
|
|
|
|
|
|
|
|
sub all_addrs {
|
|
|
|
my $self = shift;
|
2016-12-22 05:15:25 +00:00
|
|
|
return sort {$a <=> $b} keys(%{$self->{a2n}});
|
2016-12-21 15:08:47 +00:00
|
|
|
}
|
|
|
|
|
2016-12-21 15:30:02 +00:00
|
|
|
sub addr2str {
|
|
|
|
my $self = shift;
|
|
|
|
my $addr = shift;
|
|
|
|
|
|
|
|
my $name = $self->lookup_addr($addr);
|
|
|
|
if (!defined($name)) {
|
|
|
|
$name = sprintf("sym_%08x",$addr);
|
|
|
|
}
|
|
|
|
return $name;
|
|
|
|
}
|
|
|
|
|
2016-12-21 15:08:47 +00:00
|
|
|
1;
|
|
|
|
|
2016-12-22 02:52:18 +00:00
|
|
|
package MemRegions;
|
|
|
|
use warnings;
|
|
|
|
use strict;
|
|
|
|
|
|
|
|
use IO::File;
|
2016-12-21 15:08:47 +00:00
|
|
|
|
|
|
|
my $debug = 0;
|
|
|
|
|
2016-12-22 02:52:18 +00:00
|
|
|
sub new($) {
|
|
|
|
my ($class) = @_;
|
|
|
|
my $self = {};
|
|
|
|
bless $self, $class;
|
2016-12-21 15:08:47 +00:00
|
|
|
|
2016-12-22 02:52:18 +00:00
|
|
|
return $self;
|
|
|
|
}
|
2016-12-21 15:08:47 +00:00
|
|
|
|
2016-12-22 02:52:18 +00:00
|
|
|
sub add {
|
|
|
|
my $self = shift;
|
2016-12-21 15:08:47 +00:00
|
|
|
my ($phys_addr, $size, $filename, $file_offset, $flags) = @_;
|
|
|
|
|
2016-12-22 02:52:18 +00:00
|
|
|
die("bad MemRegion add") if (
|
|
|
|
!defined($phys_addr) || !defined($size) ||
|
|
|
|
!defined($filename) || !defined($file_offset)
|
|
|
|
);
|
|
|
|
|
2016-12-21 15:08:47 +00:00
|
|
|
my $region;
|
|
|
|
$region->{phys_addr} = $phys_addr;
|
|
|
|
$region->{size} = $size;
|
|
|
|
$region->{filename} = $filename;
|
|
|
|
$region->{file_offset} = $file_offset;
|
|
|
|
$region->{flags} = $flags;
|
|
|
|
|
2016-12-22 02:52:18 +00:00
|
|
|
push @{$self->{region}}, $region;
|
2016-12-21 15:08:47 +00:00
|
|
|
|
|
|
|
if ($flags & 2) {
|
|
|
|
# anonymous memory has no file backing
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
my $fh = IO::File->new($filename, O_RDONLY);
|
|
|
|
if (!defined($fh)) {
|
|
|
|
warn("Could not open $filename\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
$region->{fh} = $fh;
|
|
|
|
}
|
|
|
|
|
2016-12-22 02:52:18 +00:00
|
|
|
sub _addr2region {
|
|
|
|
my $self = shift;
|
2016-12-21 15:08:47 +00:00
|
|
|
my $phys_addr = shift;
|
|
|
|
my $size = shift;
|
|
|
|
|
|
|
|
my $region;
|
|
|
|
# find the correct region
|
2016-12-22 02:52:18 +00:00
|
|
|
for my $r (@{$self->{region}}) {
|
2016-12-21 15:08:47 +00:00
|
|
|
if ($phys_addr >= $r->{phys_addr} && $phys_addr <= $r->{phys_addr}+$r->{size}) {
|
|
|
|
$region = $r;
|
|
|
|
last;
|
|
|
|
}
|
|
|
|
}
|
2016-12-22 02:52:18 +00:00
|
|
|
return $region;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub read {
|
|
|
|
my $self = shift;
|
|
|
|
my $phys_addr = shift;
|
|
|
|
my $size = shift;
|
2016-12-21 15:08:47 +00:00
|
|
|
|
2016-12-22 02:52:18 +00:00
|
|
|
my $region = $self->_addr2region($phys_addr,$size);
|
2016-12-21 15:08:47 +00:00
|
|
|
if (!defined($region)) {
|
|
|
|
printf("unhandled address 0x%08x(0x%x)\n",$phys_addr,$size);
|
|
|
|
return undef;
|
|
|
|
}
|
|
|
|
|
|
|
|
my $offset = $phys_addr - $region->{phys_addr};
|
|
|
|
$offset += $region->{file_offset};
|
|
|
|
|
|
|
|
if ($debug) {
|
|
|
|
printf("0x%08x(%x) = 0x%08x (%s)\n",
|
|
|
|
$phys_addr,$size,
|
|
|
|
$offset,
|
|
|
|
$region->{filename},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($region->{flags} & 2) {
|
|
|
|
# anonymous memory
|
|
|
|
return chr(0)x$size;
|
|
|
|
}
|
|
|
|
|
|
|
|
$region->{fh}->seek($offset,SEEK_SET);
|
|
|
|
my $buf;
|
|
|
|
$region->{fh}->read($buf,$size);
|
|
|
|
|
|
|
|
return $buf;
|
|
|
|
}
|
|
|
|
|
2017-01-06 15:31:12 +00:00
|
|
|
sub read_word {
|
|
|
|
my $self = shift;
|
|
|
|
my $phys_addr = shift;
|
|
|
|
my $size = shift;
|
|
|
|
|
|
|
|
my $formatstr;
|
|
|
|
if ($self->{endian} eq 'little') {
|
|
|
|
if ($size == 2) {
|
|
|
|
$formatstr = 'v';
|
|
|
|
} elsif ($size == 4) {
|
|
|
|
$formatstr = 'V';
|
|
|
|
} else {
|
|
|
|
...;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
# must be 'big'
|
|
|
|
if ($size == 2) {
|
|
|
|
$formatstr = 'n';
|
|
|
|
} elsif ($size == 4) {
|
|
|
|
$formatstr = 'N';
|
|
|
|
} else {
|
|
|
|
...;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
my $buf = $self->read($phys_addr,$size);
|
|
|
|
return undef if (length($buf) != $size);
|
|
|
|
|
|
|
|
return unpack($formatstr,$buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
sub set_endian {
|
|
|
|
my $self = shift;
|
|
|
|
my $endian = shift;
|
|
|
|
|
|
|
|
if ($endian eq 'little' || $endian eq 'big') {
|
|
|
|
$self->{endian} = $endian;
|
|
|
|
} else {
|
|
|
|
die ("bad endianness");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-22 03:01:40 +00:00
|
|
|
sub all_baseaddr {
|
|
|
|
my $self = shift;
|
|
|
|
my @starts;
|
|
|
|
for my $r (@{$self->{region}}) {
|
|
|
|
push @starts, $r->{phys_addr};
|
|
|
|
}
|
|
|
|
return @starts;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub region_size {
|
|
|
|
my $self = shift;
|
|
|
|
my $phys_addr = shift;
|
|
|
|
|
2017-01-06 13:17:50 +00:00
|
|
|
my $size = 1; # a fake size
|
|
|
|
my $region = $self->_addr2region($phys_addr,$size);
|
2016-12-22 03:01:40 +00:00
|
|
|
if (!defined($region)) {
|
|
|
|
return undef;
|
|
|
|
}
|
|
|
|
return $region->{size};
|
|
|
|
}
|
|
|
|
|
2016-12-22 05:15:02 +00:00
|
|
|
sub region_base {
|
|
|
|
my $self = shift;
|
|
|
|
my $phys_addr = shift;
|
|
|
|
|
2017-01-06 13:17:50 +00:00
|
|
|
my $size = 1; # a fake size
|
|
|
|
my $region = $self->_addr2region($phys_addr,$size);
|
2016-12-22 05:15:02 +00:00
|
|
|
if (!defined($region)) {
|
|
|
|
return undef;
|
|
|
|
}
|
|
|
|
return $region->{phys_addr};
|
|
|
|
}
|
|
|
|
|
2016-12-22 02:52:18 +00:00
|
|
|
1;
|
|
|
|
|
|
|
|
package main;
|
|
|
|
use IO::File;
|
|
|
|
|
|
|
|
use Data::Dumper;
|
|
|
|
$Data::Dumper::Indent = 1;
|
|
|
|
$Data::Dumper::Sortkeys = 1;
|
|
|
|
$Data::Dumper::Quotekeys = 0;
|
|
|
|
|
|
|
|
sub load_configfile {
|
|
|
|
my $db = shift;
|
|
|
|
my $filename = shift;
|
|
|
|
|
|
|
|
my $fh = IO::File->new($filename, O_RDONLY);
|
|
|
|
if (!defined($fh)) {
|
|
|
|
warn("Could not open $filename\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
while(<$fh>) {
|
|
|
|
chomp; s/\r//g;
|
|
|
|
|
|
|
|
# remove whitespace
|
|
|
|
s/^\s+//;
|
|
|
|
|
|
|
|
# remove comment lines
|
|
|
|
s/^[#].*//;
|
|
|
|
|
|
|
|
if (m/^include\s+(\S+)/) {
|
|
|
|
load_configfile($db,$1);
|
|
|
|
} elsif (m/^load_memory\s+/) {
|
|
|
|
my @a = split(/\s+/,$_);
|
|
|
|
$db->{regions}->add(
|
|
|
|
eval "$a[1]", eval "$a[2]", $a[3], eval "$a[4]", $a[5]
|
|
|
|
);
|
|
|
|
} elsif (m/^f\W+/) {
|
|
|
|
my @a = split(/\W+/,$_);
|
|
|
|
# 0 1 2 3
|
|
|
|
# f table.00021510 1 0x00021510
|
|
|
|
$db->{symbols}->add(eval "$a[3]", $a[1]);
|
2017-01-06 14:38:43 +00:00
|
|
|
} elsif (m/^sizes\W+/) {
|
2017-01-06 13:43:35 +00:00
|
|
|
my @a = split(/\W+/,$_);
|
2017-01-06 14:38:43 +00:00
|
|
|
$db->{sizes}{$a[1]} = $a[2];
|
2017-01-25 09:10:12 +00:00
|
|
|
} elsif (m/^option\W+/) {
|
|
|
|
my @a = split(/\W+/,$_);
|
|
|
|
$db->{option}{$a[1]} = $a[2];
|
2016-12-22 02:52:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-21 15:08:47 +00:00
|
|
|
sub validate_pointer {
|
|
|
|
my $db = shift;
|
|
|
|
my $val = shift;
|
|
|
|
|
2017-01-06 14:38:28 +00:00
|
|
|
# Check it is correctly aligned
|
|
|
|
# FIXME - this only works for binary values of alignment
|
|
|
|
if (($val & ($db->{sizes}{align}-1)) != 0) {
|
2016-12-21 15:08:47 +00:00
|
|
|
return undef;
|
|
|
|
}
|
|
|
|
|
2017-01-06 13:43:35 +00:00
|
|
|
if (!defined($db->{regions}->_addr2region($val,$db->{sizes}{ptr}))) {
|
2016-12-21 15:08:47 +00:00
|
|
|
return undef;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $val;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub find_pointers {
|
|
|
|
my $db = shift;
|
|
|
|
|
2016-12-22 03:01:40 +00:00
|
|
|
for my $start ($db->{regions}->all_baseaddr()) {
|
|
|
|
my $end = $start + $db->{regions}->region_size($start);
|
|
|
|
|
2016-12-22 05:06:59 +00:00
|
|
|
$db->{symbols}->add($start,sprintf("region_%08x",$start));
|
|
|
|
|
2016-12-22 03:01:40 +00:00
|
|
|
my $i = $start;
|
2017-01-06 15:16:44 +00:00
|
|
|
while (($i+$db->{sizes}{ptr}) <= $end ) {
|
2017-01-06 15:31:12 +00:00
|
|
|
my $val = $db->{regions}->read_word($i,$db->{sizes}{ptr});
|
2016-12-22 03:01:40 +00:00
|
|
|
if (validate_pointer($db,$val)) {
|
|
|
|
$db->{symbols}->add($val,sprintf("ptr_%08x",$val));
|
2016-12-21 15:08:47 +00:00
|
|
|
|
2016-12-22 03:01:40 +00:00
|
|
|
$db->{p}{src}{$i} = $val;
|
2017-01-06 13:17:50 +00:00
|
|
|
|
2017-01-06 13:43:35 +00:00
|
|
|
$i += $db->{sizes}{ptr};
|
2017-01-06 13:17:50 +00:00
|
|
|
} else {
|
2017-01-06 13:43:35 +00:00
|
|
|
$i += $db->{sizes}{align};
|
2016-12-22 03:01:40 +00:00
|
|
|
}
|
2016-12-21 15:08:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sub glom_objects {
|
|
|
|
my $db = shift;
|
|
|
|
my @addrs = $db->{symbols}->all_addrs();
|
|
|
|
|
|
|
|
while (@addrs) {
|
|
|
|
my $addr = shift @addrs;
|
|
|
|
my $object;
|
|
|
|
$object->{addr} = $addr;
|
2016-12-22 02:34:49 +00:00
|
|
|
$db->{p}{obj}{$addr} = $object;
|
2016-12-21 15:08:47 +00:00
|
|
|
|
|
|
|
my $next_addr = $addrs[0];
|
2016-12-22 05:15:02 +00:00
|
|
|
$object->{_}{next_addr} = $next_addr;
|
2016-12-21 15:08:47 +00:00
|
|
|
if (!defined($next_addr)) {
|
|
|
|
$next_addr = $addr;
|
|
|
|
}
|
2016-12-22 05:15:02 +00:00
|
|
|
|
|
|
|
my $region_base = $db->{regions}->region_base($addr);
|
|
|
|
$object->{_}{region_base} = $region_base;
|
|
|
|
# if we know nothing, then we cannot glom anything
|
|
|
|
if (!defined($region_base)) {
|
|
|
|
$object->{size} = "-1";
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
|
|
|
|
my $region_end = $region_base + $db->{regions}->region_size($addr);
|
|
|
|
$object->{_}{region_end} = $region_end;
|
|
|
|
if ($next_addr > $region_end) {
|
|
|
|
$next_addr = $region_end;
|
|
|
|
}
|
|
|
|
|
|
|
|
$object->{_}{glom_end} = $next_addr;
|
|
|
|
|
|
|
|
my $size = $next_addr - $addr;
|
2016-12-21 15:08:47 +00:00
|
|
|
$object->{size} = $size;
|
|
|
|
|
2016-12-22 02:34:49 +00:00
|
|
|
my $offset = 0;
|
|
|
|
while ($addr < $next_addr) {
|
2017-01-06 13:43:35 +00:00
|
|
|
# TODO - handle sizeof(word) != sizeof(ptr) and the addr offsets
|
2017-01-06 13:17:50 +00:00
|
|
|
# this will cause..
|
|
|
|
|
2016-12-22 02:34:49 +00:00
|
|
|
if (defined($db->{p}{src}{$addr})) {
|
|
|
|
$object->{p}{$offset} = $db->{p}{src}{$addr};
|
|
|
|
$object->{d}{$offset} = undef;
|
2017-01-06 13:43:35 +00:00
|
|
|
$addr += $db->{sizes}{ptr};
|
|
|
|
$offset += $db->{sizes}{ptr};
|
2016-12-22 02:34:49 +00:00
|
|
|
} else {
|
2017-01-06 15:31:12 +00:00
|
|
|
my $val = $db->{regions}->read_word($addr,$db->{sizes}{word});
|
2016-12-22 05:39:37 +00:00
|
|
|
$object->{d}{$offset} = $val;
|
2017-01-06 13:43:35 +00:00
|
|
|
$addr += $db->{sizes}{word};
|
|
|
|
$offset += $db->{sizes}{word};
|
2016-12-21 15:08:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sub output_dot {
|
|
|
|
my $db = shift;
|
|
|
|
|
|
|
|
print "digraph structs {\n";
|
2016-12-22 02:34:49 +00:00
|
|
|
print " rankdir=LR;\n";
|
|
|
|
print " node [shape=record];\n";
|
|
|
|
print "\n";
|
|
|
|
|
2016-12-22 05:15:25 +00:00
|
|
|
for my $addr (sort {$a <=> $b} keys(%{$db->{p}{obj}})) {
|
2016-12-21 15:08:47 +00:00
|
|
|
my $object = $db->{p}{obj}{$addr};
|
2016-12-21 15:30:02 +00:00
|
|
|
my $name = $db->{symbols}->addr2str($addr);
|
2016-12-21 15:08:47 +00:00
|
|
|
|
2016-12-22 02:34:49 +00:00
|
|
|
my @ports;
|
|
|
|
push @ports,"<p>$name:";
|
|
|
|
for my $offset (sort {$a <=> $b} keys(%{$object->{d}})) {
|
|
|
|
my $val = $object->{d}{$offset};
|
|
|
|
if (!defined($val)) {
|
|
|
|
# this is a pointer
|
|
|
|
my $dst = $object->{p}{$offset};
|
|
|
|
my $dstname = $db->{symbols}->addr2str($dst);
|
2016-12-22 05:08:27 +00:00
|
|
|
if ($dst != $addr) {
|
|
|
|
# dont draw a self reference arrow
|
|
|
|
printf(" %s:p%i -> %s:p;\n",$name,$offset,$dstname);
|
|
|
|
} else {
|
|
|
|
# but highlight it
|
|
|
|
$dstname .= "**";
|
|
|
|
}
|
2016-12-22 02:34:49 +00:00
|
|
|
push @ports, sprintf("<p%i>%s",$offset,$dstname);
|
|
|
|
} else {
|
2016-12-22 05:39:37 +00:00
|
|
|
my $valname = $db->{symbols}->lookup_addr($val);
|
|
|
|
if (!defined($valname)) {
|
|
|
|
$valname = sprintf("0x%08x",$val);
|
|
|
|
}
|
|
|
|
push @ports, sprintf("<p%i>%s",$offset,$valname);
|
2016-12-22 02:34:49 +00:00
|
|
|
}
|
2016-12-21 15:08:47 +00:00
|
|
|
}
|
2016-12-22 08:58:05 +00:00
|
|
|
|
|
|
|
# what is the maximum number of rows per object?
|
2017-01-25 09:10:12 +00:00
|
|
|
my $want_rows = $db->{option}{want_rows};
|
2016-12-22 08:58:05 +00:00
|
|
|
|
|
|
|
if ($want_rows < 2) {
|
|
|
|
@ports = $ports[0];
|
|
|
|
} elsif (scalar(@ports) > $want_rows) {
|
|
|
|
@ports = @ports[0..$want_rows-2];
|
|
|
|
push @ports, sprintf("...size=0x%x",$object->{size});
|
|
|
|
}
|
|
|
|
|
2016-12-22 02:34:49 +00:00
|
|
|
printf(" %s [label=\"%s\"]; // %i\n",
|
|
|
|
$name,
|
|
|
|
join("|",@ports),
|
|
|
|
$object->{size}
|
|
|
|
);
|
2016-12-21 15:08:47 +00:00
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
print "}\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
sub main() {
|
|
|
|
my $configfile = shift @ARGV;
|
|
|
|
if (!defined($configfile)) {
|
|
|
|
die('need configfile');
|
|
|
|
}
|
|
|
|
|
|
|
|
my $db = {};
|
2017-01-06 13:43:35 +00:00
|
|
|
|
|
|
|
# Default config values
|
|
|
|
$db->{sizes}{align} = 4;
|
|
|
|
$db->{sizes}{word} = 4;
|
|
|
|
$db->{sizes}{ptr} = 4;
|
2017-01-06 15:31:12 +00:00
|
|
|
$db->{sizes}{endian} = "little";
|
2017-01-25 09:10:12 +00:00
|
|
|
$db->{option}{want_rows} = 1;
|
2017-01-06 13:43:35 +00:00
|
|
|
|
2016-12-21 15:08:47 +00:00
|
|
|
$db->{symbols} = Symbols->new();
|
2016-12-22 02:52:18 +00:00
|
|
|
$db->{regions} = MemRegions->new();
|
2016-12-21 15:08:47 +00:00
|
|
|
|
|
|
|
load_configfile($db,$configfile);
|
|
|
|
|
2017-01-06 15:31:12 +00:00
|
|
|
$db->{regions}->set_endian($db->{sizes}{endian});
|
|
|
|
|
2016-12-21 15:08:47 +00:00
|
|
|
find_pointers($db);
|
|
|
|
glom_objects($db);
|
|
|
|
|
|
|
|
if (@ARGV) {
|
|
|
|
print Dumper($db);
|
|
|
|
}
|
|
|
|
|
|
|
|
output_dot($db);
|
|
|
|
}
|
|
|
|
main();
|
|
|
|
|