mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
Merge remote-tracking branch 'origin/GP-2642_Dan_compatEmulatorHelper--SQUASHED'
This commit is contained in:
commit
254e749f95
25 changed files with 1815 additions and 741 deletions
|
@ -155,6 +155,11 @@ class TestThread implements PcodeThread<Void> {
|
||||||
public void setSuspended(boolean suspended) {
|
public void setSuspended(boolean suspended) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSuspended() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PcodeUseropLibrary<Void> getUseropLibrary() {
|
public PcodeUseropLibrary<Void> getUseropLibrary() {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -63,7 +63,12 @@ public class EmulatorTestRunner {
|
||||||
this.program = program;
|
this.program = program;
|
||||||
this.testGroup = testGroup;
|
this.testGroup = testGroup;
|
||||||
this.executionListener = executionListener;
|
this.executionListener = executionListener;
|
||||||
emuHelper = new EmulatorHelper(program);
|
emuHelper = new EmulatorHelper(program) {
|
||||||
|
@Override
|
||||||
|
protected Emulator newEmulator() {
|
||||||
|
return new AdaptedEmulator(this);
|
||||||
|
}
|
||||||
|
};
|
||||||
emu = emuHelper.getEmulator();
|
emu = emuHelper.getEmulator();
|
||||||
emuHelper.setMemoryFaultHandler(new MyMemoryFaultHandler(executionListener));
|
emuHelper.setMemoryFaultHandler(new MyMemoryFaultHandler(executionListener));
|
||||||
|
|
||||||
|
@ -71,7 +76,7 @@ public class EmulatorTestRunner {
|
||||||
@Override
|
@Override
|
||||||
public boolean pcodeCallback(PcodeOpRaw op) throws LowlevelError {
|
public boolean pcodeCallback(PcodeOpRaw op) throws LowlevelError {
|
||||||
int userOp = (int) op.getInput(0).getOffset();
|
int userOp = (int) op.getInput(0).getOffset();
|
||||||
String pcodeOpName = emulate.getLanguage().getUserDefinedOpName(userOp);
|
String pcodeOpName = program.getLanguage().getUserDefinedOpName(userOp);
|
||||||
unimplementedSet.add(pcodeOpName);
|
unimplementedSet.add(pcodeOpName);
|
||||||
String outStr = "";
|
String outStr = "";
|
||||||
Varnode output = op.getOutput();
|
Varnode output = op.getOutput();
|
||||||
|
@ -169,6 +174,7 @@ public class EmulatorTestRunner {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add memory dump point
|
* Add memory dump point
|
||||||
|
*
|
||||||
* @param breakAddr instruction address at which execution should pause (before it is executed)
|
* @param breakAddr instruction address at which execution should pause (before it is executed)
|
||||||
* so that the specified memory may be dumped to the log during trace execution mode.
|
* so that the specified memory may be dumped to the log during trace execution mode.
|
||||||
* @param dumpAddr memory address which should be dumped
|
* @param dumpAddr memory address which should be dumped
|
||||||
|
@ -190,6 +196,7 @@ public class EmulatorTestRunner {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add memory dump point
|
* Add memory dump point
|
||||||
|
*
|
||||||
* @param breakAddr instruction address at which execution should pause (before it is executed)
|
* @param breakAddr instruction address at which execution should pause (before it is executed)
|
||||||
* so that the specified memory may be dumped to the log during trace execution mode.
|
* so that the specified memory may be dumped to the log during trace execution mode.
|
||||||
* @param dumpAddrReg register containing the memory address offset which should be dumped
|
* @param dumpAddrReg register containing the memory address offset which should be dumped
|
||||||
|
@ -230,11 +237,11 @@ public class EmulatorTestRunner {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get number of CALLOTHER errors detected when a test pass was registered.
|
* Get number of CALLOTHER errors detected when a test pass was registered. This number should
|
||||||
* This number should be subtracted from the pass count and possibly added
|
* be subtracted from the pass count and possibly added to the failure count. Number does not
|
||||||
* to the failure count. Number does not reflect total number of CALLOTHER
|
* reflect total number of CALLOTHER pcodeops encountered but only the number of passed tests
|
||||||
* pcodeops encountered but only the number of passed tests affected.
|
* affected. See log for all CALLOTHER executions detected.
|
||||||
* See log for all CALLOTHER executions detected.
|
*
|
||||||
* @return number of CALLOTHER errors
|
* @return number of CALLOTHER errors
|
||||||
*/
|
*/
|
||||||
public int getCallOtherErrors() {
|
public int getCallOtherErrors() {
|
||||||
|
@ -243,6 +250,7 @@ public class EmulatorTestRunner {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute test group without instruction stepping/tracing
|
* Execute test group without instruction stepping/tracing
|
||||||
|
*
|
||||||
* @param timeLimitMS
|
* @param timeLimitMS
|
||||||
* @param monitor
|
* @param monitor
|
||||||
* @return
|
* @return
|
||||||
|
@ -695,7 +703,8 @@ public class EmulatorTestRunner {
|
||||||
@Override
|
@Override
|
||||||
Address getDumpAddress() {
|
Address getDumpAddress() {
|
||||||
RegisterValue regVal = getRegisterValue(dumpAddrReg);
|
RegisterValue regVal = getRegisterValue(dumpAddrReg);
|
||||||
return dumpAddrSpace.getAddress(regVal.getUnsignedValue().longValue()).add(
|
return dumpAddrSpace.getAddress(regVal.getUnsignedValue().longValue())
|
||||||
|
.add(
|
||||||
relativeOffset);
|
relativeOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,403 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.emulator;
|
||||||
|
|
||||||
|
import generic.ULongSpan;
|
||||||
|
import generic.ULongSpan.ULongSpanSet;
|
||||||
|
import ghidra.app.emulator.memory.MemoryLoadImage;
|
||||||
|
import ghidra.app.emulator.state.RegisterState;
|
||||||
|
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||||
|
import ghidra.lifecycle.Transitional;
|
||||||
|
import ghidra.pcode.emu.*;
|
||||||
|
import ghidra.pcode.emu.PcodeMachine.SwiMode;
|
||||||
|
import ghidra.pcode.emulate.*;
|
||||||
|
import ghidra.pcode.error.LowlevelError;
|
||||||
|
import ghidra.pcode.exec.*;
|
||||||
|
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||||
|
import ghidra.pcode.memstate.MemoryFaultHandler;
|
||||||
|
import ghidra.pcode.memstate.MemoryState;
|
||||||
|
import ghidra.pcode.pcoderaw.PcodeOpRaw;
|
||||||
|
import ghidra.pcode.utils.Utils;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.address.AddressSpace;
|
||||||
|
import ghidra.program.model.lang.*;
|
||||||
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of {@link Emulator} that wraps the newer {@link PcodeEmulator}
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This is a transitional utility only. It is currently used only by the pcode tests until that is
|
||||||
|
* ported to use the new {@link PcodeEmulator} directly. New use cases based on p-code emulation
|
||||||
|
* should use the {@link PcodeEmulator} directly. Older use cases still being actively maintained
|
||||||
|
* should begin work porting to {@link PcodeEmulator}. Old use cases without active maintenance may
|
||||||
|
* try this wrapper, but may have to remain using {@link DefaultEmulator}. At a minimum, to update
|
||||||
|
* such old use cases, `new Emulator(...)` must be replaced by `new DefaultEmulator(...)`.
|
||||||
|
*/
|
||||||
|
@Transitional
|
||||||
|
public class AdaptedEmulator implements Emulator {
|
||||||
|
class AdaptedPcodeEmulator extends PcodeEmulator {
|
||||||
|
private final MemoryLoadImage loadImage;
|
||||||
|
private final MemoryFaultHandler faultHandler;
|
||||||
|
|
||||||
|
public AdaptedPcodeEmulator(Language language, MemoryLoadImage loadImage,
|
||||||
|
MemoryFaultHandler faultHandler) {
|
||||||
|
super(language);
|
||||||
|
this.loadImage = loadImage;
|
||||||
|
this.faultHandler = faultHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PcodeExecutorState<byte[]> createSharedState() {
|
||||||
|
return new AdaptedBytesPcodeExecutorState(language,
|
||||||
|
new StateBacking(faultHandler, loadImage));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PcodeExecutorState<byte[]> createLocalState(PcodeThread<byte[]> thread) {
|
||||||
|
return new AdaptedBytesPcodeExecutorState(language,
|
||||||
|
new StateBacking(faultHandler, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AdaptedPcodeThread createThread(String name) {
|
||||||
|
return new AdaptedPcodeThread(name, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AdaptedPcodeThread newThread() {
|
||||||
|
return (AdaptedPcodeThread) super.newThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PcodeUseropLibrary<byte[]> createUseropLibrary() {
|
||||||
|
return new AdaptedPcodeUseropLibrary();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transitional
|
||||||
|
public class AdaptedPcodeUseropLibrary extends AnnotatedPcodeUseropLibrary<byte[]> {
|
||||||
|
@PcodeUserop
|
||||||
|
public void __addr_cb() {
|
||||||
|
adaptedBreakTable.doAddressBreak(thread.getCounter());
|
||||||
|
if (thread.isSuspended()) {
|
||||||
|
/**
|
||||||
|
* This is the convention in DefaultEmulator: A breakpoint sets halt on the emulator
|
||||||
|
* to cause it to actually break. We'll translate that into an interrupt.
|
||||||
|
*/
|
||||||
|
throw new InterruptPcodeExecutionException(null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AdaptedPcodeThread extends BytesPcodeThread {
|
||||||
|
Address lastExecuteAddress;
|
||||||
|
|
||||||
|
public AdaptedPcodeThread(String name, AbstractPcodeMachine<byte[]> machine) {
|
||||||
|
super(name, machine);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void preExecuteInstruction() {
|
||||||
|
super.preExecuteInstruction();
|
||||||
|
lastExecuteAddress = getCounter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean onMissingUseropDef(PcodeOp op, String opName) {
|
||||||
|
if (super.onMissingUseropDef(op, opName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return adaptedBreakTable.doPcodeOpBreak(new PcodeOpRaw(op));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
record StateBacking(MemoryFaultHandler faultHandler, MemoryLoadImage loadImage) {
|
||||||
|
}
|
||||||
|
|
||||||
|
static class AdaptedBytesPcodeExecutorState extends BytesPcodeExecutorState {
|
||||||
|
public AdaptedBytesPcodeExecutorState(Language language, StateBacking backing) {
|
||||||
|
super(new AdaptedBytesPcodeExecutorStatePiece(language, backing));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class AdaptedBytesPcodeExecutorStatePiece
|
||||||
|
extends AbstractBytesPcodeExecutorStatePiece<AdaptedBytesPcodeExecutorStateSpace> {
|
||||||
|
private final StateBacking backing;
|
||||||
|
|
||||||
|
public AdaptedBytesPcodeExecutorStatePiece(Language language, StateBacking backing) {
|
||||||
|
super(language);
|
||||||
|
this.backing = backing;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AbstractSpaceMap<AdaptedBytesPcodeExecutorStateSpace> newSpaceMap() {
|
||||||
|
return new SimpleSpaceMap<>() {
|
||||||
|
@Override
|
||||||
|
protected AdaptedBytesPcodeExecutorStateSpace newSpace(AddressSpace space) {
|
||||||
|
return new AdaptedBytesPcodeExecutorStateSpace(language, space, backing);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class AdaptedBytesPcodeExecutorStateSpace
|
||||||
|
extends BytesPcodeExecutorStateSpace<StateBacking> {
|
||||||
|
public AdaptedBytesPcodeExecutorStateSpace(Language language, AddressSpace space,
|
||||||
|
StateBacking backing) {
|
||||||
|
super(language, space, backing);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void readUninitializedFromBacking(ULongSpanSet uninitialized) {
|
||||||
|
if (uninitialized.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (backing.loadImage == null) {
|
||||||
|
if (space.isUniqueSpace()) {
|
||||||
|
throw new AccessPcodeExecutionException(
|
||||||
|
"Attempted to read from uninitialized unique: " + uninitialized);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ULongSpan bound = uninitialized.bound();
|
||||||
|
byte[] data = new byte[(int) bound.length()];
|
||||||
|
backing.loadImage.loadFill(data, data.length, space.getAddress(bound.min()), 0,
|
||||||
|
false);
|
||||||
|
for (ULongSpan span : uninitialized.spans()) {
|
||||||
|
bytes.putData(span.min(), data, (int) (span.min() - bound.min()),
|
||||||
|
(int) span.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void warnUninit(ULongSpanSet uninit) {
|
||||||
|
ULongSpan bound = uninit.bound();
|
||||||
|
byte[] data = new byte[(int) bound.length()];
|
||||||
|
if (backing.faultHandler.uninitializedRead(space.getAddress(bound.min()), data.length,
|
||||||
|
data, 0)) {
|
||||||
|
for (ULongSpan span : uninit.spans()) {
|
||||||
|
bytes.putData(span.min(), data, (int) (span.min() - bound.min()),
|
||||||
|
(int) span.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AdaptedBreakTableCallback extends BreakTableCallBack {
|
||||||
|
public AdaptedBreakTableCallback() {
|
||||||
|
super((SleighLanguage) language);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerAddressCallback(Address addr, BreakCallBack func) {
|
||||||
|
super.registerAddressCallback(addr, func);
|
||||||
|
emu.inject(addr, """
|
||||||
|
__addr_cb();
|
||||||
|
emu_exec_decoded();
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unregisterAddressCallback(Address addr) {
|
||||||
|
emu.clearInject(addr);
|
||||||
|
super.unregisterAddressCallback(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Language language;
|
||||||
|
private final Register pcReg;
|
||||||
|
private final AdaptedPcodeEmulator emu;
|
||||||
|
private final AdaptedPcodeThread thread;
|
||||||
|
private final MemoryState adaptedMemState;
|
||||||
|
private final AdaptedBreakTableCallback adaptedBreakTable;
|
||||||
|
|
||||||
|
private boolean isExecuting = false;
|
||||||
|
private RuntimeException lastError;
|
||||||
|
|
||||||
|
public AdaptedEmulator(EmulatorConfiguration config) {
|
||||||
|
this.language = config.getLanguage();
|
||||||
|
this.pcReg = language.getProgramCounter();
|
||||||
|
if (config.isWriteBackEnabled()) {
|
||||||
|
throw new IllegalArgumentException("write-back is not supported");
|
||||||
|
}
|
||||||
|
// I don't think we use page size directly.
|
||||||
|
|
||||||
|
this.emu = newPcodeEmulator(config);
|
||||||
|
this.thread = emu.newThread();
|
||||||
|
initializeRegisters(config);
|
||||||
|
|
||||||
|
this.adaptedMemState = new AdaptedMemoryState<>(thread.getState(), Reason.INSPECT);
|
||||||
|
this.adaptedBreakTable = new AdaptedBreakTableCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AdaptedPcodeEmulator newPcodeEmulator(EmulatorConfiguration config) {
|
||||||
|
return new AdaptedPcodeEmulator(language, config.getLoadData().getMemoryLoadImage(),
|
||||||
|
config.getMemoryFaultHandler());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void initializeRegisters(EmulatorConfiguration config) {
|
||||||
|
RegisterState initRegs = config.getLoadData().getInitialRegisterState();
|
||||||
|
PcodeExecutorState<byte[]> regState = thread.getState(); // NB. No .getLocalState()
|
||||||
|
for (String key : initRegs.getKeys()) {
|
||||||
|
if (!initRegs.isInitialized(key).get(0)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Register register = language.getRegister(key);
|
||||||
|
if (register == null) {
|
||||||
|
Msg.warn(this, "No such register '" + key + "' in language " + language);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Yes, allow memory-mapped registers to be initialized in this manner
|
||||||
|
byte[] val = initRegs.getVals(key).get(0);
|
||||||
|
// TODO: GDTR/IDTR/LDTR _Limit, _Address stuff.... Is that arch specific?
|
||||||
|
regState.setVar(register, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPCRegisterName() {
|
||||||
|
return pcReg.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setExecuteAddress(long addressableWordOffset) {
|
||||||
|
Address address =
|
||||||
|
language.getDefaultSpace().getTruncatedAddress(addressableWordOffset, true);
|
||||||
|
thread.overrideCounter(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Address getExecuteAddress() {
|
||||||
|
return thread.getCounter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Address getLastExecuteAddress() {
|
||||||
|
return thread.lastExecuteAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getPC() {
|
||||||
|
return Utils.bytesToLong(thread.getState().getVar(pcReg, Reason.INSPECT),
|
||||||
|
pcReg.getNumBytes(), language.isBigEndian());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void executeInstruction(boolean stopAtBreakpoint, TaskMonitor monitor)
|
||||||
|
throws CancelledException, LowlevelError, InstructionDecodeException {
|
||||||
|
if (!(lastError == null || lastError instanceof InterruptPcodeExecutionException)) {
|
||||||
|
throw lastError;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
emu.setSoftwareInterruptMode(stopAtBreakpoint ? SwiMode.ACTIVE : SwiMode.IGNORE_ALL);
|
||||||
|
isExecuting = true;
|
||||||
|
if (thread.getFrame() != null) {
|
||||||
|
thread.finishInstruction();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
thread.stepInstruction();
|
||||||
|
}
|
||||||
|
lastError = null;
|
||||||
|
}
|
||||||
|
catch (RuntimeException e) {
|
||||||
|
lastError = e;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
emu.setSoftwareInterruptMode(SwiMode.ACTIVE);
|
||||||
|
isExecuting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isExecuting() {
|
||||||
|
return isExecuting;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EmulateExecutionState getEmulateExecutionState() {
|
||||||
|
if (lastError instanceof InterruptPcodeExecutionException) {
|
||||||
|
return EmulateExecutionState.BREAKPOINT;
|
||||||
|
}
|
||||||
|
if (lastError != null) {
|
||||||
|
return EmulateExecutionState.FAULT;
|
||||||
|
}
|
||||||
|
PcodeFrame frame = thread.getFrame();
|
||||||
|
if (frame != null) {
|
||||||
|
return EmulateExecutionState.EXECUTE;
|
||||||
|
}
|
||||||
|
if (isExecuting) {
|
||||||
|
return EmulateExecutionState.INSTRUCTION_DECODE;
|
||||||
|
}
|
||||||
|
return EmulateExecutionState.STOPPED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MemoryState getMemState() {
|
||||||
|
return adaptedMemState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addMemoryAccessFilter(MemoryAccessFilter filter) {
|
||||||
|
filter.addFilter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FilteredMemoryState getFilteredMemState() {
|
||||||
|
// Just a dummy to prevent NPEs
|
||||||
|
return new FilteredMemoryState(language);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setContextRegisterValue(RegisterValue regValue) {
|
||||||
|
if (regValue == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
thread.overrideContext(regValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RegisterValue getContextRegisterValue() {
|
||||||
|
return thread.getContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BreakTableCallBack getBreakTable() {
|
||||||
|
return adaptedBreakTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAtBreakpoint() {
|
||||||
|
return lastError instanceof InterruptPcodeExecutionException;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setHalt(boolean halt) {
|
||||||
|
thread.setSuspended(halt);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getHalt() {
|
||||||
|
return thread.isSuspended();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.emulator;
|
||||||
|
|
||||||
|
import javax.help.UnsupportedOperationException;
|
||||||
|
|
||||||
|
import ghidra.lifecycle.Transitional;
|
||||||
|
import ghidra.pcode.emu.ModifiedPcodeThread;
|
||||||
|
import ghidra.pcode.emu.PcodeEmulator;
|
||||||
|
import ghidra.pcode.emulate.EmulateInstructionStateModifier;
|
||||||
|
import ghidra.pcode.exec.PcodeArithmetic;
|
||||||
|
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||||
|
import ghidra.pcode.exec.PcodeExecutorState;
|
||||||
|
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||||
|
import ghidra.pcode.memstate.AbstractMemoryState;
|
||||||
|
import ghidra.pcode.memstate.MemoryBank;
|
||||||
|
import ghidra.program.model.address.AddressSpace;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of {@link MemoryState} which wraps a newer {@link PcodeExecutorState}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This is a transitional component used internally by the {@link AdaptedEmulator}. It is also used
|
||||||
|
* in the {@link ModifiedPcodeThread}, which is part of the newer {@link PcodeEmulator} system, as a
|
||||||
|
* means of incorporating {@link EmulateInstructionStateModifier}, which is part of the older
|
||||||
|
* {@link EmulatorHelper} system. This class will be removed once both conditions are met:
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>An equivalent state modification system is developed for the {@link PcodeEmulator} system,
|
||||||
|
* and each {@link EmulateInstructionStateModifier} is ported to it.</li>
|
||||||
|
* <li>The {@link AdaptedEmulator} class is removed.</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Guidance for the use of this class is the same as {@link AdaptedEmulator}.
|
||||||
|
*
|
||||||
|
* @param <T> the type of values in the wrapped state. This matters not to {@link EmulatorHelper},
|
||||||
|
* so long as {@link T} can be made concrete.
|
||||||
|
*/
|
||||||
|
@Transitional
|
||||||
|
public class AdaptedMemoryState<T> extends AbstractMemoryState {
|
||||||
|
private final PcodeExecutorState<T> state;
|
||||||
|
private final PcodeArithmetic<T> arithmetic;
|
||||||
|
private final Reason reason;
|
||||||
|
|
||||||
|
public AdaptedMemoryState(PcodeExecutorState<T> state, Reason reason) {
|
||||||
|
super(state.getLanguage());
|
||||||
|
this.state = state;
|
||||||
|
this.arithmetic = state.getArithmetic();
|
||||||
|
this.reason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setMemoryBank(MemoryBank bank) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MemoryBank getMemoryBank(AddressSpace spc) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getChunk(byte[] res, AddressSpace spc, long off, int size,
|
||||||
|
boolean stopOnUnintialized) {
|
||||||
|
T t = state.getVar(spc, off, size, true, reason);
|
||||||
|
byte[] val = arithmetic.toConcrete(t, Purpose.OTHER);
|
||||||
|
System.arraycopy(val, 0, res, 0, val.length);
|
||||||
|
return val.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setChunk(byte[] val, AddressSpace spc, long off, int size) {
|
||||||
|
T t = arithmetic.fromConst(val);
|
||||||
|
state.setVar(spc, off, size, true, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setInitialized(boolean initialized, AddressSpace spc, long off, int size) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,479 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.emulator;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import ghidra.app.emulator.memory.*;
|
||||||
|
import ghidra.app.emulator.state.*;
|
||||||
|
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||||
|
import ghidra.pcode.emu.PcodeEmulator;
|
||||||
|
import ghidra.pcode.emulate.*;
|
||||||
|
import ghidra.pcode.error.LowlevelError;
|
||||||
|
import ghidra.pcode.memstate.*;
|
||||||
|
import ghidra.program.disassemble.Disassembler;
|
||||||
|
import ghidra.program.model.address.*;
|
||||||
|
import ghidra.program.model.lang.*;
|
||||||
|
import ghidra.program.model.listing.Instruction;
|
||||||
|
import ghidra.util.*;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default implementation of {@link Emulator}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This class used to be named {@code Emulator}, until it was replaced by an interface extracted
|
||||||
|
* from this class. There is now a second implementation named {@link AdaptedEmulator}, which wraps
|
||||||
|
* the newer {@link PcodeEmulator} system. If you are developing a new use case based on p-code
|
||||||
|
* emulation, please consider using {@link PcodeEmulator} directly. There are several example
|
||||||
|
* scripts in the {@code SystemEmulation} module. If you are maintaining an existing use case
|
||||||
|
* currently based on {@link Emulator}, you will at least need to change {@code new Emulator(...)}
|
||||||
|
* to {@code new DefaultEmulator(...)}. It is highly recommended to port to the newer
|
||||||
|
* {@link PcodeEmulator}. You may find the {@link AdaptedEmulator} useful during the transition, but
|
||||||
|
* that class is only transitional.
|
||||||
|
*/
|
||||||
|
public class DefaultEmulator implements Emulator {
|
||||||
|
|
||||||
|
private final MemoryFaultHandler faultHandler;
|
||||||
|
|
||||||
|
private SleighLanguage language;
|
||||||
|
private AddressFactory addrFactory;
|
||||||
|
|
||||||
|
private CompositeLoadImage loadImage = new CompositeLoadImage();
|
||||||
|
|
||||||
|
private RegisterState mstate;
|
||||||
|
private MemoryPageBank registerState;
|
||||||
|
private FilteredMemoryState memState;
|
||||||
|
private ghidra.pcode.emulate.BreakTableCallBack breakTable;
|
||||||
|
private Emulate emulator;
|
||||||
|
|
||||||
|
private boolean emuHalt = true;
|
||||||
|
private boolean isExecuting = false;
|
||||||
|
|
||||||
|
private boolean writeBack = false;
|
||||||
|
private int pageSize; // The preferred page size for a paged memory state
|
||||||
|
|
||||||
|
private String pcName;
|
||||||
|
private long initialPC;
|
||||||
|
private int instExecuted = 0;
|
||||||
|
|
||||||
|
public DefaultEmulator(EmulatorConfiguration cfg) {
|
||||||
|
|
||||||
|
this.faultHandler = cfg.getMemoryFaultHandler();
|
||||||
|
|
||||||
|
pcName = cfg.getProgramCounterName();
|
||||||
|
writeBack = cfg.isWriteBackEnabled();
|
||||||
|
pageSize = cfg.getPreferredMemoryPageSize();
|
||||||
|
|
||||||
|
Language lang = cfg.getLanguage();
|
||||||
|
if (!(lang instanceof SleighLanguage)) {
|
||||||
|
throw new IllegalArgumentException("Invalid configuartion language [" +
|
||||||
|
lang.getLanguageID() + "]: only Sleigh languages are supported by emulator");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: The way this is currently done, we are unable to emulate within overlay spaces
|
||||||
|
// The addrFactory should be obtained memState which is a reversal
|
||||||
|
// When a program load image is used the addrFactory should come from the program and
|
||||||
|
// not the language. Things may also get complex in terms of handling loads/stores and
|
||||||
|
// flow associated with overlays.
|
||||||
|
|
||||||
|
language = (SleighLanguage) lang;
|
||||||
|
addrFactory = lang.getAddressFactory();
|
||||||
|
|
||||||
|
EmulatorLoadData load = cfg.getLoadData();
|
||||||
|
loadImage.addProvider(load.getMemoryLoadImage(), load.getView());
|
||||||
|
mstate = load.getInitialRegisterState();
|
||||||
|
|
||||||
|
initMemState(mstate);
|
||||||
|
|
||||||
|
breakTable = new BreakTableCallBack(language);
|
||||||
|
emulator = new Emulate(language, memState, breakTable);
|
||||||
|
|
||||||
|
try {
|
||||||
|
setExecuteAddress(initialPC);
|
||||||
|
}
|
||||||
|
catch (LowlevelError lle) {
|
||||||
|
Msg.warn(this, "pc is unmappable -- no execution possible");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the page size to use with a specific AddressSpace. The page containers (MemoryBank)
|
||||||
|
* assume page size is always power of 2. Any address space is assigned at least 8-bits of
|
||||||
|
* addressable locations, so at the very least, the size is divisible by 256. Starting with this
|
||||||
|
* minimum, this method finds the power of 2 that is closest to the preferred page size
|
||||||
|
* (pageSize) but that still divides the size of the space.
|
||||||
|
*
|
||||||
|
* @param space is the specific AddressSpace
|
||||||
|
* @return the page size to use
|
||||||
|
*/
|
||||||
|
private int getValidPageSize(AddressSpace space) {
|
||||||
|
int ps = 256; // Minimum page size supported
|
||||||
|
long spaceSize = space.getMaxAddress().getOffset() + 1; // Number of bytes in the space (0 if 2^64 bytes)
|
||||||
|
if ((spaceSize & 0xff) != 0) {
|
||||||
|
Msg.warn(this, "Emulator using page size of 256 bytes for " + space.getName() +
|
||||||
|
" which is NOT a multiple of 256");
|
||||||
|
return ps;
|
||||||
|
}
|
||||||
|
spaceSize >>>= 8; // Divide required size by 256 (evenly)
|
||||||
|
while (ps < pageSize) { // If current page size is smaller than preferred page size
|
||||||
|
if ((spaceSize & 1) != 0) {
|
||||||
|
break; // a bigger page size does not divide the space size evenly, so use current size
|
||||||
|
}
|
||||||
|
ps <<= 1; // Bump up current page size to next power of 2
|
||||||
|
spaceSize >>>= 1; // Divide (evenly) by 2
|
||||||
|
}
|
||||||
|
|
||||||
|
return ps;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initMemState(RegisterState rstate) {
|
||||||
|
|
||||||
|
memState = new FilteredMemoryState(language);
|
||||||
|
|
||||||
|
for (AddressSpace space : addrFactory.getPhysicalSpaces()) {
|
||||||
|
if (!space.isLoadedMemorySpace()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
FilteredMemoryPageOverlay ramBank = getMemoryBank(space, getValidPageSize(space));
|
||||||
|
memState.setMemoryBank(ramBank);
|
||||||
|
}
|
||||||
|
|
||||||
|
AddressSpace registerSpace = addrFactory.getRegisterSpace();
|
||||||
|
registerState = new FilteredRegisterBank(registerSpace, pageSize, rstate, language,
|
||||||
|
writeBack, faultHandler);
|
||||||
|
|
||||||
|
memState.setMemoryBank(registerState);
|
||||||
|
|
||||||
|
initRegisters(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MemoryState cloneMemory() {
|
||||||
|
MemoryState newMemState = new FilteredMemoryState(language);
|
||||||
|
|
||||||
|
for (AddressSpace space : addrFactory.getPhysicalSpaces()) {
|
||||||
|
if (!space.isLoadedMemorySpace()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
FilteredMemoryPageOverlay ramBank = getMemoryBank(space, getValidPageSize(space));
|
||||||
|
newMemState.setMemoryBank(ramBank);
|
||||||
|
}
|
||||||
|
return newMemState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FilteredMemoryPageOverlay getMemoryBank(AddressSpace space, int ps) {
|
||||||
|
MemoryImage image =
|
||||||
|
new MemoryImage(space, language.isBigEndian(), ps, loadImage, faultHandler);
|
||||||
|
return new FilteredMemoryPageOverlay(space, image, writeBack);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize memory state using the initial register state. If restore is true, only those
|
||||||
|
* registers within the register space which have been modified will be reported and restored to
|
||||||
|
* their initial state.
|
||||||
|
*
|
||||||
|
* @param restore if true restore modified registers within the register space only
|
||||||
|
*/
|
||||||
|
private void initRegisters(boolean restore) {
|
||||||
|
DataConverter conv = DataConverter.getInstance(language.isBigEndian());
|
||||||
|
Set<String> keys = mstate.getKeys();
|
||||||
|
for (String key : keys) {
|
||||||
|
List<byte[]> vals = mstate.getVals(key);
|
||||||
|
List<Boolean> initiailizedVals = mstate.isInitialized(key);
|
||||||
|
for (int i = 0; i < vals.size(); i++) {
|
||||||
|
String useKey = "";
|
||||||
|
if (key.equals("GDTR") || key.equals("IDTR") || key.equals("LDTR")) {
|
||||||
|
if (i == 0) {
|
||||||
|
useKey = key + "_Limit";
|
||||||
|
}
|
||||||
|
if (i == 1) {
|
||||||
|
useKey = key + "_Address";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (key.equals("S.base")) {
|
||||||
|
Integer lval = conv.getInt(vals.get(i));
|
||||||
|
if (lval != 0 && i < vals.size() - 1) {
|
||||||
|
useKey = "FS_OFFSET"; // Colossal hack
|
||||||
|
memState.setValue("FS", (i + 2) * 0x8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
useKey = (vals.size() > 1) ? key + i : key;
|
||||||
|
}
|
||||||
|
Register register = language.getRegister(useKey);
|
||||||
|
if (register == null) {
|
||||||
|
useKey = useKey.toUpperCase();
|
||||||
|
register = language.getRegister(useKey);
|
||||||
|
}
|
||||||
|
if (register != null) {
|
||||||
|
if (restore && !register.getAddress().isRegisterAddress()) {
|
||||||
|
continue; // only restore registers within register space
|
||||||
|
}
|
||||||
|
byte[] valBytes = vals.get(i);
|
||||||
|
boolean initializedValue = initiailizedVals.get(i);
|
||||||
|
|
||||||
|
Address regAddr = register.getAddress();
|
||||||
|
|
||||||
|
if (restore) {
|
||||||
|
byte[] curVal = new byte[valBytes.length];
|
||||||
|
memState.getChunk(curVal, regAddr.getAddressSpace(), regAddr.getOffset(),
|
||||||
|
register.getMinimumByteSize(), false);
|
||||||
|
if (Arrays.equals(curVal, valBytes)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
System.out.println(
|
||||||
|
"resetRegisters : " + useKey + "=" + dumpBytesAsSingleValue(valBytes) +
|
||||||
|
"->" + dumpBytesAsSingleValue(curVal));
|
||||||
|
}
|
||||||
|
|
||||||
|
memState.setChunk(valBytes, regAddr.getAddressSpace(), regAddr.getOffset(),
|
||||||
|
register.getMinimumByteSize());
|
||||||
|
|
||||||
|
if (!initializedValue) {
|
||||||
|
memState.setInitialized(false, regAddr.getAddressSpace(),
|
||||||
|
regAddr.getOffset(), register.getMinimumByteSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (register.isProgramCounter() ||
|
||||||
|
register.getName().equalsIgnoreCase(pcName)) {
|
||||||
|
initialPC = conv.getValue(valBytes, valBytes.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String dumpBytesAsSingleValue(byte[] bytes) {
|
||||||
|
StringBuffer buf = new StringBuffer("0x");
|
||||||
|
if (language.isBigEndian()) {
|
||||||
|
for (byte b : bytes) {
|
||||||
|
String byteStr = Integer.toHexString(b & 0xff);
|
||||||
|
if (byteStr.length() == 1) {
|
||||||
|
buf.append('0');
|
||||||
|
}
|
||||||
|
buf.append(byteStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (int i = bytes.length - 1; i >= 0; i--) {
|
||||||
|
String byteStr = Integer.toHexString(bytes[i] & 0xff);
|
||||||
|
if (byteStr.length() == 1) {
|
||||||
|
buf.append('0');
|
||||||
|
}
|
||||||
|
buf.append(byteStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
emuHalt = true;
|
||||||
|
emulator.dispose();
|
||||||
|
if (writeBack) {
|
||||||
|
initRegisters(true);
|
||||||
|
mstate.dispose();
|
||||||
|
}
|
||||||
|
loadImage.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Address genAddress(String addr) {
|
||||||
|
return addrFactory.getDefaultAddressSpace().getAddress(NumericUtilities.parseHexLong(addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getPC() {
|
||||||
|
return memState.getValue(pcName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPCRegisterName() {
|
||||||
|
return pcName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MemoryState getMemState() {
|
||||||
|
return memState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FilteredMemoryState getFilteredMemState() {
|
||||||
|
return memState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addMemoryAccessFilter(MemoryAccessFilter filter) {
|
||||||
|
filter.addFilter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BreakTableCallBack getBreakTable() {
|
||||||
|
return breakTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setExecuteAddress(long addressableWordOffset) {
|
||||||
|
AddressSpace space = addrFactory.getDefaultAddressSpace();
|
||||||
|
Address address = space.getTruncatedAddress(addressableWordOffset, true);
|
||||||
|
emulator.setExecuteAddress(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Address getExecuteAddress() {
|
||||||
|
return emulator.getExecuteAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Address getLastExecuteAddress() {
|
||||||
|
return emulator.getLastExecuteAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getDefaultContext() {
|
||||||
|
return mstate.getKeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setHalt(boolean halt) {
|
||||||
|
emuHalt = halt;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getHalt() {
|
||||||
|
return emuHalt;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void executeInstruction(boolean stopAtBreakpoint, TaskMonitor monitor)
|
||||||
|
throws CancelledException, LowlevelError, InstructionDecodeException {
|
||||||
|
isExecuting = true;
|
||||||
|
try {
|
||||||
|
emulator.executeInstruction(stopAtBreakpoint, monitor);
|
||||||
|
instExecuted++;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
isExecuting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAtBreakpoint() {
|
||||||
|
return getHalt() && emulator.getExecutionState() == EmulateExecutionState.BREAKPOINT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return emulator execution state. This can be useful within a memory fault handler to
|
||||||
|
* determine if a memory read was associated with instruction parsing (i.e., PCODE_EMIT)
|
||||||
|
* or normal an actual emulated read (i.e., EXECUTE).
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public EmulateExecutionState getEmulateExecutionState() {
|
||||||
|
return emulator.getExecutionState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isExecuting() {
|
||||||
|
return isExecuting;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SleighLanguage getLanguage() {
|
||||||
|
return language;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disassemble from the current execute address
|
||||||
|
*
|
||||||
|
* @param count number of contiguous instructions to disassemble
|
||||||
|
* @return list of instructions
|
||||||
|
*/
|
||||||
|
public List<String> disassemble(Integer count) {
|
||||||
|
if (!emuHalt || isExecuting) {
|
||||||
|
throw new IllegalStateException("disassembly not allowed while emulator is executing");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: This can provide bad disassembly if reliant on future context state (e.g., end of loop)
|
||||||
|
|
||||||
|
List<String> disassembly = new ArrayList<>();
|
||||||
|
|
||||||
|
EmulateDisassemblerContext disassemblerContext = emulator.getNewDisassemblerContext();
|
||||||
|
Address addr = getExecuteAddress();
|
||||||
|
EmulateMemoryStateBuffer memBuffer = new EmulateMemoryStateBuffer(memState, addr);
|
||||||
|
|
||||||
|
Disassembler disassembler = Disassembler.getDisassembler(language, addrFactory,
|
||||||
|
TaskMonitor.DUMMY, null);
|
||||||
|
|
||||||
|
boolean stopOnError = false;
|
||||||
|
|
||||||
|
while (count > 0 && !stopOnError) {
|
||||||
|
memBuffer.setAddress(addr);
|
||||||
|
disassemblerContext.setCurrentAddress(addr);
|
||||||
|
|
||||||
|
InstructionBlock block = disassembler.pseudoDisassembleBlock(memBuffer,
|
||||||
|
disassemblerContext.getCurrentContextRegisterValue(), count);
|
||||||
|
|
||||||
|
if (block.hasInstructionError() && count > block.getInstructionCount()) {
|
||||||
|
InstructionError instructionError = block.getInstructionConflict();
|
||||||
|
Msg.error(this,
|
||||||
|
"Target disassembler error at " + instructionError.getConflictAddress() + ": " +
|
||||||
|
instructionError.getConflictMessage());
|
||||||
|
stopOnError = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction lastInstr = null;
|
||||||
|
Iterator<Instruction> iterator = block.iterator();
|
||||||
|
while (iterator.hasNext() && count != 0) {
|
||||||
|
Instruction instr = iterator.next();
|
||||||
|
disassembly.add(instr.getAddressString(false, true) + " " + instr.toString());
|
||||||
|
lastInstr = instr;
|
||||||
|
--count;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
addr = lastInstr.getAddress().addNoWrap(lastInstr.getLength());
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return disassembly;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTickCount() {
|
||||||
|
return instExecuted;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RegisterValue getContextRegisterValue() {
|
||||||
|
return emulator.getContextRegisterValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setContextRegisterValue(RegisterValue regValue) {
|
||||||
|
emulator.setContextRegisterValue(regValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add memory load image provider
|
||||||
|
*
|
||||||
|
* @param provider memory load image provider
|
||||||
|
* @param view memory region which corresponds to provider
|
||||||
|
*/
|
||||||
|
public void addProvider(MemoryLoadImage provider, AddressSetView view) {
|
||||||
|
loadImage.addProvider(provider, view);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -15,453 +15,162 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.emulator;
|
package ghidra.app.emulator;
|
||||||
|
|
||||||
import java.util.*;
|
import ghidra.pcode.emu.PcodeEmulator;
|
||||||
|
|
||||||
import ghidra.app.emulator.memory.*;
|
|
||||||
import ghidra.app.emulator.state.*;
|
|
||||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
|
||||||
import ghidra.pcode.emulate.*;
|
import ghidra.pcode.emulate.*;
|
||||||
import ghidra.pcode.error.LowlevelError;
|
import ghidra.pcode.error.LowlevelError;
|
||||||
import ghidra.pcode.memstate.*;
|
import ghidra.pcode.memstate.MemoryState;
|
||||||
import ghidra.program.disassemble.Disassembler;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.lang.RegisterValue;
|
||||||
import ghidra.program.model.lang.*;
|
|
||||||
import ghidra.program.model.listing.Instruction;
|
|
||||||
import ghidra.util.*;
|
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
public class Emulator {
|
/**
|
||||||
|
* The emulator interface
|
||||||
private final MemoryFaultHandler faultHandler;
|
*
|
||||||
|
* <p>
|
||||||
private SleighLanguage language;
|
* This interface may soon be deprecated. It was extracted from what has now been renamed
|
||||||
private AddressFactory addrFactory;
|
* {@link DefaultEmulator}. Please consider using {@link PcodeEmulator} instead.
|
||||||
|
*/
|
||||||
private CompositeLoadImage loadImage = new CompositeLoadImage();
|
public interface Emulator {
|
||||||
|
|
||||||
private RegisterState mstate;
|
|
||||||
private MemoryPageBank registerState;
|
|
||||||
private FilteredMemoryState memState;
|
|
||||||
private ghidra.pcode.emulate.BreakTableCallBack breakTable;
|
|
||||||
private Emulate emulator;
|
|
||||||
|
|
||||||
private boolean emuHalt = true;
|
|
||||||
private boolean isExecuting = false;
|
|
||||||
|
|
||||||
private boolean writeBack = false;
|
|
||||||
private int pageSize; // The preferred page size for a paged memory state
|
|
||||||
|
|
||||||
private String pcName;
|
|
||||||
private long initialPC;
|
|
||||||
private int instExecuted = 0;
|
|
||||||
|
|
||||||
public Emulator(EmulatorConfiguration cfg) {
|
|
||||||
|
|
||||||
this.faultHandler = cfg.getMemoryFaultHandler();
|
|
||||||
|
|
||||||
pcName = cfg.getProgramCounterName();
|
|
||||||
writeBack = cfg.isWriteBackEnabled();
|
|
||||||
pageSize = cfg.getPreferredMemoryPageSize();
|
|
||||||
|
|
||||||
Language lang = cfg.getLanguage();
|
|
||||||
if (!(lang instanceof SleighLanguage)) {
|
|
||||||
throw new IllegalArgumentException("Invalid configuartion language [" +
|
|
||||||
lang.getLanguageID() + "]: only Sleigh languages are supported by emulator");
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: The way this is currently done, we are unable to emulate within overlay spaces
|
|
||||||
// The addrFactory should be obtained memState which is a reversal
|
|
||||||
// When a program load image is used the addrFactory should come from the program and
|
|
||||||
// not the language. Things may also get complex in terms of handling loads/stores and
|
|
||||||
// flow associated with overlays.
|
|
||||||
|
|
||||||
language = (SleighLanguage) lang;
|
|
||||||
addrFactory = lang.getAddressFactory();
|
|
||||||
|
|
||||||
EmulatorLoadData load = cfg.getLoadData();
|
|
||||||
loadImage.addProvider(load.getMemoryLoadImage(), load.getView());
|
|
||||||
mstate = load.getInitialRegisterState();
|
|
||||||
|
|
||||||
initMemState(mstate);
|
|
||||||
|
|
||||||
breakTable = new BreakTableCallBack(language);
|
|
||||||
emulator = new Emulate(language, memState, breakTable);
|
|
||||||
|
|
||||||
try {
|
|
||||||
setExecuteAddress(initialPC);
|
|
||||||
}
|
|
||||||
catch (LowlevelError lle) {
|
|
||||||
Msg.warn(this, "pc is unmappable -- no execution possible");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the page size to use with a specific AddressSpace. The page containers (MemoryBank)
|
* Get the name of the program counter register
|
||||||
* assume page size is always power of 2. Any address space is assigned at least 8-bits of
|
*
|
||||||
* addressable locations, so at the very least, the size is divisible by 256. Starting with this
|
* @return the name
|
||||||
* minimum, this method finds the power of 2 that is closest to the preferred page size (pageSize)
|
|
||||||
* but that still divides the size of the space.
|
|
||||||
* @param space is the specific AddressSpace
|
|
||||||
* @return the page size to use
|
|
||||||
*/
|
*/
|
||||||
private int getValidPageSize(AddressSpace space) {
|
String getPCRegisterName();
|
||||||
int ps = 256; // Minimum page size supported
|
|
||||||
long spaceSize = space.getMaxAddress().getOffset() + 1; // Number of bytes in the space (0 if 2^64 bytes)
|
|
||||||
if ((spaceSize & 0xff) != 0) {
|
|
||||||
Msg.warn(this, "Emulator using page size of 256 bytes for " + space.getName() +
|
|
||||||
" which is NOT a multiple of 256");
|
|
||||||
return ps;
|
|
||||||
}
|
|
||||||
spaceSize >>>= 8; // Divide required size by 256 (evenly)
|
|
||||||
while (ps < pageSize) { // If current page size is smaller than preferred page size
|
|
||||||
if ((spaceSize & 1) != 0) {
|
|
||||||
break; // a bigger page size does not divide the space size evenly, so use current size
|
|
||||||
}
|
|
||||||
ps <<= 1; // Bump up current page size to next power of 2
|
|
||||||
spaceSize >>>= 1; // Divide (evenly) by 2
|
|
||||||
}
|
|
||||||
|
|
||||||
return ps;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initMemState(RegisterState rstate) {
|
|
||||||
|
|
||||||
memState = new FilteredMemoryState(language);
|
|
||||||
|
|
||||||
for (AddressSpace space : addrFactory.getPhysicalSpaces()) {
|
|
||||||
if (!space.isLoadedMemorySpace()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
FilteredMemoryPageOverlay ramBank = getMemoryBank(space, getValidPageSize(space));
|
|
||||||
memState.setMemoryBank(ramBank);
|
|
||||||
}
|
|
||||||
|
|
||||||
AddressSpace registerSpace = addrFactory.getRegisterSpace();
|
|
||||||
registerState = new FilteredRegisterBank(registerSpace, pageSize, rstate, language,
|
|
||||||
writeBack, faultHandler);
|
|
||||||
|
|
||||||
memState.setMemoryBank(registerState);
|
|
||||||
|
|
||||||
initRegisters(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MemoryState cloneMemory() {
|
|
||||||
MemoryState newMemState = new FilteredMemoryState(language);
|
|
||||||
|
|
||||||
for (AddressSpace space : addrFactory.getPhysicalSpaces()) {
|
|
||||||
if (!space.isLoadedMemorySpace()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
FilteredMemoryPageOverlay ramBank = getMemoryBank(space, getValidPageSize(space));
|
|
||||||
newMemState.setMemoryBank(ramBank);
|
|
||||||
}
|
|
||||||
return newMemState;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FilteredMemoryPageOverlay getMemoryBank(AddressSpace space, int ps) {
|
|
||||||
MemoryImage image =
|
|
||||||
new MemoryImage(space, language.isBigEndian(), ps, loadImage, faultHandler);
|
|
||||||
return new FilteredMemoryPageOverlay(space, image, writeBack);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize memory state using the initial register state. If restore is true,
|
* Set the value of the program counter
|
||||||
* only those registers within the register space which have been modified will
|
*
|
||||||
* be reported and restored to their initial state.
|
* @param addressableWordOffset the <em>word</em> offset of the instruction to execute next.
|
||||||
* @param restore if true restore modified registers within the register space only
|
|
||||||
*/
|
*/
|
||||||
private void initRegisters(boolean restore) {
|
void setExecuteAddress(long addressableWordOffset);
|
||||||
DataConverter conv = DataConverter.getInstance(language.isBigEndian());
|
|
||||||
Set<String> keys = mstate.getKeys();
|
|
||||||
for (String key : keys) {
|
|
||||||
List<byte[]> vals = mstate.getVals(key);
|
|
||||||
List<Boolean> initiailizedVals = mstate.isInitialized(key);
|
|
||||||
for (int i = 0; i < vals.size(); i++) {
|
|
||||||
String useKey = "";
|
|
||||||
if (key.equals("GDTR") || key.equals("IDTR") || key.equals("LDTR")) {
|
|
||||||
if (i == 0) {
|
|
||||||
useKey = key + "_Limit";
|
|
||||||
}
|
|
||||||
if (i == 1) {
|
|
||||||
useKey = key + "_Address";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (key.equals("S.base")) {
|
|
||||||
Integer lval = conv.getInt(vals.get(i));
|
|
||||||
if (lval != 0 && i < vals.size() - 1) {
|
|
||||||
useKey = "FS_OFFSET"; // Colossal hack
|
|
||||||
memState.setValue("FS", (i + 2) * 0x8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
useKey = (vals.size() > 1) ? key + i : key;
|
|
||||||
}
|
|
||||||
Register register = language.getRegister(useKey);
|
|
||||||
if (register == null) {
|
|
||||||
useKey = useKey.toUpperCase();
|
|
||||||
register = language.getRegister(useKey);
|
|
||||||
}
|
|
||||||
if (register != null) {
|
|
||||||
if (restore && !register.getAddress().isRegisterAddress()) {
|
|
||||||
continue; // only restore registers within register space
|
|
||||||
}
|
|
||||||
byte[] valBytes = vals.get(i);
|
|
||||||
boolean initializedValue = initiailizedVals.get(i);
|
|
||||||
|
|
||||||
Address regAddr = register.getAddress();
|
|
||||||
|
|
||||||
if (restore) {
|
|
||||||
byte[] curVal = new byte[valBytes.length];
|
|
||||||
memState.getChunk(curVal, regAddr.getAddressSpace(), regAddr.getOffset(),
|
|
||||||
register.getMinimumByteSize(), false);
|
|
||||||
if (Arrays.equals(curVal, valBytes)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
System.out.println(
|
|
||||||
"resetRegisters : " + useKey + "=" + dumpBytesAsSingleValue(valBytes) +
|
|
||||||
"->" + dumpBytesAsSingleValue(curVal));
|
|
||||||
}
|
|
||||||
|
|
||||||
memState.setChunk(valBytes, regAddr.getAddressSpace(), regAddr.getOffset(),
|
|
||||||
register.getMinimumByteSize());
|
|
||||||
|
|
||||||
if (!initializedValue) {
|
|
||||||
memState.setInitialized(false, regAddr.getAddressSpace(),
|
|
||||||
regAddr.getOffset(), register.getMinimumByteSize());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (register.isProgramCounter() ||
|
|
||||||
register.getName().equalsIgnoreCase(pcName)) {
|
|
||||||
initialPC = conv.getValue(valBytes, valBytes.length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String dumpBytesAsSingleValue(byte[] bytes) {
|
|
||||||
StringBuffer buf = new StringBuffer("0x");
|
|
||||||
if (language.isBigEndian()) {
|
|
||||||
for (byte b : bytes) {
|
|
||||||
String byteStr = Integer.toHexString(b & 0xff);
|
|
||||||
if (byteStr.length() == 1) {
|
|
||||||
buf.append('0');
|
|
||||||
}
|
|
||||||
buf.append(byteStr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (int i = bytes.length - 1; i >= 0; i--) {
|
|
||||||
String byteStr = Integer.toHexString(bytes[i] & 0xff);
|
|
||||||
if (byteStr.length() == 1) {
|
|
||||||
buf.append('0');
|
|
||||||
}
|
|
||||||
buf.append(byteStr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buf.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dispose() {
|
|
||||||
emuHalt = true;
|
|
||||||
emulator.dispose();
|
|
||||||
if (writeBack) {
|
|
||||||
initRegisters(true);
|
|
||||||
mstate.dispose();
|
|
||||||
}
|
|
||||||
loadImage.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Address genAddress(String addr) {
|
|
||||||
return addrFactory.getDefaultAddressSpace().getAddress(NumericUtilities.parseHexLong(addr));
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getPC() {
|
|
||||||
return memState.getValue(pcName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPCRegisterName() {
|
|
||||||
return pcName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MemoryState getMemState() {
|
|
||||||
return memState;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FilteredMemoryState getFilteredMemState() {
|
|
||||||
return memState;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addMemoryAccessFilter(MemoryAccessFilter filter) {
|
|
||||||
filter.addFilter(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BreakTableCallBack getBreakTable() {
|
|
||||||
return breakTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setExecuteAddress(long addressableWordOffset) {
|
|
||||||
AddressSpace space = addrFactory.getDefaultAddressSpace();
|
|
||||||
Address address = space.getTruncatedAddress(addressableWordOffset, true);
|
|
||||||
emulator.setExecuteAddress(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Address getExecuteAddress() {
|
|
||||||
return emulator.getExecuteAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Address getLastExecuteAddress() {
|
|
||||||
return emulator.getLastExecuteAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<String> getDefaultContext() {
|
|
||||||
return mstate.getKeys();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setHalt(boolean halt) {
|
|
||||||
emuHalt = halt;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getHalt() {
|
|
||||||
return emuHalt;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void executeInstruction(boolean stopAtBreakpoint, TaskMonitor monitor)
|
|
||||||
throws CancelledException, LowlevelError, InstructionDecodeException {
|
|
||||||
isExecuting = true;
|
|
||||||
try {
|
|
||||||
emulator.executeInstruction(stopAtBreakpoint, monitor);
|
|
||||||
instExecuted++;
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
isExecuting = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if halted at a breakpoint
|
* Get current execution address (or the address of the next instruction to be executed)
|
||||||
|
*
|
||||||
|
* @return current execution address
|
||||||
*/
|
*/
|
||||||
public boolean isAtBreakpoint() {
|
Address getExecuteAddress();
|
||||||
return getHalt() && emulator.getExecutionState() == EmulateExecutionState.BREAKPOINT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return emulator execution state. This can be useful within a memory fault handler to
|
* Get the address of the last instruction executed (or the instructed currently being executed)
|
||||||
* determine if a memory read was associated with instruction parsing (i.e., PCODE_EMIT) or
|
*
|
||||||
* normal an actual emulated read (i.e., EXECUTE).
|
* @return the address
|
||||||
*/
|
*/
|
||||||
public EmulateExecutionState getEmulateExecutionState() {
|
Address getLastExecuteAddress();
|
||||||
return emulator.getExecutionState();
|
|
||||||
}
|
/**
|
||||||
|
* Get the value of the program counter
|
||||||
|
*
|
||||||
|
* @return the value, i.e., offset in code space
|
||||||
|
*/
|
||||||
|
long getPC();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute instruction at current address
|
||||||
|
*
|
||||||
|
* @param stopAtBreakpoint if true and breakpoint hits at current execution address execution
|
||||||
|
* will halt without executing instruction.
|
||||||
|
* @throws CancelledException if execution was cancelled
|
||||||
|
*/
|
||||||
|
void executeInstruction(boolean stopAtBreakpoint, TaskMonitor monitor)
|
||||||
|
throws CancelledException, LowlevelError, InstructionDecodeException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if emulator is busy executing an instruction
|
* @return true if emulator is busy executing an instruction
|
||||||
*/
|
*/
|
||||||
public boolean isExecuting() {
|
boolean isExecuting();
|
||||||
return isExecuting;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SleighLanguage getLanguage() {
|
|
||||||
return language;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disassemble from the current execute address
|
* Get the low-level execution state
|
||||||
* @param count number of contiguous instructions to disassemble
|
*
|
||||||
* @return list of instructions
|
* <p>
|
||||||
|
* This can be useful within a memory fault handler to determine if a memory read was associated
|
||||||
|
* with instruction parsing (i.e., {@link EmulateExecutionState#INSTRUCTION_DECODE}) or an
|
||||||
|
* actual emulated read (i.e., {@link EmulateExecutionState#EXECUTE}).
|
||||||
|
*
|
||||||
|
* @return emulator execution state.
|
||||||
*/
|
*/
|
||||||
public List<String> disassemble(Integer count) {
|
EmulateExecutionState getEmulateExecutionState();
|
||||||
if (!emuHalt || isExecuting) {
|
|
||||||
throw new IllegalStateException("disassembly not allowed while emulator is executing");
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: This can provide bad disassembly if reliant on future context state (e.g., end of loop)
|
|
||||||
|
|
||||||
List<String> disassembly = new ArrayList<>();
|
|
||||||
|
|
||||||
EmulateDisassemblerContext disassemblerContext = emulator.getNewDisassemblerContext();
|
|
||||||
Address addr = getExecuteAddress();
|
|
||||||
EmulateMemoryStateBuffer memBuffer = new EmulateMemoryStateBuffer(memState, addr);
|
|
||||||
|
|
||||||
Disassembler disassembler = Disassembler.getDisassembler(language, addrFactory,
|
|
||||||
TaskMonitor.DUMMY, null);
|
|
||||||
|
|
||||||
boolean stopOnError = false;
|
|
||||||
|
|
||||||
while (count > 0 && !stopOnError) {
|
|
||||||
memBuffer.setAddress(addr);
|
|
||||||
disassemblerContext.setCurrentAddress(addr);
|
|
||||||
|
|
||||||
InstructionBlock block = disassembler.pseudoDisassembleBlock(memBuffer,
|
|
||||||
disassemblerContext.getCurrentContextRegisterValue(), count);
|
|
||||||
|
|
||||||
if (block.hasInstructionError() && count > block.getInstructionCount()) {
|
|
||||||
InstructionError instructionError = block.getInstructionConflict();
|
|
||||||
Msg.error(this,
|
|
||||||
"Target disassembler error at " + instructionError.getConflictAddress() + ": " +
|
|
||||||
instructionError.getConflictMessage());
|
|
||||||
stopOnError = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Instruction lastInstr = null;
|
|
||||||
Iterator<Instruction> iterator = block.iterator();
|
|
||||||
while (iterator.hasNext() && count != 0) {
|
|
||||||
Instruction instr = iterator.next();
|
|
||||||
disassembly.add(instr.getAddressString(false, true) + " " + instr.toString());
|
|
||||||
lastInstr = instr;
|
|
||||||
--count;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
addr = lastInstr.getAddress().addNoWrap(lastInstr.getLength());
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
count = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return disassembly;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getTickCount() {
|
|
||||||
return instExecuted;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current context register value. The context value returned reflects
|
* Get the memory state
|
||||||
* its state when the previously executed instruction was
|
*
|
||||||
* parsed/executed. The context value returned will feed into the next
|
* @return the state
|
||||||
* instruction to be parsed with its non-flowing bits cleared and
|
|
||||||
* any future context state merged in.
|
|
||||||
* @return context as a RegisterValue object
|
|
||||||
*/
|
*/
|
||||||
public RegisterValue getContextRegisterValue() {
|
MemoryState getMemState();
|
||||||
return emulator.getContextRegisterValue();
|
|
||||||
}
|
/**
|
||||||
|
* Add a filter on memory access
|
||||||
|
*
|
||||||
|
* @param filter the filter
|
||||||
|
*/
|
||||||
|
void addMemoryAccessFilter(MemoryAccessFilter filter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the memory state, modified by all installed access filters
|
||||||
|
*
|
||||||
|
* @return the state
|
||||||
|
*/
|
||||||
|
FilteredMemoryState getFilteredMemState();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the context register value at the current execute address.
|
* Sets the context register value at the current execute address.
|
||||||
* The Emulator should not be running when this method is invoked.
|
*
|
||||||
* Only flowing context bits should be set, as non-flowing bits
|
* <p>
|
||||||
* will be cleared prior to parsing on instruction. In addition,
|
* The Emulator should not be running when this method is invoked. Only flowing context bits
|
||||||
* any future context state set by the pcode emitter will
|
* should be set, as non-flowing bits will be cleared prior to parsing on instruction. In
|
||||||
* take precedence over context set using this method. This method
|
* addition, any future context state set by the pcode emitter will take precedence over context
|
||||||
* is primarily intended to be used to establish the initial
|
* set using this method. This method is primarily intended to be used to establish the initial
|
||||||
* context state.
|
* context state.
|
||||||
|
*
|
||||||
* @param regValue is the value to set context to
|
* @param regValue is the value to set context to
|
||||||
*/
|
*/
|
||||||
public void setContextRegisterValue(RegisterValue regValue) {
|
void setContextRegisterValue(RegisterValue regValue);
|
||||||
emulator.setContextRegisterValue(regValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add memory load image provider
|
* Returns the current context register value.
|
||||||
* @param provider memory load image provider
|
*
|
||||||
* @param view memory region which corresponds to provider
|
* <p>
|
||||||
|
* The context value returned reflects its state when the previously executed instruction was
|
||||||
|
* parsed/executed. The context value returned will feed into the next instruction to be parsed
|
||||||
|
* with its non-flowing bits cleared and any future context state merged in.
|
||||||
|
*
|
||||||
|
* @return context as a RegisterValue object
|
||||||
*/
|
*/
|
||||||
public void addProvider(MemoryLoadImage provider, AddressSetView view) {
|
RegisterValue getContextRegisterValue();
|
||||||
loadImage.addProvider(provider, view);
|
|
||||||
}
|
/**
|
||||||
|
* Get the breakpoint table
|
||||||
|
*
|
||||||
|
* @return the breakpoint table
|
||||||
|
*/
|
||||||
|
BreakTableCallBack getBreakTable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if halted at a breakpoint
|
||||||
|
*/
|
||||||
|
boolean isAtBreakpoint();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Halt or un-halt the emulator
|
||||||
|
*
|
||||||
|
* @param halt true to halt
|
||||||
|
*/
|
||||||
|
void setHalt(boolean halt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the emulator has been halted
|
||||||
|
*
|
||||||
|
* @return true if halted
|
||||||
|
*/
|
||||||
|
boolean getHalt();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean up resources used by the emulator
|
||||||
|
*/
|
||||||
|
void dispose();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import ghidra.app.emulator.memory.*;
|
||||||
import ghidra.app.emulator.state.DumpMiscState;
|
import ghidra.app.emulator.state.DumpMiscState;
|
||||||
import ghidra.app.emulator.state.RegisterState;
|
import ghidra.app.emulator.state.RegisterState;
|
||||||
import ghidra.framework.store.LockException;
|
import ghidra.framework.store.LockException;
|
||||||
|
import ghidra.pcode.emu.PcodeEmulator;
|
||||||
import ghidra.pcode.emulate.BreakCallBack;
|
import ghidra.pcode.emulate.BreakCallBack;
|
||||||
import ghidra.pcode.emulate.EmulateExecutionState;
|
import ghidra.pcode.emulate.EmulateExecutionState;
|
||||||
import ghidra.pcode.memstate.MemoryFaultHandler;
|
import ghidra.pcode.memstate.MemoryFaultHandler;
|
||||||
|
@ -38,6 +39,17 @@ import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.exception.DuplicateNameException;
|
import ghidra.util.exception.DuplicateNameException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the primary "entry point" for using an {@link Emulator}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This is part of the older p-code emulation system. For information about the newer p-code
|
||||||
|
* emulation system, see {@link PcodeEmulator}. There are several example scripts in the
|
||||||
|
* {@code SystemEmulation} module.
|
||||||
|
*
|
||||||
|
* @see PcodeEmulator
|
||||||
|
* @see Emulator
|
||||||
|
*/
|
||||||
public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration {
|
public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration {
|
||||||
|
|
||||||
private final Program program;
|
private final Program program;
|
||||||
|
@ -68,11 +80,15 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
stackPtrReg = program.getCompilerSpec().getStackPointer();
|
stackPtrReg = program.getCompilerSpec().getStackPointer();
|
||||||
stackMemorySpace = program.getCompilerSpec().getStackBaseSpace();
|
stackMemorySpace = program.getCompilerSpec().getStackBaseSpace();
|
||||||
|
|
||||||
emulator = new Emulator(this);
|
emulator = newEmulator();
|
||||||
|
|
||||||
converter = DataConverter.getInstance(program.getMemory().isBigEndian());
|
converter = DataConverter.getInstance(program.getMemory().isBigEndian());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Emulator newEmulator() {
|
||||||
|
return new DefaultEmulator(this);
|
||||||
|
}
|
||||||
|
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
emulator.dispose();
|
emulator.dispose();
|
||||||
if (memoryWriteTracker != null) {
|
if (memoryWriteTracker != null) {
|
||||||
|
@ -115,6 +131,7 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Program Counter (PC) register defined by applicable processor specification
|
* Get Program Counter (PC) register defined by applicable processor specification
|
||||||
|
*
|
||||||
* @return Program Counter register
|
* @return Program Counter register
|
||||||
*/
|
*/
|
||||||
public Register getPCRegister() {
|
public Register getPCRegister() {
|
||||||
|
@ -123,6 +140,7 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Stack Pointer register defined by applicable compiler specification
|
* Get Stack Pointer register defined by applicable compiler specification
|
||||||
|
*
|
||||||
* @return Stack Pointer register
|
* @return Stack Pointer register
|
||||||
*/
|
*/
|
||||||
public Register getStackPointerRegister() {
|
public Register getStackPointerRegister() {
|
||||||
|
@ -130,14 +148,12 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides ability to install a low-level memory fault handler.
|
* Provides ability to install a low-level memory fault handler. The handler methods should
|
||||||
* The handler methods should generally return 'false' to allow
|
* generally return 'false' to allow the default handler to generate the appropriate target
|
||||||
* the default handler to generate the appropriate target error.
|
* error. Within the fault handler, the EmulateExecutionState can be used to distinguish the
|
||||||
* Within the fault handler, the EmulateExecutionState can be used
|
* pcode-emit state and the actual execution state since an attempt to execute an instruction at
|
||||||
* to distinguish the pcode-emit state and the actual execution state
|
* an uninitialized memory location will cause an uninitializedRead during the PCODE_EMIT state.
|
||||||
* since an attempt to execute an instruction at an uninitialized
|
*
|
||||||
* memory location will cause an uninitializedRead during the PCODE_EMIT
|
|
||||||
* state.
|
|
||||||
* @param handler memory fault handler.
|
* @param handler memory fault handler.
|
||||||
*/
|
*/
|
||||||
public void setMemoryFaultHandler(MemoryFaultHandler handler) {
|
public void setMemoryFaultHandler(MemoryFaultHandler handler) {
|
||||||
|
@ -215,9 +231,10 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read string from memory state.
|
* Read string from memory state.
|
||||||
|
*
|
||||||
* @param addr memory address
|
* @param addr memory address
|
||||||
* @param maxLength limit string read to this length. If return string is
|
* @param maxLength limit string read to this length. If return string is truncated, "..." will
|
||||||
* truncated, "..." will be appended.
|
* be appended.
|
||||||
* @return string read from memory state
|
* @return string read from memory state
|
||||||
*/
|
*/
|
||||||
public String readNullTerminatedString(Address addr, int maxLength) {
|
public String readNullTerminatedString(Address addr, int maxLength) {
|
||||||
|
@ -242,7 +259,8 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
|
|
||||||
public byte[] readMemory(Address addr, int length) {
|
public byte[] readMemory(Address addr, int length) {
|
||||||
byte[] res = new byte[length];
|
byte[] res = new byte[length];
|
||||||
int len = emulator.getMemState().getChunk(res, addr.getAddressSpace(), addr.getOffset(),
|
int len = emulator.getMemState()
|
||||||
|
.getChunk(res, addr.getAddressSpace(), addr.getOffset(),
|
||||||
length, false);
|
length, false);
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
Msg.error(this, "Failed to read memory from Emulator at: " + addr);
|
Msg.error(this, "Failed to read memory from Emulator at: " + addr);
|
||||||
|
@ -256,7 +274,8 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeMemory(Address addr, byte[] bytes) {
|
public void writeMemory(Address addr, byte[] bytes) {
|
||||||
emulator.getMemState().setChunk(bytes, addr.getAddressSpace(), addr.getOffset(),
|
emulator.getMemState()
|
||||||
|
.setChunk(bytes, addr.getAddressSpace(), addr.getOffset(),
|
||||||
bytes.length);
|
bytes.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,6 +285,7 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a stack value from the memory state.
|
* Read a stack value from the memory state.
|
||||||
|
*
|
||||||
* @param relativeOffset offset relative to current stack pointer
|
* @param relativeOffset offset relative to current stack pointer
|
||||||
* @param size data size in bytes
|
* @param size data size in bytes
|
||||||
* @param signed true if value read is signed, false if unsigned
|
* @param signed true if value read is signed, false if unsigned
|
||||||
|
@ -281,6 +301,7 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write a value onto the stack
|
* Write a value onto the stack
|
||||||
|
*
|
||||||
* @param relativeOffset offset relative to current stack pointer
|
* @param relativeOffset offset relative to current stack pointer
|
||||||
* @param size data size in bytes
|
* @param size data size in bytes
|
||||||
* @param value
|
* @param value
|
||||||
|
@ -295,6 +316,7 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write a value onto the stack
|
* Write a value onto the stack
|
||||||
|
*
|
||||||
* @param relativeOffset offset relative to current stack pointer
|
* @param relativeOffset offset relative to current stack pointer
|
||||||
* @param size data size in bytes
|
* @param size data size in bytes
|
||||||
* @param value
|
* @param value
|
||||||
|
@ -309,6 +331,7 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Establish breakpoint
|
* Establish breakpoint
|
||||||
|
*
|
||||||
* @param addr memory address for new breakpoint
|
* @param addr memory address for new breakpoint
|
||||||
*/
|
*/
|
||||||
public void setBreakpoint(Address addr) {
|
public void setBreakpoint(Address addr) {
|
||||||
|
@ -317,6 +340,7 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear breakpoint
|
* Clear breakpoint
|
||||||
|
*
|
||||||
* @param addr memory address for breakpoint to be cleared
|
* @param addr memory address for breakpoint to be cleared
|
||||||
*/
|
*/
|
||||||
public void clearBreakpoint(Address addr) {
|
public void clearBreakpoint(Address addr) {
|
||||||
|
@ -324,8 +348,9 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set current context register value.
|
* Set current context register value. Keep in mind that any non-flowing context values will be
|
||||||
* Keep in mind that any non-flowing context values will be stripped.
|
* stripped.
|
||||||
|
*
|
||||||
* @param ctxRegValue
|
* @param ctxRegValue
|
||||||
*/
|
*/
|
||||||
public void setContextRegister(RegisterValue ctxRegValue) {
|
public void setContextRegister(RegisterValue ctxRegValue) {
|
||||||
|
@ -333,8 +358,9 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set current context register value.
|
* Set current context register value. Keep in mind that any non-flowing context values will be
|
||||||
* Keep in mind that any non-flowing context values will be stripped.
|
* stripped.
|
||||||
|
*
|
||||||
* @param ctxReg context register
|
* @param ctxReg context register
|
||||||
* @param value context value
|
* @param value context value
|
||||||
*/
|
*/
|
||||||
|
@ -344,6 +370,7 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current context register value
|
* Get the current context register value
|
||||||
|
*
|
||||||
* @return context register value or null if not set or unknown
|
* @return context register value or null if not set or unknown
|
||||||
*/
|
*/
|
||||||
public RegisterValue getContextRegister() {
|
public RegisterValue getContextRegister() {
|
||||||
|
@ -351,9 +378,9 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register callback for language defined pcodeop (call other).
|
* Register callback for language defined pcodeop (call other). WARNING! Using this method may
|
||||||
* WARNING! Using this method may circumvent the default CALLOTHER emulation support
|
* circumvent the default CALLOTHER emulation support when supplied by the Processor module.
|
||||||
* when supplied by the Processor module.
|
*
|
||||||
* @param pcodeOpName the name of the pcode op
|
* @param pcodeOpName the name of the pcode op
|
||||||
* @param callback the callback to register
|
* @param callback the callback to register
|
||||||
*/
|
*/
|
||||||
|
@ -362,9 +389,10 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register default callback for language defined pcodeops (call other).
|
* Register default callback for language defined pcodeops (call other). WARNING! Using this
|
||||||
* WARNING! Using this method may circumvent the default CALLOTHER emulation support
|
* method may circumvent the default CALLOTHER emulation support when supplied by the Processor
|
||||||
* when supplied by the Processor module.
|
* module.
|
||||||
|
*
|
||||||
* @param callback the default callback to register
|
* @param callback the default callback to register
|
||||||
*/
|
*/
|
||||||
public void registerDefaultCallOtherCallback(BreakCallBack callback) {
|
public void registerDefaultCallOtherCallback(BreakCallBack callback) {
|
||||||
|
@ -373,6 +401,7 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregister callback for language defined pcodeop (call other).
|
* Unregister callback for language defined pcodeop (call other).
|
||||||
|
*
|
||||||
* @param pcodeOpName the name of the pcode op
|
* @param pcodeOpName the name of the pcode op
|
||||||
*/
|
*/
|
||||||
public void unregisterCallOtherCallback(String pcodeOpName) {
|
public void unregisterCallOtherCallback(String pcodeOpName) {
|
||||||
|
@ -380,9 +409,9 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregister default callback for language defined pcodeops (call other).
|
* Unregister default callback for language defined pcodeops (call other). WARNING! Using this
|
||||||
* WARNING! Using this method may circumvent the default CALLOTHER emulation support
|
* method may circumvent the default CALLOTHER emulation support when supplied by the Processor
|
||||||
* when supplied by the Processor module.
|
* module.
|
||||||
*/
|
*/
|
||||||
public void unregisterDefaultCallOtherCallback() {
|
public void unregisterDefaultCallOtherCallback() {
|
||||||
emulator.getBreakTable().unregisterPcodeCallback("*");
|
emulator.getBreakTable().unregisterPcodeCallback("*");
|
||||||
|
@ -390,6 +419,7 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get current execution address
|
* Get current execution address
|
||||||
|
*
|
||||||
* @return current execution address
|
* @return current execution address
|
||||||
*/
|
*/
|
||||||
public Address getExecutionAddress() {
|
public Address getExecutionAddress() {
|
||||||
|
@ -397,11 +427,11 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start execution at the specified address using the initial context specified.
|
* Start execution at the specified address using the initial context specified. Method will
|
||||||
* Method will block until execution stops. This method will initialize context
|
* block until execution stops. This method will initialize context register based upon the
|
||||||
* register based upon the program stored context if not already done. In addition,
|
* program stored context if not already done. In addition, both general register value and the
|
||||||
* both general register value and the context register may be further modified
|
* context register may be further modified via the context parameter if specified.
|
||||||
* via the context parameter if specified.
|
*
|
||||||
* @param addr initial program address
|
* @param addr initial program address
|
||||||
* @param context optional context settings which override current program context
|
* @param context optional context settings which override current program context
|
||||||
* @param monitor
|
* @param monitor
|
||||||
|
@ -463,10 +493,10 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Continue execution from the current execution address.
|
* Continue execution from the current execution address. No adjustment will be made to the
|
||||||
* No adjustment will be made to the context beyond the normal
|
* context beyond the normal context flow behavior defined by the language. Method will block
|
||||||
* context flow behavior defined by the language.
|
* until execution stops.
|
||||||
* Method will block until execution stops.
|
*
|
||||||
* @param monitor
|
* @param monitor
|
||||||
* @return true if execution completes without error (i.e., is at breakpoint)
|
* @return true if execution completes without error (i.e., is at breakpoint)
|
||||||
* @throws CancelledException if execution cancelled via monitor
|
* @throws CancelledException if execution cancelled via monitor
|
||||||
|
@ -482,6 +512,7 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Continue execution and block until either a breakpoint hits or error occurs.
|
* Continue execution and block until either a breakpoint hits or error occurs.
|
||||||
|
*
|
||||||
* @throws CancelledException if execution was cancelled
|
* @throws CancelledException if execution was cancelled
|
||||||
*/
|
*/
|
||||||
private void continueExecution(TaskMonitor monitor) throws CancelledException {
|
private void continueExecution(TaskMonitor monitor) throws CancelledException {
|
||||||
|
@ -494,8 +525,9 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute instruction at current address
|
* Execute instruction at current address
|
||||||
* @param stopAtBreakpoint if true and breakpoint hits at current execution address
|
*
|
||||||
* execution will halt without executing instruction.
|
* @param stopAtBreakpoint if true and breakpoint hits at current execution address execution
|
||||||
|
* will halt without executing instruction.
|
||||||
* @throws CancelledException if execution was cancelled
|
* @throws CancelledException if execution was cancelled
|
||||||
*/
|
*/
|
||||||
private void executeInstruction(boolean stopAtBreakpoint, TaskMonitor monitor)
|
private void executeInstruction(boolean stopAtBreakpoint, TaskMonitor monitor)
|
||||||
|
@ -515,8 +547,8 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
lastError = t.toString();
|
lastError = t.toString();
|
||||||
}
|
}
|
||||||
emulator.setHalt(true); // force execution to stop
|
emulator.setHalt(true); // force execution to stop
|
||||||
if (t instanceof CancelledException) {
|
if (t instanceof CancelledException ce) {
|
||||||
throw (CancelledException) t;
|
throw ce;
|
||||||
}
|
}
|
||||||
Msg.error(this,
|
Msg.error(this,
|
||||||
"Emulation failure at " + emulator.getExecuteAddress() + ": " + program.getName(),
|
"Emulation failure at " + emulator.getExecuteAddress() + ": " + program.getName(),
|
||||||
|
@ -525,9 +557,8 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used when the emulator has had the execution address changed to
|
* Used when the emulator has had the execution address changed to make sure it has a context
|
||||||
* make sure it has a context consistent with the program context
|
* consistent with the program context if there is one.
|
||||||
* if there is one.
|
|
||||||
*/
|
*/
|
||||||
private void setProcessorContext() {
|
private void setProcessorContext() {
|
||||||
// this assumes you have set the emulation address
|
// this assumes you have set the emulation address
|
||||||
|
@ -554,10 +585,10 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Step execution one instruction which may consist of multiple
|
* Step execution one instruction which may consist of multiple pcode operations. No adjustment
|
||||||
* pcode operations. No adjustment will be made to the context beyond the normal
|
* will be made to the context beyond the normal context flow behavior defined by the language.
|
||||||
* context flow behavior defined by the language.
|
|
||||||
* Method will block until execution stops.
|
* Method will block until execution stops.
|
||||||
|
*
|
||||||
* @return true if execution completes without error
|
* @return true if execution completes without error
|
||||||
* @throws CancelledException if execution cancelled via monitor
|
* @throws CancelledException if execution cancelled via monitor
|
||||||
*/
|
*/
|
||||||
|
@ -568,19 +599,19 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new initialized memory block using the current emulator memory state
|
* Create a new initialized memory block using the current emulator memory state
|
||||||
|
*
|
||||||
* @param name block name
|
* @param name block name
|
||||||
* @param start start address of the block
|
* @param start start address of the block
|
||||||
* @param length the size of the block
|
* @param length the size of the block
|
||||||
* @param overlay if true, the block will be created as an OVERLAY which means that a new
|
* @param overlay if true, the block will be created as an OVERLAY which means that a new
|
||||||
* overlay address space will be created and the block will have a starting address at the same
|
* overlay address space will be created and the block will have a starting address
|
||||||
* offset as the given start address parameter, but in the new address space.
|
* at the same offset as the given start address parameter, but in the new address
|
||||||
|
* space.
|
||||||
* @param monitor
|
* @param monitor
|
||||||
* @return new memory block
|
* @return new memory block
|
||||||
* @throws LockException if exclusive lock not in place (see haveLock())
|
* @throws LockException if exclusive lock not in place (see haveLock())
|
||||||
* @throws MemoryConflictException if the new block overlaps with a
|
* @throws MemoryConflictException if the new block overlaps with a previous block
|
||||||
* previous block
|
* @throws AddressOverflowException if the start is beyond the address space
|
||||||
* @throws AddressOverflowException if the start is beyond the
|
|
||||||
* address space
|
|
||||||
* @throws CancelledException user cancelled operation
|
* @throws CancelledException user cancelled operation
|
||||||
* @throws DuplicateNameException
|
* @throws DuplicateNameException
|
||||||
*/
|
*/
|
||||||
|
@ -626,7 +657,8 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
int txId = program.startTransaction("Create Memory Block");
|
int txId = program.startTransaction("Create Memory Block");
|
||||||
try {
|
try {
|
||||||
block = program.getMemory().createInitializedBlock(name, start, memStateStream, length,
|
block = program.getMemory()
|
||||||
|
.createInitializedBlock(name, start, memStateStream, length,
|
||||||
monitor, overlay);
|
monitor, overlay);
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
|
@ -637,8 +669,8 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable/Disable tracking of memory writes in the form of an
|
* Enable/Disable tracking of memory writes in the form of an address set.
|
||||||
* address set.
|
*
|
||||||
* @param enable
|
* @param enable
|
||||||
*/
|
*/
|
||||||
public void enableMemoryWriteTracking(boolean enable) {
|
public void enableMemoryWriteTracking(boolean enable) {
|
||||||
|
@ -656,10 +688,9 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return address set of memory locations written by the emulator
|
* @return address set of memory locations written by the emulator if memory write tracking is
|
||||||
* if memory write tracking is enabled, otherwise null is returned.
|
* enabled, otherwise null is returned. The address set returned will continue to be
|
||||||
* The address set returned will continue to be updated unless
|
* updated unless memory write tracking becomes disabled.
|
||||||
* memory write tracking becomes disabled.
|
|
||||||
*/
|
*/
|
||||||
public AddressSetView getTrackedMemoryWriteSet() {
|
public AddressSetView getTrackedMemoryWriteSet() {
|
||||||
if (memoryWriteTracker != null) {
|
if (memoryWriteTracker != null) {
|
||||||
|
|
|
@ -15,11 +15,11 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.emulator;
|
package ghidra.app.emulator;
|
||||||
|
|
||||||
import ghidra.pcode.memstate.MemoryState;
|
import ghidra.pcode.memstate.DefaultMemoryState;
|
||||||
import ghidra.program.model.address.AddressSpace;
|
import ghidra.program.model.address.AddressSpace;
|
||||||
import ghidra.program.model.lang.Language;
|
import ghidra.program.model.lang.Language;
|
||||||
|
|
||||||
class FilteredMemoryState extends MemoryState {
|
class FilteredMemoryState extends DefaultMemoryState {
|
||||||
|
|
||||||
private MemoryAccessFilter filter;
|
private MemoryAccessFilter filter;
|
||||||
private boolean filterEnabled = true; // used to prevent filtering filter queries
|
private boolean filterEnabled = true; // used to prevent filtering filter queries
|
||||||
|
|
|
@ -17,6 +17,14 @@ package ghidra.app.emulator;
|
||||||
|
|
||||||
import ghidra.program.model.address.AddressSpace;
|
import ghidra.program.model.address.AddressSpace;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A means of intercepting and/or modifying the emulator's memory access.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Several of these filters may be chained together, each being invoked in the reverse of the order
|
||||||
|
* added. In this way, the first added gets the "final say," but it also is farthest from the
|
||||||
|
* original request.
|
||||||
|
*/
|
||||||
public abstract class MemoryAccessFilter {
|
public abstract class MemoryAccessFilter {
|
||||||
|
|
||||||
private MemoryAccessFilter prevFilter;
|
private MemoryAccessFilter prevFilter;
|
||||||
|
@ -27,23 +35,41 @@ public abstract class MemoryAccessFilter {
|
||||||
private boolean filterOnExecutionOnly = true;
|
private boolean filterOnExecutionOnly = true;
|
||||||
|
|
||||||
final void filterRead(AddressSpace spc, long off, int size, byte[] values) {
|
final void filterRead(AddressSpace spc, long off, int size, byte[] values) {
|
||||||
if (filterOnExecutionOnly() && !emu.isExecuting()) return; // do not filter idle queries
|
if (filterOnExecutionOnly() && !emu.isExecuting())
|
||||||
|
return; // do not filter idle queries
|
||||||
processRead(spc, off, size, values);
|
processRead(spc, off, size, values);
|
||||||
if (nextFilter != null) {
|
if (nextFilter != null) {
|
||||||
nextFilter.filterRead(spc, off, size, values);
|
nextFilter.filterRead(spc, off, size, values);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked after a read
|
||||||
|
*
|
||||||
|
* @param spc the space read from
|
||||||
|
* @param off the offset within the space
|
||||||
|
* @param size the number of bytes read
|
||||||
|
* @param values the bytes read
|
||||||
|
*/
|
||||||
protected abstract void processRead(AddressSpace spc, long off, int size, byte[] values);
|
protected abstract void processRead(AddressSpace spc, long off, int size, byte[] values);
|
||||||
|
|
||||||
final void filterWrite(AddressSpace spc, long off, int size, byte[] values) {
|
final void filterWrite(AddressSpace spc, long off, int size, byte[] values) {
|
||||||
if (filterOnExecutionOnly() && !emu.isExecuting()) return; // do not filter idle queries
|
if (filterOnExecutionOnly() && !emu.isExecuting())
|
||||||
|
return; // do not filter idle queries
|
||||||
processWrite(spc, off, size, values);
|
processWrite(spc, off, size, values);
|
||||||
if (nextFilter != null) {
|
if (nextFilter != null) {
|
||||||
nextFilter.filterWrite(spc, off, size, values);
|
nextFilter.filterWrite(spc, off, size, values);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked <em>after</em> a write
|
||||||
|
*
|
||||||
|
* @param spc the space written to
|
||||||
|
* @param off the offset within the space
|
||||||
|
* @param size the number of bytes written
|
||||||
|
* @param values the bytes written
|
||||||
|
*/
|
||||||
protected abstract void processWrite(AddressSpace spc, long off, int size, byte[] values);
|
protected abstract void processWrite(AddressSpace spc, long off, int size, byte[] values);
|
||||||
|
|
||||||
final void addFilter(Emulator emu) {
|
final void addFilter(Emulator emu) {
|
||||||
|
@ -56,7 +82,9 @@ public abstract class MemoryAccessFilter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispose this filter which will cause it to be removed from the memory state.
|
* Dispose this filter which will cause it to be removed from the memory state.
|
||||||
* If overriden, be sure to invoke super.dispose().
|
*
|
||||||
|
* <p>
|
||||||
|
* If overriden, be sure to invoke {@code super.dispose()}.
|
||||||
*/
|
*/
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
if (nextFilter != null) {
|
if (nextFilter != null) {
|
||||||
|
|
|
@ -22,8 +22,22 @@ public interface RegisterState {
|
||||||
|
|
||||||
public Set<String> getKeys();
|
public Set<String> getKeys();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the byte array value for a register name
|
||||||
|
*
|
||||||
|
* @param key the register name
|
||||||
|
* @return a list (used as an optional) containing at most the one byte array giving the
|
||||||
|
* register's value. If empty, the value if unspecified.
|
||||||
|
*/
|
||||||
public List<byte[]> getVals(String key);
|
public List<byte[]> getVals(String key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the register is initialized
|
||||||
|
*
|
||||||
|
* @param key the register name
|
||||||
|
* @return a list (used an an optional) containing at most the one initialization state. True if
|
||||||
|
* initialized, false if not. Empty if unspecified.
|
||||||
|
*/
|
||||||
public List<Boolean> isInitialized(String key);
|
public List<Boolean> isInitialized(String key);
|
||||||
|
|
||||||
public void setVals(String key, byte[] vals, boolean setInitiailized);
|
public void setVals(String key, byte[] vals, boolean setInitiailized);
|
||||||
|
|
|
@ -266,6 +266,11 @@ public abstract class AbstractPcodeMachine<T> implements PcodeMachine<T> {
|
||||||
this.suspended = suspended;
|
this.suspended = suspended;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSuspended() {
|
||||||
|
return suspended;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check for a p-code injection (override) at the given address
|
* Check for a p-code injection (override) at the given address
|
||||||
*
|
*
|
||||||
|
@ -391,8 +396,8 @@ public abstract class AbstractPcodeMachine<T> implements PcodeMachine<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify the machine a thread has been stepped, so that it may re-enable software interrupts,
|
* Notify the machine a thread has been stepped a p-code op, so that it may re-enable software
|
||||||
* if applicable
|
* interrupts, if applicable
|
||||||
*/
|
*/
|
||||||
protected void stepped() {
|
protected void stepped() {
|
||||||
if (swiMode == SwiMode.IGNORE_STEP) {
|
if (swiMode == SwiMode.IGNORE_STEP) {
|
||||||
|
|
|
@ -320,15 +320,19 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void branchToAddress(Address target) {
|
protected void branchToAddress(Address target) {
|
||||||
overrideCounter(target);
|
writeCounter(target);
|
||||||
decoder.branched(counter);
|
decoder.branched(counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void writeCounter(Address counter) {
|
||||||
|
setCounter(counter);
|
||||||
|
state.setVar(pc,
|
||||||
|
arithmetic.fromConst(counter.getAddressableWordOffset(), pc.getMinimumByteSize()));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void overrideCounter(Address counter) {
|
public void overrideCounter(Address counter) {
|
||||||
setCounter(counter);
|
writeCounter(counter);
|
||||||
state.setVar(pc,
|
|
||||||
arithmetic.fromConst(counter.getAddressableWordOffset(), pc.getMinimumByteSize()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -375,13 +379,13 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reInitialize() {
|
public void reInitialize() {
|
||||||
long offset = arithmetic.toLong(state.getVar(pc, executor.getReason()), Purpose.BRANCH);
|
long offset = arithmetic.toLong(state.getVar(pc, Reason.RE_INIT), Purpose.BRANCH);
|
||||||
setCounter(language.getDefaultSpace().getAddress(offset, true));
|
setCounter(language.getDefaultSpace().getAddress(offset, true));
|
||||||
|
|
||||||
if (contextreg != Register.NO_CONTEXT) {
|
if (contextreg != Register.NO_CONTEXT) {
|
||||||
try {
|
try {
|
||||||
BigInteger ctx = arithmetic.toBigInteger(state.getVar(
|
BigInteger ctx = arithmetic.toBigInteger(state.getVar(contextreg, Reason.RE_INIT),
|
||||||
contextreg, executor.getReason()), Purpose.CONTEXT);
|
Purpose.CONTEXT);
|
||||||
assignContext(new RegisterValue(contextreg, ctx));
|
assignContext(new RegisterValue(contextreg, ctx));
|
||||||
}
|
}
|
||||||
catch (AccessPcodeExecutionException e) {
|
catch (AccessPcodeExecutionException e) {
|
||||||
|
@ -582,6 +586,11 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
||||||
executor.suspended = suspended;
|
executor.suspended = suspended;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSuspended() {
|
||||||
|
return executor.suspended;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SleighLanguage getLanguage() {
|
public SleighLanguage getLanguage() {
|
||||||
return language;
|
return language;
|
||||||
|
@ -677,8 +686,8 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify the machine a thread has been stepped, so that it may re-enable software interrupts,
|
* Notify the machine a thread has been stepped a p-code op, so that it may re-enable software
|
||||||
* if applicable
|
* interrupts, if applicable
|
||||||
*/
|
*/
|
||||||
protected void stepped() {
|
protected void stepped() {
|
||||||
machine.stepped();
|
machine.stepped();
|
||||||
|
|
|
@ -17,14 +17,15 @@ package ghidra.pcode.emu;
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
|
|
||||||
|
import ghidra.app.emulator.AdaptedMemoryState;
|
||||||
import ghidra.app.emulator.Emulator;
|
import ghidra.app.emulator.Emulator;
|
||||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||||
import ghidra.pcode.emulate.*;
|
import ghidra.pcode.emulate.*;
|
||||||
import ghidra.pcode.exec.ConcretionError;
|
import ghidra.pcode.exec.ConcretionError;
|
||||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||||
|
import ghidra.pcode.memstate.MemoryBank;
|
||||||
import ghidra.pcode.memstate.MemoryState;
|
import ghidra.pcode.memstate.MemoryState;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.address.AddressSpace;
|
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.model.pcode.PcodeOp;
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
@ -85,34 +86,6 @@ public class ModifiedPcodeThread<T> extends DefaultPcodeThread<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Glue for incorporating state modifiers
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* This allows the modifiers to access the thread's state (memory and registers).
|
|
||||||
*/
|
|
||||||
protected class GlueMemoryState extends MemoryState {
|
|
||||||
public GlueMemoryState(Language language) {
|
|
||||||
super(language);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getChunk(byte[] res, AddressSpace spc, long off, int size,
|
|
||||||
boolean stopOnUnintialized) {
|
|
||||||
return getBytesChunk(res, spc, off, size, stopOnUnintialized);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setChunk(byte[] val, AddressSpace spc, long off, int size) {
|
|
||||||
setBytesChunk(val, spc, off, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setInitialized(boolean initialized, AddressSpace spc, long off, int size) {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Part of the glue that makes existing state modifiers work in new emulation framework
|
* Part of the glue that makes existing state modifiers work in new emulation framework
|
||||||
*
|
*
|
||||||
|
@ -125,8 +98,6 @@ public class ModifiedPcodeThread<T> extends DefaultPcodeThread<T> {
|
||||||
protected final EmulateInstructionStateModifier modifier;
|
protected final EmulateInstructionStateModifier modifier;
|
||||||
protected final Emulate emulate;
|
protected final Emulate emulate;
|
||||||
|
|
||||||
protected Address savedCounter;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new thread with the given name belonging to the given machine
|
* Construct a new thread with the given name belonging to the given machine
|
||||||
*
|
*
|
||||||
|
@ -141,8 +112,12 @@ public class ModifiedPcodeThread<T> extends DefaultPcodeThread<T> {
|
||||||
* These two exist as a way to integrate the language-specific injects that are already
|
* These two exist as a way to integrate the language-specific injects that are already
|
||||||
* written for {@link Emulator}.
|
* written for {@link Emulator}.
|
||||||
*/
|
*/
|
||||||
emulate = new GlueEmulate(language, new GlueMemoryState(language),
|
emulate = new GlueEmulate(language, new AdaptedMemoryState<>(state, Reason.EXECUTE) {
|
||||||
new BreakTableCallBack(language));
|
@Override
|
||||||
|
public void setMemoryBank(MemoryBank bank) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
}, new BreakTableCallBack(language));
|
||||||
modifier = createModifier();
|
modifier = createModifier();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,42 +153,19 @@ public class ModifiedPcodeThread<T> extends DefaultPcodeThread<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by a state modifier to read concrete bytes from the thread's state
|
|
||||||
*
|
|
||||||
* @see {@link MemoryState#getChunk(byte[], AddressSpace, long, int, boolean)}
|
|
||||||
*/
|
|
||||||
protected int getBytesChunk(byte[] res, AddressSpace spc, long off, int size,
|
|
||||||
boolean stopOnUnintialized) {
|
|
||||||
T t = state.getVar(spc, off, size, true, executor.getReason());
|
|
||||||
byte[] val = arithmetic.toConcrete(t, Purpose.OTHER);
|
|
||||||
System.arraycopy(val, 0, res, 0, val.length);
|
|
||||||
return val.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by a state modifier to write concrete bytes to the thread's state
|
|
||||||
*
|
|
||||||
* @see {@link MemoryState#setChunk(byte[], AddressSpace, long, int)}
|
|
||||||
*/
|
|
||||||
protected void setBytesChunk(byte[] val, AddressSpace spc, long off, int size) {
|
|
||||||
T t = arithmetic.fromConst(val);
|
|
||||||
state.setVar(spc, off, size, true, t);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reInitialize() {
|
public void overrideCounter(Address counter) {
|
||||||
super.reInitialize();
|
super.overrideCounter(counter);
|
||||||
if (modifier != null) {
|
if (modifier != null) {
|
||||||
savedCounter = getCounter();
|
modifier.initialExecuteCallback(emulate, counter, getContext());
|
||||||
modifier.initialExecuteCallback(emulate, savedCounter, getContext());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void postExecuteInstruction() {
|
protected void postExecuteInstruction() {
|
||||||
if (modifier != null) {
|
if (modifier != null) {
|
||||||
modifier.postExecuteCallback(emulate, savedCounter, frame.copyCode(),
|
modifier.postExecuteCallback(emulate,
|
||||||
|
instruction == null ? null : instruction.getAddress(), frame.copyCode(),
|
||||||
frame.getBranched(), getCounter());
|
frame.getBranched(), getCounter());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -203,6 +203,13 @@ public interface PcodeMachine<T> {
|
||||||
*/
|
*/
|
||||||
void setSuspended(boolean suspended);
|
void setSuspended(boolean suspended);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the suspension state of the machine
|
||||||
|
*
|
||||||
|
* @see PcodeThread#getSuspended()
|
||||||
|
*/
|
||||||
|
boolean isSuspended();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compile the given Sleigh code for execution by a thread of this machine
|
* Compile the given Sleigh code for execution by a thread of this machine
|
||||||
*
|
*
|
||||||
|
|
|
@ -19,7 +19,6 @@ import java.util.List;
|
||||||
|
|
||||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||||
import ghidra.pcode.emu.DefaultPcodeThread.PcodeEmulationLibrary;
|
import ghidra.pcode.emu.DefaultPcodeThread.PcodeEmulationLibrary;
|
||||||
import ghidra.pcode.emu.PcodeMachine.SwiMode;
|
|
||||||
import ghidra.pcode.exec.*;
|
import ghidra.pcode.exec.*;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.lang.Register;
|
import ghidra.program.model.lang.Register;
|
||||||
|
@ -286,6 +285,13 @@ public interface PcodeThread<T> {
|
||||||
*/
|
*/
|
||||||
void setSuspended(boolean suspended);
|
void setSuspended(boolean suspended);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the suspension state of the thread's executor
|
||||||
|
*
|
||||||
|
* @return true if suspended
|
||||||
|
*/
|
||||||
|
boolean isSuspended();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the thread's Sleigh language (processor model)
|
* Get the thread's Sleigh language (processor model)
|
||||||
*
|
*
|
||||||
|
|
|
@ -87,6 +87,15 @@ public class ThreadPcodeExecutorState<T> implements PcodeExecutorState<T> {
|
||||||
sharedState.setVar(space, offset, size, quantize, val);
|
sharedState.setVar(space, offset, size, quantize, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setVar(AddressSpace space, long offset, int size, boolean quantize, T val) {
|
||||||
|
if (isThreadLocalSpace(space)) {
|
||||||
|
localState.setVar(space, offset, size, quantize, val);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sharedState.setVar(space, offset, size, quantize, val);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T getVar(AddressSpace space, T offset, int size, boolean quantize, Reason reason) {
|
public T getVar(AddressSpace space, T offset, int size, boolean quantize, Reason reason) {
|
||||||
if (isThreadLocalSpace(space)) {
|
if (isThreadLocalSpace(space)) {
|
||||||
|
@ -95,6 +104,14 @@ public class ThreadPcodeExecutorState<T> implements PcodeExecutorState<T> {
|
||||||
return sharedState.getVar(space, offset, size, quantize, reason);
|
return sharedState.getVar(space, offset, size, quantize, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T getVar(AddressSpace space, long offset, int size, boolean quantize, Reason reason) {
|
||||||
|
if (isThreadLocalSpace(space)) {
|
||||||
|
return localState.getVar(space, offset, size, quantize, reason);
|
||||||
|
}
|
||||||
|
return sharedState.getVar(space, offset, size, quantize, reason);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<Register, T> getRegisterValues() {
|
public Map<Register, T> getRegisterValues() {
|
||||||
Map<Register, T> result = new HashMap<>();
|
Map<Register, T> result = new HashMap<>();
|
||||||
|
|
|
@ -65,7 +65,9 @@ public abstract class AbstractLongOffsetPcodeExecutorStatePiece<A, T, S>
|
||||||
*
|
*
|
||||||
* @return the copy
|
* @return the copy
|
||||||
*/
|
*/
|
||||||
public abstract AbstractSpaceMap<S> fork();
|
public AbstractSpaceMap<S> fork() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deep copy the given space
|
* Deep copy the given space
|
||||||
|
@ -73,7 +75,9 @@ public abstract class AbstractLongOffsetPcodeExecutorStatePiece<A, T, S>
|
||||||
* @param s the space
|
* @param s the space
|
||||||
* @return the copy
|
* @return the copy
|
||||||
*/
|
*/
|
||||||
public abstract S fork(S s);
|
public S fork(S s) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produce a deep copy of the given map
|
* Produce a deep copy of the given map
|
||||||
|
|
|
@ -69,11 +69,21 @@ public class DefaultPcodeExecutorState<T> implements PcodeExecutorState<T> {
|
||||||
return piece.getVar(space, offset, size, quantize, reason);
|
return piece.getVar(space, offset, size, quantize, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T getVar(AddressSpace space, long offset, int size, boolean quantize, Reason reason) {
|
||||||
|
return piece.getVar(space, offset, size, quantize, reason);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setVar(AddressSpace space, T offset, int size, boolean quantize, T val) {
|
public void setVar(AddressSpace space, T offset, int size, boolean quantize, T val) {
|
||||||
piece.setVar(space, offset, size, quantize, val);
|
piece.setVar(space, offset, size, quantize, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setVar(AddressSpace space, long offset, int size, boolean quantize, T val) {
|
||||||
|
piece.setVar(space, offset, size, quantize, val);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<Register, T> getRegisterValues() {
|
public Map<Register, T> getRegisterValues() {
|
||||||
return piece.getRegisterValues();
|
return piece.getRegisterValues();
|
||||||
|
|
|
@ -46,7 +46,7 @@ public class PcodeExecutor<T> {
|
||||||
protected final PcodeExecutorState<T> state;
|
protected final PcodeExecutorState<T> state;
|
||||||
protected final Reason reason;
|
protected final Reason reason;
|
||||||
protected final Register pc;
|
protected final Register pc;
|
||||||
protected final int pointerSize;
|
protected final int pcSize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct an executor with the given bindings
|
* Construct an executor with the given bindings
|
||||||
|
@ -64,7 +64,7 @@ public class PcodeExecutor<T> {
|
||||||
this.reason = reason;
|
this.reason = reason;
|
||||||
|
|
||||||
this.pc = language.getProgramCounter();
|
this.pc = language.getProgramCounter();
|
||||||
this.pointerSize = language.getDefaultSpace().getPointerSize();
|
this.pcSize = pc.getNumBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -431,7 +431,7 @@ public class PcodeExecutor<T> {
|
||||||
frame.branch((int) target.getOffset());
|
frame.branch((int) target.getOffset());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
branchToOffset(arithmetic.fromConst(target.getOffset(), pointerSize), frame);
|
branchToOffset(arithmetic.fromConst(target.getOffset(), pcSize), frame);
|
||||||
branchToAddress(target);
|
branchToAddress(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -510,7 +510,7 @@ public class PcodeExecutor<T> {
|
||||||
*/
|
*/
|
||||||
public void executeCall(PcodeOp op, PcodeFrame frame, PcodeUseropLibrary<T> library) {
|
public void executeCall(PcodeOp op, PcodeFrame frame, PcodeUseropLibrary<T> library) {
|
||||||
Address target = op.getInput(0).getAddress();
|
Address target = op.getInput(0).getAddress();
|
||||||
branchToOffset(arithmetic.fromConst(target.getOffset(), pointerSize), frame);
|
branchToOffset(arithmetic.fromConst(target.getOffset(), pcSize), frame);
|
||||||
branchToAddress(target);
|
branchToAddress(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package ghidra.pcode.exec;
|
package ghidra.pcode.exec;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.lang.Language;
|
import ghidra.program.model.lang.Language;
|
||||||
|
@ -44,6 +45,8 @@ public interface PcodeExecutorStatePiece<A, T> {
|
||||||
* Reasons for reading state
|
* Reasons for reading state
|
||||||
*/
|
*/
|
||||||
enum Reason {
|
enum Reason {
|
||||||
|
/** The value is needed as the default program counter or disassembly context */
|
||||||
|
RE_INIT,
|
||||||
/** The value is needed by the emulator in the course of execution */
|
/** The value is needed by the emulator in the course of execution */
|
||||||
EXECUTE,
|
EXECUTE,
|
||||||
/** The value is being inspected */
|
/** The value is being inspected */
|
||||||
|
@ -59,6 +62,9 @@ public interface PcodeExecutorStatePiece<A, T> {
|
||||||
*/
|
*/
|
||||||
default void checkRange(AddressSpace space, long offset, int size) {
|
default void checkRange(AddressSpace space, long offset, int size) {
|
||||||
// TODO: Perhaps get/setVar should just take an AddressRange?
|
// TODO: Perhaps get/setVar should just take an AddressRange?
|
||||||
|
if (space.isConstantSpace()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
new AddressRangeImpl(space.getAddress(offset), size);
|
new AddressRangeImpl(space.getAddress(offset), size);
|
||||||
}
|
}
|
||||||
|
@ -93,7 +99,9 @@ public interface PcodeExecutorStatePiece<A, T> {
|
||||||
*
|
*
|
||||||
* @return the copy
|
* @return the copy
|
||||||
*/
|
*/
|
||||||
PcodeExecutorStatePiece<A, T> fork();
|
default PcodeExecutorStatePiece<A, T> fork() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the value of a register variable
|
* Set the value of a register variable
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -31,7 +30,7 @@ import ghidra.program.model.address.Address;
|
||||||
///
|
///
|
||||||
/// depending on the type of breakpoint they currently want to invoke
|
/// depending on the type of breakpoint they currently want to invoke
|
||||||
|
|
||||||
public abstract class BreakTable {
|
public interface BreakTable {
|
||||||
|
|
||||||
/// \brief Associate a particular emulator with breakpoints in this table
|
/// \brief Associate a particular emulator with breakpoints in this table
|
||||||
///
|
///
|
||||||
|
|
|
@ -29,7 +29,7 @@ import ghidra.program.model.address.Address;
|
||||||
///
|
///
|
||||||
/// Breakpoints are stored in map containers, and the core BreakTable methods
|
/// Breakpoints are stored in map containers, and the core BreakTable methods
|
||||||
/// are implemented to search in these containers
|
/// are implemented to search in these containers
|
||||||
public class BreakTableCallBack extends BreakTable {
|
public class BreakTableCallBack implements BreakTable {
|
||||||
|
|
||||||
public static final String DEFAULT_NAME = "*";
|
public static final String DEFAULT_NAME = "*";
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,269 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.pcode.memstate;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
import ghidra.pcode.utils.Utils;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.address.AddressSpace;
|
||||||
|
import ghidra.program.model.lang.Language;
|
||||||
|
import ghidra.program.model.lang.Register;
|
||||||
|
import ghidra.program.model.pcode.Varnode;
|
||||||
|
|
||||||
|
public abstract class AbstractMemoryState implements MemoryState {
|
||||||
|
final Language language;
|
||||||
|
|
||||||
|
public AbstractMemoryState(Language language) {
|
||||||
|
this.language = language;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A convenience method for setting a value directly on a varnode rather than breaking out the
|
||||||
|
* components
|
||||||
|
*
|
||||||
|
* @param vn the varnode location to be written
|
||||||
|
* @param cval the value to write into the varnode location
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final void setValue(Varnode vn, long cval) {
|
||||||
|
Address addr = vn.getAddress();
|
||||||
|
setValue(addr.getAddressSpace(), addr.getOffset(), vn.getSize(), cval);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A convenience method for setting a value directly on a register rather than breaking out the
|
||||||
|
* components
|
||||||
|
*
|
||||||
|
* @param reg the register location to be written
|
||||||
|
* @param cval the value to write into the register location
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final void setValue(Register reg, long cval) {
|
||||||
|
Address addr = reg.getAddress();
|
||||||
|
setValue(addr.getAddressSpace(), addr.getOffset(), reg.getMinimumByteSize(), cval);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a convenience method for setting registers by name. Any register name known to the
|
||||||
|
* language can be used as a write location. The associated address space, offset, and size is
|
||||||
|
* looked up and automatically passed to the main setValue routine.
|
||||||
|
*
|
||||||
|
* @param nm is the name of the register
|
||||||
|
* @param cval is the value to write to the register
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final void setValue(String nm, long cval) {
|
||||||
|
// Set a "register" value
|
||||||
|
setValue(language.getRegister(nm), cval);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the main interface for writing values to the MemoryState. If there is no registered
|
||||||
|
* MemoryBank for the desired address space, or if there is some other error, an exception is
|
||||||
|
* thrown.
|
||||||
|
*
|
||||||
|
* @param spc is the address space to write to
|
||||||
|
* @param off is the offset where the value should be written
|
||||||
|
* @param size is the number of bytes to be written
|
||||||
|
* @param cval is the value to be written
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final void setValue(AddressSpace spc, long off, int size, long cval) {
|
||||||
|
setChunk(Utils.longToBytes(cval, size, language.isBigEndian()), spc, off, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A convenience method for reading a value directly from a varnode rather than querying for the
|
||||||
|
* offset and space
|
||||||
|
*
|
||||||
|
* @param vn the varnode location to be read
|
||||||
|
* @return the value read from the varnode location
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final long getValue(Varnode vn) {
|
||||||
|
Address addr = vn.getAddress();
|
||||||
|
return getValue(addr.getAddressSpace(), addr.getOffset(), vn.getSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A convenience method for reading a value directly from a register rather than querying for
|
||||||
|
* the offset and space
|
||||||
|
*
|
||||||
|
* @param reg the register location to be read
|
||||||
|
* @return the value read from the register location
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final long getValue(Register reg) {
|
||||||
|
Address addr = reg.getAddress();
|
||||||
|
return getValue(addr.getAddressSpace(), addr.getOffset(), reg.getMinimumByteSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a convenience method for reading registers by name. any register name known to the
|
||||||
|
* language can be used as a read location. The associated address space, offset, and size is
|
||||||
|
* looked up and automatically passed to the main getValue routine.
|
||||||
|
*
|
||||||
|
* @param nm is the name of the register
|
||||||
|
* @return the value associated with that register
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final long getValue(String nm) {
|
||||||
|
// Get a "register" value
|
||||||
|
return getValue(language.getRegister(nm));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the main interface for reading values from the MemoryState. If there is no registered
|
||||||
|
* MemoryBank for the desired address space, or if there is some other error, an exception is
|
||||||
|
* thrown.
|
||||||
|
*
|
||||||
|
* @param spc is the address space being queried
|
||||||
|
* @param off is the offset of the value being queried
|
||||||
|
* @param size is the number of bytes to query
|
||||||
|
* @return the queried value
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final long getValue(AddressSpace spc, long off, int size) {
|
||||||
|
if (spc.isConstantSpace()) {
|
||||||
|
return off;
|
||||||
|
}
|
||||||
|
byte[] bytes = new byte[size];
|
||||||
|
getChunk(bytes, spc, off, size, false);
|
||||||
|
return Utils.bytesToLong(bytes, size, language.isBigEndian());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A convenience method for setting a value directly on a varnode rather than breaking out the
|
||||||
|
* components
|
||||||
|
*
|
||||||
|
* @param vn the varnode location to be written
|
||||||
|
* @param cval the value to write into the varnode location
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final void setValue(Varnode vn, BigInteger cval) {
|
||||||
|
Address addr = vn.getAddress();
|
||||||
|
setValue(addr.getAddressSpace(), addr.getOffset(), vn.getSize(), cval);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A convenience method for setting a value directly on a register rather than breaking out the
|
||||||
|
* components
|
||||||
|
*
|
||||||
|
* @param reg the register location to be written
|
||||||
|
* @param cval the value to write into the register location
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final void setValue(Register reg, BigInteger cval) {
|
||||||
|
Address addr = reg.getAddress();
|
||||||
|
setValue(addr.getAddressSpace(), addr.getOffset(), reg.getMinimumByteSize(), cval);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a convenience method for setting registers by name. Any register name known to the
|
||||||
|
* language can be used as a write location. The associated address space, offset, and size is
|
||||||
|
* looked up and automatically passed to the main setValue routine.
|
||||||
|
*
|
||||||
|
* @param nm is the name of the register
|
||||||
|
* @param cval is the value to write to the register
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final void setValue(String nm, BigInteger cval) {
|
||||||
|
// Set a "register" value
|
||||||
|
setValue(language.getRegister(nm), cval);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the main interface for writing values to the MemoryState. If there is no registered
|
||||||
|
* MemoryBank for the desired address space, or if there is some other error, an exception is
|
||||||
|
* thrown.
|
||||||
|
*
|
||||||
|
* @param spc is the address space to write to
|
||||||
|
* @param off is the offset where the value should be written
|
||||||
|
* @param size is the number of bytes to be written
|
||||||
|
* @param cval is the value to be written
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final void setValue(AddressSpace spc, long off, int size, BigInteger cval) {
|
||||||
|
setChunk(Utils.bigIntegerToBytes(cval, size, language.isBigEndian()), spc, off, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A convenience method for reading a value directly from a varnode rather than querying for the
|
||||||
|
* offset and space
|
||||||
|
*
|
||||||
|
* @param vn the varnode location to be read
|
||||||
|
* @param signed true if signed value should be returned, false for unsigned value
|
||||||
|
* @return the unsigned value read from the varnode location
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final BigInteger getBigInteger(Varnode vn, boolean signed) {
|
||||||
|
Address addr = vn.getAddress();
|
||||||
|
return getBigInteger(addr.getAddressSpace(), addr.getOffset(), vn.getSize(), signed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A convenience method for reading a value directly from a register rather than querying for
|
||||||
|
* the offset and space
|
||||||
|
*
|
||||||
|
* @param reg the register location to be read
|
||||||
|
* @return the unsigned value read from the register location
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final BigInteger getBigInteger(Register reg) {
|
||||||
|
Address addr = reg.getAddress();
|
||||||
|
return getBigInteger(addr.getAddressSpace(), addr.getOffset(), reg.getMinimumByteSize(),
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a convenience method for reading registers by name. any register name known to the
|
||||||
|
* language can be used as a read location. The associated address space, offset, and size is
|
||||||
|
* looked up and automatically passed to the main getValue routine.
|
||||||
|
*
|
||||||
|
* @param nm is the name of the register
|
||||||
|
* @return the unsigned value associated with that register
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final BigInteger getBigInteger(String nm) {
|
||||||
|
// Get a "register" value
|
||||||
|
return getBigInteger(language.getRegister(nm));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the main interface for reading values from the MemoryState. If there is no registered
|
||||||
|
* MemoryBank for the desired address space, or if there is some other error, an exception is
|
||||||
|
* thrown.
|
||||||
|
*
|
||||||
|
* @param spc is the address space being queried
|
||||||
|
* @param off is the offset of the value being queried
|
||||||
|
* @param size is the number of bytes to query
|
||||||
|
* @param signed true if signed value should be returned, false for unsigned value
|
||||||
|
* @return the queried unsigned value
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final BigInteger getBigInteger(AddressSpace spc, long off, int size, boolean signed) {
|
||||||
|
if (spc.isConstantSpace()) {
|
||||||
|
if (!signed && off < 0) {
|
||||||
|
return new BigInteger(1, Utils.longToBytes(off, 8, true));
|
||||||
|
}
|
||||||
|
return BigInteger.valueOf(off);
|
||||||
|
}
|
||||||
|
byte[] bytes = new byte[size];
|
||||||
|
getChunk(bytes, spc, off, size, false);
|
||||||
|
return Utils.bytesToBigInteger(bytes, size, language.isBigEndian(), signed);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,148 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.pcode.memstate;
|
||||||
|
|
||||||
|
import generic.stl.VectorSTL;
|
||||||
|
import ghidra.pcode.error.LowlevelError;
|
||||||
|
import ghidra.pcode.utils.Utils;
|
||||||
|
import ghidra.program.model.address.AddressSpace;
|
||||||
|
import ghidra.program.model.lang.Language;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All storage/state for a pcode emulator machine
|
||||||
|
*
|
||||||
|
* Every piece of information in a pcode emulator machine is representable as a triple
|
||||||
|
* (AddressSpace,offset,size). This class allows getting and setting of all state information of
|
||||||
|
* this form.
|
||||||
|
*/
|
||||||
|
public class DefaultMemoryState extends AbstractMemoryState {
|
||||||
|
|
||||||
|
VectorSTL<MemoryBank> memspace = new VectorSTL<MemoryBank>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MemoryState constructor for a specified processor language
|
||||||
|
*
|
||||||
|
* @param language
|
||||||
|
*/
|
||||||
|
public DefaultMemoryState(Language language) {
|
||||||
|
super(language);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MemoryBanks associated with specific address spaces must be registers with this MemoryState
|
||||||
|
* via this method. Each address space that will be used during emulation must be registered
|
||||||
|
* separately. The MemoryState object does not assume responsibility for freeing the MemoryBank.
|
||||||
|
*
|
||||||
|
* @param bank is a pointer to the MemoryBank to be registered
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final void setMemoryBank(MemoryBank bank) {
|
||||||
|
AddressSpace spc = bank.getSpace();
|
||||||
|
int index = spc.getUnique();
|
||||||
|
|
||||||
|
while (index >= memspace.size())
|
||||||
|
memspace.push_back(null);
|
||||||
|
|
||||||
|
memspace.set(index, bank);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Any MemoryBank that has been registered with this MemoryState can be retrieved via this
|
||||||
|
* method if the MemoryBank's associated address space is known.
|
||||||
|
*
|
||||||
|
* @param spc is the address space of the desired MemoryBank
|
||||||
|
* @return the MemoryBank or null if no bank is associated with spc.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final MemoryBank getMemoryBank(AddressSpace spc) {
|
||||||
|
int index = spc.getUnique();
|
||||||
|
if (index >= memspace.size())
|
||||||
|
return null;
|
||||||
|
return memspace.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the main interface for reading a range of bytes from the MemorySate. The MemoryBank
|
||||||
|
* associated with the address space of the query is looked up and the request is forwarded to
|
||||||
|
* the getChunk method on the MemoryBank. If there is no registered MemoryBank or some other
|
||||||
|
* error, an exception is thrown. All getLongValue methods utilize this method to read the bytes
|
||||||
|
* from the appropriate memory bank.
|
||||||
|
*
|
||||||
|
* @param res the result buffer for storing retrieved bytes
|
||||||
|
* @param spc the desired address space
|
||||||
|
* @param off the starting offset of the byte range being read
|
||||||
|
* @param size the number of bytes being read
|
||||||
|
* @param stopOnUnintialized if true a partial read is permitted and returned size may be
|
||||||
|
* smaller than size requested
|
||||||
|
* @return number of bytes actually read
|
||||||
|
* @throws LowlevelError if spc has not been mapped within this MemoryState or memory fault
|
||||||
|
* handler generated error
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int getChunk(byte[] res, AddressSpace spc, long off, int size,
|
||||||
|
boolean stopOnUnintialized) {
|
||||||
|
if (spc.isConstantSpace()) {
|
||||||
|
System.arraycopy(Utils.longToBytes(off, size, language.isBigEndian()), 0, res, 0, size);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
MemoryBank mspace = getMemoryBank(spc);
|
||||||
|
if (mspace == null)
|
||||||
|
throw new LowlevelError("Getting chunk from unmapped memory space: " + spc.getName());
|
||||||
|
return mspace.getChunk(off, size, res, stopOnUnintialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the main interface for setting values for a range of bytes in the MemoryState. The
|
||||||
|
* MemoryBank associated with the desired address space is looked up and the write is forwarded
|
||||||
|
* to the setChunk method on the MemoryBank. If there is no registered MemoryBank or some other
|
||||||
|
* error, an exception is throw. All setValue methods utilize this method to read the bytes from
|
||||||
|
* the appropriate memory bank.
|
||||||
|
*
|
||||||
|
* @param val the byte values to be written into the MemoryState
|
||||||
|
* @param spc the address space being written
|
||||||
|
* @param off the starting offset of the range being written
|
||||||
|
* @param size the number of bytes to write
|
||||||
|
* @throws LowlevelError if spc has not been mapped within this MemoryState
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setChunk(byte[] val, AddressSpace spc, long off, int size) {
|
||||||
|
MemoryBank mspace = getMemoryBank(spc);
|
||||||
|
if (mspace == null)
|
||||||
|
throw new LowlevelError("Setting chunk of unmapped memory space: " + spc.getName());
|
||||||
|
mspace.setChunk(off, size, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the main interface for setting the initialization status for a range of bytes in the
|
||||||
|
* MemoryState. The MemoryBank associated with the desired address space is looked up and the
|
||||||
|
* write is forwarded to the setInitialized method on the MemoryBank. If there is no registered
|
||||||
|
* MemoryBank or some other error, an exception is throw. All setValue methods utilize this
|
||||||
|
* method to read the bytes from the appropriate memory bank.
|
||||||
|
*
|
||||||
|
* @param initialized indicates if range should be marked as initialized or not
|
||||||
|
* @param spc the address space being written
|
||||||
|
* @param off the starting offset of the range being written
|
||||||
|
* @param size the number of bytes to write
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setInitialized(boolean initialized, AddressSpace spc, long off, int size) {
|
||||||
|
MemoryBank mspace = getMemoryBank(spc);
|
||||||
|
if (mspace == null)
|
||||||
|
throw new LowlevelError("Setting intialization status of unmapped memory space: " +
|
||||||
|
spc.getName());
|
||||||
|
mspace.setInitialized(off, size, initialized);
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,47 +16,13 @@
|
||||||
package ghidra.pcode.memstate;
|
package ghidra.pcode.memstate;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import generic.stl.VectorSTL;
|
|
||||||
import ghidra.pcode.error.LowlevelError;
|
import ghidra.pcode.error.LowlevelError;
|
||||||
import ghidra.pcode.utils.Utils;
|
|
||||||
import ghidra.program.model.address.Address;
|
|
||||||
import ghidra.program.model.address.AddressSpace;
|
import ghidra.program.model.address.AddressSpace;
|
||||||
import ghidra.program.model.lang.Language;
|
|
||||||
import ghidra.program.model.lang.Register;
|
import ghidra.program.model.lang.Register;
|
||||||
import ghidra.program.model.pcode.Varnode;
|
import ghidra.program.model.pcode.Varnode;
|
||||||
|
|
||||||
/**
|
public interface MemoryState {
|
||||||
* All storage/state for a pcode emulator machine
|
|
||||||
*
|
|
||||||
* Every piece of information in a pcode emulator machine is representable as a triple
|
|
||||||
* (AddressSpace,offset,size). This class allows getting and setting
|
|
||||||
* of all state information of this form.
|
|
||||||
*/
|
|
||||||
public class MemoryState {
|
|
||||||
|
|
||||||
Language language;
|
|
||||||
VectorSTL<MemoryBank> memspace = new VectorSTL<MemoryBank>();
|
|
||||||
Map<Register, Varnode> regVarnodeCache = new HashMap<Register, Varnode>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MemoryState constructor for a specified processor language
|
|
||||||
* @param language
|
|
||||||
*/
|
|
||||||
public MemoryState(Language language) {
|
|
||||||
this.language = language;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Varnode getVarnode(Register reg) {
|
|
||||||
Varnode varnode = regVarnodeCache.get(reg);
|
|
||||||
if (varnode == null) {
|
|
||||||
varnode = new Varnode(reg.getAddress(), reg.getMinimumByteSize());
|
|
||||||
regVarnodeCache.put(reg, varnode);
|
|
||||||
}
|
|
||||||
return varnode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MemoryBanks associated with specific address spaces must be registers with this MemoryState
|
* MemoryBanks associated with specific address spaces must be registers with this MemoryState
|
||||||
|
@ -64,15 +30,7 @@ public class MemoryState {
|
||||||
* separately. The MemoryState object does not assume responsibility for freeing the MemoryBank.
|
* separately. The MemoryState object does not assume responsibility for freeing the MemoryBank.
|
||||||
* @param bank is a pointer to the MemoryBank to be registered
|
* @param bank is a pointer to the MemoryBank to be registered
|
||||||
*/
|
*/
|
||||||
public final void setMemoryBank(MemoryBank bank) {
|
void setMemoryBank(MemoryBank bank);
|
||||||
AddressSpace spc = bank.getSpace();
|
|
||||||
int index = spc.getUnique();
|
|
||||||
|
|
||||||
while (index >= memspace.size())
|
|
||||||
memspace.push_back(null);
|
|
||||||
|
|
||||||
memspace.set(index, bank);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Any MemoryBank that has been registered with this MemoryState can be retrieved via this
|
* Any MemoryBank that has been registered with this MemoryState can be retrieved via this
|
||||||
|
@ -80,12 +38,7 @@ public class MemoryState {
|
||||||
* @param spc is the address space of the desired MemoryBank
|
* @param spc is the address space of the desired MemoryBank
|
||||||
* @return the MemoryBank or null if no bank is associated with spc.
|
* @return the MemoryBank or null if no bank is associated with spc.
|
||||||
*/
|
*/
|
||||||
public final MemoryBank getMemoryBank(AddressSpace spc) {
|
MemoryBank getMemoryBank(AddressSpace spc);
|
||||||
int index = spc.getUnique();
|
|
||||||
if (index >= memspace.size())
|
|
||||||
return null;
|
|
||||||
return memspace.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A convenience method for setting a value directly on a varnode rather than
|
* A convenience method for setting a value directly on a varnode rather than
|
||||||
|
@ -93,10 +46,7 @@ public class MemoryState {
|
||||||
* @param vn the varnode location to be written
|
* @param vn the varnode location to be written
|
||||||
* @param cval the value to write into the varnode location
|
* @param cval the value to write into the varnode location
|
||||||
*/
|
*/
|
||||||
public final void setValue(Varnode vn, long cval) {
|
void setValue(Varnode vn, long cval);
|
||||||
Address addr = vn.getAddress();
|
|
||||||
setValue(addr.getAddressSpace(), addr.getOffset(), vn.getSize(), cval);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A convenience method for setting a value directly on a register rather than
|
* A convenience method for setting a value directly on a register rather than
|
||||||
|
@ -104,10 +54,7 @@ public class MemoryState {
|
||||||
* @param reg the register location to be written
|
* @param reg the register location to be written
|
||||||
* @param cval the value to write into the register location
|
* @param cval the value to write into the register location
|
||||||
*/
|
*/
|
||||||
public final void setValue(Register reg, long cval) {
|
void setValue(Register reg, long cval);
|
||||||
Address addr = reg.getAddress();
|
|
||||||
setValue(addr.getAddressSpace(), addr.getOffset(), reg.getMinimumByteSize(), cval);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a convenience method for setting registers by name.
|
* This is a convenience method for setting registers by name.
|
||||||
|
@ -117,12 +64,7 @@ public class MemoryState {
|
||||||
* @param nm is the name of the register
|
* @param nm is the name of the register
|
||||||
* @param cval is the value to write to the register
|
* @param cval is the value to write to the register
|
||||||
*/
|
*/
|
||||||
public final void setValue(String nm, long cval) {
|
void setValue(String nm, long cval);
|
||||||
// Set a "register" value
|
|
||||||
Varnode vdata = getVarnode(language.getRegister(nm));
|
|
||||||
Address addr = vdata.getAddress();
|
|
||||||
setValue(addr.getAddressSpace(), addr.getOffset(), vdata.getSize(), cval);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the main interface for writing values to the MemoryState.
|
* This is the main interface for writing values to the MemoryState.
|
||||||
|
@ -133,9 +75,7 @@ public class MemoryState {
|
||||||
* @param size is the number of bytes to be written
|
* @param size is the number of bytes to be written
|
||||||
* @param cval is the value to be written
|
* @param cval is the value to be written
|
||||||
*/
|
*/
|
||||||
public final void setValue(AddressSpace spc, long off, int size, long cval) {
|
void setValue(AddressSpace spc, long off, int size, long cval);
|
||||||
setChunk(Utils.longToBytes(cval, size, language.isBigEndian()), spc, off, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A convenience method for reading a value directly from a varnode rather
|
* A convenience method for reading a value directly from a varnode rather
|
||||||
|
@ -143,10 +83,7 @@ public class MemoryState {
|
||||||
* @param vn the varnode location to be read
|
* @param vn the varnode location to be read
|
||||||
* @return the value read from the varnode location
|
* @return the value read from the varnode location
|
||||||
*/
|
*/
|
||||||
public final long getValue(Varnode vn) {
|
long getValue(Varnode vn);
|
||||||
Address addr = vn.getAddress();
|
|
||||||
return getValue(addr.getAddressSpace(), addr.getOffset(), vn.getSize());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A convenience method for reading a value directly from a register rather
|
* A convenience method for reading a value directly from a register rather
|
||||||
|
@ -154,10 +91,7 @@ public class MemoryState {
|
||||||
* @param reg the register location to be read
|
* @param reg the register location to be read
|
||||||
* @return the value read from the register location
|
* @return the value read from the register location
|
||||||
*/
|
*/
|
||||||
public final long getValue(Register reg) {
|
long getValue(Register reg);
|
||||||
Address addr = reg.getAddress();
|
|
||||||
return getValue(addr.getAddressSpace(), addr.getOffset(), reg.getMinimumByteSize());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a convenience method for reading registers by name.
|
* This is a convenience method for reading registers by name.
|
||||||
|
@ -167,12 +101,7 @@ public class MemoryState {
|
||||||
* @param nm is the name of the register
|
* @param nm is the name of the register
|
||||||
* @return the value associated with that register
|
* @return the value associated with that register
|
||||||
*/
|
*/
|
||||||
public final long getValue(String nm) {
|
long getValue(String nm);
|
||||||
// Get a "register" value
|
|
||||||
Varnode vdata = getVarnode(language.getRegister(nm));
|
|
||||||
Address addr = vdata.getAddress();
|
|
||||||
return getValue(addr.getAddressSpace(), addr.getOffset(), vdata.getSize());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the main interface for reading values from the MemoryState.
|
* This is the main interface for reading values from the MemoryState.
|
||||||
|
@ -183,14 +112,7 @@ public class MemoryState {
|
||||||
* @param size is the number of bytes to query
|
* @param size is the number of bytes to query
|
||||||
* @return the queried value
|
* @return the queried value
|
||||||
*/
|
*/
|
||||||
public final long getValue(AddressSpace spc, long off, int size) {
|
long getValue(AddressSpace spc, long off, int size);
|
||||||
if (spc.isConstantSpace()) {
|
|
||||||
return off;
|
|
||||||
}
|
|
||||||
byte[] bytes = new byte[size];
|
|
||||||
getChunk(bytes, spc, off, size, false);
|
|
||||||
return Utils.bytesToLong(bytes, size, language.isBigEndian());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A convenience method for setting a value directly on a varnode rather than
|
* A convenience method for setting a value directly on a varnode rather than
|
||||||
|
@ -198,10 +120,7 @@ public class MemoryState {
|
||||||
* @param vn the varnode location to be written
|
* @param vn the varnode location to be written
|
||||||
* @param cval the value to write into the varnode location
|
* @param cval the value to write into the varnode location
|
||||||
*/
|
*/
|
||||||
public final void setValue(Varnode vn, BigInteger cval) {
|
void setValue(Varnode vn, BigInteger cval);
|
||||||
Address addr = vn.getAddress();
|
|
||||||
setValue(addr.getAddressSpace(), addr.getOffset(), vn.getSize(), cval);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A convenience method for setting a value directly on a register rather than
|
* A convenience method for setting a value directly on a register rather than
|
||||||
|
@ -209,10 +128,7 @@ public class MemoryState {
|
||||||
* @param reg the register location to be written
|
* @param reg the register location to be written
|
||||||
* @param cval the value to write into the register location
|
* @param cval the value to write into the register location
|
||||||
*/
|
*/
|
||||||
public final void setValue(Register reg, BigInteger cval) {
|
void setValue(Register reg, BigInteger cval);
|
||||||
Address addr = reg.getAddress();
|
|
||||||
setValue(addr.getAddressSpace(), addr.getOffset(), reg.getMinimumByteSize(), cval);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a convenience method for setting registers by name.
|
* This is a convenience method for setting registers by name.
|
||||||
|
@ -222,12 +138,7 @@ public class MemoryState {
|
||||||
* @param nm is the name of the register
|
* @param nm is the name of the register
|
||||||
* @param cval is the value to write to the register
|
* @param cval is the value to write to the register
|
||||||
*/
|
*/
|
||||||
public final void setValue(String nm, BigInteger cval) {
|
void setValue(String nm, BigInteger cval);
|
||||||
// Set a "register" value
|
|
||||||
Varnode vdata = getVarnode(language.getRegister(nm));
|
|
||||||
Address addr = vdata.getAddress();
|
|
||||||
setValue(addr.getAddressSpace(), addr.getOffset(), vdata.getSize(), cval);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the main interface for writing values to the MemoryState.
|
* This is the main interface for writing values to the MemoryState.
|
||||||
|
@ -238,9 +149,7 @@ public class MemoryState {
|
||||||
* @param size is the number of bytes to be written
|
* @param size is the number of bytes to be written
|
||||||
* @param cval is the value to be written
|
* @param cval is the value to be written
|
||||||
*/
|
*/
|
||||||
public final void setValue(AddressSpace spc, long off, int size, BigInteger cval) {
|
void setValue(AddressSpace spc, long off, int size, BigInteger cval);
|
||||||
setChunk(Utils.bigIntegerToBytes(cval, size, language.isBigEndian()), spc, off, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A convenience method for reading a value directly from a varnode rather
|
* A convenience method for reading a value directly from a varnode rather
|
||||||
|
@ -249,10 +158,7 @@ public class MemoryState {
|
||||||
* @param signed true if signed value should be returned, false for unsigned value
|
* @param signed true if signed value should be returned, false for unsigned value
|
||||||
* @return the unsigned value read from the varnode location
|
* @return the unsigned value read from the varnode location
|
||||||
*/
|
*/
|
||||||
public final BigInteger getBigInteger(Varnode vn, boolean signed) {
|
BigInteger getBigInteger(Varnode vn, boolean signed);
|
||||||
Address addr = vn.getAddress();
|
|
||||||
return getBigInteger(addr.getAddressSpace(), addr.getOffset(), vn.getSize(), signed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A convenience method for reading a value directly from a register rather
|
* A convenience method for reading a value directly from a register rather
|
||||||
|
@ -260,11 +166,7 @@ public class MemoryState {
|
||||||
* @param reg the register location to be read
|
* @param reg the register location to be read
|
||||||
* @return the unsigned value read from the register location
|
* @return the unsigned value read from the register location
|
||||||
*/
|
*/
|
||||||
public final BigInteger getBigInteger(Register reg) {
|
BigInteger getBigInteger(Register reg);
|
||||||
Address addr = reg.getAddress();
|
|
||||||
return getBigInteger(addr.getAddressSpace(), addr.getOffset(), reg.getMinimumByteSize(),
|
|
||||||
false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a convenience method for reading registers by name.
|
* This is a convenience method for reading registers by name.
|
||||||
|
@ -274,12 +176,7 @@ public class MemoryState {
|
||||||
* @param nm is the name of the register
|
* @param nm is the name of the register
|
||||||
* @return the unsigned value associated with that register
|
* @return the unsigned value associated with that register
|
||||||
*/
|
*/
|
||||||
public final BigInteger getBigInteger(String nm) {
|
BigInteger getBigInteger(String nm);
|
||||||
// Get a "register" value
|
|
||||||
Varnode vdata = getVarnode(language.getRegister(nm));
|
|
||||||
Address addr = vdata.getAddress();
|
|
||||||
return getBigInteger(addr.getAddressSpace(), addr.getOffset(), vdata.getSize(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the main interface for reading values from the MemoryState.
|
* This is the main interface for reading values from the MemoryState.
|
||||||
|
@ -291,17 +188,7 @@ public class MemoryState {
|
||||||
* @param signed true if signed value should be returned, false for unsigned value
|
* @param signed true if signed value should be returned, false for unsigned value
|
||||||
* @return the queried unsigned value
|
* @return the queried unsigned value
|
||||||
*/
|
*/
|
||||||
public final BigInteger getBigInteger(AddressSpace spc, long off, int size, boolean signed) {
|
BigInteger getBigInteger(AddressSpace spc, long off, int size, boolean signed);
|
||||||
if (spc.isConstantSpace()) {
|
|
||||||
if (!signed && off < 0) {
|
|
||||||
return new BigInteger(1, Utils.longToBytes(off, 8, true));
|
|
||||||
}
|
|
||||||
return BigInteger.valueOf(off);
|
|
||||||
}
|
|
||||||
byte[] bytes = new byte[size];
|
|
||||||
getChunk(bytes, spc, off, size, false);
|
|
||||||
return Utils.bytesToBigInteger(bytes, size, language.isBigEndian(), signed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the main interface for reading a range of bytes from the MemorySate.
|
* This is the main interface for reading a range of bytes from the MemorySate.
|
||||||
|
@ -320,17 +207,8 @@ public class MemoryState {
|
||||||
* @throws LowlevelError if spc has not been mapped within this MemoryState or memory fault
|
* @throws LowlevelError if spc has not been mapped within this MemoryState or memory fault
|
||||||
* handler generated error
|
* handler generated error
|
||||||
*/
|
*/
|
||||||
public int getChunk(byte[] res, AddressSpace spc, long off, int size,
|
int getChunk(byte[] res, AddressSpace spc, long off, int size,
|
||||||
boolean stopOnUnintialized) {
|
boolean stopOnUnintialized);
|
||||||
if (spc.isConstantSpace()) {
|
|
||||||
System.arraycopy(Utils.longToBytes(off, size, language.isBigEndian()), 0, res, 0, size);
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
MemoryBank mspace = getMemoryBank(spc);
|
|
||||||
if (mspace == null)
|
|
||||||
throw new LowlevelError("Getting chunk from unmapped memory space: " + spc.getName());
|
|
||||||
return mspace.getChunk(off, size, res, stopOnUnintialized);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the main interface for setting values for a range of bytes in the MemoryState.
|
* This is the main interface for setting values for a range of bytes in the MemoryState.
|
||||||
|
@ -345,12 +223,7 @@ public class MemoryState {
|
||||||
* @param size the number of bytes to write
|
* @param size the number of bytes to write
|
||||||
* @throws LowlevelError if spc has not been mapped within this MemoryState
|
* @throws LowlevelError if spc has not been mapped within this MemoryState
|
||||||
*/
|
*/
|
||||||
public void setChunk(byte[] val, AddressSpace spc, long off, int size) {
|
void setChunk(byte[] val, AddressSpace spc, long off, int size);
|
||||||
MemoryBank mspace = getMemoryBank(spc);
|
|
||||||
if (mspace == null)
|
|
||||||
throw new LowlevelError("Setting chunk of unmapped memory space: " + spc.getName());
|
|
||||||
mspace.setChunk(off, size, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the main interface for setting the initialization status for a range of bytes
|
* This is the main interface for setting the initialization status for a range of bytes
|
||||||
|
@ -365,12 +238,6 @@ public class MemoryState {
|
||||||
* @param off the starting offset of the range being written
|
* @param off the starting offset of the range being written
|
||||||
* @param size the number of bytes to write
|
* @param size the number of bytes to write
|
||||||
*/
|
*/
|
||||||
public void setInitialized(boolean initialized, AddressSpace spc, long off, int size) {
|
void setInitialized(boolean initialized, AddressSpace spc, long off, int size);
|
||||||
MemoryBank mspace = getMemoryBank(spc);
|
|
||||||
if (mspace == null)
|
|
||||||
throw new LowlevelError("Setting intialization status of unmapped memory space: " +
|
|
||||||
spc.getName());
|
|
||||||
mspace.setInitialized(off, size, initialized);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue