package GetElfBase;

require Exporter;

@ISA = qw(Exporter);
@EXPORT_OK = qw( get_elf_base );

use strict;
use Misc();
use Entity qw( %ENTITY );
use Segment();

#
# readelf_segments
#
sub readelf_segments($$$)
{
  my $line = shift;
  my $base = shift;
  my $filename = shift;

  my $elfbase;
  for(my $i = 0; $i < $#$line; $i++)
  {
    $_ = $$line[$i];
    if (m/^\s*LOAD\s+/)
    {
      my $segment = Segment->new();
      my $msg = $segment->from_readelf($_, $$line[++$i]);
      return $msg if (defined($msg));

      Entity::check_eq 'ELF_BASE', $segment->{VirtAddr};
      Entity::check_eq 'ELF_ALIGN', $segment->{Align};
      Entity::check_eq 'ELF_ADDR_SIZE', $segment->{AddrSize};

      # assume first LOAD segment is code
      return undef;
    }
  }
  return 'returned no result.';
}

#
# objdump_segments
#
sub objdump_segments($$$)
{
  my $line = shift;
  my $base = shift;
  my $filename = shift;

  for(my $i = 0; $i < $#$line; $i++)
  {
    $_ = $$line[$i];
    if (m/\s+file format elf([36][24])-/)
    {
      $ENTITY{'ELF_ADDR_SIZE'} = $1;
      for my $c('Ehdr', 'Phdr', 'Shdr', 'Off', 'Addr')
      {
	$ENTITY{'ELF_' . uc($c)} = 'Elf' . $1 . '_' . $c;
      }
      next;
    }
    if (m/^\s*LOAD\s+/)
    {
      my $segment = Segment->new();
      my $msg = $segment->from_objdump($_, $$line[++$i]);
      return $msg if (defined($msg));

      my $arch = $ENTITY{'ARCH'} || die;
      $msg = $segment->check_platform($arch);
      return $msg if (defined($msg));

      $ENTITY{'ELF_PAGE_SIZE'} = Segment::page_size($arch);
      $ENTITY{'BYTE_ORDER'} = Segment::byte_order($arch);

      $ENTITY{'ELF_BASE'} = $segment->{VirtAddr} || die;
      $ENTITY{'ELF_ALIGN'} = $segment->{Align} || die;

      my $elfmagic = $segment->{VirtAddr};
      return "Last digit of ELF base address ($elfmagic) is not zero"
      unless ($elfmagic =~ s/0$/1/);
      $ENTITY{'ELF_MAGIC'} = $elfmagic;

      # assume first LOAD segment is code
      return undef;
    }
  }
  return 'returned no result.';
}

#
# get_elf_base
#
sub get_elf_base($;$)
{
  my $objdump = shift;
  my $readelf = shift;

  Misc::parse_file __PACKAGE__ . '::objdump_segments',
    "$objdump -p /bin/sh |";

  return if (!defined($readelf));
  Misc::parse_file __PACKAGE__ . '::readelf_segments',
    "$readelf -l /bin/sh |";
}
