/*
JPC: An x86 PC Hardware Emulator for a pure Java Virtual Machine
Copyright (C) 2012-2013 Ian Preston
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as published by
the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Details (including contact information) can be found at:
jpc.sourceforge.net
or the developer website
sourceforge.net/projects/jpc/
End of licence header
*/
package tools;
import java.io.*;
import java.util.*;
import java.lang.reflect.*;
public class Comparison
{
public static String[] names = new String[]
{
"eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi","eip", "flags",
/*10*/"es", "cs", "ss", "ds", "fs", "gs", "ticks",
/*17*/"es-lim", "cs-lim", "ss-lim", "ds-lim", "fs-lim", "gs-lim", "cs-prop",
/*24*/"gdtrbase", "gdtr-lim", "idtrbase", "idtr-lim", "ldtrbase", "ldtr-lim",
/*30*/"es-base", "cs-base", "ss-base", "ds-base", "fs-base", "gs-base",
/*36*/"cr0",
/*37*/"ST0H", "ST0L","ST1H", "ST1L","ST2H", "ST2L","ST3H", "ST3L",
/*45*/"ST4H", "ST4L","ST5H", "ST5L","ST6H", "ST6L","ST7H", "ST7L"
//"expiry"
};
static String newJar = "JPCApplication.jar";
static String debugJar = "JPCDebugger.jar";
public static final int flagMask = ~0;
public static final boolean compareFlags = true;
public static final String[] perf = {"-fda", "floppy.img", "-boot", "fda", "-hda", "dir:dos"};
public static final String[] doom = {"-fda", "floppy.img", "-boot", "fda", "-hda", "../../tmpdrives/doom10m.img"};
public static final String[] doom2 = {"-fda", "floppy.img", "-boot", "fda", "-hda", "../../tmpdrives/doom2.img"};
public static final String[] prince1 = {"-fda", "floppy.img", "-boot", "fda", "-hda", "../../tmpdrives/prince1.img"};
public static final String[] pascalcrash = {"-fda", "floppy.img", "-boot", "fda", "-hda", "tests/CRASHES.img"};
public static final String[] worms = {"-fda", "floppy.img", "-boot", "fda", "-hda", "worms.img"};
public static final String[] war2 = {"-fda", "floppy.img", "-boot", "fda", "-hda", "war2demo.img"};
public static final String[] linux = {"-hda", "../../tmpdrives/linux.img", "-boot", "hda"};
public static final String[] bsd = {"-hda", "../../tmpdrives/netbsd.img", "-boot", "hda"};
public static final String[] mosa = {"-hda", "mosa-project.img", "-boot", "hda"};
public static final String[] dsl = {"-hda", "dsl-desktop-demo2.img", "-boot", "hda"};
public static final String[] isolinux = {"-cdrom", "isolinux.iso", "-boot", "cdrom"};
public static final String[] dslCD = {"-cdrom", "../../tmpdrives/dsl-n-01RC4.iso", "-boot", "cdrom"};
public static final String[] hurd = {"-cdrom", "hurd.iso", "-boot", "cdrom"};
public static final String[] tty = {"-cdrom", "ttylinux-i386-5.3.iso", "-boot", "cdrom"};
public static final String[] win311 = {"-hda", "../../tmpdrives/win311.img", "-boot", "hda"};
public static final String[] win98 = {"-hda", "caching:../../tmpdrives/win98harddisk.img", "-boot", "hda", "-ips", "1193181",
"-max-block-size", "1", "-start-time", "1370072774000", "-no-screen"};
public static final String[] wolf3d = {"-hda", "WOLF3D.img", "-boot", "hda", "-fda", "floppy.img", "-ips", "1193181"};
public static final Map<String, String[]> possibleArgs = new HashMap();
static {
possibleArgs.put("win98", win98);
}
public static void main(String[] args) throws Exception
{
boolean mem = false;
if ((args.length >0) && args[0].equals("-mem"))
{
mem = true;
String[] temp = new String[args.length];
System.arraycopy(args, 1, temp, 0, temp.length-1);
temp[temp.length-1] = "-track-writes"; // Force JPC Physical memory to track dirty pages
args = temp;
}
String[] pcargs = possibleArgs.get(args[0]);
EmulatorControl disciple = new JPCDebuggerControl(debugJar, pcargs);
EmulatorControl oracle = new JPCControl(newJar, pcargs, true, false);
byte[] sdata1 = new byte[4096];
byte[] sdata2 = new byte[4096];
while (true)
{
String line = oracle.executeInstruction();
disciple.executeInstruction();
int[] fast = oracle.getState();
int[] old = disciple.getState();
if (fast[16] % 0x1000000 == 0)
System.out.printf("Reached %x ticks!", fast[16]);
if (history[historyIndex] == null)
history[historyIndex] = new Object[3];
history[historyIndex][0] = fast;
history[historyIndex][1] = old;
history[historyIndex][2] = line;
historyIndex = (historyIndex+1)%history.length;
// if (fast[16] == 0x1B3E656)
// System.out.println("Here comes the bug!");
if (fast[8] + fast[31] == 0x80147130)
System.out.printf("80147130, ticks = %08x\n", fast[16]);
Set<Integer> diff = new HashSet<Integer>();
if (!sameStates(fast, old, compareFlags, diff))
{
printHistory();
System.exit(0);
}
if (!mem)
continue;
Set<Integer> dirtyPages = new HashSet<Integer>();
//dirty1.invoke(newpc, dirtyPages);
// dirty2.invoke(oldpc, dirtyPages);
//for (int i=0; i < 2*1024; i++)
// dirtyPages.add(i);
if (dirtyPages.size() > 0)
{
System.out.printf("Comparing");
for (int i: dirtyPages)
System.out.printf(" %08x", i << 12);
System.out.println(" after " + previousInstruction());
}
for (int i : dirtyPages)
{
Integer l1 = oracle.getPhysicalPage(i << 12, sdata1);
Integer l2 = disciple.getPhysicalPage(i << 12, sdata2);
if (l2 > 0)
if (!samePage(i, sdata1, sdata2, null))
{
printHistory();
System.out.println("Error here... look above");
printPage(sdata1, sdata2, i << 12);
System.exit(0);
}
}
}
}
private static void compareStacks(int espPageIndex, int esp, Method save1, Object newpc, byte[] sdata1, Method save2, Object oldpc,byte[] sdata2, boolean pm, Method load1) throws Exception
{
Integer sl1 = (Integer)save1.invoke(newpc, new Integer(espPageIndex), sdata1, pm);
Integer sl2 = (Integer)save2.invoke(oldpc, new Integer(espPageIndex), sdata2, pm);
List<Integer> addrs = new ArrayList();
if (sl2 > 0)
if (!samePage(espPageIndex, sdata1, sdata2, addrs))
{
int addr = addrs.get(0);
if ((addrs.size() == 1) && ((sdata1[addr]^sdata2[addr]) == 0x10))
{ // ignore differences from pushing different AF to stack
System.out.println("ignoring different AF on stack...");
load1.invoke(newpc, new Integer(espPageIndex), sdata2, pm);
}
else
{
printHistory();
System.out.println("Error here... look above");
printPage(sdata1, sdata2, esp);
load1.invoke(newpc, new Integer(espPageIndex), sdata2, pm);
}
}
}
private static String previousInstruction()
{
Object[] prev = history[(((historyIndex-2)%history.length) + history.length) % history.length];
if (prev == null)
return "null";
return (String)prev[2];
}
static Object[][] history = new Object[32][];
static int historyIndex=0;
private static void printHistory()
{
printState(history[historyIndex]);
int end = historyIndex;
for (int j = (end+1)%history.length; j != end ; j = (j+1)%history.length)
{
printState(history[j]);
}
}
private static void printState(Object s)
{
if (s == null)
return;
Object[] sarr = (Object[]) s;
int[] fast = (int[]) sarr[0];
int[] old = (int[]) sarr[1];
String line = (String) sarr[2];
System.out.println("JPC Application:");
Fuzzer.printState(fast);
System.out.println("JPC Debugger:");
Fuzzer.printState(old);
System.out.println(line);
}
public static void printPage(byte[] fast, byte[] old, int esp)
{
int address = esp&0xfffff000;
// print page
for (int i=0; i < 1 << 8; i++)
{
int v1 = getInt(fast, 16*i);
int v2 = getInt(fast, 16*i+4);
int v3 = getInt(fast, 16*i+8);
int v4 = getInt(fast, 16*i+12);
int r1 = getInt(old, 16*i);
int r2 = getInt(old, 16*i+4);
int r3 = getInt(old, 16*i+8);
int r4 = getInt(old, 16*i+12);
System.out.printf("0x%8x: %8x %8x %8x %8x -- %8x %8x %8x %8x ==== ", address + 16*i, v1, v2, v3, v4, r1, r2, r3, r4);
printIntChars(v1, r1);
printIntChars(v2, r2);
printIntChars(v3, r3);
printIntChars(v4, r4);
System.out.print(" -- ");
printIntChars(r1, v1);
printIntChars(r2, v2);
printIntChars(r3, v3);
printIntChars(r4, v4);
System.out.println();
}
// print differences
for (int i =0; i < 1<< 12; i++)
{
byte b1 = fast[i];
byte b2 = old[i];
if (b1 != b2)
{
System.out.println("Memory difference at 0x" + Integer.toHexString(address+i) + ", values: " + Integer.toHexString(b1 & 0xff) + " " + Integer.toHexString(b2 & 0xff));
}
}
}
public static int getInt(byte[] data, int offset)
{
return data[offset] & 0xff | ((data[offset+1] & 0xff) << 8) | ((data[offset+2] & 0xff) << 16) | ((data[offset+3] & 0xff) << 24);
}
public static void printIntChars(int i, int c)
{
int[] ia = new int[] {(i & 0xFF), ((i >> 8) & 0xFF), ((i >> 16) & 0xFF), ((i >> 24) & 0xFF)};
int[] ca = new int[] {(c & 0xFF), ((c >> 8) & 0xFF), ((c >> 16) & 0xFF), ((c >> 24) & 0xFF)};
for (int a = 0; a < 4; a++)
if (ia[a] == ca[a])
System.out.printf("%c", (ia[a] == 0 ? ' ' : (char)ia[a]));
else
System.out.printf("\u001b[1;44m%c\u001b[1;49m", (ia[a] == 0 ? ' ' : (char)ia[a]));
System.out.printf(" ");
}
public static boolean samePage(int index, byte[] fast, byte[] old, List<Integer> addrs)
{
if (fast.length != old.length)
throw new IllegalStateException(String.format("different page data lengths %d != %d", fast.length, old.length));
for (int i=0; i < fast.length; i++)
if (fast[i] != old[i])
{
if (addrs!= null)
addrs.add(i);
System.out.printf("Difference in memory state: %08x=> %02x - %02x\n", index*4096+i, fast[i], old[i]);
return false;
}
return true;
}
public static boolean sameStates(int[] fast, int[] old, boolean compareFlags, Set<Integer> diff)
{
if (fast.length != names.length)
throw new IllegalArgumentException(String.format("new state length: %d != %d",fast.length, names.length));
if (old.length != names.length)
throw new IllegalArgumentException("old state length = "+old.length);
boolean same = true;
for (int i=0; i < fast.length; i++)
if (i != 9)
{
if (fast[i] != old[i])
{
diff.add(i);
same = false;
}
}
else
{
if (compareFlags && ((fast[i]&flagMask) != (old[i]&flagMask)))
{
if (same)
{
same = false;
diff.add(i);
}
}
}
return same;
}
public static boolean continueExecution(String state)
{
System.out.println("Adopt "+state+"? (y/n)");
String line = null;
try {
line = new BufferedReader(new InputStreamReader(System.in)).readLine();
} catch (IOException f)
{
f.printStackTrace();
System.exit(0);
}
if (line.equals("y"))
return true;
else
return false;
}
public static class MouseEvent implements Comparable<MouseEvent>
{
public final long time;
public final int dx, dy, dz;
public final int buttons;
MouseEvent(long time, int dx, int dy, int dz, boolean leftDown, boolean middleDown, boolean rightDown)
{
this.time = time;
this.dx = dx;
this.dy = dy;
this.dz = dz;
int buttons = 0;
if (leftDown)
buttons |= 1;
if (middleDown)
buttons |= 2;
if (rightDown)
buttons |= 4;
this.buttons = buttons;
}
public int compareTo(MouseEvent o)
{
return (int)(time - o.time);
}
}
public static class KeyBoardEvent implements Comparable<KeyBoardEvent>
{
public final long time;
public final String text;
KeyBoardEvent(long time, String text)
{
this.time = time;
this.text = text;
}
public int compareTo(KeyBoardEvent o)
{
return (int)(time - o.time);
}
}
}