blob: 3e39f7cada992681e51029e934455407e2ba1195 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2010 Nokia and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Nokia - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.debug.edc.x86.disassembler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.cdt.core.IAddress;
import org.eclipse.cdt.debug.edc.IJumpToAddress;
import org.eclipse.cdt.debug.edc.JumpToAddress;
import org.eclipse.cdt.debug.edc.disassembler.CodeBufferUnderflowException;
import org.eclipse.cdt.debug.edc.disassembler.DisassembledInstruction;
import org.eclipse.cdt.debug.edc.disassembler.IDisassembledInstruction;
import org.eclipse.cdt.debug.edc.disassembler.IDisassembler.IDisassemblerOptions;
import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
import org.eclipse.cdt.debug.edc.x86.X86Plugin;
import org.eclipse.cdt.debug.edc.x86.disassembler.DisassemblerX86.IDisassemblerOptionsX86;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
/**
* Single instruction disassembler for x86.
*/
public class InstructionParserX86 {
class ModRM {
public int mod, reg, rm;
};
class SIB {
public int scale, index, base;
};
public static final int ADDRESS_MODE_16BIT = 1, ADDRESS_MODE_32BIT = 2, ADDRESS_MODE_64BIT = 4;
private final List<Integer> prefixes = new ArrayList<Integer>();
private final List<Integer> prefixesUsed = new ArrayList<Integer>();
private final List<Byte> opcode = new ArrayList<Byte>();
private ModRM modRM;
private SIB sib;
private String[] operandStrings;
private int addressMode = ADDRESS_MODE_32BIT;
private Map<String, Object> disassemblerOptions = null;
/**
* address of the first byte of the instruction
*/
private final IAddress address;
/**
* raw data of the instruction.
*/
private final ByteBuffer codeBuffer;
/**
* start value of position (read pointer) of the code ByteBuffer.
*/
final private int startPosition;
/**
* result of disassembling the instruction, an
* {@link DisassembledInstruction} object.
*/
private DisassembledInstruction result = null;
/**
* Whether the instruction has been parsed/disassembled. If this flag is
* true but the result is null, the given byte buffer does not contain valid
* instruction.
*/
private boolean parsed = false;
private boolean indirectAddressing;
private boolean isMemoryOperand;
private int operandSize;
/**
* suffix for instruction name to indicate operand size. e.g. 'b', 'w', and
* 'l'.
*/
private String nameSizeSuffix;
/**
* Some instruction use source operand (the second one in Intel syntax) to
* determine operand size, some use target operand (the first one).
*/
private boolean honorSrcSize;
private IAddress jumpToAddr;
/**
* prepare to disassemble the instruction at the current position of the
* given byte buffer.
*/
public InstructionParserX86(IAddress addr, ByteBuffer codeBuffer) {
this.address = addr;
this.codeBuffer = codeBuffer;
this.startPosition = codeBuffer.position();
}
private void initialize() {
// reset position
codeBuffer.position(startPosition);
prefixes.clear();
prefixesUsed.clear();
opcode.clear();
nameSizeSuffix = null; // we'll know this after parsing the operands.
honorSrcSize = false; // use target operand by default.
modRM = null;
sib = null;
operandStrings = null;
jumpToAddr = null;
result = new DisassembledInstruction(); // start new
}
public IDisassembledInstruction getResult() throws CoreException {
if (!parsed) {
// Default: 32 bit address mode.
Map<String, Object> options = new HashMap<String, Object>();
options.put(IDisassemblerOptionsX86.ADDRESS_MODE, ADDRESS_MODE_32BIT);
disassemble(options);
}
return result;
}
/**
* Disassemble the given byte buffer to get one instruction. This method can
* be called more than once to disassemble the same instruction with
* different options.
*
* @param options
* - disassembler options
* @return output of disassembling the instruction, an
* {@link DisassembledInstruction} object.
* @throws CoreException
*/
public DisassembledInstruction disassemble(Map<String, Object> options) throws CoreException {
initialize();
parsed = true;
disassemblerOptions = options;
// Make sure the code buffer is in little-endian
if (codeBuffer.order() == ByteOrder.BIG_ENDIAN)
codeBuffer.order(ByteOrder.LITTLE_ENDIAN);
Object mode = options.get(IDisassemblerOptionsX86.ADDRESS_MODE);
if (mode != null)
addressMode = ((Integer) mode).intValue();
String mnemonics = null;
CoreException err = null;
try {
getPrefix();
byte b1 = codeBuffer.get();
opcode.add(b1);
if (b1 == 0x0F) {
byte b2 = codeBuffer.get();
opcode.add(b2);
if (b2 == 0x38 || b2 == 0x3A) {
byte b3 = codeBuffer.get();
opcode.add(b3);
mnemonics = parseOpcode(3, b3);
} else {
mnemonics = parseOpcode(2, b2);
}
} else
mnemonics = parseOpcode(1, b1);
} catch (BufferUnderflowException e) {
throw new CodeBufferUnderflowException(e);
} catch (CodeBufferUnderflowException e) {
throw e;
} catch (CoreException e) {
IStatus s = e.getStatus();
Throwable t = (s != null) ? s.getException() : null;
if (t instanceof BufferUnderflowException) {
throw new CodeBufferUnderflowException(t);
}
err = e;
}
// Now we are done with parsing.
// Fill in result.
//
int instSize = codeBuffer.position() - startPosition;
result.setSize(instSize);
StringBuffer asmOutput = new StringBuffer();
// Note we want to show "address" and "bytes" in
// error message when error/exception occurs.
if (checkBooleanOption(IDisassemblerOptions.MNEMONICS_SHOW_ADDRESS) || err != null) {
asmOutput.append(AssemblyFormatterX86.formatForAddressColumn(address));
}
if (checkBooleanOption(IDisassemblerOptions.MNEMONICS_SHOW_BYTES) || err != null) {
int currPos = codeBuffer.position();
asmOutput.append(AssemblyFormatterX86.formatForByteColumn(codeBuffer, startPosition, instSize));
codeBuffer.position(currPos);
}
if (err != null) {
// create informative error message
String msg = "Fail to disassemble this instruction (address + code-bytes): " + asmOutput.toString();
msg += "\nCause: " + err.getMessage();
throw EDCDebugger.newCoreException(msg);
} else {
// Now fill in output.
//
result.setAddress(address);
fillInJumpToAddres();
// Append the instruction proper
asmOutput.append(mnemonics);
result.setMnemonics(asmOutput.toString());
}
return result;
}
private void fillInJumpToAddres() {
boolean isSoleDestination = false;
boolean isSubroutineAddress = false;
String addrExpression = null;
IJumpToAddress jta = null;
if (isConditonalJump(opcode)) {
assert jumpToAddr != null;
isSoleDestination = false;
} else if (isAbsoluteJump(opcode)) {
isSoleDestination = true;
addrExpression = operandStrings[0];
} else if (isCall(opcode)) {
isSoleDestination = true;
isSubroutineAddress = true;
addrExpression = operandStrings[0];
} else if (isNearReturn(opcode)) {
isSoleDestination = true;
isSubroutineAddress = false;
addrExpression = JumpToAddress.EXPRESSION_RETURN_NEAR;
} else if (isFarReturn(opcode)) {
isSoleDestination = true;
isSubroutineAddress = false;
addrExpression = JumpToAddress.EXPRESSION_RETURN_FAR;
} else if (isLoop(opcode)) {
isSoleDestination = false;
isSubroutineAddress = false;
assert jumpToAddr != null;
} else if (isSelfRepeat(prefixes)) {
// An instruction like
// repnz scas ...
// is a self contained loop that executes many CPU cycles.
// When to step-over the instruction, we cannot just do single instruction
// execution (set TRACE/TRAP bit of EFLAGS register and run) as that would
// just execute one loop. We need to set a breakpoint after the instruction
// and resume. When use steps-in on the instruction, it's fine we just do
// single instruction execution. To achieve that, we treat such instruction
// as if it were a subroutine-call instruction........ 07/28/11
//
isSoleDestination = false;
isSubroutineAddress = true;
jumpToAddr = address; // jump to itself
} else
// non control-transfer instruction
return;
if (jumpToAddr != null)
// Immediate address is available, use it.
jta = new JumpToAddress(jumpToAddr, isSoleDestination, isSubroutineAddress);
else {
// Otherwise use the expression string.
assert addrExpression != null;
jta = new JumpToAddress(addrExpression, isSoleDestination, isSubroutineAddress);
}
result.setJumpToAddress(jta);
}
/**
* Is the instruction self-repeating ?
* @return
*/
private boolean isSelfRepeat(List<Integer> _prefixes) {
return _prefixes.contains(OpcodeX86.PREFIX_REPNZ) ||
_prefixes.contains(OpcodeX86.PREFIX_REPZ);
}
private boolean isLoop(List<Byte> opcode) {
int code;
if (opcode.size() == 1) { // one byte
code = opcode.get(0) & 0xff;
if (code >= 0xe0 && code <= 0xe2)
return true;
}
return false;
}
private boolean isNearReturn(List<Byte> opcode) {
int code;
if (opcode.size() == 1) { // one byte
code = opcode.get(0) & 0xff;
if (code == 0xc2 || code == 0xc3)
return true;
}
return false;
}
private boolean isFarReturn(List<Byte> opcode) {
int code;
if (opcode.size() == 1) { // one byte
code = opcode.get(0) & 0xff;
if (code == 0xca || code == 0xcb)
return true;
}
return false;
}
private boolean isCall(List<Byte> opcode) {
int code;
if (opcode.size() == 1) { // one byte
code = opcode.get(0) & 0xff;
if (code == 0xe8 || code == 0x9a)
return true;
else if (code == 0xff) {
assert modRM != null;
if (modRM.reg == 2 || modRM.reg == 3)
return true;
}
}
return false;
}
private boolean isAbsoluteJump(List<Byte> opcode) {
int code;
if (opcode.size() == 1) { // one byte
code = opcode.get(0) & 0xff;
if (code >= 0xe9 && code <= 0xeb)
return true;
else if (code == 0xff) {
assert modRM != null;
if (modRM.reg == 4 || modRM.reg == 5)
return true;
}
}
return false;
}
private boolean isConditonalJump(List<Byte> opcode) {
int code;
if (opcode.size() == 1) { // one byte
code = opcode.get(0) & 0xff;
if (code >= 0x70 && code <= 0x7f)
return true;
} else if (opcode.size() == 2) {
code = opcode.get(1) & 0xff;
if (code >= 0x80 && code <= 0x8f)
return true;
}
return false;
}
private boolean checkBooleanOption(String option) {
if (!disassemblerOptions.containsKey(option))
return false;
Boolean value = (Boolean) disassemblerOptions.get(option);
return value.booleanValue();
}
private String parseOpcode(int opcodeByteCnt, byte opcodeLastByte) throws CoreException {
OpcodeX86 opc = null;
int opcodeID = opcodeLastByte & 0xff; // convert to integer ID
if (opcodeByteCnt == 1) {
opc = lookupOneByteOpcode(opcodeID);
} else if (opcodeByteCnt == 2) {
opc = lookupTwoByteOpcode(opcodeID);
} else if (opcodeByteCnt == 3) {
opc = lookupThreeByteOpcode(opcodeID);
} else {
throw new CoreException(new Status(IStatus.ERROR, X86Plugin.PLUGIN_ID, "Invalid x86 opcode: opcode has more than three bytes."));
}
if (opc.needModRM() && modRM == null) {
modRM = getModRM(codeBuffer.get());
}
// Check which operand should be used to determine size suffix.
if (opc.getName().endsWith(OpcodeX86.SIZE_FLAG_SOURCE))
honorSrcSize = true;
// Parse operands one by one.
//
String[] operandDescs = opc.getOperandDescriptors();
if (operandDescs != null) {
int opCnt = operandDescs.length;
String opr;
operandStrings = new String[opCnt];
for (int i = 0; i < opCnt; i++) {
// Default. These will be set in parsing the operand.
isMemoryOperand = false;
operandSize = 32;
opr = parseOperand(operandDescs[i]);
if (isMemoryOperand) {
// check if there is segment-override prefix.
String seg = getSegmentRegisterFromPrefix();
if (seg != null)
opr = seg + ":" + opr;
}
operandStrings[i] = opr;
// determine the operand size suffix
if (!honorSrcSize && i == 0 || // honor target, the first
// operand
honorSrcSize && i > 0) // honor source, the second
// operand
{
// Don't set suffix if the determining operand is register
// operand.
// Example case: "imul OD_Ev":
// f7 ea imul %edx
// f7 ad 74 fb ff ff imull -0x48c(%ebp)
//
// Exception case:
// The target register operand determines the size suffix.
// "0f be c2", "movsbl %dl,%eax",
// "66 0f be 82 00 b0 2c", "movsbw 0x82cb000(%edx),%ax",
//
if (isMemoryOperand
|| AssemblyFormatterX86.sInstructionsSuffixFromRegisterOperand.contains(opc.getName()))
nameSizeSuffix = AssemblyFormatterX86.instructionNameSizeSuffix(operandSize);
}
}
}
// Now printable output
//
ArrayList<Integer> instPrefixes = new ArrayList<Integer>(prefixes);
instPrefixes.removeAll(prefixesUsed);
return AssemblyFormatterX86.formatInstruction(instPrefixes, opc.getName(), nameSizeSuffix, operandStrings);
}
private OpcodeX86 lookupOneByteOpcode(int opcodeID) throws CoreException {
OpcodeX86 opc;
// Step 1: look up code with mandatory prefix
//
for (int p : prefixes) {
if (OpcodeX86.sOpcodeMap_OneByteWithPrefix.containsKey(p)) {
Map<Integer, OpcodeX86> submap = OpcodeX86.sOpcodeMap_OneByteWithPrefix.get(p);
if (submap.containsKey(opcodeID)) {
prefixesUsed.add(p);
opc = submap.get(opcodeID);
return opc;
}
}
}
// Step 2: now look up extended opcode.
//
if (OpcodeX86.sOpcodeMap_OneByteExtension.containsKey(opcodeID)) {
// The opcode falls in the extension map,
// namely "Reg" bits of ModR/M byte are extending the opcode.
//
modRM = getModRM(codeBuffer.get());
opc = OpcodeX86.sOpcodeMap_OneByteExtension.get(opcodeID).get(modRM.reg);
if (opc == null)
throw EDCDebugger.newCoreException(MessageFormat.format(
"Extension opcode \"0x{0} /{1}\" is not supported by the disassembler yet.", Integer
.toHexString(opcodeID), modRM.reg));
else
return opc;
}
// Step 3: look up escape opcode
//
if (opcodeID >= 0xd8 && opcodeID <= 0xdf) {
opc = lookupEscapeOpcode(opcodeID);
if (opc != null)
return opc;
} else {
// Step 4: look up regular opcode
//
opc = OpcodeX86.sOpcodeMap_OneByte.get(opcodeID);
}
if (opc == null)
throw EDCDebugger.newCoreException(MessageFormat.format(
"Opcode \"0x{0}\" is not supported by the disassembler yet.", Integer.toHexString(opcodeID)));
return opc;
}
private OpcodeX86 lookupEscapeOpcode(int opcodeID) throws CoreException {
OpcodeX86 opc = null;
if (opcodeID < 0xd8 || opcodeID > 0xdf)
return null;
if (modRM == null) // read in ModRM if not yet.
modRM = getModRM(codeBuffer.get());
int modRMByte = modRM.mod << 6 | modRM.reg << 3 | modRM.rm;
int index = modRMByte <= 0xbf ? 0 : 1;
int key = modRMByte <= 0xbf ? modRM.reg : modRMByte;
if (!OpcodeX86.sOpcodeMap_Escape.containsKey(opcodeID))
return null;
Map<Integer, OpcodeX86> subMap = OpcodeX86.sOpcodeMap_Escape.get(opcodeID).get(index);
opc = subMap.get(key);
if (opc == null)
throw EDCDebugger.newCoreException(MessageFormat.format(
"Escape Opcode \"0x{0}\" with ModRM \"{1}\" is not supported by the disassembler yet.", Integer
.toHexString(opcodeID), Integer.toHexString(modRMByte)));
return opc;
}
private OpcodeX86 lookupTwoByteOpcode(int opcodeID) throws CoreException {
OpcodeX86 opc;
// Step 1: look up code with mandatory prefix
//
for (int p : prefixes) {
if (OpcodeX86.sOpcodeMap_TwoByteWithPrefix.containsKey(p)) {
Map<Integer, OpcodeX86> submap = OpcodeX86.sOpcodeMap_TwoByteWithPrefix.get(p);
if (submap.containsKey(opcodeID)) {
prefixesUsed.add(p);
opc = submap.get(opcodeID);
return opc;
}
}
}
// Step 2: now look up extended opcode.
//
int keyToExtensionMap = opcodeID << 4;
if (OpcodeX86.sOpcodeMap_TwoByteExtension.containsKey(keyToExtensionMap)) {
// The opcode falls in the extension map,
// namely "Reg" bits of ModR/M byte are extending the opcode.
//
modRM = getModRM(codeBuffer.get());
keyToExtensionMap |= modRM.mod; // real key
opc = OpcodeX86.sOpcodeMap_TwoByteExtension.get(keyToExtensionMap).get(modRM.reg);
if (opc == OpcodeX86.sVaringOpcode)
opc = OpcodeX86.selectExtensionOpcodeByRM(opcodeID, modRM);
if (opc == null)
throw EDCDebugger.newCoreException(MessageFormat.format(
"Extension opcode \"{0} {1} /{2}\" is not supported by the disassembler yet.", Integer
.toHexString(opcode.get(0)), Integer.toHexString(opcodeID), modRM.reg));
else
return opc;
}
// Step 3: look up regular opcode
//
opc = OpcodeX86.sOpcodeMap_TwoByte.get(opcodeID);
if (opc == null)
throw EDCDebugger.newCoreException(MessageFormat.format(
"Two-byte opcode \"{0} {1}\" is not supported by the disassembler yet.", AssemblyFormatterX86
.toHexString(opcode.get(0), false), AssemblyFormatterX86.toHexString(opcodeID, false)));
return opc;
}
private OpcodeX86 lookupThreeByteOpcode(int opcodeID) throws CoreException {
OpcodeX86 opc;
// Step 1: look up code with mandatory prefix
//
for (int p : prefixes) {
if (OpcodeX86.sOpcodeMap_ThreeByteWithPrefix.containsKey(p)) {
Map<Integer, OpcodeX86> submap = OpcodeX86.sOpcodeMap_ThreeByteWithPrefix.get(p);
if (submap.containsKey(opcodeID)) {
prefixesUsed.add(p);
opc = submap.get(opcodeID);
return opc;
}
}
}
// Step 2: look up regular opcode for 0F38
//
if (opcode.get(1) == 0x38)
opc = OpcodeX86.sOpcodeMap_ThreeByte_0F38.get(opcodeID);
else {
assert opcode.get(1) == 0x3a : "Invalid three byte opcode.";
opc = OpcodeX86.sOpcodeMap_ThreeByte_0F3A.get(opcodeID);
}
if (opc == null)
throw EDCDebugger.newCoreException(MessageFormat.format(
"Three-byte opcode \"{0} {1}\" is not supported by the disassembler yet.", AssemblyFormatterX86
.toHexString(opcode.get(0), false), AssemblyFormatterX86.toHexString(opcode.get(1), false),
AssemblyFormatterX86.toHexString(opcodeID, false)));
return opc;
}
private ModRM getModRM(byte b) {
ModRM ret = new ModRM();
ret.mod = (b >> 6) & 0x03;
ret.reg = (b >> 3) & 0x07;
ret.rm = b & 0x07;
return ret;
}
private SIB getSIB(byte b) {
SIB ret = new SIB();
ret.scale = (b >> 6) & 0x03;
ret.index = (b >> 3) & 0x07;
ret.base = b & 0x07;
return ret;
}
private String parseOperand(String operandDescriptor) throws CoreException {
indirectAddressing = false; // default
String ret = null;
if (operandDescriptor.charAt(0) == OpcodeX86.INDIRECT_INDICATOR) {
indirectAddressing = true;
operandDescriptor = operandDescriptor.substring(1);
}
if (isRegisterID(operandDescriptor)) {
// immediate register like: AL, eAX, rAX, rSP, etc.
String reg = new String(operandDescriptor);
if (addressMode == ADDRESS_MODE_32BIT) {
if (prefixes.contains(OpcodeX86.PREFIX_OPERAND_SIZE_OVERRIDE)) { // 16-bit
prefixesUsed.add(OpcodeX86.PREFIX_OPERAND_SIZE_OVERRIDE);
if (reg.length() == 3)
reg = reg.substring(1);
} else
reg = reg.replace('r', 'e'); // only consider 32-bit mode at
// present.
}
ret = AssemblyFormatterX86.formatRegister(reg.toLowerCase(), indirectAddressing);
} else {
// addressing operand
char addressingMethod = operandDescriptor.charAt(0);
assert Character.isUpperCase(addressingMethod);
char[] operandType = operandDescriptor.substring(1).toCharArray();
ret = addressOperand(addressingMethod, operandType);
}
return ret;
}
/**
* Addressing dispatcher that calls corresponding addressing method.<br>
* Note the addressing method must be in this form:<br>
* <i>public String addressing_X(char addressingMethod char[] operandType)
* throws CoreException </i>
*
* @param addressingMethod
* - letter defined in section
* "A.2.1 Codes for Addressing Method" in Intel manual Vol 3B.
* @param operandType
* - letters defined in section "A.2.2 Codes for Operand Type"
* and "A.2.3 Register Codes" in Intel Manual Vol 3B.
* @return String representation of the operand, e.g. "0x1(%ecx)",
* "0x12345".
* @throws CoreException
*/
private String addressOperand(char addressingMethod, char[] operandType) throws CoreException {
String methodName = "addressing_" + addressingMethod;
Method handler = null;
try {
handler = this.getClass().getMethod(methodName, new Class[] { char[].class });
} catch (SecurityException e) {
// should not happen
EDCDebugger.getMessageLogger().logError(null, e);
return null;
} catch (NoSuchMethodException e) {
throw EDCDebugger.newCoreException(MessageFormat.format("Addressing method \"{0}\" is not supported yet.",
addressingMethod));
}
try {
String op = (String) handler.invoke(this, new Object[] { operandType });
return op;
} catch (IllegalArgumentException e) {
// should not happen
EDCDebugger.getMessageLogger().logError(null, e);
} catch (IllegalAccessException e) {
// should not happen
EDCDebugger.getMessageLogger().logError(null, e);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
// BufferUnderflowException is fairly common, because x86 by nature is
// hard to feed the right number of bytes to disassemble. even if the
// parser recognized ahead of time that a buffer was running out, some
// exceptional return would still be forced. so BufferUnderflow is
// an appropriate response.
//
// ... just don't log or report it to printStackTrace() every time!
if (cause instanceof BufferUnderflowException)
throw new CodeBufferUnderflowException(e);
EDCDebugger.getMessageLogger().logError(null, e);
if (cause instanceof CoreException)
throw (CoreException) cause;
else {
String ex = cause.getMessage() != null ? cause.getMessage() : cause.getClass().getName();
throw EDCDebugger.newCoreException("Exception or error: " + ex);
}
}
return null;
}
/**
* @param operandType
* @throws CoreException
*/
public String addressing_A(char[] operandType) throws CoreException {
final char addressingMethod = 'A';
int offset;
String operand = null;
switch (operandType[0]) {
case 'p':
if (addressMode == ADDRESS_MODE_16BIT) {
offset = codeBuffer.getShort();
} else {
offset = codeBuffer.getInt();
}
short seg = codeBuffer.getShort();
operand = AssemblyFormatterX86.formatFarPointer(seg, offset);
break;
default:
throw EDCDebugger.newCoreException(MessageFormat.format(
"Operand descriptor \"{0}{1}{2}\" is not supported yet.", addressingMethod, operandType[0],
(operandType.length > 1 ? operandType[1] : "")));
}
return operand;
}
public String addressing_C(char[] operandType) throws CoreException {
final char addressingMethod = 'C';
return registerFromModRM_Reg(addressingMethod, operandType);
}
public String addressing_D(char[] operandType) throws CoreException {
final char addressingMethod = 'D';
return registerFromModRM_Reg(addressingMethod, operandType);
}
public String addressing_E(char[] operandType) throws CoreException {
final char addressingMethod = 'E';
if (modRM == null)
throw EDCDebugger.newCoreException("ModRM required but not available.");
/*
* see page 2-7 of Intel Developer's Manual Vol. 2A: Table 2-2: 32-Bit
* Addressing Forms with the ModR/M Byte
*/
if (modRM.mod == 3) { // registers
return registerFromModRM_RM(addressingMethod, operandType);
} else { // memory addressing
isMemoryOperand = true;
return memoryFromModRM(addressingMethod, operandType);
}
}
public String addressing_G(char[] operandType) throws CoreException {
final char addressingMethod = 'G';
return registerFromModRM_Reg(addressingMethod, operandType);
}
public String addressing_I(char[] operandType) throws CoreException {
final char addressingMethod = 'I';
int imm;
switch (operandType[0]) {
case 'b':
imm = codeBuffer.get();
break;
case 'w':
imm = codeBuffer.getShort();
break;
case 'v':
case 'z':
if (addressMode == ADDRESS_MODE_16BIT) {
imm = codeBuffer.getShort();
} else {
if (prefixes.contains(OpcodeX86.PREFIX_OPERAND_SIZE_OVERRIDE)) {
prefixesUsed.add(OpcodeX86.PREFIX_OPERAND_SIZE_OVERRIDE);
imm = codeBuffer.getShort();
} else
imm = codeBuffer.getInt();
}
break;
default:
throw EDCDebugger.newCoreException(MessageFormat.format(
"Operand descriptor \"{0}{1}{2}\" is not supported yet.", addressingMethod, operandType[0],
(operandType.length > 1 ? operandType[1] : "")));
}
return AssemblyFormatterX86.formatImmediate(imm);
}
public String addressing_J(char[] operandType) throws CoreException {
final char addressingMethod = 'J';
int offset;
switch (operandType[0]) {
case 'b':
offset = codeBuffer.get();
break;
case 'z':
if (addressMode == ADDRESS_MODE_16BIT) {
offset = codeBuffer.getShort();
} else {
offset = codeBuffer.getInt();
}
break;
default:
throw EDCDebugger.newCoreException(MessageFormat.format(
"Operand descriptor \"{0}{1}{2}\" is not supported yet.", addressingMethod, operandType[0],
(operandType.length > 1 ? operandType[1] : "")));
}
// Offset needs be relative to the end of the instruction.
offset += codeBuffer.position() - startPosition;
jumpToAddr = address.add(offset);
isMemoryOperand = true;
return AssemblyFormatterX86.formatForCode(jumpToAddr);
}
public String addressing_M(char[] operandType) throws CoreException {
final char addressingMethod = 'M';
assert modRM != null;
if (modRM.mod == 3) { // registers
throw EDCDebugger.newCoreException("Invalid Opcode at " + address.toHexAddressString());
} else { // memory addressing
isMemoryOperand = true;
return memoryFromModRM(addressingMethod, operandType);
}
}
public String addressing_N(char[] operandType) throws CoreException {
final char addressingMethod = 'N';
return registerFromModRM_RM(addressingMethod, operandType);
}
public String addressing_O(char[] operandType) throws CoreException {
int offset;
// operandType ('b' or 'v') only indicate operand size.
//
if (addressMode == ADDRESS_MODE_16BIT) {
offset = codeBuffer.getShort();
} else {
offset = codeBuffer.getInt();
}
isMemoryOperand = true;
return AssemblyFormatterX86.formatOffset(offset);
}
public String addressing_R(char[] operandType) throws CoreException {
final char addressingMethod = 'R';
return registerFromModRM_RM(addressingMethod, operandType);
}
public String addressing_S(char[] operandType) throws CoreException {
final char addressingMethod = 'S';
return registerFromModRM_Reg(addressingMethod, operandType);
}
public String addressing_U(char[] operandType) throws CoreException {
final char addressingMethod = 'U';
return registerFromModRM_RM(addressingMethod, operandType);
}
public String addressing_V(char[] operandType) throws CoreException {
final char addressingMethod = 'V';
return registerFromModRM_Reg(addressingMethod, operandType);
}
public String addressing_X(char[] operandType) throws CoreException {
final char addressingMethod = 'X';
switch (operandType[0]) {
case 'b':
operandSize = 8;
break;
case 'v':
case 'z':
operandSize = 32;
if (prefixes.contains(OpcodeX86.PREFIX_OPERAND_SIZE_OVERRIDE)) {
prefixesUsed.add(OpcodeX86.PREFIX_OPERAND_SIZE_OVERRIDE);
operandSize = 16;
}
break;
default:
throw EDCDebugger.newCoreException(MessageFormat.format(
"Operand descriptor \"{0}{1}{2}\" is not supported yet.", addressingMethod, operandType[0],
(operandType.length > 1 ? operandType[1] : "")));
}
isMemoryOperand = true;
// setNameSizeSuffix(sizeSuffix);
return AssemblyFormatterX86.registerPair_DSrSI(addressMode);
}
public String addressing_Y(char[] operandType) throws CoreException {
final char addressingMethod = 'Y';
switch (operandType[0]) {
case 'b':
operandSize = 8;
break;
case 'v':
case 'z':
operandSize = 32; // default to double-word
if (prefixes.contains(OpcodeX86.PREFIX_OPERAND_SIZE_OVERRIDE)) {
prefixesUsed.add(OpcodeX86.PREFIX_OPERAND_SIZE_OVERRIDE);
operandSize = 16;
}
break;
default:
throw EDCDebugger.newCoreException(MessageFormat.format(
"Operand descriptor \"{0}{1}{2}\" is not supported yet.", addressingMethod, operandType[0],
(operandType.length > 1 ? operandType[1] : "")));
}
isMemoryOperand = true;
return AssemblyFormatterX86.registerPair_ESrDI(addressMode);
}
/**
* Check if an operand descriptor is denoting a register.
*
* @param operandDescriptor
* things like "AL", "Ev", etc.
* @return
*/
private boolean isRegisterID(String operandDescriptor) {
// If the second character is upper case, the descriptor is register ID.
return operandDescriptor.length() > 1 && Character.isUpperCase(operandDescriptor.charAt(1));
}
private void getPrefix() {
codeBuffer.mark(); // mark current position
int b = codeBuffer.get() & 0xff;
if (b >= 0x40 && b <= 0x4F) {
if (addressMode == ADDRESS_MODE_64BIT) {
// REX prefix.
prefixes.add(b);
getPrefix();
} else
codeBuffer.reset();
} else if (OpcodeX86.sAllPrefixes.contains(b)) {
prefixes.add(b);
getPrefix();
} else {
codeBuffer.reset();
}
}
private String memoryFromModRM(char addressingMethod, char[] operandType) throws CoreException {
/*
* see page 2-7 of Intel Developer's Manual Vol. 2A: Table 2-2: 32-Bit
* Addressing Forms with the ModR/M Byte
*/
// Example (from objdump):
// 80abe02: f6 44 50 01 08 testb $0x8,0x1(%eax,%edx,2)
// First read SIB byte if needed.
if (modRM.rm == 4) {
// SIB follows, read in.
byte b = codeBuffer.get();
sib = getSIB(b);
}
// now read displacement if needed
int displacement = 0;
boolean useBase, useIndex;
int baseRegID = 0, indexRegID = 0;
boolean useDisplacement = true;
if (modRM.mod == 1) { // 8-bit displacement, signed
displacement = codeBuffer.get();
} else if (modRM.mod == 2 || modRM.mod == 0 && modRM.rm == 5 || modRM.mod == 0 && modRM.rm == 4
&& sib.base == 5) { // 32-bit displacement, signed
displacement = codeBuffer.getInt();
} else
useDisplacement = false;
if (modRM.rm == 4) {
// SIB used.
useIndex = (sib.index != 4);
useBase = !(sib.base == 5 && modRM.mod == 0);
indexRegID = sib.index;
baseRegID = sib.base;
} else {
// no SIB involved.
useIndex = false; // no index reg.
useBase = !(modRM.rm == 5 && modRM.mod == 0);
baseRegID = modRM.rm; // base reg denoted by "r/m" bits.
}
operandSize = 32;
if (operandType.length > 0 && operandType[0] == 'b')
operandSize = 8;
else if (prefixes.contains(OpcodeX86.PREFIX_OPERAND_SIZE_OVERRIDE)) {
prefixesUsed.add(OpcodeX86.PREFIX_OPERAND_SIZE_OVERRIDE);
operandSize = 16;
}
// Now format the operand string.
//
if (!useBase)
baseRegID = -1;
if (!useIndex)
indexRegID = -1;
return AssemblyFormatterX86.format(indirectAddressing, useDisplacement ? displacement : null, baseRegID,
indexRegID, sib != null ? sib.scale : 0);
}
/**
* Get register names.
*/
private String getRegisterName(int regID, char addressingMethod, char[] operandType) throws CoreException {
String operand;
int key;
switch (operandType[0]) {
case 'b':
key = 8;
break;
case 'w':
key = 16;
break;
case 'd':
key = 32;
break;
case 'q':
key = 64;
break;
case 'v':
case 'z':
if (addressMode == ADDRESS_MODE_16BIT)
key = 16;
else {
key = 32;
if (prefixes.contains(OpcodeX86.PREFIX_OPERAND_SIZE_OVERRIDE)) {
prefixesUsed.add(OpcodeX86.PREFIX_OPERAND_SIZE_OVERRIDE);
key = 16;
}
}
break;
default:
throw EDCDebugger.newCoreException(MessageFormat.format(
"Operand descriptor \"{0}{1}{2}\" is not supported yet.", addressingMethod, operandType[0],
(operandType.length > 1 ? operandType[1] : "")));
}
operandSize = key;
// String sizeSuffix = key==8 ? "b" : (key==16 ? "w" : "l");
// setNameSizeSuffix(sizeSuffix);
// print out
//
if (indirectAddressing)
operand = "*";
else
operand = "";
try {
String[] names = null;
switch (addressingMethod) {
case 'G': // General register
case 'E':
case 'R':
names = (AssemblyFormatterX86.sGPRNames.get(key));
break;
case 'C':
names = AssemblyFormatterX86.sControlRegisterNames;
break;
case 'D':
names = AssemblyFormatterX86.sDebugRegisterNames;
break;
case 'S':
names = AssemblyFormatterX86.sSegmentRegisterNames;
break;
case 'N': // MMX registers
case 'P':
throw EDCDebugger.newCoreException("MMX registers are not supported by the disassembler yet.");
case 'V': // XMM registers
case 'U':
throw EDCDebugger.newCoreException("XMM registers are not supported by the disassembler yet.");
default:
assert false : MessageFormat.format("unsupported addressing method \"{0}\" for getting register.",
addressingMethod);
break;
}
if (names != null) {
operand += names[regID];
}
} catch (Exception e) { // out-of-bound, etc.
e.printStackTrace(); // for debug
throw EDCDebugger.newCoreException(MessageFormat.format("Invalid register ID {0} in instruction.", regID));
}
return operand;
}
/**
* Get register from REG bits of ModRM byte.
*/
private String registerFromModRM_Reg(char addressingMethod, char[] operandType) throws CoreException {
return getRegisterName(modRM.reg, addressingMethod, operandType);
}
/**
* Get register from RM bits of ModRM byte.
*/
private String registerFromModRM_RM(char addressingMethod, char[] operandType) throws CoreException {
return getRegisterName(modRM.rm, addressingMethod, operandType);
}
/**
* return null if not found.
*
* @return
*/
private String getSegmentRegisterFromPrefix() {
String seg = null;
for (int p : prefixes) {
if (OpcodeX86.sPrefixesForOperand.contains(p)) {
prefixesUsed.add(p);
seg = AssemblyFormatterX86.formatPrefix(p);
}
}
return seg;
}
}