/* JPC: An x86 PC Hardware Emulator for a pure Java Virtual Machine Release Version 2.4 A project from the Physics Dept, The University of Oxford Copyright (C) 2007-2010 The University of Oxford 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/ Conceived and Developed by: Rhys Newman, Ian Preston, Chris Dennis End of licence header */ package org.jpc.debugger; import java.lang.reflect.*; import java.util.Arrays; import org.jpc.emulator.PC; import org.jpc.emulator.execution.Executable; import org.jpc.emulator.execution.decoder.Instruction; import org.jpc.emulator.processor.*; import org.jpc.emulator.memory.*; import org.jpc.emulator.execution.codeblock.*; import org.jpc.j2se.Option; public class CodeBlockRecord { private static Method getMemory, convertMemory, validateBlock; static { try { getMemory = AddressSpace.class.getDeclaredMethod("getReadMemoryBlockAt", new Class[]{Integer.TYPE}); getMemory.setAccessible(true); } catch (NoSuchMethodException e) { getMemory = null; } } static { try { convertMemory = LazyCodeBlockMemory.class.getDeclaredMethod("convertMemory", new Class[]{Processor.class}); convertMemory.setAccessible(true); } catch (NoSuchMethodException e) { convertMemory = null; } } static { try { validateBlock = LinearAddressSpace.class.getDeclaredMethod("validateTLBEntryRead", new Class[]{Integer.TYPE}); validateBlock.setAccessible(true); } catch (NoSuchMethodException e) { validateBlock = null; } } private long blockCount, instructionCount, decodedCount; private int maxBlockSize; private PC pc; private Processor processor; private AddressSpace linear, physical; private CodeBlockHolder[] trace; private int[] addresses; private CodeBlockListener listener; public CodeBlockRecord(PC pc) { this.pc = pc; this.linear = (AddressSpace) pc.getComponent(LinearAddressSpace.class); this.physical = (AddressSpace) pc.getComponent(PhysicalAddressSpace.class); this.processor = pc.getProcessor(); listener = null; blockCount = 0; decodedCount = 0; instructionCount = 0; maxBlockSize = 1000; trace = new CodeBlockHolder[5000]; addresses = new int[trace.length]; } public void setCodeBlockListener(CodeBlockListener l) { listener = l; } public int getMaximumBlockSize() { return maxBlockSize; } public void setMaximumBlockSize(int value) { if (value == maxBlockSize) { return; } maxBlockSize = value; CodeBlockManager.BLOCK_LIMIT = value; // LazyCodeBlockMemory.setMaxBlockSize(value); System.out.println("failed to set max block size"); } public boolean isDecodedAt(int address) { return true; } public Memory getMemory(int address) { AddressSpace addressSpace = physical; if (processor.isProtectedMode()) { addressSpace = linear; } Memory memory = null; try { memory = (Memory) getMemory.invoke(addressSpace, new Object[]{Integer.valueOf(address)}); } catch (IllegalAccessException ex) { ex.printStackTrace(); } catch (InvocationTargetException ex) { ex.printStackTrace(); } if ((memory == null) && (addressSpace == linear)) { try { memory = (Memory) validateBlock.invoke(addressSpace, new Object[]{Integer.valueOf(address)}); } catch (IllegalAccessException ex) { ex.printStackTrace(); } catch (InvocationTargetException ex) { ex.printStackTrace(); } } return memory; } public CodeBlock decodeBlockAt(int address) { AddressSpace addressSpace = physical; if (processor.isProtectedMode()) { addressSpace = linear; } Memory memory = null; try { memory = (Memory) getMemory.invoke(addressSpace, new Object[]{Integer.valueOf(address)}); } catch (IllegalAccessException ex) { ex.printStackTrace(); } catch (InvocationTargetException ex) { ex.printStackTrace(); } if ((memory == null) && (addressSpace == linear)) { try { memory = (Memory) validateBlock.invoke(addressSpace, new Object[]{Integer.valueOf(address)}); } catch (IllegalAccessException ex) { ex.printStackTrace(); } catch (InvocationTargetException ex) { ex.printStackTrace(); } } //put in exception handler here?? if (memory instanceof LinearAddressSpace.PageFaultWrapper) { LinearAddressSpace.PageFaultWrapper fault = (LinearAddressSpace.PageFaultWrapper) memory; return new PageFaultCodeBlock(fault); } if (!(memory instanceof LazyCodeBlockMemory)) { System.err.println("Memory " + memory + " is not code memory. Address " + Integer.toHexString(address)); return null; } LazyCodeBlockMemory codeMemory = (LazyCodeBlockMemory) memory; CodeBlock block = null; int offset = address & AddressSpace.BLOCK_MASK; try { if (processor.isProtectedMode()) { if (processor.isVirtual8086Mode()) { block = codeMemory.getVirtual8086Block(offset); } else { block = codeMemory.getProtectedBlock(offset, processor.cs.getDefaultSizeFlag()); } } else { block = codeMemory.getRealBlock(offset); } } catch (SpanningDecodeException s) { block = s.getBlock(); } if (listener != null) { listener.codeBlockDecoded(address, addressSpace, block); } return block; } public CodeBlock executeBlock() { int ip = processor.getInstructionPointer(); CodeBlock block = decodeBlockAt(ip); if (block == null) { return null; } pc.checkInterrupts(1, false); int blockLength = pc.executeBlock(); if (blockLength > 1) pc.checkInterrupts(blockLength - 1, false); if (listener != null) { if (processor.isProtectedMode()) { listener.codeBlockExecuted(ip, linear, block); } else { listener.codeBlockExecuted(ip, physical, block); } } synchronized (this) { instructionCount += blockLength; } return block; } public CodeBlock advanceDecode() { int ip = processor.getInstructionPointer(); try { CodeBlock block = decodeBlockAt(ip); CodeBlockHolder priorState = new CodeBlockHolder(block, processor); trace[(int) (blockCount % trace.length)] = priorState; addresses[(int) (blockCount % trace.length)] = ip; blockCount++; decodedCount += block.getX86Count(); return block; } catch (ProcessorException e) { if (Option.noScreen.isSet()) { return new PageFaultCodeBlock(e); } processor.handleProtectedModeException(e); return advanceDecode(); } } public class PageFaultCodeBlock implements CodeBlock { private String message; public PageFaultCodeBlock(LinearAddressSpace.PageFaultWrapper pf) { message = pf.toString(); } public PageFaultCodeBlock(ProcessorException e) { message = e.toString(); } @Override public int getX86Length() { return 0; } @Override public int getX86Count() { return 0; } @Override public Executable.Branch execute(Processor cpu) { return null; } @Override public String getDisplayString() { return message; } @Override public Instruction getInstructions() { return null; } @Override public boolean handleMemoryRegionChange(int startAddress, int endAddress) { return false; } } public synchronized void reset() { Arrays.fill(trace, null); instructionCount = 0; blockCount = 0; decodedCount = 0; } public int getBlockAddress(int row) { if (blockCount <= trace.length) { return addresses[row]; } row += (blockCount % trace.length); if (row >= trace.length) { row -= trace.length; } return addresses[row]; } public CodeBlock getTraceBlockAt(int row) { if (blockCount <= trace.length) { return trace[row].block; } row += (blockCount % trace.length); if (row >= trace.length) { row -= trace.length; } return trace[row].block; } public int getTraceSSESPAt(int row) { if (blockCount <= trace.length) { return trace[row].ssESP; } row += (blockCount % trace.length); if (row >= trace.length) { row -= trace.length; } return trace[row].ssESP; } public int getTraceESPAt(int row) { if (blockCount <= trace.length) { return trace[row].state[ProcessorState.ESP]; } row += (blockCount % trace.length); if (row >= trace.length) { row -= trace.length; } return trace[row].state[ProcessorState.ESP]; } public int getTraceEBPAt(int row) { if (blockCount <= trace.length) { return trace[row].state[ProcessorState.EBP]; } row += (blockCount % trace.length); if (row >= trace.length) { row -= trace.length; } return trace[row].state[ProcessorState.EBP]; } public int[] getStateAt(int row) { if (blockCount <= trace.length) { return trace[row].state; } row += (blockCount % trace.length); if (row >= trace.length) { row -= trace.length; } return trace[row].state; } public int getRowForIndex(long index) { if (blockCount <= trace.length) { return (int) index; } long offset = blockCount - index - 1; if ((offset < 0) || (offset >= trace.length)) { return -1; } return trace.length - 1 - (int) offset; } public long getIndexNumberForRow(int row) { if (blockCount <= trace.length) { return row; } return (int) (blockCount - trace.length + row); } public int getTraceLength() { if (blockCount <= trace.length) { return (int) blockCount; } return trace.length; } public int getMaximumTrace() { return trace.length; } public long getExecutedBlockCount() { return blockCount-1; } public synchronized long getInstructionCount() { return instructionCount; } public synchronized long getDecodedCount() { return decodedCount; } private class CodeBlockHolder { CodeBlock block; int ssESP; int[] state; private CodeBlockHolder(CodeBlock b, Processor cpu) { block = b; boolean is32BitStack = cpu.ss.getDefaultSizeFlag(); this.ssESP = cpu.ss.getBase() + (is32BitStack ? cpu.r_esp.get32() : 0xffff & cpu.r_esp.get32()); this.state = ProcessorState.extract(cpu); } } }