#!/usr/bin/perl
# test_x86dis.pl
# ... actually this is a general test utility for libdisasm; however, it
# is also useful for comparing the AT&T syntax output of x86dis (which is
# generated by libdisasm) against what GNU as(1) expects.
# NOTE: this script expects to be run from $LIBDISASM/libdisasm/test
# and uses the instructions in ia32_test_insn.S to test disassembly.
use warnings;
use strict;

# assembler settings
my $asm_file = 'ia32_test_insn.S';
my $obj_file = '/tmp/ia32.o';
my $text_off = 0x34;	# hopefully this won't change :)
my $text_size = 1986;	# same
my $as = `which as`;

# relative path, ld_library_path for running x86dis from compile dir
my $x86dis = '../x86dis/x86dis';
# path is not needed now that libtool is being used
#my $ldpath = 'LD_LIBRARY_PATH="../libdisasm"';
my $ldpath = '';

my $x86dis_opt_str = "-f $obj_file -s att -r %d %d";

# uninitialized stuff
my ($line, @asm_lines, $x86dis_opts, $output, @disasm_lines, @bytes, $i);
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$jnk);
my ($a_mnem, $d_mnem, $a_op, $d_op);

print "## open asm file \n";
open ASMFILE, $asm_file || die "Cannot open $asm_file : $!\n";
foreach (<ASMFILE>) {
	chomp;
	next if /^#/;		# skip comment lines
	$line = $_;
	$line =~ s/\s+/ /g;
	$line =~ s/\s+$//;
	$line =~ s/#.*//;
	$line = lc $line;
	push @asm_lines, $line;
}

$as =~ s/\n//g;
print "## assemble asm file: $as -o $obj_file $asm_file\n";
system "$as -o $obj_file $asm_file";
exit(1) if ($? != 0);

print "## stat obj file $obj_file\n";
($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$jnk) = stat $obj_file;
#$x86dis_opts = sprintf $x86dis_opt_str, $text_off, $size;
$x86dis_opts = sprintf $x86dis_opt_str, $text_off, $text_size;

print "## disasm obj file: $ldpath $x86dis $x86dis_opts\n";
$output = `$ldpath $x86dis $x86dis_opts`;
exit(1) if ($? != 0);

foreach ( split /\n/, $output ) {
	chomp;
	next if /^#/;		# skip comment lines
	$line = $_;
	$line =~ s/\s+/ /g;
	$line =~ s/\s+$//;
	$line =~ s/#.*//;
	$line = lc $line;
	$line =~ s/^[0-9][0-9a-f]+\s+(([0-9a-f]{2} )+)//;
	push @bytes, $1;
	push @disasm_lines, $line;
}

my $status = 0;
for ( $i = 0; $i <= $#disasm_lines and $i <= $#asm_lines; $i = $i + 1 ) {
	if ( $disasm_lines[$i] ne $asm_lines[$i] ) {
		# check if this is a jump or call, in which case
		# the targets will be different addresses
		($a_mnem, $a_op, $jnk) = split / /, $asm_lines[$i];
		($d_mnem, $d_op, $jnk) = split / /, $disasm_lines[$i];
		if ( $a_mnem eq $d_mnem and
		     ($d_mnem =~ /^j/ or $d_mnem =~/call/ or
		      $d_mnem =~ /loop/ ) and $a_op =~ /^[-0-9]+/ ){
		     next;
		}

		print "ERROR: orig '$asm_lines[$i]'\n";
		print "       dis  '$disasm_lines[$i]' BYTES $bytes[$i]\n";
		$status = 1;
	}
}

exit($status);


syntax highlighted by Code2HTML, v. 0.9.1