#!/usr/bin/perl -w
use strict;

#
# constants
#
my $ENTITY_PREFIX = "calc_";

#
# global variables
#
my %ENTITY;
my $BASE_ADDR = 0x08048000;

#
# parse_od
#
sub parse_od()
{
  open(FILE, '<', 'out/evil_magic/od') || die "$!";
  LOOP: while(<FILE>)
  {
    chomp;
    my @number = split / /;
    my $start = hex($number[1]);

    ($start == 0x08048080) || die "$start != 0x08048080";
    $ENTITY{'evil_magic_start'} = "0x08048080";

    my $ofs = $start - $BASE_ADDR;
    ($ofs == 0x80) || die "$ofs != 0x80";
    $ENTITY{"evil_magic_ofs_dec"} = $ofs;
    $ENTITY{"evil_magic_ofs_hex"} = sprintf('%#x', $ofs);

    last LOOP;
  }
  close FILE;
}

sub set_num($$)
{
  my $base = shift;
  my $num = shift;

  $ENTITY{$base . '_dec'} = $num;
  $ENTITY{$base . '_hex'} = sprintf('%#x', $num);
}

sub parse_readelf_load($$$)
{
  my $filename = shift;
  my $base = shift;
  my $nr_load = shift;

  my @number = split / +/;
  my $virtaddr = hex($number[3]);
  my $filesiz = hex($number[5]);
  my $end = $virtaddr + $filesiz;

  # flags may contain spaces, e.g. "R E"
  # this will confuse the hell out of split(), so we use substr instead
  my $align = hex(substr($_, 68));
  ($align == 4096) || die "$align != 4096 ($filename)";

  if ($nr_load == 1)
  {
    ($virtaddr == $BASE_ADDR) || die "$virtaddr != $BASE_ADDR ($filename)";
  }
  else
  {
    my $prev_end = $ENTITY{$base . '_end_dec'};
    my $diff = $virtaddr - $prev_end;
    $base .= $nr_load;
    set_num($base . '_diff', $diff);

    my $diff_align = $diff - $align;
    ($diff_align >= 0) || die "$diff_align < 0 ($filename)";
    set_num($base . '_diff_align', $diff_align);
  }

  set_num($base . '_virtaddr', $virtaddr);
  set_num($base . '_filesiz', $filesiz);
  set_num($base . '_end', $end);
  set_num($base . '_align', $align);

  my $ofs = $ENTITY{$base . '_ofs_dec'};
  if (defined($ofs))
  {
    my $codesize = $filesiz - $ofs;
    set_num($base . '_codesize', $codesize);

    my $bloat = int($codesize) / int($ENTITY{$base . '_size'});
    $ENTITY{$base . '_bloat'} = sprintf("%.3f", $bloat);
    $ENTITY{$base . '_bloat_percent'} = sprintf("%.0f", $bloat * 100);
  }
}

#
# parse_readelf
#
sub parse_readelf($)
{
  my $filename = shift;

  $filename =~ /\/([^\/]*)$/;
  my $base = $1;
  my $nr_load = 1;

  open(FILE, '<', $filename) || die "Can't open $filename: $!";
  LOOP: while(<FILE>)
  {
    chomp;
    if (m/\/nasm$/)
    {
      my @number = split / +/;
      $ENTITY{$base . '_size'} = $number[4];
    }
    if (m/^\s*LOAD\s*/)
    {
      parse_readelf_load($filename, $base, $nr_load++);
    }
  }
  close FILE;
}

#
# main
#
$ENTITY{'base_addr'} = sprintf('%#x', $BASE_ADDR);
parse_od();
parse_readelf('out/readelf/sh');
parse_readelf('out/readelf/evil_magic');

($ENTITY{'evil_magic_filesiz_dec'} == 0x99)
|| die 'evil_magic_filesiz != 0x99';

for my $name(keys(%ENTITY))
{
  printf "<!ENTITY %s%s \"%s\">\n", $ENTITY_PREFIX, $name, $ENTITY{$name};
}
