package Segment;

require Exporter;

@ISA = qw(Exporter);
@EXPORT_OK = qw(
  new
  page_size
  byte_order
  write_xml_elf_defaults
);

use strict;
use Misc();

my %PLATFORM_TEST = (
  'AddrSize' => 0, 'Offset' => 1, 'VirtAddr' => 2, 'Align' => 3,
  '_SC_PAGESIZE' => 4, 'LMSB' => 5
);
my %PLATFORM = (
  'alpha' => [ 64, '00000000', '120000000', '10000', '2000', 'L' ],
  'i386'  => [ 32, '00000000',  '08048000',  '1000', '1000', 'L' ],
  'sparc' => [ 32, '00000000',  '00010000', '10000', '1000', 'M' ]
);

sub page_size($)
{
  my $arch = shift;
  return $PLATFORM{$arch}[4];
}

sub byte_order($)
{
  my $arch = shift;
  return $PLATFORM{$arch}[5];
}

sub new
{
  my $proto = shift;
  my $class = ref($proto) || $proto;
  my $self  = {};

  $self->{Offset} = undef;
  $self->{VirtAddr} = undef;
  $self->{PhysAddr} = undef;
  $self->{FileSiz} = undef;
  $self->{MemSiz} = undef;
  $self->{Flags} = undef;
  $self->{Align} = undef;
  $self->{AddrSize} = undef;

  bless ($self, $class);
  return $self;
}

# parse output of readelf -l
# $line1 = line with LOAD statement
# $line2 = following line (required for 64 bit)
sub from_readelf($$$)
{
  my $self = shift;
  my $line1 = shift;
  my $line2 = shift;

  my @number = Misc::split_hex($line1);

  # for 32-bit the output looks like this:
  # LOAD 0x000000 0x08048000 0x08048000 0x7e414 0x7e414 R E 0x1000

  # for 64-bit the output looks like this:
  # LOAD           0x0000000000000000 0x0000000120000000 0x0000000120000000
  #                 0x00000000000a5140 0x00000000000a5140  R E    10000

  # in both cases the flags may contain spaces, e.g. "R E"
  # this will confuse the hell out of split(),
  # so we use substr instead

  $self->{TypeName} = $number[1];
  $self->{Offset} = $number[2];
  $self->{VirtAddr} = $number[3];
  $self->{PhysAddr} = $number[4];

  if ($#number == 4)
  { # this is 64-bit
    $self->{AddrSize} = 64;

    my @number = Misc::split_hex($line2);
    $self->{FileSiz} = $number[1];
    $self->{MemSiz} = $number[2];
    $self->{Flg} = substr($line2, 59, 3);
    $self->{Align} = substr($line2, 63);
  }
  elsif ($#number >= 8)
  { # this is 32-bit
    $self->{AddrSize} = 32;

    $self->{FileSiz} = $number[5];
    $self->{MemSiz} = $number[6];
    $self->{Flg} = substr($line1, 64, 3);
    $self->{Align} = substr($line1, 68);
  }
  else
  {
    return "Invalid number of fields.";
  }
  # unfortunately we can encounter value "0" and "0x0" 
  $self->{Align} =~ s/^0x//;
  return undef;
}

# parse output of objdump -l
# $line1 = line with LOAD statement
# $line2 = following line
sub from_objdump($$$)
{
  my $self = shift;
  my $line1 = shift;
  my $line2 = shift;

  my @number = Misc::split_hex($line1);

  # for 32-bit the output looks like this:
  # LOAD off    0x00000000 vaddr 0x08048000 paddr 0x08048000 align 2**12
  #      filesz 0x0007e414 memsz 0x0007e414 flags r-x

  return "Invalid number of fields ($#number)." if ($#number != 9);

  $self->{TypeName} = $number[1];
  $self->{Offset} = $number[3];
  $self->{VirtAddr} = $number[5];
  $self->{PhysAddr} = $number[7];
  my $align = $number[9];

  return "Can't parse alignment" if (!($align =~ s/^2\*\*//));
  $self->{Align} = sprintf('%x', 1 << $align);

  @number = Misc::split_hex($line2);
  $self->{FileSiz} = $number[2];
  $self->{MemSiz} = $number[4];
  $self->{Flg} = $number[6];

  return undef;
}

sub num_eq_by_strcmp($$)
{
  my $a = lc(shift());
  my $b = lc(shift());

  my $prefix = '';
  if ($a =~ s/^0x//)
  {
    return "$b not prefixed with 0x" if (!($b =~ s/^0x//));
    $prefix = '0x';
  }

  $a =~ s/^0+//;
  $b =~ s/^0+//;
  return "$prefix$a != $prefix$b" if ($a ne $b);
  return undef;
}

sub check_platform($$)
{
  my $self = shift;
  my $arch = shift;

  my $platform = $PLATFORM{$arch};
  return "No entry for architecture $arch in \%PLATFORM"
  if (!defined($platform));

  while( my ( $name, $index ) = each %PLATFORM_TEST )
  {
    my $a = $self->{$name};
    next if (!defined($a));
    # return "$name: $a != $b" if (lc($a) ne lc($$platform[$index]));

    my $msg = num_eq_by_strcmp($a, $$platform[$index]);
    return "$name: $msg" if (defined($msg));
  }
  return undef;
}

sub write_xml_elf_defaults($)
{
  my $FILE = shift;
  print $FILE <<BLOCK_1;
<tgroup cols="7">
  <colspec colname="platform" align="left"/>
  <colspec colname="address.size" align="right"/>
  <colspec colname="offset" align="right"/>
  <colspec colname="base.address" align="right"/>
  <colspec colname="alignment" align="right"/>
  <colspec colname="pagesize" align="right"/>
  <colspec colname="byte.order" align="center"/>
  <thead>
    <row>
      <entry>Platform</entry>
      <entry>Address size</entry>
      <entry>Offset</entry>
      <entry>Base address</entry>
      <entry>Alignment</entry>
      <entry>_SC_PAGESIZE</entry>
      <entry>Byte order</entry>
    </row>
  </thead>
  <tbody>
BLOCK_1

  my @key = sort(keys(%PLATFORM));
  for my $key(@key)
  {
    printf $FILE "  <row>\n";
    printf $FILE "    <entry><literal>%s</literal></entry>\n", $key;
    my $platform = $PLATFORM{$key};
    for my $value(@$platform)
    {
      printf $FILE "    <entry><literal>%s</literal></entry>\n", $value;
    }
    printf $FILE "  </row>\n";
  }
  printf $FILE "  </tbody>\n</tgroup>\n";
}

sub calc_gap($$)
{
  my $self = shift;
  my $after = shift;

  return (
    hex($after->{Offset}) - hex($self->{Offset}) - hex($self->{FileSiz}),
    hex($after->{VirtAddr}) - hex($self->{VirtAddr}) - hex($self->{MemSiz})
  ) ;
}

1;
