Merge remote-tracking branch 'origin/GP-2642_Dan_compatEmulatorHelper--SQUASHED'

This commit is contained in:
Ryan Kurtz 2023-03-14 06:23:07 -04:00
commit 254e749f95
25 changed files with 1815 additions and 741 deletions

View file

@ -155,6 +155,11 @@ class TestThread implements PcodeThread<Void> {
public void setSuspended(boolean suspended) {
}
@Override
public boolean isSuspended() {
return false;
}
@Override
public PcodeUseropLibrary<Void> getUseropLibrary() {
return null;

View file

@ -63,7 +63,12 @@ public class EmulatorTestRunner {
this.program = program;
this.testGroup = testGroup;
this.executionListener = executionListener;
emuHelper = new EmulatorHelper(program);
emuHelper = new EmulatorHelper(program) {
@Override
protected Emulator newEmulator() {
return new AdaptedEmulator(this);
}
};
emu = emuHelper.getEmulator();
emuHelper.setMemoryFaultHandler(new MyMemoryFaultHandler(executionListener));
@ -71,7 +76,7 @@ public class EmulatorTestRunner {
@Override
public boolean pcodeCallback(PcodeOpRaw op) throws LowlevelError {
int userOp = (int) op.getInput(0).getOffset();
String pcodeOpName = emulate.getLanguage().getUserDefinedOpName(userOp);
String pcodeOpName = program.getLanguage().getUserDefinedOpName(userOp);
unimplementedSet.add(pcodeOpName);
String outStr = "";
Varnode output = op.getOutput();
@ -169,8 +174,9 @@ public class EmulatorTestRunner {
/**
* Add memory dump point
*
* @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 dumpSize number elements which should be dumped
* @param elementSize size of each element in bytes (be reasonable!)
@ -190,8 +196,9 @@ public class EmulatorTestRunner {
/**
* Add memory dump point
*
* @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 relativeOffset dump register relative offset
* @param dumpAddrSpace address space to which memory offset should be applied
@ -230,11 +237,11 @@ public class EmulatorTestRunner {
}
/**
* Get number of CALLOTHER errors detected when a test pass was registered.
* This number should be subtracted from the pass count and possibly added
* to the failure count. Number does not reflect total number of CALLOTHER
* pcodeops encountered but only the number of passed tests affected.
* See log for all CALLOTHER executions detected.
* Get number of CALLOTHER errors detected when a test pass was registered. This number should
* be subtracted from the pass count and possibly added to the failure count. Number does not
* reflect total number of CALLOTHER pcodeops encountered but only the number of passed tests
* affected. See log for all CALLOTHER executions detected.
*
* @return number of CALLOTHER errors
*/
public int getCallOtherErrors() {
@ -243,6 +250,7 @@ public class EmulatorTestRunner {
/**
* Execute test group without instruction stepping/tracing
*
* @param timeLimitMS
* @param monitor
* @return
@ -695,8 +703,9 @@ public class EmulatorTestRunner {
@Override
Address getDumpAddress() {
RegisterValue regVal = getRegisterValue(dumpAddrReg);
return dumpAddrSpace.getAddress(regVal.getUnsignedValue().longValue()).add(
relativeOffset);
return dumpAddrSpace.getAddress(regVal.getUnsignedValue().longValue())
.add(
relativeOffset);
}
@Override

View file

@ -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() {
}
}

View file

@ -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
}
}

View file

@ -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);
}
}

View file

@ -15,453 +15,162 @@
*/
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.pcode.memstate.MemoryState;
import ghidra.program.model.address.Address;
import ghidra.program.model.lang.RegisterValue;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
public class 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 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");
}
}
/**
* The emulator interface
*
* <p>
* This interface may soon be deprecated. It was extracted from what has now been renamed
* {@link DefaultEmulator}. Please consider using {@link PcodeEmulator} instead.
*/
public interface Emulator {
/**
* 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
* Get the name of the program counter register
*
* @return the name
*/
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);
}
String getPCRegisterName();
/**
* 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
* Set the value of the program counter
*
* @param addressableWordOffset the <em>word</em> offset of the instruction to execute next.
*/
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();
}
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;
}
}
void setExecuteAddress(long addressableWordOffset);
/**
* @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() {
return getHalt() && emulator.getExecutionState() == EmulateExecutionState.BREAKPOINT;
}
Address getExecuteAddress();
/**
* @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).
* Get the address of the last instruction executed (or the instructed currently being executed)
*
* @return the address
*/
public EmulateExecutionState getEmulateExecutionState() {
return emulator.getExecutionState();
}
Address getLastExecuteAddress();
/**
* 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
*/
public boolean isExecuting() {
return isExecuting;
}
public SleighLanguage getLanguage() {
return language;
}
boolean isExecuting();
/**
* Disassemble from the current execute address
* @param count number of contiguous instructions to disassemble
* @return list of instructions
* Get the low-level execution state
*
* <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) {
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;
}
EmulateExecutionState getEmulateExecutionState();
/**
* Returns the current context register value. 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
* Get the memory state
*
* @return the state
*/
public RegisterValue getContextRegisterValue() {
return emulator.getContextRegisterValue();
}
MemoryState getMemState();
/**
* 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.
* The Emulator should not be running when this method is invoked.
* Only flowing context bits should be set, as non-flowing bits
* will be cleared prior to parsing on instruction. In addition,
* any future context state set by the pcode emitter will
* take precedence over context set using this method. This method
* is primarily intended to be used to establish the initial
*
* <p>
* The Emulator should not be running when this method is invoked. Only flowing context bits
* should be set, as non-flowing bits will be cleared prior to parsing on instruction. In
* addition, any future context state set by the pcode emitter will take precedence over context
* set using this method. This method is primarily intended to be used to establish the initial
* context state.
*
* @param regValue is the value to set context to
*/
public void setContextRegisterValue(RegisterValue regValue) {
emulator.setContextRegisterValue(regValue);
}
void setContextRegisterValue(RegisterValue regValue);
/**
* Add memory load image provider
* @param provider memory load image provider
* @param view memory region which corresponds to provider
* Returns the current context register value.
*
* <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) {
loadImage.addProvider(provider, view);
}
RegisterValue getContextRegisterValue();
/**
* 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();
}

View file

@ -23,6 +23,7 @@ import ghidra.app.emulator.memory.*;
import ghidra.app.emulator.state.DumpMiscState;
import ghidra.app.emulator.state.RegisterState;
import ghidra.framework.store.LockException;
import ghidra.pcode.emu.PcodeEmulator;
import ghidra.pcode.emulate.BreakCallBack;
import ghidra.pcode.emulate.EmulateExecutionState;
import ghidra.pcode.memstate.MemoryFaultHandler;
@ -38,6 +39,17 @@ import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
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 {
private final Program program;
@ -68,11 +80,15 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
stackPtrReg = program.getCompilerSpec().getStackPointer();
stackMemorySpace = program.getCompilerSpec().getStackBaseSpace();
emulator = new Emulator(this);
emulator = newEmulator();
converter = DataConverter.getInstance(program.getMemory().isBigEndian());
}
protected Emulator newEmulator() {
return new DefaultEmulator(this);
}
public void dispose() {
emulator.dispose();
if (memoryWriteTracker != null) {
@ -115,6 +131,7 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
/**
* Get Program Counter (PC) register defined by applicable processor specification
*
* @return Program Counter register
*/
public Register getPCRegister() {
@ -123,6 +140,7 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
/**
* Get Stack Pointer register defined by applicable compiler specification
*
* @return Stack Pointer register
*/
public Register getStackPointerRegister() {
@ -130,14 +148,12 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
}
/**
* Provides ability to install a low-level memory fault handler.
* The handler methods should generally return 'false' to allow
* the default handler to generate the appropriate target error.
* Within the fault handler, the EmulateExecutionState can be used
* to distinguish the pcode-emit state and the actual execution state
* since an attempt to execute an instruction at an uninitialized
* memory location will cause an uninitializedRead during the PCODE_EMIT
* state.
* Provides ability to install a low-level memory fault handler. The handler methods should
* generally return 'false' to allow the default handler to generate the appropriate target
* error. Within the fault handler, the EmulateExecutionState can be used to distinguish the
* pcode-emit state and the actual execution 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.
*/
public void setMemoryFaultHandler(MemoryFaultHandler handler) {
@ -215,9 +231,10 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
/**
* Read string from memory state.
*
* @param addr memory address
* @param maxLength limit string read to this length. If return string is
* truncated, "..." will be appended.
* @param maxLength limit string read to this length. If return string is truncated, "..." will
* be appended.
* @return string read from memory state
*/
public String readNullTerminatedString(Address addr, int maxLength) {
@ -242,8 +259,9 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
public byte[] readMemory(Address addr, int length) {
byte[] res = new byte[length];
int len = emulator.getMemState().getChunk(res, addr.getAddressSpace(), addr.getOffset(),
length, false);
int len = emulator.getMemState()
.getChunk(res, addr.getAddressSpace(), addr.getOffset(),
length, false);
if (len == 0) {
Msg.error(this, "Failed to read memory from Emulator at: " + addr);
return null;
@ -256,8 +274,9 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
}
public void writeMemory(Address addr, byte[] bytes) {
emulator.getMemState().setChunk(bytes, addr.getAddressSpace(), addr.getOffset(),
bytes.length);
emulator.getMemState()
.setChunk(bytes, addr.getAddressSpace(), addr.getOffset(),
bytes.length);
}
public void writeMemoryValue(Address addr, int size, long value) {
@ -266,6 +285,7 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
/**
* Read a stack value from the memory state.
*
* @param relativeOffset offset relative to current stack pointer
* @param size data size in bytes
* @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
*
* @param relativeOffset offset relative to current stack pointer
* @param size data size in bytes
* @param value
@ -295,6 +316,7 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
/**
* Write a value onto the stack
*
* @param relativeOffset offset relative to current stack pointer
* @param size data size in bytes
* @param value
@ -309,6 +331,7 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
/**
* Establish breakpoint
*
* @param addr memory address for new breakpoint
*/
public void setBreakpoint(Address addr) {
@ -317,6 +340,7 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
/**
* Clear breakpoint
*
* @param addr memory address for breakpoint to be cleared
*/
public void clearBreakpoint(Address addr) {
@ -324,8 +348,9 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
}
/**
* Set current context register value.
* Keep in mind that any non-flowing context values will be stripped.
* Set current context register value. Keep in mind that any non-flowing context values will be
* stripped.
*
* @param ctxRegValue
*/
public void setContextRegister(RegisterValue ctxRegValue) {
@ -333,8 +358,9 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
}
/**
* Set current context register value.
* Keep in mind that any non-flowing context values will be stripped.
* Set current context register value. Keep in mind that any non-flowing context values will be
* stripped.
*
* @param ctxReg context register
* @param value context value
*/
@ -344,6 +370,7 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
/**
* Get the current context register value
*
* @return context register value or null if not set or unknown
*/
public RegisterValue getContextRegister() {
@ -351,9 +378,9 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
}
/**
* Register callback for language defined pcodeop (call other).
* WARNING! Using this method may circumvent the default CALLOTHER emulation support
* when supplied by the Processor module.
* Register callback for language defined pcodeop (call other). WARNING! Using this method may
* circumvent the default CALLOTHER emulation support when supplied by the Processor module.
*
* @param pcodeOpName the name of the pcode op
* @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).
* WARNING! Using this method may circumvent the default CALLOTHER emulation support
* when supplied by the Processor module.
* Register default callback for language defined pcodeops (call other). WARNING! Using this
* method may circumvent the default CALLOTHER emulation support when supplied by the Processor
* module.
*
* @param callback the default callback to register
*/
public void registerDefaultCallOtherCallback(BreakCallBack callback) {
@ -373,6 +401,7 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
/**
* Unregister callback for language defined pcodeop (call other).
*
* @param pcodeOpName the name of the pcode op
*/
public void unregisterCallOtherCallback(String pcodeOpName) {
@ -380,9 +409,9 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
}
/**
* Unregister default callback for language defined pcodeops (call other).
* WARNING! Using this method may circumvent the default CALLOTHER emulation support
* when supplied by the Processor module.
* Unregister default callback for language defined pcodeops (call other). WARNING! Using this
* method may circumvent the default CALLOTHER emulation support when supplied by the Processor
* module.
*/
public void unregisterDefaultCallOtherCallback() {
emulator.getBreakTable().unregisterPcodeCallback("*");
@ -390,6 +419,7 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
/**
* Get current execution address
*
* @return current execution address
*/
public Address getExecutionAddress() {
@ -397,11 +427,11 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
}
/**
* Start execution at the specified address using the initial context specified.
* Method will block until execution stops. This method will initialize context
* register based upon the program stored context if not already done. In addition,
* both general register value and the context register may be further modified
* via the context parameter if specified.
* Start execution at the specified address using the initial context specified. Method will
* block until execution stops. This method will initialize context register based upon the
* program stored context if not already done. In addition, both general register value and the
* context register may be further modified via the context parameter if specified.
*
* @param addr initial program address
* @param context optional context settings which override current program context
* @param monitor
@ -463,10 +493,10 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
}
/**
* Continue execution from the current execution address.
* No adjustment will be made to the context beyond the normal
* context flow behavior defined by the language.
* Method will block until execution stops.
* Continue execution from the current execution address. No adjustment will be made to the
* context beyond the normal context flow behavior defined by the language. Method will block
* until execution stops.
*
* @param monitor
* @return true if execution completes without error (i.e., is at breakpoint)
* @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.
*
* @throws CancelledException if execution was cancelled
*/
private void continueExecution(TaskMonitor monitor) throws CancelledException {
@ -494,8 +525,9 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
/**
* 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
*/
private void executeInstruction(boolean stopAtBreakpoint, TaskMonitor monitor)
@ -515,8 +547,8 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
lastError = t.toString();
}
emulator.setHalt(true); // force execution to stop
if (t instanceof CancelledException) {
throw (CancelledException) t;
if (t instanceof CancelledException ce) {
throw ce;
}
Msg.error(this,
"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
* make sure it has a context consistent with the program context
* if there is one.
* Used when the emulator has had the execution address changed to make sure it has a context
* consistent with the program context if there is one.
*/
private void setProcessorContext() {
// 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
* pcode operations. No adjustment will be made to the context beyond the normal
* context flow behavior defined by the language.
* Step execution one instruction which may consist of multiple pcode operations. No adjustment
* will be made to the context beyond the normal context flow behavior defined by the language.
* Method will block until execution stops.
*
* @return true if execution completes without error
* @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
*
* @param name block name
* @param start start address 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
* overlay address space will be created and the block will have a starting address at the same
* offset as the given start address parameter, but in the new address space.
* overlay address space will be created and the block will have a starting address
* at the same offset as the given start address parameter, but in the new address
* space.
* @param monitor
* @return new memory block
* @throws LockException if exclusive lock not in place (see haveLock())
* @throws MemoryConflictException if the new block overlaps with a
* previous block
* @throws AddressOverflowException if the start is beyond the
* address space
* @throws MemoryConflictException if the new block overlaps with a previous block
* @throws AddressOverflowException if the start is beyond the address space
* @throws CancelledException user cancelled operation
* @throws DuplicateNameException
*/
@ -626,8 +657,9 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
boolean success = false;
int txId = program.startTransaction("Create Memory Block");
try {
block = program.getMemory().createInitializedBlock(name, start, memStateStream, length,
monitor, overlay);
block = program.getMemory()
.createInitializedBlock(name, start, memStateStream, length,
monitor, overlay);
success = true;
}
finally {
@ -637,8 +669,8 @@ public class EmulatorHelper implements MemoryFaultHandler, EmulatorConfiguration
}
/**
* Enable/Disable tracking of memory writes in the form of an
* address set.
* Enable/Disable tracking of memory writes in the form of an address set.
*
* @param 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
* if memory write tracking is enabled, otherwise null is returned.
* The address set returned will continue to be updated unless
* memory write tracking becomes disabled.
* @return address set of memory locations written by the emulator if memory write tracking is
* enabled, otherwise null is returned. The address set returned will continue to be
* updated unless memory write tracking becomes disabled.
*/
public AddressSetView getTrackedMemoryWriteSet() {
if (memoryWriteTracker != null) {

View file

@ -15,11 +15,11 @@
*/
package ghidra.app.emulator;
import ghidra.pcode.memstate.MemoryState;
import ghidra.pcode.memstate.DefaultMemoryState;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.lang.Language;
class FilteredMemoryState extends MemoryState {
class FilteredMemoryState extends DefaultMemoryState {
private MemoryAccessFilter filter;
private boolean filterEnabled = true; // used to prevent filtering filter queries

View file

@ -17,6 +17,14 @@ package ghidra.app.emulator;
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 {
private MemoryAccessFilter prevFilter;
@ -26,24 +34,42 @@ public abstract class MemoryAccessFilter {
private boolean filterOnExecutionOnly = true;
final void filterRead(AddressSpace spc, long off, int size, byte [] values) {
if (filterOnExecutionOnly() && !emu.isExecuting()) return; // do not filter idle queries
final void filterRead(AddressSpace spc, long off, int size, byte[] values) {
if (filterOnExecutionOnly() && !emu.isExecuting())
return; // do not filter idle queries
processRead(spc, off, size, values);
if (nextFilter != null) {
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);
final void filterWrite(AddressSpace spc, long off, int size, byte [] values) {
if (filterOnExecutionOnly() && !emu.isExecuting()) return; // do not filter idle queries
final void filterWrite(AddressSpace spc, long off, int size, byte[] values) {
if (filterOnExecutionOnly() && !emu.isExecuting())
return; // do not filter idle queries
processWrite(spc, off, size, values);
if (nextFilter != null) {
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);
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.
* If overriden, be sure to invoke super.dispose().
*
* <p>
* If overriden, be sure to invoke {@code super.dispose()}.
*/
public void dispose() {
if (nextFilter != null) {

View file

@ -22,8 +22,22 @@ public interface RegisterState {
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);
/**
* 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 void setVals(String key, byte[] vals, boolean setInitiailized);

View file

@ -266,6 +266,11 @@ public abstract class AbstractPcodeMachine<T> implements PcodeMachine<T> {
this.suspended = suspended;
}
@Override
public boolean isSuspended() {
return suspended;
}
/**
* 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,
* if applicable
* Notify the machine a thread has been stepped a p-code op, so that it may re-enable software
* interrupts, if applicable
*/
protected void stepped() {
if (swiMode == SwiMode.IGNORE_STEP) {

View file

@ -320,15 +320,19 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
}
protected void branchToAddress(Address target) {
overrideCounter(target);
writeCounter(target);
decoder.branched(counter);
}
protected void writeCounter(Address counter) {
setCounter(counter);
state.setVar(pc,
arithmetic.fromConst(counter.getAddressableWordOffset(), pc.getMinimumByteSize()));
}
@Override
public void overrideCounter(Address counter) {
setCounter(counter);
state.setVar(pc,
arithmetic.fromConst(counter.getAddressableWordOffset(), pc.getMinimumByteSize()));
writeCounter(counter);
}
@Override
@ -375,13 +379,13 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
@Override
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));
if (contextreg != Register.NO_CONTEXT) {
try {
BigInteger ctx = arithmetic.toBigInteger(state.getVar(
contextreg, executor.getReason()), Purpose.CONTEXT);
BigInteger ctx = arithmetic.toBigInteger(state.getVar(contextreg, Reason.RE_INIT),
Purpose.CONTEXT);
assignContext(new RegisterValue(contextreg, ctx));
}
catch (AccessPcodeExecutionException e) {
@ -582,6 +586,11 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
executor.suspended = suspended;
}
@Override
public boolean isSuspended() {
return executor.suspended;
}
@Override
public SleighLanguage getLanguage() {
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,
* if applicable
* Notify the machine a thread has been stepped a p-code op, so that it may re-enable software
* interrupts, if applicable
*/
protected void stepped() {
machine.stepped();

View file

@ -17,14 +17,15 @@ package ghidra.pcode.emu;
import java.lang.reflect.Constructor;
import ghidra.app.emulator.AdaptedMemoryState;
import ghidra.app.emulator.Emulator;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.pcode.emulate.*;
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.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;
@ -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
*
@ -125,8 +98,6 @@ public class ModifiedPcodeThread<T> extends DefaultPcodeThread<T> {
protected final EmulateInstructionStateModifier modifier;
protected final Emulate emulate;
protected Address savedCounter;
/**
* 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
* written for {@link Emulator}.
*/
emulate = new GlueEmulate(language, new GlueMemoryState(language),
new BreakTableCallBack(language));
emulate = new GlueEmulate(language, new AdaptedMemoryState<>(state, Reason.EXECUTE) {
@Override
public void setMemoryBank(MemoryBank bank) {
// Ignore
}
}, new BreakTableCallBack(language));
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
public void reInitialize() {
super.reInitialize();
public void overrideCounter(Address counter) {
super.overrideCounter(counter);
if (modifier != null) {
savedCounter = getCounter();
modifier.initialExecuteCallback(emulate, savedCounter, getContext());
modifier.initialExecuteCallback(emulate, counter, getContext());
}
}
@Override
protected void postExecuteInstruction() {
if (modifier != null) {
modifier.postExecuteCallback(emulate, savedCounter, frame.copyCode(),
modifier.postExecuteCallback(emulate,
instruction == null ? null : instruction.getAddress(), frame.copyCode(),
frame.getBranched(), getCounter());
}
}

View file

@ -203,6 +203,13 @@ public interface PcodeMachine<T> {
*/
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
*

View file

@ -19,7 +19,6 @@ import java.util.List;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.pcode.emu.DefaultPcodeThread.PcodeEmulationLibrary;
import ghidra.pcode.emu.PcodeMachine.SwiMode;
import ghidra.pcode.exec.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.lang.Register;
@ -286,6 +285,13 @@ public interface PcodeThread<T> {
*/
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)
*

View file

@ -87,6 +87,15 @@ public class ThreadPcodeExecutorState<T> implements PcodeExecutorState<T> {
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
public T getVar(AddressSpace space, T offset, int size, boolean quantize, Reason reason) {
if (isThreadLocalSpace(space)) {
@ -95,6 +104,14 @@ public class ThreadPcodeExecutorState<T> implements PcodeExecutorState<T> {
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
public Map<Register, T> getRegisterValues() {
Map<Register, T> result = new HashMap<>();

View file

@ -65,7 +65,9 @@ public abstract class AbstractLongOffsetPcodeExecutorStatePiece<A, T, S>
*
* @return the copy
*/
public abstract AbstractSpaceMap<S> fork();
public AbstractSpaceMap<S> fork() {
throw new UnsupportedOperationException();
}
/**
* Deep copy the given space
@ -73,7 +75,9 @@ public abstract class AbstractLongOffsetPcodeExecutorStatePiece<A, T, S>
* @param s the space
* @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

View file

@ -69,11 +69,21 @@ public class DefaultPcodeExecutorState<T> implements PcodeExecutorState<T> {
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
public void setVar(AddressSpace space, T offset, int size, boolean quantize, T 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
public Map<Register, T> getRegisterValues() {
return piece.getRegisterValues();

View file

@ -46,7 +46,7 @@ public class PcodeExecutor<T> {
protected final PcodeExecutorState<T> state;
protected final Reason reason;
protected final Register pc;
protected final int pointerSize;
protected final int pcSize;
/**
* Construct an executor with the given bindings
@ -64,7 +64,7 @@ public class PcodeExecutor<T> {
this.reason = reason;
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());
}
else {
branchToOffset(arithmetic.fromConst(target.getOffset(), pointerSize), frame);
branchToOffset(arithmetic.fromConst(target.getOffset(), pcSize), frame);
branchToAddress(target);
}
}
@ -510,7 +510,7 @@ public class PcodeExecutor<T> {
*/
public void executeCall(PcodeOp op, PcodeFrame frame, PcodeUseropLibrary<T> library) {
Address target = op.getInput(0).getAddress();
branchToOffset(arithmetic.fromConst(target.getOffset(), pointerSize), frame);
branchToOffset(arithmetic.fromConst(target.getOffset(), pcSize), frame);
branchToAddress(target);
}

View file

@ -16,6 +16,7 @@
package ghidra.pcode.exec;
import java.util.Map;
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.Language;
@ -44,6 +45,8 @@ public interface PcodeExecutorStatePiece<A, T> {
* Reasons for reading state
*/
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 */
EXECUTE,
/** The value is being inspected */
@ -59,6 +62,9 @@ public interface PcodeExecutorStatePiece<A, T> {
*/
default void checkRange(AddressSpace space, long offset, int size) {
// TODO: Perhaps get/setVar should just take an AddressRange?
if (space.isConstantSpace()) {
return;
}
try {
new AddressRangeImpl(space.getAddress(offset), size);
}
@ -93,7 +99,9 @@ public interface PcodeExecutorStatePiece<A, T> {
*
* @return the copy
*/
PcodeExecutorStatePiece<A, T> fork();
default PcodeExecutorStatePiece<A, T> fork() {
throw new UnsupportedOperationException();
}
/**
* Set the value of a register variable

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (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
public abstract class BreakTable {
public interface BreakTable {
/// \brief Associate a particular emulator with breakpoints in this table
///

View file

@ -29,7 +29,7 @@ import ghidra.program.model.address.Address;
///
/// Breakpoints are stored in map containers, and the core BreakTable methods
/// are implemented to search in these containers
public class BreakTableCallBack extends BreakTable {
public class BreakTableCallBack implements BreakTable {
public static final String DEFAULT_NAME = "*";

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -16,47 +16,13 @@
package ghidra.pcode.memstate;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import generic.stl.VectorSTL;
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.lang.Language;
import ghidra.program.model.lang.Register;
import ghidra.program.model.pcode.Varnode;
/**
* 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;
}
public interface 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.
* @param bank is a pointer to the MemoryBank to be registered
*/
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);
}
void setMemoryBank(MemoryBank bank);
/**
* 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
* @return the MemoryBank or null if no bank is associated with spc.
*/
public final MemoryBank getMemoryBank(AddressSpace spc) {
int index = spc.getUnique();
if (index >= memspace.size())
return null;
return memspace.get(index);
}
MemoryBank getMemoryBank(AddressSpace spc);
/**
* 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 cval the value to write into the varnode location
*/
public final void setValue(Varnode vn, long cval) {
Address addr = vn.getAddress();
setValue(addr.getAddressSpace(), addr.getOffset(), vn.getSize(), cval);
}
void setValue(Varnode vn, long cval);
/**
* 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 cval the value to write into the register location
*/
public final void setValue(Register reg, long cval) {
Address addr = reg.getAddress();
setValue(addr.getAddressSpace(), addr.getOffset(), reg.getMinimumByteSize(), cval);
}
void setValue(Register reg, long cval);
/**
* 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 cval is the value to write to the register
*/
public final 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);
}
void setValue(String nm, long cval);
/**
* 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 cval is the value to be written
*/
public final void setValue(AddressSpace spc, long off, int size, long cval) {
setChunk(Utils.longToBytes(cval, size, language.isBigEndian()), spc, off, size);
}
void setValue(AddressSpace spc, long off, int size, long cval);
/**
* 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
* @return the value read from the varnode location
*/
public final long getValue(Varnode vn) {
Address addr = vn.getAddress();
return getValue(addr.getAddressSpace(), addr.getOffset(), vn.getSize());
}
long getValue(Varnode vn);
/**
* 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
* @return the value read from the register location
*/
public final long getValue(Register reg) {
Address addr = reg.getAddress();
return getValue(addr.getAddressSpace(), addr.getOffset(), reg.getMinimumByteSize());
}
long getValue(Register reg);
/**
* 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
* @return the value associated with that register
*/
public final 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());
}
long getValue(String nm);
/**
* 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
* @return the queried value
*/
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());
}
long getValue(AddressSpace spc, long off, int size);
/**
* 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 cval the value to write into the varnode location
*/
public final void setValue(Varnode vn, BigInteger cval) {
Address addr = vn.getAddress();
setValue(addr.getAddressSpace(), addr.getOffset(), vn.getSize(), cval);
}
void setValue(Varnode vn, BigInteger cval);
/**
* 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 cval the value to write into the register location
*/
public final void setValue(Register reg, BigInteger cval) {
Address addr = reg.getAddress();
setValue(addr.getAddressSpace(), addr.getOffset(), reg.getMinimumByteSize(), cval);
}
void setValue(Register reg, BigInteger cval);
/**
* 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 cval is the value to write to the register
*/
public final 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);
}
void setValue(String nm, BigInteger cval);
/**
* 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 cval is the value to be written
*/
public final void setValue(AddressSpace spc, long off, int size, BigInteger cval) {
setChunk(Utils.bigIntegerToBytes(cval, size, language.isBigEndian()), spc, off, size);
}
void setValue(AddressSpace spc, long off, int size, BigInteger cval);
/**
* 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
* @return the unsigned value read from the varnode location
*/
public final BigInteger getBigInteger(Varnode vn, boolean signed) {
Address addr = vn.getAddress();
return getBigInteger(addr.getAddressSpace(), addr.getOffset(), vn.getSize(), signed);
}
BigInteger getBigInteger(Varnode vn, boolean signed);
/**
* 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
* @return the unsigned value read from the register location
*/
public final BigInteger getBigInteger(Register reg) {
Address addr = reg.getAddress();
return getBigInteger(addr.getAddressSpace(), addr.getOffset(), reg.getMinimumByteSize(),
false);
}
BigInteger getBigInteger(Register reg);
/**
* 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
* @return the unsigned value associated with that register
*/
public final 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);
}
BigInteger getBigInteger(String nm);
/**
* 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
* @return the queried unsigned value
*/
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);
}
BigInteger getBigInteger(AddressSpace spc, long off, int size, boolean signed);
/**
* 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
* handler generated error
*/
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);
}
int getChunk(byte[] res, AddressSpace spc, long off, int size,
boolean stopOnUnintialized);
/**
* 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
* @throws LowlevelError if spc has not been mapped within this MemoryState
*/
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);
}
void setChunk(byte[] val, AddressSpace spc, long off, int size);
/**
* 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 size the number of bytes to write
*/
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);
}
void setInitialized(boolean initialized, AddressSpace spc, long off, int size);
}