/*
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 org.jpc.emulator.execution.decoder;
import org.jpc.j2se.Option;
import java.util.*;
public class Instruction
{
private static Set<String> invalid = new HashSet();
private static Set<String> call = new HashSet();
private static Set<String> ret = new HashSet();
private static Set<String> jmp = new HashSet();
private static Set<String> jcc = new HashSet();
private static Set<String> hlt = new HashSet();
private static Set<String> reps = new HashSet();
private static Set<String> repnes = new HashSet();
//public static enum Branch {None, T1, T2, Jmp_Unknown, Call, Call_Unknown, Ret, Exception};
static
{
invalid.add("invalid");
call.add("call");
call.add("syscall");
call.add("vmcall");
call.add("vmmcall");
ret.add("ret");
ret.add("retf");
ret.add("sysret");
ret.add("iretw");
ret.add("iretd");
ret.add("iretq");
jmp.add("jmp");
jcc.add("jo");
jcc.add("jno");
jcc.add("jb");
jcc.add("jbe");
jcc.add("ja");
jcc.add("jae");
jcc.add("je");
jcc.add("jne");
jcc.add("js");
jcc.add("jns");
jcc.add("jp");
jcc.add("jnp");
jcc.add("jl");
jcc.add("jle");
jcc.add("jg");
jcc.add("jge");
jcc.add("jcxz");
jcc.add("jecxz");
jcc.add("jrcxz");
jcc.add("loop");
jcc.add("loope");
jcc.add("loopnz");
hlt.add("hlt");
reps.add("movsb");
reps.add("movsw");
reps.add("movsd");
reps.add("cmpsb");
reps.add("cmpsw");
reps.add("cmpsd");
reps.add("lodsb");
reps.add("lodsw");
reps.add("lodsd");
reps.add("stosb");
reps.add("stosw");
reps.add("stosd");
reps.add("scasb");
reps.add("scasw");
reps.add("scasd");
reps.add("insb");
reps.add("insw");
reps.add("insd");
reps.add("outsb");
reps.add("outsw");
reps.add("outsd");
repnes.add("cmpsb");
repnes.add("cmpsw");
repnes.add("cmpsd");
repnes.add("scasb");
repnes.add("scasw");
repnes.add("scasd");
}
public int x86Length;
public long eip;
ZygoteInstruction zygote;
public String operator = "invalid";
public Operand[] operand = new Operand[0];
public Prefix pfx = new Prefix();
public int opr_mode, adr_mode;
String branch_dist;
public Instruction next;
public Instruction() {}
public String getGeneralClassName(boolean includeOperandSize, boolean includeAddressSize)
{
StringBuffer b = new StringBuffer();
if ((pfx.rep != 0) && reps.contains(operator)) // what happens if a scas has a rep and repne prefix...
b.append("rep_");
if (pfx.repne != 0)
{
if (repnes.contains(operator))
b.append("repne_");
else if (reps.contains(operator))
b.append("rep_");
}
b.append(operator);
if (includeOperandSize)
b.append("_o"+opr_mode);
if (includeAddressSize)
b.append("_a"+adr_mode);
for (int i=0; i < zygote.operand.length; i++)
if (!zygote.operand[i].name.equals("NONE"))
b.append("_"+getOperandType(zygote.operand[i], operand[i]));
boolean mem = false;
for (Operand o : operand)
if ((o.type != null) && o.type.equals("OP_MEM"))
mem = true;
if (mem)
b.append("_mem");
return b.toString();
}
public String[] getArgsTypes()
{
List<String> args = new ArrayList<String>();
for (int i=0; i < zygote.operand.length; i++)
if (!zygote.operand[i].name.equals("NONE"))
args.add(getOperandType(zygote.operand[i], operand[i]));
return args.toArray(new String[args.size()]);
}
private static String getOperandType(ZygoteOperand op, Operand val)
{
if (!op.name.endsWith("v") && !op.name.endsWith("z"))
return op.name;
if (op.name.equals("Ev"))
{
if (val.size == 16)
return "Ew";
else if (val.size == 32)
return "Ed";
else throw new IllegalStateException("Unknown Ev size: "+val.size);
}
if (op.name.equals("Gv") || op.name.equals("Gz"))
{
if (val.size == 16)
return "Gw";
else if (val.size == 32)
return "Gd";
else throw new IllegalStateException("Unknown Gv/z size: "+val.size);
}
if (op.name.equals("Iv") || op.name.equals("Iz"))
{
if (val.size == 16)
return "Iw";
else if (val.size == 32)
return "Id";
else throw new IllegalStateException("Unknown Iv/z size: "+val.size);
}
if (op.name.equals("Jz"))
{
if (val.size == 16)
return "Jw";
else if (val.size == 32)
return "Jd";
else throw new IllegalStateException("Unknown Jv size: "+val.size);
}
if (op.name.equals("Ov"))
{
if (val.size == 16)
return "Ow";
else if (val.size == 32)
return "Od";
else throw new IllegalStateException("Unknown Ov size: "+val.size);
}
throw new IllegalStateException("Unknown operand type "+op.name);
}
public void configure(Instruction parent)
{}
public int opSize(int arg)
{
return operand[arg].size;
}
public static class Ptr
{
public int off, seg;
public Ptr(int off, int seg)
{
this.off = off;
this.seg = seg;
}
}
public static class Prefix
{
public int rex, opr, adr, lock, rep, repe, repne, insn;
public String seg;
public String toString()
{
StringBuffer b = new StringBuffer();
if (lock != 0)
b.append("lock ");
if (rep != 0)
b.append("rep ");
//if (repe != 0)
// b.append("repe ");
if (repne != 0)
b.append("repne ");
//if (seg != null)
// b.append(seg+" ");
return b.toString();
}
}
public String getSegment()
{
if (pfx.seg != null)
return pfx.seg;
return "ds";
}
public void cast()
{
for (Operand op: operand)
op.cast();
}
public static class Operand
{
private int x86Length;
public Instruction parent;
long eip;
public String seg;
public String type;
public String base;
public int size;
public long lval, offset;
public Ptr ptr; // should be same as lval somehow
public String index;
public long scale;
int cast;
// required for patterns
int maxSize;
int imm_start, dis_start;
public void cast()
{
if (type.equals("OP_MEM"))
{
if (offset ==8)
lval = (byte) lval;
else if (offset == 16)
lval = (short) lval;
else if (offset == 32)
lval = (int) lval;
}
else if (type.equals("OP_IMM") || type.equals("OP_JIMM"))
{
if (size ==8)
lval = (byte) lval;
else if (size == 16)
lval = (short) lval;
else if (size == 32)
lval = (int) lval;
}
}
public String toString()
{
return toString(false);
}
public String toString(boolean pattern)
{
if (type == null)
return "UNKNOWN - AVX?";
if (type.equals("OP_REG"))
return base;
boolean first = true;
StringBuffer b = new StringBuffer();
//if (cast == 1)
// b.append(intel_size(size));
if (type.equals("OP_MEM"))
{
b.append(intel_size(size));
if (seg != null)
b.append(seg + ":");
else if ((base == null) && (index == null))
b.append("ds:");
if ((base != null) || (index != null))
b.append("[");
if (base != null)
{
b.append(base);
first = false;
}
if (index != null)
{
if (!first)
b.append("+");
b.append(index);
first = false;
}
if (scale != 0)
b.append("*"+scale);
if ((offset == 8) || (offset == 16) || (offset == 32) || (offset == 64))
{
if (!pattern)
{
if ((lval <0) && ((base != null) || (index != null)))
b.append("-"+String.format("0x%x", -lval));
else
{
if (!first)
b.append("+"+String.format("0x%x", lval & ((1L << offset)-1)));
else
b.append(String.format("0x%x", lval & ((1L << offset)-1)));
}
}
else
{
b.append("$");
for (int i=0; i < offset/8; i++)
b.append("D");
}
}
if ((base != null) || (index != null))
b.append("]");
}
else if (type.equals("OP_IMM"))
{
if (!pattern)
{
if (lval <0)
{
if (sign_extends.contains(parent.operator)) // these are sign extended
b.append(String.format("0x%x", lval & ((1L << maxSize)-1)));
else
b.append(String.format("0x%x", lval & ((1L << size)-1)));
}
else
b.append(String.format("0x%x", lval));
}
else
{
b.append("$");
//if (sign_extends.contains(parent.operator)) // these are sign extended
// for (int i=0; i < maxSize/8; i++)
// b.append("I");
//else
for (int i=0; i < size/8; i++)
b.append("I");
if (size == 0) // shr etc.
b.append("I");
}
}
else if (type.equals("OP_JIMM"))
{
if (!pattern)
{
if (Option.debug_blocks.value())
{
if (lval < 0)
b.append(String.format("0x%x", (lval) & ((1L << maxSize)-1)));
else
b.append(String.format("0x%x", lval));
}
else if (eip+x86Length+lval < 0)
b.append(String.format("0x%x", (eip+x86Length+lval) & ((1L << maxSize)-1)));
else
b.append(String.format("0x%x", eip+x86Length+lval));
}
else
{
b.append("$");
for (int i=0; i < size/8; i++)
b.append("I");
}
}
else if (type.equals("OP_PTR"))
{
if (!pattern)
b.append(String.format("0x%x:0x%x", ptr.seg & 0xFFFF, ptr.off));
else
b.append("$SSSS:$DDDDDDDD");
}
return b.toString();
//return String.format("[%s %s %s %d %x %x %x]", type, base, index, size, lval, offset, scale);
}
}
private static List<String> sign_extends = Arrays.asList("cmp", "or", "imul", "adc", "add", "sbb", "xor", "push", "and");
private static String intel_size(int size)
{
switch (size)
{
case 0:
return "";//XMMWORD PTR ";
case 8:
return "BYTE PTR ";
case 16:
return "WORD PTR ";
case 32:
return "DWORD PTR ";
case 64:
return "QWORD PTR ";
case 80:
return "TBYTE PTR ";
default:
throw new IllegalStateException("Unknown operand size " + size);
}
}
private static boolean print_size = false;
public String getPattern()
{
return toString(true);
}
public String toString()
{
return toString(false);
}
public String toString(boolean pattern)
{
int maxSize = 0;
for (Operand op: operand)
{
op.parent = this;
if (op.size > maxSize)
maxSize = op.size;
op.eip = eip;
op.x86Length = x86Length;
}
for (Operand op: operand)
op.maxSize = maxSize;
if (print_size)
{
if (operand.length == 1)
return String.format("(%d bytes) %s%s %s", x86Length, pfx, operator + (branch_dist == null ? "": " "+branch_dist), operand[0].toString(pattern));
else if (operand.length == 2)
return String.format("(%d bytes) %s%s %s, %s", x86Length, pfx, operator + (branch_dist == null ? "": " "+branch_dist), operand[0].toString(pattern), operand[1].toString(pattern));
else if (operand.length == 3)
return String.format("(%d bytes) %s%s %s, %s, %s", x86Length, pfx, operator + (branch_dist == null ? "": " "+branch_dist), operand[0].toString(pattern), operand[1].toString(pattern), operand[2].toString(pattern));
return String.format("(%d bytes) %s%s", x86Length, pfx, operator + (branch_dist == null ? "": " "+branch_dist));
}
if (operand.length == 1)
return String.format("%s%s %s", pfx, operator /*+ (branch_dist == null ? "": " "+branch_dist)*/, operand[0].toString(pattern));
else if (operand.length == 2)
return String.format("%s%s %s,%s", pfx, operator /*+ (branch_dist == null ? "": " "+branch_dist)*/, operand[0].toString(pattern), operand[1].toString(pattern));
else if (operand.length == 3)
return String.format("%s%s %s,%s,%s", pfx, operator /*+ (branch_dist == null ? "": " "+branch_dist)*/, operand[0].toString(pattern), operand[1].toString(pattern), operand[2].toString(pattern));
return String.format("%s%s", pfx, operator /*+ (branch_dist == null ? "": " "+branch_dist)*/);
}
public boolean isBranch()
{
return jcc.contains(operator) || jmp.contains(operator) || call.contains(operator) || ret.contains(operator) || hlt.contains(operator) || usesControlReg(0) || operator.equals("int");
}
public boolean usesControlReg(int op)
{
return (zygote.operand[op].name.equals("C")) | operator.startsWith("lmsw");
}
public boolean isJcc()
{
return jcc.contains(operator);
}
public boolean isJmp()
{
return jmp.contains(operator);
}
public boolean isCall()
{
return call.contains(operator);
}
public boolean isRet()
{
return ret.contains(operator);
}
public boolean isHalt()
{
return hlt.contains(operator);
}
}