mirror of
https://github.com/NationalSecurityAgency/ghidra
synced 2024-09-13 21:56:19 +00:00
Merge remote-tracking branch 'origin/GP-2426_Dan_registerPlatformMapping--SQUASHED-REBASED'
This commit is contained in:
commit
4a676eea9c
|
@ -22,8 +22,7 @@ import java.util.concurrent.CompletableFuture;
|
|||
import agent.gdb.manager.GdbStackFrame;
|
||||
import agent.gdb.manager.impl.cmd.GdbStateChangeRecord;
|
||||
import ghidra.dbg.agent.DefaultTargetObject;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.TargetStackFrame;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.schema.*;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
import ghidra.lifecycle.Internal;
|
||||
|
@ -36,7 +35,7 @@ import ghidra.program.model.address.Address;
|
|||
attributes = {
|
||||
@TargetAttributeType(type = Void.class) })
|
||||
public class GdbModelTargetStackFrame extends DefaultTargetObject<TargetObject, GdbModelTargetStack>
|
||||
implements TargetStackFrame, GdbModelSelectableObject {
|
||||
implements TargetStackFrame, TargetAggregate, GdbModelSelectableObject {
|
||||
public static final String FUNC_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "function";
|
||||
public static final String FROM_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "from"; // TODO
|
||||
|
||||
|
|
|
@ -42,7 +42,8 @@ import ghidra.util.Msg;
|
|||
@TargetAttributeType(type = Void.class) })
|
||||
public class GdbModelTargetThread
|
||||
extends DefaultTargetObject<TargetObject, GdbModelTargetThreadContainer> implements
|
||||
TargetThread, TargetExecutionStateful, TargetSteppable, GdbModelSelectableObject {
|
||||
TargetThread, TargetExecutionStateful, TargetSteppable, TargetAggregate,
|
||||
GdbModelSelectableObject {
|
||||
protected static final TargetStepKindSet SUPPORTED_KINDS = TargetStepKindSet.of( //
|
||||
TargetStepKind.ADVANCE, //
|
||||
TargetStepKind.FINISH, //
|
||||
|
|
|
@ -29,6 +29,7 @@ import ghidra.app.plugin.assembler.Assembler;
|
|||
import ghidra.app.plugin.assembler.Assemblers;
|
||||
import ghidra.app.plugin.core.debug.service.emulation.BytesDebuggerPcodeEmulator;
|
||||
import ghidra.app.plugin.core.debug.service.emulation.ProgramEmulationUtils;
|
||||
import ghidra.app.plugin.core.debug.service.emulation.data.DefaultPcodeDebuggerAccess;
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.app.services.DebuggerTraceManagerService;
|
||||
|
@ -46,6 +47,7 @@ import ghidra.program.model.listing.InstructionIterator;
|
|||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.model.time.TraceSnapshot;
|
||||
import ghidra.trace.model.time.TraceTimeManager;
|
||||
|
@ -135,7 +137,9 @@ public class DebuggerEmuExampleScript extends GhidraScript {
|
|||
* library. This emulator will still know how to integrate with the UI, reading through to
|
||||
* open programs and writing state back into the trace.
|
||||
*/
|
||||
BytesDebuggerPcodeEmulator emulator = new BytesDebuggerPcodeEmulator(tool, trace, 0, null) {
|
||||
TracePlatform host = trace.getPlatformManager().getHostPlatform();
|
||||
DefaultPcodeDebuggerAccess access = new DefaultPcodeDebuggerAccess(tool, null, host, 0);
|
||||
BytesDebuggerPcodeEmulator emulator = new BytesDebuggerPcodeEmulator(access) {
|
||||
@Override
|
||||
protected PcodeUseropLibrary<byte[]> createUseropLibrary() {
|
||||
return new DemoPcodeUseropLibrary(language, DebuggerEmuExampleScript.this);
|
||||
|
@ -169,7 +173,7 @@ public class DebuggerEmuExampleScript extends GhidraScript {
|
|||
thread.stepInstruction();
|
||||
snapshot =
|
||||
time.createSnapshot("Stepped to " + thread.getCounter());
|
||||
emulator.writeDown(trace, snapshot.getKey(), 0);
|
||||
emulator.writeDown(host, snapshot.getKey(), 0);
|
||||
}
|
||||
printerr("We should not have completed 10 steps!");
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import docking.action.DockingAction;
|
|||
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
|
||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources.GoToAction;
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.pcode.exec.*;
|
||||
|
@ -93,12 +94,13 @@ public abstract class DebuggerGoToTrait {
|
|||
}
|
||||
|
||||
public CompletableFuture<Boolean> goToSleigh(AddressSpace space, PcodeExpression expression) {
|
||||
AsyncPcodeExecutor<byte[]> executor = DebuggerPcodeUtils.executorForCoordinates(current);
|
||||
CompletableFuture<byte[]> result = expression.evaluate(executor);
|
||||
return result.thenApply(offset -> {
|
||||
PcodeExecutor<byte[]> executor = DebuggerPcodeUtils.executorForCoordinates(tool, current);
|
||||
CompletableFuture<byte[]> result =
|
||||
CompletableFuture.supplyAsync(() -> expression.evaluate(executor));
|
||||
return result.thenApplyAsync(offset -> {
|
||||
Address address = space.getAddress(
|
||||
Utils.bytesToLong(offset, offset.length, expression.getLanguage().isBigEndian()));
|
||||
return goToAddress(address);
|
||||
});
|
||||
}, AsyncUtils.SWING_EXECUTOR);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -434,6 +434,10 @@ public class DebuggerStackProvider extends ComponentProviderAdapter {
|
|||
return;
|
||||
}
|
||||
Register pc = curTrace.getBaseLanguage().getProgramCounter();
|
||||
if (pc == null) {
|
||||
contextChanged();
|
||||
return;
|
||||
}
|
||||
RegisterValue value = regs.getViewValue(current.getViewSnap(), pc);
|
||||
if (value == null) {
|
||||
contextChanged();
|
||||
|
|
|
@ -17,6 +17,7 @@ package ghidra.app.plugin.core.debug.gui.watch;
|
|||
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
|
@ -30,9 +31,9 @@ import ghidra.app.services.DebuggerStateEditingService.StateEditor;
|
|||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.docking.settings.SettingsImpl;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.pcode.exec.trace.DirectBytesTracePcodeExecutorStatePiece;
|
||||
import ghidra.pcode.exec.trace.TraceSleighUtils;
|
||||
import ghidra.pcode.exec.trace.*;
|
||||
import ghidra.pcode.utils.Utils;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.DataType;
|
||||
|
@ -45,7 +46,7 @@ import ghidra.program.model.mem.MemBuffer;
|
|||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.trace.model.*;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.memory.TraceMemoryState;
|
||||
import ghidra.trace.model.symbol.TraceLabelSymbol;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
@ -63,7 +64,7 @@ public class WatchRow {
|
|||
private SleighLanguage language;
|
||||
private PcodeExecutor<Pair<byte[], TraceMemoryState>> executorWithState;
|
||||
private ReadDepsPcodeExecutor executorWithAddress;
|
||||
private AsyncPcodeExecutor<byte[]> asyncExecutor;
|
||||
private PcodeExecutor<byte[]> asyncExecutor; // name is reminder to use asynchronously
|
||||
|
||||
private String expression;
|
||||
private String typePath;
|
||||
|
@ -117,7 +118,9 @@ public class WatchRow {
|
|||
|
||||
protected void doTargetReads() {
|
||||
if (compiled != null && asyncExecutor != null) {
|
||||
compiled.evaluate(asyncExecutor).exceptionally(ex -> {
|
||||
CompletableFuture<byte[]> asyncEvaluation =
|
||||
CompletableFuture.supplyAsync(() -> compiled.evaluate(asyncExecutor));
|
||||
asyncEvaluation.exceptionally(ex -> {
|
||||
error = ex;
|
||||
Swing.runIfSwingOrRunLater(() -> {
|
||||
provider.watchTableModel.notifyUpdated(this);
|
||||
|
@ -174,9 +177,10 @@ public class WatchRow {
|
|||
extends DirectBytesTracePcodeExecutorStatePiece {
|
||||
private AddressSet reads = new AddressSet();
|
||||
|
||||
public ReadDepsTraceBytesPcodeExecutorStatePiece(Trace trace, long snap, TraceThread thread,
|
||||
int frame) {
|
||||
super(trace, snap, thread, frame);
|
||||
public ReadDepsTraceBytesPcodeExecutorStatePiece(TracePlatform platform, long snap,
|
||||
TraceThread thread, int frame) {
|
||||
super(DirectBytesTracePcodeExecutorState.getDefaultThreadAccess(platform, snap, thread,
|
||||
frame));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -197,7 +201,7 @@ public class WatchRow {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void setInSpace(TraceMemorySpace space, long offset, int size, byte[] val) {
|
||||
protected void setInSpace(AddressSpace space, long offset, int size, byte[] val) {
|
||||
throw new UnsupportedOperationException("Expression cannot write to trace");
|
||||
}
|
||||
|
||||
|
@ -233,18 +237,31 @@ public class WatchRow {
|
|||
}
|
||||
}
|
||||
|
||||
protected static ReadDepsPcodeExecutor buildAddressDepsExecutor(
|
||||
/**
|
||||
* Build an executor that can compute three things simultaneously
|
||||
*
|
||||
* <p>
|
||||
* This computes the concrete value, its address, and the set of physical addresses involved in
|
||||
* the computation. The resulting pair gives the value and its address. To get the addresses
|
||||
* involved, invoke {@link ReadDepsPcodeExecutor#getReads()} after evaluation.
|
||||
*
|
||||
* @param tool the plugin tool
|
||||
* @param coordinates the coordinates providing context for the evaluation
|
||||
* @return an executor for evaluating the watch
|
||||
*/
|
||||
protected static ReadDepsPcodeExecutor buildAddressDepsExecutor(PluginTool tool,
|
||||
DebuggerCoordinates coordinates) {
|
||||
Trace trace = coordinates.getTrace();
|
||||
TracePlatform platform = DebuggerPcodeUtils.getCurrentPlatform(tool, coordinates);
|
||||
ReadDepsTraceBytesPcodeExecutorStatePiece piece =
|
||||
new ReadDepsTraceBytesPcodeExecutorStatePiece(trace, coordinates.getViewSnap(),
|
||||
new ReadDepsTraceBytesPcodeExecutorStatePiece(platform, coordinates.getViewSnap(),
|
||||
coordinates.getThread(), coordinates.getFrame());
|
||||
Language language = trace.getBaseLanguage();
|
||||
if (!(language instanceof SleighLanguage)) {
|
||||
throw new IllegalArgumentException("Watch expressions require a SLEIGH language");
|
||||
}
|
||||
PcodeExecutorState<Pair<byte[], Address>> paired = new DefaultPcodeExecutorState<>(piece)
|
||||
.paired(new AddressOfPcodeExecutorStatePiece(language.isBigEndian()));
|
||||
.paired(new AddressOfPcodeExecutorStatePiece(language));
|
||||
PairedPcodeArithmetic<byte[], Address> arithmetic = new PairedPcodeArithmetic<>(
|
||||
BytesPcodeArithmetic.forLanguage(language), AddressOfPcodeArithmetic.INSTANCE);
|
||||
return new ReadDepsPcodeExecutor(piece, (SleighLanguage) language, arithmetic, paired);
|
||||
|
@ -270,11 +287,12 @@ public class WatchRow {
|
|||
recompile();
|
||||
}
|
||||
if (coordinates.isAliveAndReadsPresent()) {
|
||||
asyncExecutor = DebuggerPcodeUtils.executorForCoordinates(coordinates);
|
||||
asyncExecutor =
|
||||
DebuggerPcodeUtils.executorForCoordinates(provider.getTool(), coordinates);
|
||||
}
|
||||
executorWithState = TraceSleighUtils.buildByteWithStateExecutor(trace,
|
||||
coordinates.getViewSnap(), coordinates.getThread(), coordinates.getFrame());
|
||||
executorWithAddress = buildAddressDepsExecutor(coordinates);
|
||||
executorWithAddress = buildAddressDepsExecutor(provider.getTool(), coordinates);
|
||||
}
|
||||
|
||||
public void setExpression(String expression) {
|
||||
|
|
|
@ -38,6 +38,9 @@ public class DefaultDebuggerMemoryMapper implements DebuggerMemoryMapper {
|
|||
}
|
||||
|
||||
protected static Address toSameNamedSpace(Address addr, AddressFactory factory) {
|
||||
if (addr.isRegisterAddress()) {
|
||||
throw new IllegalArgumentException("Memory mapper cannot handle register addresses");
|
||||
}
|
||||
return factory.getAddressSpace(addr.getAddressSpace().getName())
|
||||
.getAddress(addr.getOffset());
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import ghidra.trace.model.Trace;
|
|||
import ghidra.trace.model.guest.*;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.util.MathUtilities;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.database.UndoableTransaction;
|
||||
|
||||
public class DefaultDebuggerPlatformMapper extends AbstractDebuggerPlatformMapper {
|
||||
|
@ -98,5 +99,14 @@ public class DefaultDebuggerPlatformMapper extends AbstractDebuggerPlatformMappe
|
|||
catch (AddressOverflowException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
||||
try {
|
||||
platform.addMappedRegisterRange();
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
Msg.showError(this, null, "Map Registers",
|
||||
"The host language cannot accomodate register storage for the" +
|
||||
" guest platform (language: " + platform.getLanguage() + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
/* ###
|
||||
* 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.plugin.core.debug.service.emulation;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerDataAccess;
|
||||
import ghidra.pcode.exec.AccessPcodeExecutionException;
|
||||
import ghidra.pcode.exec.trace.BytesTracePcodeExecutorStatePiece;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.trace.model.memory.TraceMemoryState;
|
||||
|
||||
/**
|
||||
* An abstract executor state piece that knows to read live state if applicable
|
||||
*
|
||||
* <p>
|
||||
* This requires a memory-access shim for the debugger. It will check if the shim is associated with
|
||||
* a live session. If so, it will direct the recorder to capture the desired state, if they're not
|
||||
* already {@link TraceMemoryState#KNOWN}. When such a target comments is required, the state will
|
||||
* wait up to 1 second for it to complete (see
|
||||
* {@link AbstractRWTargetCachedSpace#waitTimeout(CompletableFuture)}).
|
||||
*/
|
||||
public abstract class AbstractRWTargetPcodeExecutorStatePiece
|
||||
extends BytesTracePcodeExecutorStatePiece {
|
||||
|
||||
abstract class AbstractRWTargetCachedSpace extends CachedSpace {
|
||||
|
||||
public AbstractRWTargetCachedSpace(Language language, AddressSpace space,
|
||||
PcodeDebuggerDataAccess backing) {
|
||||
super(language, space, backing);
|
||||
}
|
||||
|
||||
protected abstract void fillUninitialized(AddressSet uninitialized);
|
||||
|
||||
@Override
|
||||
public byte[] read(long offset, int size) {
|
||||
if (backing != null) {
|
||||
AddressSet uninitialized =
|
||||
addrSet(bytes.getUninitialized(offset, offset + size - 1));
|
||||
if (uninitialized.isEmpty()) {
|
||||
return super.read(offset, size);
|
||||
}
|
||||
|
||||
fillUninitialized(uninitialized);
|
||||
|
||||
AddressSetView unknown = backing.intersectUnknown(
|
||||
addrSet(bytes.getUninitialized(offset, offset + size - 1)));
|
||||
if (!unknown.isEmpty()) {
|
||||
warnUnknown(unknown);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: What to flush when bytes in the trace change?
|
||||
return super.read(offset, size);
|
||||
}
|
||||
|
||||
protected <T> T waitTimeout(CompletableFuture<T> future) {
|
||||
try {
|
||||
return future.get(1, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (TimeoutException e) {
|
||||
throw new AccessPcodeExecutionException("Timed out reading or writing target", e);
|
||||
}
|
||||
catch (InterruptedException | ExecutionException e) {
|
||||
throw new AccessPcodeExecutionException("Error reading or writing target", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected final PcodeDebuggerDataAccess data;
|
||||
|
||||
/**
|
||||
* Construct a piece
|
||||
*
|
||||
* @param data the trace-data access shim
|
||||
*/
|
||||
public AbstractRWTargetPcodeExecutorStatePiece(PcodeDebuggerDataAccess data) {
|
||||
super(data);
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* A partially implemented space map which retrieves "backing" objects from the trace's memory
|
||||
* and register spaces.
|
||||
*/
|
||||
protected abstract class TargetBackedSpaceMap
|
||||
extends CacheingSpaceMap<PcodeDebuggerDataAccess, CachedSpace> {
|
||||
@Override
|
||||
protected PcodeDebuggerDataAccess getBacking(AddressSpace space) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,141 +0,0 @@
|
|||
/* ###
|
||||
* 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.plugin.core.debug.service.emulation;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.pcode.exec.AccessPcodeExecutionException;
|
||||
import ghidra.pcode.exec.trace.BytesTracePcodeExecutorStatePiece;
|
||||
import ghidra.pcode.exec.trace.TraceSleighUtils;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
import ghidra.trace.model.memory.TraceMemoryState;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.database.UndoableTransaction;
|
||||
|
||||
/**
|
||||
* An executor state piece that knows to read live state if applicable
|
||||
*
|
||||
* <p>
|
||||
* This takes a handle to the trace's recorder, if applicable, and will check if the source snap is
|
||||
* the recorder's snap. If so, it will direct the recorder to capture the desired state, if they're
|
||||
* not already {@link TraceMemoryState#KNOWN}. When such reads occur, the state will wait up to 1
|
||||
* second (see {@link AbstractReadsTargetCachedSpace#waitTimeout(CompletableFuture)}).
|
||||
*/
|
||||
public abstract class AbstractReadsTargetPcodeExecutorStatePiece
|
||||
extends BytesTracePcodeExecutorStatePiece {
|
||||
|
||||
abstract class AbstractReadsTargetCachedSpace extends CachedSpace {
|
||||
public AbstractReadsTargetCachedSpace(Language language, AddressSpace space,
|
||||
TraceMemorySpace backing, long snap) {
|
||||
super(language, space, backing, snap);
|
||||
}
|
||||
|
||||
protected abstract void fillUninitialized(AddressSet uninitialized);
|
||||
|
||||
protected boolean isLive() {
|
||||
return recorder != null && recorder.isRecording() && recorder.getSnap() == snap;
|
||||
}
|
||||
|
||||
protected AddressSet computeUnknown(AddressSet uninitialized) {
|
||||
return uninitialized.subtract(backing.getAddressesWithState(snap, uninitialized,
|
||||
s -> s != null && s != TraceMemoryState.UNKNOWN));
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] read(long offset, int size) {
|
||||
if (backing != null) {
|
||||
AddressSet uninitialized =
|
||||
addrSet(bytes.getUninitialized(offset, offset + size - 1));
|
||||
if (uninitialized.isEmpty()) {
|
||||
return super.read(offset, size);
|
||||
}
|
||||
|
||||
fillUninitialized(uninitialized);
|
||||
|
||||
AddressSet unknown =
|
||||
computeUnknown(addrSet(bytes.getUninitialized(offset, offset + size - 1)));
|
||||
if (!unknown.isEmpty()) {
|
||||
warnUnknown(unknown);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: What to flush when bytes in the trace change?
|
||||
return super.read(offset, size);
|
||||
}
|
||||
|
||||
protected <T> T waitTimeout(CompletableFuture<T> future) {
|
||||
try {
|
||||
return future.get(1, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||
throw new AccessPcodeExecutionException("Timed out reading target", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected final TraceRecorder recorder;
|
||||
protected final PluginTool tool;
|
||||
|
||||
public AbstractReadsTargetPcodeExecutorStatePiece(PluginTool tool, Trace trace, long snap,
|
||||
TraceThread thread, int frame, TraceRecorder recorder) {
|
||||
super(trace, snap, thread, frame);
|
||||
this.tool = tool;
|
||||
this.recorder = recorder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tool that manages this state's emulator.
|
||||
*
|
||||
* <p>
|
||||
* This is necessary to obtain the static mapping service, in case memory should be filled from
|
||||
* static images.
|
||||
*
|
||||
* @return the tool
|
||||
*/
|
||||
public PluginTool getTool() {
|
||||
return tool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the recorder associated with the trace
|
||||
*
|
||||
* @return this is used to check for and perform live reads
|
||||
*/
|
||||
public TraceRecorder getRecorder() {
|
||||
return recorder;
|
||||
}
|
||||
|
||||
/**
|
||||
* A partially implemented space map which retrieves "backing" objects from the trace's memory
|
||||
* and register spaces.
|
||||
*/
|
||||
protected abstract class TargetBackedSpaceMap
|
||||
extends CacheingSpaceMap<TraceMemorySpace, CachedSpace> {
|
||||
@Override
|
||||
protected TraceMemorySpace getBacking(AddressSpace space) {
|
||||
try (UndoableTransaction tid =
|
||||
UndoableTransaction.start(trace, "Create space")) {
|
||||
return TraceSleighUtils.getSpaceForExecution(space, trace, thread, frame, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,13 +15,11 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.debug.service.emulation;
|
||||
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.pcode.emu.*;
|
||||
import ghidra.app.plugin.core.debug.service.emulation.data.*;
|
||||
import ghidra.pcode.emu.PcodeEmulator;
|
||||
import ghidra.pcode.emu.PcodeThread;
|
||||
import ghidra.pcode.exec.trace.BytesTracePcodeEmulator;
|
||||
import ghidra.pcode.exec.trace.TracePcodeExecutorState;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
||||
/**
|
||||
* A trace emulator that knows how to read target memory when necessary
|
||||
|
@ -39,51 +37,27 @@ import ghidra.trace.model.thread.TraceThread;
|
|||
*/
|
||||
public class BytesDebuggerPcodeEmulator extends BytesTracePcodeEmulator
|
||||
implements DebuggerPcodeMachine<byte[]> {
|
||||
protected final PluginTool tool;
|
||||
protected final TraceRecorder recorder;
|
||||
|
||||
protected final PcodeDebuggerAccess access;
|
||||
|
||||
/**
|
||||
* Create the emulator
|
||||
*
|
||||
* @param tool the tool creating the emulator
|
||||
* @param trace the trace from which the emulator loads state
|
||||
* @param snap the snap from which the emulator loads state
|
||||
* @param recorder if applicable, the recorder for the trace's live target
|
||||
* @param access the trace-and-debugger access shim
|
||||
*/
|
||||
public BytesDebuggerPcodeEmulator(PluginTool tool, Trace trace, long snap,
|
||||
TraceRecorder recorder) {
|
||||
super(trace, snap);
|
||||
this.tool = tool;
|
||||
this.recorder = recorder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginTool getTool() {
|
||||
return tool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceRecorder getRecorder() {
|
||||
return recorder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BytesPcodeThread createThread(String name) {
|
||||
BytesPcodeThread thread = super.createThread(name);
|
||||
initializeThreadContext(thread);
|
||||
return thread;
|
||||
public BytesDebuggerPcodeEmulator(PcodeDebuggerAccess access) {
|
||||
super(access);
|
||||
this.access = access;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TracePcodeExecutorState<byte[]> createSharedState() {
|
||||
return new ReadsTargetMemoryPcodeExecutorState(tool, trace, snap, null, 0, recorder);
|
||||
return new RWTargetMemoryPcodeExecutorState(access.getDataForSharedState(), Mode.RO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TracePcodeExecutorState<byte[]> createLocalState(PcodeThread<byte[]> emuThread) {
|
||||
TraceThread traceThread =
|
||||
trace.getThreadManager().getLiveThreadByPath(snap, emuThread.getName());
|
||||
return new ReadsTargetRegistersPcodeExecutorState(tool, trace, snap, traceThread, 0,
|
||||
recorder);
|
||||
return new RWTargetRegistersPcodeExecutorState(access.getDataForLocalState(emuThread, 0),
|
||||
Mode.RO);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,9 +15,7 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.debug.service.emulation;
|
||||
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerAccess;
|
||||
|
||||
/**
|
||||
* The Debugger's default emulator factory
|
||||
|
@ -32,8 +30,7 @@ public class BytesDebuggerPcodeEmulatorFactory implements DebuggerPcodeEmulatorF
|
|||
}
|
||||
|
||||
@Override
|
||||
public DebuggerPcodeMachine<?> create(PluginTool tool, Trace trace, long snap,
|
||||
TraceRecorder recorder) {
|
||||
return new BytesDebuggerPcodeEmulator(tool, trace, snap, recorder);
|
||||
public DebuggerPcodeMachine<?> create(PcodeDebuggerAccess access) {
|
||||
return new BytesDebuggerPcodeEmulator(access);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,10 +43,12 @@ import ghidra.async.AsyncLazyMap;
|
|||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.pcode.exec.DebuggerPcodeUtils;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.trace.model.*;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.model.time.TraceSnapshot;
|
||||
|
@ -183,6 +185,8 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
|
|||
@AutoServiceConsumed
|
||||
private DebuggerModelService modelService;
|
||||
@AutoServiceConsumed
|
||||
private DebuggerPlatformService platformService;
|
||||
@AutoServiceConsumed
|
||||
private DebuggerStaticMappingService staticMappings;
|
||||
@SuppressWarnings("unused")
|
||||
private AutoService.Wiring autoServiceWiring;
|
||||
|
@ -456,7 +460,14 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
|
|||
|
||||
protected long doEmulate(CacheKey key, TaskMonitor monitor) throws CancelledException {
|
||||
Trace trace = key.trace;
|
||||
/**
|
||||
* TODO: object and/or platform should somehow be incorporated into the key, the schedule?
|
||||
* something?
|
||||
*/
|
||||
TracePlatform platform = DebuggerPcodeUtils.getCurrentPlatform(platformService,
|
||||
traceManager.getCurrentFor(trace).trace(trace));
|
||||
TraceSchedule time = key.time;
|
||||
|
||||
CachedEmulator ce;
|
||||
DebuggerPcodeMachine<?> emu;
|
||||
Map.Entry<CacheKey, CachedEmulator> ancestor = findNearestPrefix(key);
|
||||
|
@ -473,19 +484,23 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
|
|||
ce = ancestor.getValue();
|
||||
emu = ce.emulator;
|
||||
monitor.initialize(time.totalTickCount() - prevKey.time.totalTickCount());
|
||||
createRegisterSpaces(trace, time, monitor);
|
||||
monitor.setMessage("Emulating");
|
||||
time.finish(trace, prevKey.time, emu, monitor);
|
||||
}
|
||||
else {
|
||||
emu = emulatorFactory.create(tool, trace, time.getSnap(),
|
||||
emu = emulatorFactory.create(tool, platform, time.getSnap(),
|
||||
modelService == null ? null : modelService.getRecorder(trace));
|
||||
ce = new CachedEmulator(emu);
|
||||
monitor.initialize(time.totalTickCount());
|
||||
createRegisterSpaces(trace, time, monitor);
|
||||
monitor.setMessage("Emulating");
|
||||
time.execute(trace, emu, monitor);
|
||||
}
|
||||
TraceSnapshot destSnap;
|
||||
try (UndoableTransaction tid = UndoableTransaction.start(trace, "Emulate")) {
|
||||
destSnap = findScratch(trace, time);
|
||||
emu.writeDown(trace, destSnap.getKey(), time.getSnap());
|
||||
emu.writeDown(platform, destSnap.getKey(), time.getSnap());
|
||||
}
|
||||
|
||||
synchronized (cache) {
|
||||
|
@ -502,6 +517,19 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
|
|||
return destSnap.getKey();
|
||||
}
|
||||
|
||||
protected void createRegisterSpaces(Trace trace, TraceSchedule time, TaskMonitor monitor) {
|
||||
if (trace.getObjectManager().getRootObject() == null) {
|
||||
return;
|
||||
}
|
||||
// Cause object-register support to copy values into new register spaces
|
||||
monitor.setMessage("Creating register spaces");
|
||||
try (UndoableTransaction tid = UndoableTransaction.start(trace, "Prepare emulation")) {
|
||||
for (TraceThread thread : time.getThreads(trace)) {
|
||||
trace.getMemoryManager().getMemoryRegisterSpace(thread, 0, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long emulate(Trace trace, TraceSchedule time, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
|
|
|
@ -15,9 +15,11 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.debug.service.emulation;
|
||||
|
||||
import ghidra.app.plugin.core.debug.service.emulation.data.DefaultPcodeDebuggerAccess;
|
||||
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerAccess;
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.util.classfinder.ExtensionPoint;
|
||||
|
||||
/**
|
||||
|
@ -41,11 +43,21 @@ public interface DebuggerPcodeEmulatorFactory extends ExtensionPoint {
|
|||
* Create the emulator
|
||||
*
|
||||
* @param tool the tool creating the emulator
|
||||
* @param trace the user's current trace from which the emulator should load state
|
||||
* @param platform the user's current trace platform from which the emulator should load state
|
||||
* @param snap the user's current snap from which the emulator should load state
|
||||
* @param recorder if applicable, the recorder for the trace's live target
|
||||
* @return the emulator
|
||||
*/
|
||||
DebuggerPcodeMachine<?> create(PluginTool tool, Trace trace, long snap,
|
||||
TraceRecorder recorder);
|
||||
default DebuggerPcodeMachine<?> create(PluginTool tool, TracePlatform platform, long snap,
|
||||
TraceRecorder recorder) {
|
||||
return create(new DefaultPcodeDebuggerAccess(tool, recorder, platform, snap));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the emulator
|
||||
*
|
||||
* @param access the trace-and-debugger access shim
|
||||
* @return the emulator
|
||||
*/
|
||||
DebuggerPcodeMachine<?> create(PcodeDebuggerAccess access);
|
||||
}
|
||||
|
|
|
@ -15,9 +15,6 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.debug.service.emulation;
|
||||
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.pcode.emu.PcodeMachine;
|
||||
import ghidra.pcode.exec.debug.auxiliary.AuxDebuggerEmulatorPartsFactory;
|
||||
import ghidra.pcode.exec.debug.auxiliary.AuxDebuggerPcodeEmulator;
|
||||
import ghidra.pcode.exec.trace.TracePcodeMachine;
|
||||
|
@ -26,26 +23,10 @@ import ghidra.pcode.exec.trace.TracePcodeMachine;
|
|||
* A Debugger-integrated emulator (or p-code machine)
|
||||
*
|
||||
* <p>
|
||||
* This is a "mix in" interface. It is part of the SPI, but not the API. That is, emulator
|
||||
* developers should use this interface, but emulator clients should not. Clients should use
|
||||
* {@link PcodeMachine} instead. A common implementation is an emulator with concrete plus some
|
||||
* auxiliary state. To realize such a machine, please see {@link AuxDebuggerPcodeEmulator} and
|
||||
* {@link AuxDebuggerEmulatorPartsFactory}.
|
||||
* A common implementation is an emulator with concrete plus some auxiliary state. To realize such a
|
||||
* machine, please see {@link AuxDebuggerPcodeEmulator} and {@link AuxDebuggerEmulatorPartsFactory}.
|
||||
*
|
||||
* @param <T> the type of values in the machine's memory and registers
|
||||
*/
|
||||
public interface DebuggerPcodeMachine<T> extends TracePcodeMachine<T> {
|
||||
/**
|
||||
* Get the tool where this emulator is integrated
|
||||
*
|
||||
* @return the tool
|
||||
*/
|
||||
PluginTool getTool();
|
||||
|
||||
/**
|
||||
* Get the trace's recorder for its live target, if applicable
|
||||
*
|
||||
* @return the recorder, or null
|
||||
*/
|
||||
TraceRecorder getRecorder();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/* ###
|
||||
* 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.plugin.core.debug.service.emulation;
|
||||
|
||||
/**
|
||||
* A write flag for target-associated emulator states
|
||||
*/
|
||||
public enum Mode {
|
||||
/**
|
||||
* The state can write the target directly
|
||||
*/
|
||||
RW {
|
||||
@Override
|
||||
public boolean isWriteTarget() {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The state will never write the target
|
||||
*/
|
||||
RO {
|
||||
@Override
|
||||
public boolean isWriteTarget() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the mode permits writing the target
|
||||
*
|
||||
* @return true to allow, false to prohibit
|
||||
*/
|
||||
public abstract boolean isWriteTarget();
|
||||
}
|
|
@ -216,8 +216,8 @@ public enum ProgramEmulationUtils {
|
|||
*/
|
||||
public static void initializeRegisters(Trace trace, long snap, TraceThread thread,
|
||||
Program program, Address tracePc, Address programPc, TraceMemoryRegion stack) {
|
||||
TraceMemorySpace space =
|
||||
trace.getMemoryManager().getMemoryRegisterSpace(thread, true);
|
||||
TraceMemoryManager memory = trace.getMemoryManager();
|
||||
TraceMemorySpace regSpace = memory.getMemoryRegisterSpace(thread, true);
|
||||
if (program != null) {
|
||||
ProgramContext ctx = program.getProgramContext();
|
||||
for (Register reg : Stream.of(ctx.getRegistersWithValues())
|
||||
|
@ -227,20 +227,30 @@ public enum ProgramEmulationUtils {
|
|||
if (rv == null || !rv.hasAnyValue()) {
|
||||
continue;
|
||||
}
|
||||
TraceMemoryOperations space =
|
||||
reg.getAddressSpace().isRegisterSpace() ? regSpace : memory;
|
||||
// Set all the mask bits
|
||||
space.setValue(snap, new RegisterValue(reg, BigInteger.ZERO).combineValues(rv));
|
||||
}
|
||||
}
|
||||
space.setValue(snap, new RegisterValue(trace.getBaseLanguage().getProgramCounter(),
|
||||
Register regPC = trace.getBaseLanguage().getProgramCounter();
|
||||
TraceMemoryOperations spacePC =
|
||||
regPC.getAddressSpace().isRegisterSpace() ? regSpace : memory;
|
||||
spacePC.setValue(snap, new RegisterValue(regPC,
|
||||
NumericUtilities.unsignedLongToBigInteger(tracePc.getAddressableWordOffset())));
|
||||
if (stack != null) {
|
||||
CompilerSpec cSpec = trace.getBaseCompilerSpec();
|
||||
Address sp = cSpec.stackGrowsNegative()
|
||||
? stack.getMaxAddress()
|
||||
: stack.getMinAddress();
|
||||
space.setValue(snap,
|
||||
new RegisterValue(cSpec.getStackPointer(),
|
||||
NumericUtilities.unsignedLongToBigInteger(sp.getAddressableWordOffset())));
|
||||
Register regSP = cSpec.getStackPointer();
|
||||
if (regSP != null) {
|
||||
TraceMemoryOperations spaceSP =
|
||||
regSP.getAddressSpace().isRegisterSpace() ? regSpace : memory;
|
||||
spaceSP.setValue(snap,
|
||||
new RegisterValue(regSP,
|
||||
NumericUtilities.unsignedLongToBigInteger(sp.getAddressableWordOffset())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/* ###
|
||||
* 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.plugin.core.debug.service.emulation;
|
||||
|
||||
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerMemoryAccess;
|
||||
import ghidra.pcode.exec.trace.DefaultTracePcodeExecutorState;
|
||||
|
||||
/**
|
||||
* A state composing a single {@link RWTargetMemoryPcodeExecutorStatePiece}
|
||||
*/
|
||||
public class RWTargetMemoryPcodeExecutorState extends DefaultTracePcodeExecutorState<byte[]> {
|
||||
/**
|
||||
* Create the state
|
||||
*
|
||||
* @param data the trace-memory access shim
|
||||
* @param mode whether to ever write the target
|
||||
*/
|
||||
public RWTargetMemoryPcodeExecutorState(PcodeDebuggerMemoryAccess data, Mode mode) {
|
||||
super(new RWTargetMemoryPcodeExecutorStatePiece(data, mode));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
/* ###
|
||||
* 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.plugin.core.debug.service.emulation;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerDataAccess;
|
||||
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerMemoryAccess;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.trace.model.memory.TraceMemoryState;
|
||||
|
||||
/**
|
||||
* An executor state piece that knows to read live memory if applicable
|
||||
*
|
||||
* <p>
|
||||
* This requires a trace-memory access shim for the debugger. It will check if the shim is
|
||||
* associated with a live session. If so, it will direct the recorder to capture the block(s)
|
||||
* containing the read, if they're not already {@link TraceMemoryState#KNOWN}. When such a target
|
||||
* comments is required, the state will wait up to 1 second for it to complete (see
|
||||
* {@link AbstractRWTargetCachedSpace#waitTimeout(CompletableFuture)}).
|
||||
*
|
||||
* <p>
|
||||
* This state will also attempt to fill unknown bytes with values from mapped static images. The
|
||||
* order to retrieve state is:
|
||||
* <ol>
|
||||
* <li>The cache, i.e., this state object</li>
|
||||
* <li>The trace</li>
|
||||
* <li>The live target, if applicable</li>
|
||||
* <li>Mapped static images, if available</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>
|
||||
* If all those defer, the state is read as if filled with 0s.
|
||||
*/
|
||||
public class RWTargetMemoryPcodeExecutorStatePiece
|
||||
extends AbstractRWTargetPcodeExecutorStatePiece {
|
||||
|
||||
/**
|
||||
* A space, corresponding to a memory space, of this state
|
||||
*
|
||||
* <p>
|
||||
* All of the actual read logic is contained here. We override the space map factory so that it
|
||||
* creates these spaces.
|
||||
*/
|
||||
protected class RWTargetMemoryCachedSpace extends AbstractRWTargetCachedSpace {
|
||||
|
||||
protected final PcodeDebuggerMemoryAccess backing;
|
||||
|
||||
public RWTargetMemoryCachedSpace(Language language, AddressSpace space,
|
||||
PcodeDebuggerMemoryAccess backing) {
|
||||
super(language, space, backing);
|
||||
this.backing = backing;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fillUninitialized(AddressSet uninitialized) {
|
||||
if (space.isUniqueSpace()) {
|
||||
return;
|
||||
}
|
||||
AddressSetView unknown;
|
||||
unknown = backing.intersectUnknown(uninitialized);
|
||||
if (unknown.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (waitTimeout(backing.readFromTargetMemory(unknown))) {
|
||||
unknown = backing.intersectUnknown(uninitialized);
|
||||
if (unknown.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (backing.readFromStaticImages(bytes, unknown)) {
|
||||
unknown = backing.intersectUnknown(uninitialized);
|
||||
if (unknown.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(long offset, byte[] val, int srcOffset, int length) {
|
||||
if (mode.isWriteTarget() && !space.isUniqueSpace() &&
|
||||
waitTimeout(backing.writeTargetMemory(space.getAddress(offset), val))) {
|
||||
// Change should already be recorded, if successful
|
||||
return;
|
||||
}
|
||||
super.write(offset, val, srcOffset, length);
|
||||
}
|
||||
}
|
||||
|
||||
private final Mode mode;
|
||||
|
||||
/**
|
||||
* Construct a piece
|
||||
*
|
||||
* @param data the trace-memory access shim
|
||||
* @param mode whether to ever write the target
|
||||
*/
|
||||
public RWTargetMemoryPcodeExecutorStatePiece(PcodeDebuggerMemoryAccess data, Mode mode) {
|
||||
super(data);
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractSpaceMap<CachedSpace> newSpaceMap() {
|
||||
return new TargetBackedSpaceMap() {
|
||||
@Override
|
||||
protected CachedSpace newSpace(AddressSpace space, PcodeDebuggerDataAccess data) {
|
||||
return new RWTargetMemoryCachedSpace(language, space,
|
||||
(PcodeDebuggerMemoryAccess) data);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/* ###
|
||||
* 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.plugin.core.debug.service.emulation;
|
||||
|
||||
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerRegistersAccess;
|
||||
import ghidra.pcode.exec.trace.DefaultTracePcodeExecutorState;
|
||||
|
||||
/**
|
||||
* A state composing a single {@link RWTargetRegistersPcodeExecutorStatePiece}
|
||||
*/
|
||||
public class RWTargetRegistersPcodeExecutorState extends DefaultTracePcodeExecutorState<byte[]> {
|
||||
/**
|
||||
* Create the state
|
||||
*
|
||||
* @param data the trace-registers access shim
|
||||
* @param mode whether to ever write the target
|
||||
*/
|
||||
public RWTargetRegistersPcodeExecutorState(PcodeDebuggerRegistersAccess data, Mode mode) {
|
||||
super(new RWTargetRegistersPcodeExecutorStatePiece(data, mode));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
/* ###
|
||||
* 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.plugin.core.debug.service.emulation;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerDataAccess;
|
||||
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerRegistersAccess;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.trace.model.memory.TraceMemoryState;
|
||||
|
||||
/**
|
||||
* An executor state piece that knows to read live registers if applicable
|
||||
*
|
||||
* <p>
|
||||
* This requires a trace-register access shim for the debugger. It will check if the shim is
|
||||
* associated with a live session. If so, it will direct the recorder to capture the register(s) to
|
||||
* be read, if they're not already {@link TraceMemoryState#KNOWN}. When such a target comments is
|
||||
* required, the state will wait up to 1 second for it to complete (see
|
||||
* {@link AbstractRWTargetCachedSpace#waitTimeout(CompletableFuture)}).
|
||||
*
|
||||
* <ol>
|
||||
* <li>The cache, i.e., this state object</li>
|
||||
* <li>The trace</li>
|
||||
* <li>The live target, if applicable</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>
|
||||
* If all those defer, the state is read as if filled with 0s.
|
||||
*/
|
||||
public class RWTargetRegistersPcodeExecutorStatePiece
|
||||
extends AbstractRWTargetPcodeExecutorStatePiece {
|
||||
|
||||
/**
|
||||
* A space, corresponding to a register space (really a thread) of this state
|
||||
*
|
||||
* <p>
|
||||
* All of the actual read logic is contained here. We override the space map factory so that it
|
||||
* creates these spaces.
|
||||
*/
|
||||
protected class RWTargetRegistersCachedSpace extends AbstractRWTargetCachedSpace {
|
||||
|
||||
protected final PcodeDebuggerRegistersAccess backing;
|
||||
|
||||
public RWTargetRegistersCachedSpace(Language language, AddressSpace space,
|
||||
PcodeDebuggerRegistersAccess backing) {
|
||||
super(language, space, backing);
|
||||
this.backing = backing;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fillUninitialized(AddressSet uninitialized) {
|
||||
if (space.isUniqueSpace()) {
|
||||
return;
|
||||
}
|
||||
if (!backing.isLive()) {
|
||||
return;
|
||||
}
|
||||
AddressSetView unknown = backing.intersectUnknown(uninitialized);
|
||||
waitTimeout(backing.readFromTargetRegisters(unknown));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(long offset, byte[] val, int srcOffset, int length) {
|
||||
if (mode.isWriteTarget() && !space.isUniqueSpace() &&
|
||||
waitTimeout(backing.writeTargetRegister(space.getAddress(offset), val))) {
|
||||
// Change should already be recorded, if successful
|
||||
return;
|
||||
}
|
||||
super.write(offset, val, srcOffset, length);
|
||||
}
|
||||
}
|
||||
|
||||
private final Mode mode;
|
||||
|
||||
/**
|
||||
* Construct a piece
|
||||
*
|
||||
* @param data the trace-register access shim
|
||||
* @param mode whether to ever write the target
|
||||
*/
|
||||
public RWTargetRegistersPcodeExecutorStatePiece(PcodeDebuggerRegistersAccess data, Mode mode) {
|
||||
super(data);
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractSpaceMap<CachedSpace> newSpaceMap() {
|
||||
return new TargetBackedSpaceMap() {
|
||||
@Override
|
||||
protected CachedSpace newSpace(AddressSpace space, PcodeDebuggerDataAccess data) {
|
||||
return new RWTargetRegistersCachedSpace(language, space,
|
||||
(PcodeDebuggerRegistersAccess) data);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
/* ###
|
||||
* 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.plugin.core.debug.service.emulation;
|
||||
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.pcode.exec.trace.DefaultTracePcodeExecutorState;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
||||
/**
|
||||
* A state composing a single {@link ReadsTargetMemoryPcodeExecutorStatePiece}
|
||||
*/
|
||||
public class ReadsTargetMemoryPcodeExecutorState extends DefaultTracePcodeExecutorState<byte[]> {
|
||||
/**
|
||||
* Create the state
|
||||
*
|
||||
* @param tool the tool of the emulator
|
||||
* @param trace the trace of the emulator
|
||||
* @param snap the snap of the emulator
|
||||
* @param thread probably null, since this the shared part
|
||||
* @param frame probably 0, because frame only matters for non-null thread
|
||||
* @param recorder the recorder of the emulator
|
||||
*/
|
||||
public ReadsTargetMemoryPcodeExecutorState(PluginTool tool, Trace trace, long snap,
|
||||
TraceThread thread, int frame, TraceRecorder recorder) {
|
||||
super(new ReadsTargetMemoryPcodeExecutorStatePiece(tool, trace, snap, thread, frame,
|
||||
recorder));
|
||||
}
|
||||
}
|
|
@ -1,173 +0,0 @@
|
|||
/* ###
|
||||
* 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.plugin.core.debug.service.emulation;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import ghidra.app.services.DebuggerStaticMappingService;
|
||||
import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
import ghidra.trace.model.memory.TraceMemoryState;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.MathUtilities;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* An executor state piece that knows to read live memory if applicable
|
||||
*
|
||||
* <p>
|
||||
* This takes a handle to the trace's recorder, if applicable, and will check if the source snap is
|
||||
* the recorder's snap. If so, it will direct the recorder to capture the block(s) containing the
|
||||
* read, if they're not already {@link TraceMemoryState#KNOWN}. When such reads occur, the state
|
||||
* will wait up to 1 second (see
|
||||
* {@link AbstractReadsTargetCachedSpace#waitTimeout(CompletableFuture)}).
|
||||
*
|
||||
* <p>
|
||||
* This state will also attempt to fill unknown bytes with values from mapped static images. The
|
||||
* order to retrieve state is:
|
||||
* <ol>
|
||||
* <li>The cache, i.e., this state object</li>
|
||||
* <li>The trace</li>
|
||||
* <li>The live target, if applicable</li>
|
||||
* <li>Mapped static images, if available</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>
|
||||
* If all those defer, the state is read as if filled with 0s.
|
||||
*/
|
||||
public class ReadsTargetMemoryPcodeExecutorStatePiece
|
||||
extends AbstractReadsTargetPcodeExecutorStatePiece {
|
||||
|
||||
/**
|
||||
* A space, corresponding to a memory space, of this state
|
||||
*
|
||||
* <p>
|
||||
* All of the actual read logic is contained here. We override the space map factory so that it
|
||||
* creates these spaces.
|
||||
*/
|
||||
protected class ReadsTargetMemoryCachedSpace extends AbstractReadsTargetCachedSpace {
|
||||
|
||||
public ReadsTargetMemoryCachedSpace(Language language, AddressSpace space,
|
||||
TraceMemorySpace backing, long snap) {
|
||||
super(language, space, backing, snap);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fillUninitialized(AddressSet uninitialized) {
|
||||
AddressSet unknown;
|
||||
unknown = computeUnknown(uninitialized);
|
||||
if (unknown.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (fillUnknownWithRecorder(unknown)) {
|
||||
unknown = computeUnknown(uninitialized);
|
||||
if (unknown.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (fillUnknownWithStaticImages(unknown)) {
|
||||
unknown = computeUnknown(uninitialized);
|
||||
if (unknown.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean fillUnknownWithRecorder(AddressSet unknown) {
|
||||
if (!isLive()) {
|
||||
return false;
|
||||
}
|
||||
waitTimeout(recorder.readMemoryBlocks(unknown, TaskMonitor.DUMMY, false));
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean fillUnknownWithStaticImages(AddressSet unknown) {
|
||||
boolean result = false;
|
||||
// TODO: Expand to block? DON'T OVERWRITE KNOWN!
|
||||
DebuggerStaticMappingService mappingService =
|
||||
tool.getService(DebuggerStaticMappingService.class);
|
||||
byte[] data = new byte[4096];
|
||||
for (Entry<Program, Collection<MappedAddressRange>> ent : mappingService
|
||||
.getOpenMappedViews(trace, unknown, snap)
|
||||
.entrySet()) {
|
||||
Program program = ent.getKey();
|
||||
Memory memory = program.getMemory();
|
||||
AddressSetView initialized = memory.getLoadedAndInitializedAddressSet();
|
||||
|
||||
Collection<MappedAddressRange> mappedSet = ent.getValue();
|
||||
for (MappedAddressRange mappedRng : mappedSet) {
|
||||
AddressRange drng = mappedRng.getDestinationAddressRange();
|
||||
long shift = mappedRng.getShift();
|
||||
for (AddressRange subdrng : initialized.intersectRange(drng.getMinAddress(),
|
||||
drng.getMaxAddress())) {
|
||||
Msg.debug(this,
|
||||
"Filling in unknown trace memory in emulator using mapped image: " +
|
||||
program + ": " + subdrng);
|
||||
long lower = subdrng.getMinAddress().getOffset();
|
||||
long fullLen = subdrng.getLength();
|
||||
while (fullLen > 0) {
|
||||
int len = MathUtilities.unsignedMin(data.length, fullLen);
|
||||
try {
|
||||
int read =
|
||||
memory.getBytes(space.getAddress(lower), data, 0, len);
|
||||
if (read < len) {
|
||||
Msg.warn(this,
|
||||
" Partial read of " + subdrng + ". Got " + read +
|
||||
" bytes");
|
||||
}
|
||||
// write(lower - shift, data, 0 ,read);
|
||||
bytes.putData(lower - shift, data, 0, read);
|
||||
}
|
||||
catch (MemoryAccessException | AddressOutOfBoundsException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
lower += len;
|
||||
fullLen -= len;
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public ReadsTargetMemoryPcodeExecutorStatePiece(PluginTool tool, Trace trace, long snap,
|
||||
TraceThread thread, int frame, TraceRecorder recorder) {
|
||||
super(tool, trace, snap, thread, frame, recorder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractSpaceMap<CachedSpace> newSpaceMap() {
|
||||
return new TargetBackedSpaceMap() {
|
||||
@Override
|
||||
protected CachedSpace newSpace(AddressSpace space, TraceMemorySpace backing) {
|
||||
return new ReadsTargetMemoryCachedSpace(language, space, backing, snap);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
/* ###
|
||||
* 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.plugin.core.debug.service.emulation;
|
||||
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.pcode.exec.trace.DefaultTracePcodeExecutorState;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
||||
/**
|
||||
* A state composing a single {@link ReadsTargetRegistersPcodeExecutorStatePiece}
|
||||
*/
|
||||
public class ReadsTargetRegistersPcodeExecutorState extends DefaultTracePcodeExecutorState<byte[]> {
|
||||
/**
|
||||
* Create the state
|
||||
*
|
||||
* @param tool the tool of the emulator
|
||||
* @param trace the trace of the emulator
|
||||
* @param snap the snap of the emulator
|
||||
* @param thread the thread to which the state is assigned
|
||||
* @param frame the frame to which the state is assigned, probably 0
|
||||
* @param recorder the recorder of the emulator
|
||||
*/
|
||||
public ReadsTargetRegistersPcodeExecutorState(PluginTool tool, Trace trace, long snap,
|
||||
TraceThread thread, int frame, TraceRecorder recorder) {
|
||||
super(new ReadsTargetRegistersPcodeExecutorStatePiece(tool, trace, snap, thread, frame,
|
||||
recorder));
|
||||
}
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
/* ###
|
||||
* 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.plugin.core.debug.service.emulation;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
import ghidra.trace.model.memory.TraceMemoryState;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* An executor state piece that knows to read live memory if applicable
|
||||
*
|
||||
* <p>
|
||||
* This takes a handle to the trace's recorder, if applicable, and will check if the source snap is
|
||||
* the recorder's snap. If so, it will direct the recorder to capture the register to be read, if
|
||||
* it's not already {@link TraceMemoryState#KNOWN}. When such reads occur, the state will wait up to
|
||||
* 1 second (see {@link AbstractReadsTargetCachedSpace#waitTimeout(CompletableFuture)}).
|
||||
*
|
||||
* <ol>
|
||||
* <li>The cache, i.e., this state object</li>
|
||||
* <li>The trace</li>
|
||||
* <li>The live target, if applicable</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>
|
||||
* If all those defer, the state is read as if filled with 0s.
|
||||
*/
|
||||
public class ReadsTargetRegistersPcodeExecutorStatePiece
|
||||
extends AbstractReadsTargetPcodeExecutorStatePiece {
|
||||
|
||||
/**
|
||||
* A space, corresponding to a register space (really a thread) of this state
|
||||
*
|
||||
* <p>
|
||||
* All of the actual read logic is contained here. We override the space map factory so that it
|
||||
* creates these spaces.
|
||||
*/
|
||||
protected class ReadsTargetRegistersCachedSpace extends AbstractReadsTargetCachedSpace {
|
||||
|
||||
public ReadsTargetRegistersCachedSpace(Language language, AddressSpace space,
|
||||
TraceMemorySpace source, long snap) {
|
||||
super(language, space, source, snap);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fillUninitialized(AddressSet uninitialized) {
|
||||
if (!isLive()) {
|
||||
return;
|
||||
}
|
||||
AddressSet unknown = computeUnknown(uninitialized);
|
||||
Set<Register> toRead = new HashSet<>();
|
||||
for (AddressRange rng : unknown) {
|
||||
Register register =
|
||||
language.getRegister(rng.getMinAddress(), (int) rng.getLength());
|
||||
if (register == null) {
|
||||
Msg.error(this, "Could not figure register for " + rng);
|
||||
}
|
||||
else if (!recorder.getRegisterMapper(thread)
|
||||
.getRegistersOnTarget()
|
||||
.contains(register)) {
|
||||
Msg.warn(this, "Register not recognized by target: " + register);
|
||||
}
|
||||
else {
|
||||
toRead.add(register);
|
||||
}
|
||||
}
|
||||
waitTimeout(recorder.captureThreadRegisters(thread, 0, toRead));
|
||||
}
|
||||
}
|
||||
|
||||
public ReadsTargetRegistersPcodeExecutorStatePiece(PluginTool tool, Trace trace, long snap,
|
||||
TraceThread thread, int frame, TraceRecorder recorder) {
|
||||
super(tool, trace, snap, thread, frame, recorder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractSpaceMap<CachedSpace> newSpaceMap() {
|
||||
return new TargetBackedSpaceMap() {
|
||||
@Override
|
||||
protected CachedSpace newSpace(AddressSpace space, TraceMemorySpace backing) {
|
||||
return new ReadsTargetRegistersCachedSpace(language, space, backing, snap);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/* ###
|
||||
* 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.plugin.core.debug.service.emulation.data;
|
||||
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.pcode.exec.trace.data.AbstractPcodeTraceAccess;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
|
||||
/**
|
||||
* An abstract implementation of {@link PcodeDebuggerAccess}
|
||||
*
|
||||
* @param <S> the type of shared data-access shims provided
|
||||
* @param <L> the type of thread-local data-access shims provided
|
||||
*/
|
||||
public abstract class AbstractPcodeDebuggerAccess<S extends PcodeDebuggerMemoryAccess, L extends PcodeDebuggerRegistersAccess>
|
||||
extends AbstractPcodeTraceAccess<S, L>
|
||||
implements PcodeDebuggerAccess {
|
||||
|
||||
protected final PluginTool tool;
|
||||
protected final TraceRecorder recorder;
|
||||
|
||||
/**
|
||||
* Construct a shim
|
||||
*
|
||||
* @param tool the tool controlling the session
|
||||
* @param recorder the target's recorder
|
||||
* @param platform the associated platform, having the same trace as the recorder
|
||||
* @param snap the associated snap
|
||||
*/
|
||||
public AbstractPcodeDebuggerAccess(PluginTool tool, TraceRecorder recorder,
|
||||
TracePlatform platform, long snap) {
|
||||
super(platform, snap);
|
||||
this.tool = tool;
|
||||
this.recorder = recorder;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/* ###
|
||||
* 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.plugin.core.debug.service.emulation.data;
|
||||
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
||||
/**
|
||||
* The default debugger-and-trace access shim for a session
|
||||
*/
|
||||
public class DefaultPcodeDebuggerAccess extends
|
||||
AbstractPcodeDebuggerAccess //
|
||||
<DefaultPcodeDebuggerMemoryAccess, DefaultPcodeDebuggerRegistersAccess> {
|
||||
|
||||
/**
|
||||
* Construct a shim
|
||||
*
|
||||
* @param tool the tool controlling the session
|
||||
* @param recorder the target's recorder
|
||||
* @param platform the associated platform, having the same trace as the recorder
|
||||
* @param snap the associated snap
|
||||
*/
|
||||
public DefaultPcodeDebuggerAccess(PluginTool tool, TraceRecorder recorder,
|
||||
TracePlatform platform, long snap) {
|
||||
super(tool, recorder, platform, snap);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DefaultPcodeDebuggerMemoryAccess newDataForSharedState() {
|
||||
return new DefaultPcodeDebuggerMemoryAccess(tool, recorder, platform, snap, viewport);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DefaultPcodeDebuggerRegistersAccess newDataForLocalState(TraceThread thread,
|
||||
int frame) {
|
||||
return new DefaultPcodeDebuggerRegistersAccess(tool, recorder, platform, snap, thread,
|
||||
frame, viewport);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
/* ###
|
||||
* 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.plugin.core.debug.service.emulation.data;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import ghidra.app.services.DebuggerStaticMappingService;
|
||||
import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.generic.util.datastruct.SemisparseByteArray;
|
||||
import ghidra.pcode.exec.trace.data.DefaultPcodeTraceMemoryAccess;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTracePropertyAccess;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.util.TraceTimeViewport;
|
||||
import ghidra.util.MathUtilities;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* The default data-and-debugger-access shim for session memory
|
||||
*/
|
||||
public class DefaultPcodeDebuggerMemoryAccess extends DefaultPcodeTraceMemoryAccess
|
||||
implements PcodeDebuggerMemoryAccess, InternalPcodeDebuggerDataAccess {
|
||||
|
||||
protected final PluginTool tool;
|
||||
protected final TraceRecorder recorder;
|
||||
|
||||
/**
|
||||
* Construct a shim
|
||||
*
|
||||
* @param tool the tool controlling the session
|
||||
* @param recorder the target's recorder
|
||||
* @param platform the associated platform, having the same trace as the recorder
|
||||
* @param snap the associated snap
|
||||
* @param viewport the viewport, set to the same snapshot
|
||||
*/
|
||||
protected DefaultPcodeDebuggerMemoryAccess(PluginTool tool, TraceRecorder recorder,
|
||||
TracePlatform platform, long snap, TraceTimeViewport viewport) {
|
||||
super(platform, snap, viewport);
|
||||
this.tool = Objects.requireNonNull(tool);
|
||||
this.recorder = recorder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLive() {
|
||||
return recorder != null && recorder.isRecording() && recorder.getSnap() == snap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginTool getTool() {
|
||||
return tool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceRecorder getRecorder() {
|
||||
return recorder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> readFromTargetMemory(AddressSetView guestView) {
|
||||
if (!isLive()) {
|
||||
return CompletableFuture.completedFuture(false);
|
||||
}
|
||||
AddressSetView hostView = platform.mapGuestToHost(guestView);
|
||||
return recorder.readMemoryBlocks(hostView, TaskMonitor.DUMMY, false)
|
||||
.thenCompose(__ -> recorder.getTarget().getModel().flushEvents())
|
||||
.thenCompose(__ -> recorder.flushTransactions())
|
||||
.thenAccept(__ -> platform.getTrace().flushEvents())
|
||||
.thenApply(__ -> true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> writeTargetMemory(Address address, byte[] data) {
|
||||
if (!isLive()) {
|
||||
return CompletableFuture.completedFuture(false);
|
||||
}
|
||||
return recorder.writeMemory(address, data)
|
||||
.thenCompose(__ -> recorder.getTarget().getModel().flushEvents())
|
||||
.thenCompose(__ -> recorder.flushTransactions())
|
||||
.thenAccept(__ -> platform.getTrace().flushEvents())
|
||||
.thenApply(__ -> true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readFromStaticImages(SemisparseByteArray bytes, AddressSetView guestView) {
|
||||
boolean result = false;
|
||||
// TODO: Expand to block? DON'T OVERWRITE KNOWN!
|
||||
DebuggerStaticMappingService mappingService =
|
||||
tool.getService(DebuggerStaticMappingService.class);
|
||||
byte[] data = new byte[4096];
|
||||
|
||||
Trace trace = platform.getTrace();
|
||||
AddressSetView hostView = platform.mapGuestToHost(guestView);
|
||||
for (Entry<Program, Collection<MappedAddressRange>> ent : mappingService
|
||||
.getOpenMappedViews(trace, hostView, snap)
|
||||
.entrySet()) {
|
||||
Program program = ent.getKey();
|
||||
Memory memory = program.getMemory();
|
||||
AddressSetView initialized = memory.getLoadedAndInitializedAddressSet();
|
||||
|
||||
Collection<MappedAddressRange> mappedSet = ent.getValue();
|
||||
for (MappedAddressRange mappedRng : mappedSet) {
|
||||
AddressRange progRng = mappedRng.getDestinationAddressRange();
|
||||
AddressSpace progSpace = progRng.getAddressSpace();
|
||||
for (AddressRange subProgRng : initialized.intersectRange(progRng.getMinAddress(),
|
||||
progRng.getMaxAddress())) {
|
||||
Msg.debug(this,
|
||||
"Filling in unknown trace memory in emulator using mapped image: " +
|
||||
program + ": " + subProgRng);
|
||||
long lower = subProgRng.getMinAddress().getOffset();
|
||||
long fullLen = subProgRng.getLength();
|
||||
while (fullLen > 0) {
|
||||
int len = MathUtilities.unsignedMin(data.length, fullLen);
|
||||
try {
|
||||
Address progAddr = progSpace.getAddress(lower);
|
||||
int read = memory.getBytes(progAddr, data, 0, len);
|
||||
if (read < len) {
|
||||
Msg.warn(this,
|
||||
" Partial read of " + subProgRng + ". Got " + read +
|
||||
" bytes");
|
||||
}
|
||||
Address hostAddr = mappedRng.mapDestinationToSource(progAddr);
|
||||
Address guestAddr = platform.mapHostToGuest(hostAddr);
|
||||
// write(lower - shift, data, 0 ,read);
|
||||
bytes.putData(guestAddr.getOffset(), data, 0, read);
|
||||
}
|
||||
catch (MemoryAccessException | AddressOutOfBoundsException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
lower += len;
|
||||
fullLen -= len;
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> PcodeTracePropertyAccess<T> getPropertyAccess(String name, Class<T> type) {
|
||||
return new DefaultPcodeDebuggerPropertyAccess<>(this, name, type);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/* ###
|
||||
* 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.plugin.core.debug.service.emulation.data;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.app.services.DebuggerStaticMappingService;
|
||||
import ghidra.pcode.exec.trace.data.DefaultPcodeTracePropertyAccess;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.util.PropertyMap;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.trace.model.DefaultTraceLocation;
|
||||
|
||||
/**
|
||||
* The default trace-and-debugger-property access shim
|
||||
*
|
||||
* <p>
|
||||
* This implementation defers to the same property of mapped static images when the property is not
|
||||
* set in the trace.
|
||||
*
|
||||
* @param <T> the type of the property's values
|
||||
*/
|
||||
public class DefaultPcodeDebuggerPropertyAccess<T>
|
||||
extends DefaultPcodeTracePropertyAccess<T> {
|
||||
|
||||
protected final InternalPcodeDebuggerDataAccess data;
|
||||
|
||||
/**
|
||||
* Construct the shim
|
||||
*
|
||||
* @param data the trace-and-debugger-data access shim providing this property access shim
|
||||
* @param name the name of the property
|
||||
* @param type the type of the property
|
||||
*/
|
||||
protected DefaultPcodeDebuggerPropertyAccess(InternalPcodeDebuggerDataAccess data,
|
||||
String name, Class<T> type) {
|
||||
super(data, name, type);
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T whenNull(Address hostAddress) {
|
||||
DebuggerStaticMappingService mappingService =
|
||||
data.getTool().getService(DebuggerStaticMappingService.class);
|
||||
if (mappingService == null) {
|
||||
return super.whenNull(hostAddress);
|
||||
}
|
||||
ProgramLocation progLoc = mappingService.getOpenMappedLocation(new DefaultTraceLocation(
|
||||
data.getPlatform().getTrace(), null, Range.singleton(data.getSnap()), hostAddress));
|
||||
if (progLoc == null) {
|
||||
return super.whenNull(hostAddress);
|
||||
}
|
||||
|
||||
// NB. This is stored in the program, not the user data, despite what the name implies
|
||||
PropertyMap map =
|
||||
progLoc.getProgram().getUsrPropertyManager().getPropertyMap(name);
|
||||
if (map == null) {
|
||||
return super.whenNull(hostAddress);
|
||||
}
|
||||
Object object = map.getObject(progLoc.getByteAddress());
|
||||
if (!type.isInstance(object)) {
|
||||
// TODO: Warn?
|
||||
return super.whenNull(hostAddress);
|
||||
}
|
||||
return type.cast(object);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
/* ###
|
||||
* 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.plugin.core.debug.service.emulation.data;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.pcode.exec.trace.data.DefaultPcodeTraceRegistersAccess;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.TraceTimeViewport;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* The default data-and-debugger access shim for session registers
|
||||
*/
|
||||
public class DefaultPcodeDebuggerRegistersAccess extends DefaultPcodeTraceRegistersAccess
|
||||
implements PcodeDebuggerRegistersAccess, InternalPcodeDebuggerDataAccess {
|
||||
|
||||
protected final PluginTool tool;
|
||||
protected final TraceRecorder recorder;
|
||||
|
||||
/**
|
||||
* Construct a shim
|
||||
*
|
||||
* @param tool the tool controlling the session
|
||||
* @param recorder the target's recorder
|
||||
* @param platform the associated platform, having the same trace as the recorder
|
||||
* @param snap the associated snap
|
||||
* @param thread the associated thread whose registers to access
|
||||
* @param frame the associated frame, or 0 if not applicable
|
||||
* @param viewport the viewport, set to the same snapshot
|
||||
*/
|
||||
protected DefaultPcodeDebuggerRegistersAccess(PluginTool tool, TraceRecorder recorder,
|
||||
TracePlatform platform, long snap, TraceThread thread, int frame,
|
||||
TraceTimeViewport viewport) {
|
||||
super(platform, snap, thread, frame, viewport);
|
||||
this.tool = tool;
|
||||
this.recorder = recorder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLive() {
|
||||
return recorder != null && recorder.isRecording() && recorder.getSnap() == snap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginTool getTool() {
|
||||
return tool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceRecorder getRecorder() {
|
||||
return recorder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> readFromTargetRegisters(AddressSetView guestView) {
|
||||
if (!isLive()) {
|
||||
return CompletableFuture.completedFuture(false);
|
||||
}
|
||||
Set<Register> toRead = new HashSet<>();
|
||||
Language language = platform.getLanguage();
|
||||
for (AddressRange guestRng : guestView) {
|
||||
Register register =
|
||||
language.getRegister(guestRng.getMinAddress().getPhysicalAddress(),
|
||||
(int) guestRng.getLength());
|
||||
if (register == null) {
|
||||
Msg.error(this, "Could not figure register for " + guestRng);
|
||||
}
|
||||
else if (!recorder.getRegisterMapper(thread)
|
||||
.getRegistersOnTarget()
|
||||
.contains(register)) {
|
||||
Msg.warn(this, "Register not recognized by target: " + register);
|
||||
}
|
||||
else {
|
||||
toRead.add(register);
|
||||
}
|
||||
}
|
||||
return recorder.captureThreadRegisters(thread, 0, toRead)
|
||||
.thenCompose(__ -> recorder.getTarget().getModel().flushEvents())
|
||||
.thenCompose(__ -> recorder.flushTransactions())
|
||||
.thenAccept(__ -> platform.getTrace().flushEvents())
|
||||
.thenApply(__ -> true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> writeTargetRegister(Address address, byte[] data) {
|
||||
if (!isLive()) {
|
||||
return CompletableFuture.completedFuture(false);
|
||||
}
|
||||
return recorder.writeRegister(thread, frame, address.getPhysicalAddress(), data)
|
||||
.thenCompose(__ -> recorder.getTarget().getModel().flushEvents())
|
||||
.thenCompose(__ -> recorder.flushTransactions())
|
||||
.thenAccept(__ -> platform.getTrace().flushEvents())
|
||||
.thenApply(__ -> true);
|
||||
}
|
||||
|
||||
// No need to override getPropertyAccess. Registers are not static mapped.
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/* ###
|
||||
* 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.plugin.core.debug.service.emulation.data;
|
||||
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.lifecycle.Internal;
|
||||
import ghidra.pcode.exec.trace.data.InternalPcodeTraceDataAccess;
|
||||
|
||||
@Internal
|
||||
public interface InternalPcodeDebuggerDataAccess extends InternalPcodeTraceDataAccess {
|
||||
PluginTool getTool();
|
||||
|
||||
TraceRecorder getRecorder();
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/* ###
|
||||
* 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.plugin.core.debug.service.emulation.data;
|
||||
|
||||
import ghidra.pcode.emu.PcodeThread;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTraceAccess;
|
||||
|
||||
/**
|
||||
* A trace-and-debugger access shim
|
||||
*
|
||||
* <p>
|
||||
* In addition to the trace "coordinates" encapsulated by {@link PcodeTraceAccess}, this
|
||||
* encapsulates the tool controlling a session and the session's trace recorder. This permits p-code
|
||||
* executor/emulator states to access target data and to access session data, e.g., data from mapped
|
||||
* static images. It supports the same method chain pattern as {@link PcodeTraceAccess}, but
|
||||
* starting with {@link DefaultPcodeDebuggerAccess}.
|
||||
*/
|
||||
public interface PcodeDebuggerAccess extends PcodeTraceAccess {
|
||||
@Override
|
||||
PcodeDebuggerMemoryAccess getDataForSharedState();
|
||||
|
||||
@Override
|
||||
PcodeDebuggerRegistersAccess getDataForLocalState(PcodeThread<?> thread, int frame);
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/* ###
|
||||
* 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.plugin.core.debug.service.emulation.data;
|
||||
|
||||
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
|
||||
|
||||
/**
|
||||
* A data-access shim for a trace and the debugger
|
||||
*
|
||||
* <p>
|
||||
* This shim, in addition to the trace, can also access its associated target, as well as session
|
||||
* information maintained by the Debugger tool.
|
||||
*/
|
||||
public interface PcodeDebuggerDataAccess extends PcodeTraceDataAccess {
|
||||
/**
|
||||
* Check if the associated trace represents a live session
|
||||
*
|
||||
* <p>
|
||||
* The session is live if it's trace has a recorder and the source snapshot matches the
|
||||
* recorder's destination snapshot.
|
||||
*
|
||||
* @return true if live, false otherwise
|
||||
*/
|
||||
boolean isLive();
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/* ###
|
||||
* 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.plugin.core.debug.service.emulation.data;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import ghidra.generic.util.datastruct.SemisparseByteArray;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTraceMemoryAccess;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
|
||||
/**
|
||||
* A data-access shim for a trace's memory and the debugger
|
||||
*/
|
||||
public interface PcodeDebuggerMemoryAccess
|
||||
extends PcodeTraceMemoryAccess, PcodeDebuggerDataAccess {
|
||||
|
||||
/**
|
||||
* Instruct the associated recorder to read memory from the target
|
||||
*
|
||||
* <p>
|
||||
* The recorder may quantize the given address set to pages. It will include all the requested
|
||||
* addresses, though. If this shim is not associated with a live session, the returned future
|
||||
* completes immediately with false.
|
||||
*
|
||||
* @param unknown the address set to read
|
||||
* @return a future which completes when the read is complete and its results recorded to the
|
||||
* trace. It completes with true when any part of target memory was successfully read.
|
||||
* It completes with false if there is no target, or if the target was not read.
|
||||
*/
|
||||
CompletableFuture<Boolean> readFromTargetMemory(AddressSetView unknown);
|
||||
|
||||
/**
|
||||
* Use the Debugger's static mapping service to read bytes from relocated program images
|
||||
*
|
||||
* <p>
|
||||
* To be read, the program database for the static image must be open in the same tool as the
|
||||
* trace being emulated. Depending on the use case, this may only be approximately correct. In
|
||||
* particular, if the trace was from a live session that has since been terminated, and the
|
||||
* image was relocated with fixups, reads at those fixups which fall through to static images
|
||||
* will be incorrect, and may lead to undefined behavior in the emulated program.
|
||||
*
|
||||
* @param bytes the destination byte store
|
||||
* @param unknown the address set to read
|
||||
* @return true if any bytes were read, false if there was no effect
|
||||
*/
|
||||
boolean readFromStaticImages(SemisparseByteArray bytes, AddressSetView unknown);
|
||||
|
||||
/**
|
||||
* Instruct the associated recorder to write target memory
|
||||
*
|
||||
* <p>
|
||||
* In normal operation, this will also cause the recorder, upon a successful write, to record
|
||||
* the same bytes into the destination trace. If this shim is not associated with a live
|
||||
* session, the returned future completes immediately with false.
|
||||
*
|
||||
* @param address the address of the first byte to write
|
||||
* @param data the bytes to write
|
||||
* @return a future which completes when the write is complete and its results recorded to the
|
||||
* trace. It completes with true when the target was written. It completes with false if
|
||||
* there is no target, or if the target is not effected.
|
||||
*/
|
||||
CompletableFuture<Boolean> writeTargetMemory(Address address, byte[] data);
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/* ###
|
||||
* 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.plugin.core.debug.service.emulation.data;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import ghidra.pcode.exec.trace.data.PcodeTraceRegistersAccess;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
|
||||
/**
|
||||
* A data-access shim for a trace's registers and the debugger
|
||||
*/
|
||||
public interface PcodeDebuggerRegistersAccess
|
||||
extends PcodeTraceRegistersAccess, PcodeDebuggerDataAccess {
|
||||
|
||||
/**
|
||||
* Instruct the associated recorder to read registers from the target
|
||||
*
|
||||
* @param unknown the address set (in the platform's {@code register} space) of registers to
|
||||
* read
|
||||
* @return a future which completes when the read is complete and its results recorded to the
|
||||
* trace. It completes with true when any part of target state was successfully read. It
|
||||
* completes with false if there is no target, or if the target was not read.
|
||||
*/
|
||||
CompletableFuture<Boolean> readFromTargetRegisters(AddressSetView unknown);
|
||||
|
||||
/**
|
||||
* Instruct the associated recorder to write target registers
|
||||
*
|
||||
* <p>
|
||||
* In normal operation, this will also cause the recorder, upon a successful write, to record
|
||||
* the same values into the destination trace. If this shim is not associated with a live
|
||||
* session, the returned future completes immediately with false.
|
||||
*
|
||||
* @param address the address of the first byte to write (in the platform's {@code register}
|
||||
* space)
|
||||
* @param data the bytes to write
|
||||
* @return a future which completes when the write is complete and its results recorded to the
|
||||
* trace. It completes with true when the target was written. It completes with false if
|
||||
* there is no target, or if the target is not effected.
|
||||
*/
|
||||
CompletableFuture<Boolean> writeTargetRegister(Address address, byte[] data);
|
||||
}
|
|
@ -16,7 +16,6 @@
|
|||
package ghidra.app.services;
|
||||
|
||||
import ghidra.app.plugin.core.debug.mapping.DebuggerPlatformMapper;
|
||||
import ghidra.app.plugin.core.debug.mapping.DebuggerPlatformOffer;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
|
||||
|
|
|
@ -356,23 +356,37 @@ public interface TraceRecorder {
|
|||
return writeMemory(address, data);
|
||||
}
|
||||
if (address.isRegisterAddress()) {
|
||||
Language lang = getTrace().getBaseLanguage();
|
||||
Register register = lang.getRegister(address, data.length);
|
||||
if (register == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot identify the (single) register to write: " + address);
|
||||
}
|
||||
|
||||
RegisterValue rv = new RegisterValue(register,
|
||||
Utils.bytesToBigInteger(data, data.length, lang.isBigEndian(), false));
|
||||
TraceMemorySpace regs =
|
||||
getTrace().getMemoryManager().getMemoryRegisterSpace(thread, frameLevel, false);
|
||||
rv = TraceRegisterUtils.combineWithTraceBaseRegisterValue(rv, getSnap(), regs, true);
|
||||
return writeThreadRegisters(thread, frameLevel, Map.of(rv.getRegister(), rv));
|
||||
return writeRegister(thread, frameLevel, address, data);
|
||||
}
|
||||
throw new IllegalArgumentException("Address is not in a recognized space: " + address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a register (by address) of the given thread
|
||||
*
|
||||
* @param thread the thread
|
||||
* @param frameLevel the frame, usually 0.
|
||||
* @param address the address of the register
|
||||
* @param data the value to write
|
||||
* @return a future which completes when the write is complete
|
||||
*/
|
||||
default CompletableFuture<Void> writeRegister(TraceThread thread, int frameLevel,
|
||||
Address address, byte[] data) {
|
||||
Language lang = getTrace().getBaseLanguage();
|
||||
Register register = lang.getRegister(address, data.length);
|
||||
if (register == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot identify the (single) register to write: " + address);
|
||||
}
|
||||
|
||||
RegisterValue rv = new RegisterValue(register,
|
||||
Utils.bytesToBigInteger(data, data.length, lang.isBigEndian(), false));
|
||||
TraceMemorySpace regs =
|
||||
getTrace().getMemoryManager().getMemoryRegisterSpace(thread, frameLevel, false);
|
||||
rv = TraceRegisterUtils.combineWithTraceBaseRegisterValue(rv, getSnap(), regs, true);
|
||||
return writeThreadRegisters(thread, frameLevel, Map.of(rv.getRegister(), rv));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given register exists on target (is mappable) for the given thread
|
||||
*
|
||||
|
|
|
@ -1,109 +0,0 @@
|
|||
/* ###
|
||||
* 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.exec;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* An executor which can perform (some of) its work asynchronously
|
||||
*
|
||||
* <p>
|
||||
* Note that a future returned from, e.g., {@link #executeAsync(SleighProgram, PcodeUseropLibrary)}
|
||||
* may complete before the computation has actually been performed. They complete when all of the
|
||||
* operations have been scheduled, and the last future has been written into the state. (This
|
||||
* typically happens when any branch conditions have completed). Instead, a caller should read from
|
||||
* the async state, which will return a future. The state will ensure that future does not complete
|
||||
* until the computation has been performed -- assuming the requested variable actually depends on
|
||||
* that computation.
|
||||
*
|
||||
* <p>
|
||||
* TODO: Deprecate this? It's clever, but it'd probably be easier to just use a synchronous executor
|
||||
* on a separate thread. The necessity of {@link #stepAsync(PcodeFrame, PcodeUseropLibrary)}, etc.,
|
||||
* indicates a failure of the interface to encapsulate this use case. We can adjust the interface,
|
||||
* which would probably not end well, or we can continue to allow the CompletableFuture-specific
|
||||
* steppers to leak out, or we can just torch this and use another thread.
|
||||
*
|
||||
* @param <T> the type of values in the state
|
||||
*/
|
||||
public class AsyncPcodeExecutor<T> extends PcodeExecutor<CompletableFuture<T>> {
|
||||
public AsyncPcodeExecutor(SleighLanguage language,
|
||||
PcodeArithmetic<CompletableFuture<T>> arithmetic,
|
||||
PcodeExecutorState<CompletableFuture<T>> state) {
|
||||
super(language, arithmetic, state);
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> stepOpAsync(PcodeOp op, PcodeFrame frame,
|
||||
PcodeUseropLibrary<CompletableFuture<T>> library) {
|
||||
if (op.getOpcode() == PcodeOp.CBRANCH) {
|
||||
return executeConditionalBranchAsync(op, frame);
|
||||
}
|
||||
stepOp(op, frame, library);
|
||||
return AsyncUtils.NIL;
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> stepAsync(PcodeFrame frame,
|
||||
PcodeUseropLibrary<CompletableFuture<T>> library) {
|
||||
try {
|
||||
return stepOpAsync(frame.nextOp(), frame, library);
|
||||
}
|
||||
catch (PcodeExecutionException e) {
|
||||
e.frame = frame;
|
||||
return CompletableFuture.failedFuture(e);
|
||||
}
|
||||
catch (Exception e) {
|
||||
return CompletableFuture.failedFuture(
|
||||
new PcodeExecutionException("Exception during pcode execution", frame, e));
|
||||
}
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> executeConditionalBranchAsync(PcodeOp op, PcodeFrame frame) {
|
||||
Varnode condVar = op.getInput(1);
|
||||
CompletableFuture<T> cond = state.getVar(condVar);
|
||||
return cond.thenAccept(c -> {
|
||||
if (arithmetic.isTrue(cond, Purpose.CONDITION)) {
|
||||
executeBranch(op, frame);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> executeAsync(PcodeProgram program,
|
||||
PcodeUseropLibrary<CompletableFuture<T>> library) {
|
||||
return executeAsync(program.code, program.useropNames, library);
|
||||
}
|
||||
|
||||
protected CompletableFuture<Void> executeAsyncLoop(PcodeFrame frame,
|
||||
PcodeUseropLibrary<CompletableFuture<T>> library) {
|
||||
if (frame.isFinished()) {
|
||||
return AsyncUtils.NIL;
|
||||
}
|
||||
return stepAsync(frame, library)
|
||||
.thenComposeAsync(__ -> executeAsyncLoop(frame, library));
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> executeAsync(List<PcodeOp> code,
|
||||
Map<Integer, String> useropNames, PcodeUseropLibrary<CompletableFuture<T>> library) {
|
||||
PcodeFrame frame = new PcodeFrame(language, code, useropNames);
|
||||
return executeAsyncLoop(frame, library);
|
||||
}
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
/* ###
|
||||
* 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.exec;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.program.model.lang.Language;
|
||||
|
||||
/**
|
||||
* An arithmetic which can operate on futures of a wrapped type
|
||||
*
|
||||
* @see AsyncPcodeExecutor for comment regarding potential deprecation or immediate removal
|
||||
* @param <T> the type of values wrapped
|
||||
*/
|
||||
public class AsyncWrappedPcodeArithmetic<T> implements PcodeArithmetic<CompletableFuture<T>> {
|
||||
public static final AsyncWrappedPcodeArithmetic<byte[]> BYTES_BE =
|
||||
new AsyncWrappedPcodeArithmetic<>(BytesPcodeArithmetic.BIG_ENDIAN);
|
||||
public static final AsyncWrappedPcodeArithmetic<byte[]> BYTES_LE =
|
||||
new AsyncWrappedPcodeArithmetic<>(BytesPcodeArithmetic.LITTLE_ENDIAN);
|
||||
|
||||
public static AsyncWrappedPcodeArithmetic<byte[]> forEndian(boolean isBigEndian) {
|
||||
return isBigEndian ? BYTES_BE : BYTES_LE;
|
||||
}
|
||||
|
||||
public static AsyncWrappedPcodeArithmetic<byte[]> forLanguage(Language language) {
|
||||
return forEndian(language.isBigEndian());
|
||||
}
|
||||
|
||||
private final PcodeArithmetic<T> arithmetic;
|
||||
|
||||
public AsyncWrappedPcodeArithmetic(PcodeArithmetic<T> arithmetic) {
|
||||
this.arithmetic = arithmetic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (this.getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
AsyncWrappedPcodeArithmetic<?> that = (AsyncWrappedPcodeArithmetic<?>) obj;
|
||||
return Objects.equals(this.arithmetic, that.arithmetic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Endian getEndian() {
|
||||
return arithmetic.getEndian();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<T> unaryOp(int opcode, int sizeout, int sizein1,
|
||||
CompletableFuture<T> in1) {
|
||||
return in1.thenApply(t1 -> arithmetic.unaryOp(opcode, sizeout, sizein1, t1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<T> binaryOp(int opcode, int sizeout, int sizein1,
|
||||
CompletableFuture<T> in1, int sizein2, CompletableFuture<T> in2) {
|
||||
return in1.thenCombine(in2,
|
||||
(t1, t2) -> arithmetic.binaryOp(opcode, sizeout, sizein1, t1, sizein2, t2));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<T> modBeforeStore(int sizeout, int sizeinAddress,
|
||||
CompletableFuture<T> inAddress, int sizeinValue, CompletableFuture<T> inValue) {
|
||||
return inValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<T> modAfterLoad(int sizeout, int sizeinAddress,
|
||||
CompletableFuture<T> inAddress, int sizeinValue, CompletableFuture<T> inValue) {
|
||||
return inValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<T> fromConst(byte[] value) {
|
||||
return CompletableFuture.completedFuture(arithmetic.fromConst(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] toConcrete(CompletableFuture<T> value, Purpose purpose) {
|
||||
if (!value.isDone()) {
|
||||
throw new ConcretionError("You need a better 8-ball", purpose);
|
||||
}
|
||||
return arithmetic.toConcrete(value.getNow(null), purpose);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long sizeOf(CompletableFuture<T> value) {
|
||||
if (!value.isDone()) {
|
||||
// TODO: Make a class which has future and expected size?
|
||||
throw new RuntimeException("You need a better 8-ball");
|
||||
}
|
||||
return arithmetic.sizeOf(value.getNow(null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<T> sizeOfAbstract(CompletableFuture<T> value) {
|
||||
return value.thenApply(v -> arithmetic.sizeOfAbstract(v));
|
||||
}
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
/* ###
|
||||
* 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.exec;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
|
||||
/**
|
||||
* An executor state piece which can operate on futures of a wrapped type
|
||||
*
|
||||
* @see AsyncPcodeExecutor for comment regarding potential deprecation or immediate removal
|
||||
* @param <T> the type of values wrapped
|
||||
*/
|
||||
public class AsyncWrappedPcodeExecutorStatePiece<A, T>
|
||||
implements PcodeExecutorStatePiece<CompletableFuture<A>, CompletableFuture<T>> {
|
||||
protected final PcodeExecutorStatePiece<A, T> state;
|
||||
protected final AsyncWrappedPcodeArithmetic<A> addressArithmetic;
|
||||
protected final AsyncWrappedPcodeArithmetic<T> arithmetic;
|
||||
private CompletableFuture<?> lastWrite = AsyncUtils.NIL;
|
||||
|
||||
public AsyncWrappedPcodeExecutorStatePiece(PcodeExecutorStatePiece<A, T> state) {
|
||||
this.state = state;
|
||||
this.addressArithmetic = new AsyncWrappedPcodeArithmetic<>(state.getAddressArithmetic());
|
||||
this.arithmetic = new AsyncWrappedPcodeArithmetic<>(state.getArithmetic());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncWrappedPcodeArithmetic<A> getAddressArithmetic() {
|
||||
return addressArithmetic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncWrappedPcodeArithmetic<T> getArithmetic() {
|
||||
return arithmetic;
|
||||
}
|
||||
|
||||
protected boolean isWriteDone() {
|
||||
return lastWrite.isDone();
|
||||
}
|
||||
|
||||
protected <U> CompletableFuture<U> nextRead(Supplier<CompletableFuture<U>> next) {
|
||||
return lastWrite.thenCompose(__ -> next.get()).exceptionally(ex -> null);
|
||||
}
|
||||
|
||||
protected <U> void nextWrite(Supplier<CompletableFuture<U>> next) {
|
||||
lastWrite = nextRead(next);
|
||||
}
|
||||
|
||||
protected CompletableFuture<?> doSetVar(AddressSpace space, CompletableFuture<A> offset,
|
||||
int size, boolean quantize, CompletableFuture<T> val) {
|
||||
return offset.thenCompose(off -> val.thenAccept(v -> {
|
||||
state.setVar(space, off, size, quantize, v);
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVar(AddressSpace space, CompletableFuture<A> offset, int size,
|
||||
boolean quantize, CompletableFuture<T> val) {
|
||||
nextWrite(() -> doSetVar(space, offset, size, quantize, val));
|
||||
}
|
||||
|
||||
protected CompletableFuture<T> doGetVar(AddressSpace space, CompletableFuture<A> offset,
|
||||
int size, boolean quantize) {
|
||||
return offset.thenApply(off -> {
|
||||
return state.getVar(space, off, size, quantize);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<T> getVar(AddressSpace space, CompletableFuture<A> offset, int size,
|
||||
boolean quantize) {
|
||||
return nextRead(() -> doGetVar(space, offset, size, quantize));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemBuffer getConcreteBuffer(Address address, Purpose purpose) {
|
||||
if (!isWriteDone()) {
|
||||
throw new AssertionError("An async write is still pending");
|
||||
}
|
||||
return state.getConcreteBuffer(address, purpose);
|
||||
}
|
||||
}
|
|
@ -15,48 +15,118 @@
|
|||
*/
|
||||
package ghidra.pcode.exec;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.Objects;
|
||||
|
||||
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
|
||||
import ghidra.app.plugin.core.debug.mapping.DebuggerPlatformMapper;
|
||||
import ghidra.app.plugin.core.debug.service.emulation.*;
|
||||
import ghidra.app.plugin.core.debug.service.emulation.data.DefaultPcodeDebuggerAccess;
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.pcode.exec.trace.DirectBytesTracePcodeExecutorStatePiece;
|
||||
import ghidra.app.services.DebuggerPlatformService;
|
||||
import ghidra.app.services.DebuggerTraceManagerService;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.pcode.emu.ThreadPcodeExecutorState;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
|
||||
/**
|
||||
* Utilities for evaluating or executing Sleigh/p-code in the Debugger
|
||||
*/
|
||||
public enum DebuggerPcodeUtils {
|
||||
;
|
||||
|
||||
/**
|
||||
* Get an executor which can be used to evaluate Sleigh expressions at the given coordinates,
|
||||
* asynchronously.
|
||||
* Get the current platform
|
||||
*
|
||||
* <p>
|
||||
* TODO: Change this to be synchronous and have clients evaluate expressions in another thread?
|
||||
* TODO: This should be part of {@link DebuggerTraceManagerService}.
|
||||
*
|
||||
* @param platformService the platform service
|
||||
* @param coordinates the coordinates
|
||||
* @return the executor
|
||||
* @return the "current platform" for the coordinates
|
||||
*/
|
||||
public static AsyncPcodeExecutor<byte[]> executorForCoordinates(
|
||||
public static TracePlatform getCurrentPlatform(DebuggerPlatformService platformService,
|
||||
DebuggerCoordinates coordinates) {
|
||||
Trace trace = coordinates.getTrace();
|
||||
if (platformService == null) {
|
||||
return trace.getPlatformManager().getHostPlatform();
|
||||
}
|
||||
DebuggerPlatformMapper mapper = platformService.getCurrentMapperFor(trace);
|
||||
if (mapper == null) {
|
||||
return trace.getPlatformManager().getHostPlatform();
|
||||
}
|
||||
return Objects.requireNonNull(trace.getPlatformManager()
|
||||
.getPlatform(mapper.getCompilerSpec(coordinates.getObject())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current platform
|
||||
*
|
||||
* <p>
|
||||
* TODO: This should be part of {@link DebuggerTraceManagerService}.
|
||||
*
|
||||
* @param tool the plugin tool
|
||||
* @param coordinates the coordinates
|
||||
* @return the "current platform" for the coordinates
|
||||
*/
|
||||
public static TracePlatform getCurrentPlatform(PluginTool tool,
|
||||
DebuggerCoordinates coordinates) {
|
||||
return getCurrentPlatform(tool.getService(DebuggerPlatformService.class), coordinates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a p-code executor state for the given coordinates
|
||||
*
|
||||
* <p>
|
||||
* If a thread is included, the executor state will have access to both the memory and registers
|
||||
* in the context of that thread. Otherwise, only memory access is permitted.
|
||||
*
|
||||
* @param tool the plugin tool. TODO: This shouldn't be required
|
||||
* @param coordinates the coordinates
|
||||
* @return the state
|
||||
*/
|
||||
public static PcodeExecutorState<byte[]> executorStateForCoordinates(PluginTool tool,
|
||||
DebuggerCoordinates coordinates) {
|
||||
// TODO: Make platform part of coordinates
|
||||
Trace trace = coordinates.getTrace();
|
||||
if (trace == null) {
|
||||
throw new IllegalArgumentException("Coordinates have no trace");
|
||||
}
|
||||
Language language = trace.getBaseLanguage();
|
||||
TracePlatform platform = getCurrentPlatform(tool, coordinates);
|
||||
Language language = platform.getLanguage();
|
||||
if (!(language instanceof SleighLanguage)) {
|
||||
throw new IllegalArgumentException("Given trace does not use a Sleigh language");
|
||||
throw new IllegalArgumentException(
|
||||
"Given trace or platform does not use a Sleigh language");
|
||||
}
|
||||
SleighLanguage slang = (SleighLanguage) language;
|
||||
PcodeExecutorState<CompletableFuture<byte[]>> state;
|
||||
if (coordinates.getRecorder() == null) {
|
||||
state = new AsyncWrappedPcodeExecutorState<>(
|
||||
new DirectBytesTracePcodeExecutorStatePiece(trace, coordinates.getViewSnap(),
|
||||
coordinates.getThread(), coordinates.getFrame()));
|
||||
DefaultPcodeDebuggerAccess access = new DefaultPcodeDebuggerAccess(tool,
|
||||
coordinates.getRecorder(), platform, coordinates.getSnap());
|
||||
PcodeExecutorState<byte[]> shared =
|
||||
new RWTargetMemoryPcodeExecutorState(access.getDataForSharedState(), Mode.RW);
|
||||
if (coordinates.getThread() == null) {
|
||||
return shared;
|
||||
}
|
||||
else {
|
||||
state = new TraceRecorderAsyncPcodeExecutorState(coordinates.getRecorder(),
|
||||
coordinates.getSnap(), coordinates.getThread(), coordinates.getFrame());
|
||||
}
|
||||
return new AsyncPcodeExecutor<>(slang, AsyncWrappedPcodeArithmetic.forLanguage(slang),
|
||||
state);
|
||||
PcodeExecutorState<byte[]> local = new RWTargetRegistersPcodeExecutorState(
|
||||
access.getDataForLocalState(coordinates.getThread(), coordinates.getFrame()), Mode.RW);
|
||||
return new ThreadPcodeExecutorState<>(shared, local);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an executor which can be used to evaluate Sleigh expressions at the given coordinates
|
||||
*
|
||||
* <p>
|
||||
* If a thread is included, the executor will have access to both the memory and registers in
|
||||
* the context of that thread. Otherwise, only memory access is permitted.
|
||||
*
|
||||
* @param tool the plugin tool. TODO: This shouldn't be required
|
||||
* @param coordinates the coordinates
|
||||
* @return the executor
|
||||
*/
|
||||
public static PcodeExecutor<byte[]> executorForCoordinates(PluginTool tool,
|
||||
DebuggerCoordinates coordinates) {
|
||||
PcodeExecutorState<byte[]> state = executorStateForCoordinates(tool, coordinates);
|
||||
|
||||
SleighLanguage slang = (SleighLanguage) state.getLanguage();
|
||||
return new PcodeExecutor<>(slang, BytesPcodeArithmetic.forLanguage(slang), state);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
/* ###
|
||||
* 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.exec;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
||||
/**
|
||||
* A state composing a single {@link TraceRecorderAsyncPcodeExecutorStatePiece}
|
||||
*/
|
||||
public class TraceRecorderAsyncPcodeExecutorState
|
||||
extends DefaultPcodeExecutorState<CompletableFuture<byte[]>> {
|
||||
/**
|
||||
* Create the state
|
||||
*
|
||||
* @param recorder the recorder for the trace's live target
|
||||
* @param snap the user's current snap
|
||||
* @param thread the user's current thread
|
||||
* @param frame the user's current frame
|
||||
*/
|
||||
public TraceRecorderAsyncPcodeExecutorState(TraceRecorder recorder, long snap,
|
||||
TraceThread thread, int frame) {
|
||||
super(new TraceRecorderAsyncPcodeExecutorStatePiece(recorder, snap, thread, frame));
|
||||
}
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
/* ###
|
||||
* 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.exec;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.pcode.exec.trace.DirectBytesTracePcodeExecutorStatePiece;
|
||||
import ghidra.pcode.exec.trace.TraceMemoryStatePcodeExecutorStatePiece;
|
||||
import ghidra.pcode.utils.Utils;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.trace.model.memory.TraceMemoryState;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* An executor state which can asynchronously read and write a live target, if applicable
|
||||
*
|
||||
* <p>
|
||||
* This is used for executing Sleigh code to manipulate trace history or a live target.
|
||||
*
|
||||
* <p>
|
||||
* TODO: It might be easier to re-factor this to operate synchronously, executing Sleigh programs in
|
||||
* a separate thread.
|
||||
*/
|
||||
public class TraceRecorderAsyncPcodeExecutorStatePiece
|
||||
extends AsyncWrappedPcodeExecutorStatePiece<byte[], byte[]> {
|
||||
private final TraceRecorder recorder;
|
||||
private final DirectBytesTracePcodeExecutorStatePiece traceState;
|
||||
private final TraceMemoryStatePcodeExecutorStatePiece traceMemState;
|
||||
|
||||
public TraceRecorderAsyncPcodeExecutorStatePiece(TraceRecorder recorder, long snap,
|
||||
TraceThread thread, int frame) {
|
||||
super(
|
||||
new DirectBytesTracePcodeExecutorStatePiece(recorder.getTrace(), snap, thread, frame));
|
||||
this.recorder = recorder;
|
||||
this.traceState = (DirectBytesTracePcodeExecutorStatePiece) state;
|
||||
this.traceMemState =
|
||||
new TraceMemoryStatePcodeExecutorStatePiece(recorder.getTrace(), snap, thread, frame);
|
||||
}
|
||||
|
||||
protected CompletableFuture<?> doSetTargetVar(AddressSpace space, long offset, int size,
|
||||
boolean quantize, byte[] val) {
|
||||
return recorder.writeVariable(traceState.getThread(), 0, space.getAddress(offset), val);
|
||||
}
|
||||
|
||||
protected byte[] knitFromResults(NavigableMap<Address, byte[]> map, Address addr, int size) {
|
||||
Address floor = map.floorKey(addr);
|
||||
NavigableMap<Address, byte[]> tail;
|
||||
if (floor == null) {
|
||||
tail = map;
|
||||
}
|
||||
else {
|
||||
tail = map.tailMap(floor, true);
|
||||
}
|
||||
byte[] result = new byte[size];
|
||||
for (Map.Entry<Address, byte[]> ent : tail.entrySet()) {
|
||||
long off = ent.getKey().subtract(addr);
|
||||
if (off >= size || off < 0) {
|
||||
break;
|
||||
}
|
||||
int subSize = Math.min(size - (int) off, ent.getValue().length);
|
||||
System.arraycopy(ent.getValue(), 0, result, (int) off, subSize);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected CompletableFuture<byte[]> doGetTargetVar(AddressSpace space, long offset,
|
||||
int size, boolean quantize) {
|
||||
if (space.isMemorySpace()) {
|
||||
Address addr = space.getAddress(quantizeOffset(space, offset));
|
||||
AddressSet set = new AddressSet(addr, space.getAddress(offset + size - 1));
|
||||
CompletableFuture<NavigableMap<Address, byte[]>> future =
|
||||
recorder.readMemoryBlocks(set, TaskMonitor.DUMMY, true);
|
||||
return future.thenApply(map -> {
|
||||
return knitFromResults(map, addr, size);
|
||||
});
|
||||
}
|
||||
assert space.isRegisterSpace();
|
||||
|
||||
Language lang = recorder.getTrace().getBaseLanguage();
|
||||
Register register = lang.getRegister(space, offset, size);
|
||||
if (register == null) {
|
||||
// TODO: Is this too restrictive?
|
||||
throw new IllegalArgumentException(
|
||||
"read from register space must be from one register");
|
||||
}
|
||||
Register baseRegister = register.getBaseRegister();
|
||||
|
||||
CompletableFuture<Map<Register, RegisterValue>> future =
|
||||
recorder.captureThreadRegisters(traceState.getThread(), traceState.getFrame(),
|
||||
Set.of(baseRegister));
|
||||
return future.thenApply(map -> {
|
||||
RegisterValue baseVal = map.get(baseRegister);
|
||||
if (baseVal == null) {
|
||||
return state.getVar(space, offset, size, quantize);
|
||||
}
|
||||
BigInteger val = baseVal.getRegisterValue(register).getUnsignedValue();
|
||||
return Utils.bigIntegerToBytes(val, size,
|
||||
recorder.getTrace().getBaseLanguage().isBigEndian());
|
||||
});
|
||||
}
|
||||
|
||||
protected boolean isTargetSpace(AddressSpace space) {
|
||||
return traceState.getSnap() == recorder.getSnap() && !space.isConstantSpace() &&
|
||||
!space.isUniqueSpace();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<?> doSetVar(AddressSpace space,
|
||||
CompletableFuture<byte[]> offset, int size, boolean quantize,
|
||||
CompletableFuture<byte[]> val) {
|
||||
if (!isTargetSpace(space)) {
|
||||
return super.doSetVar(space, offset, size, quantize, val);
|
||||
}
|
||||
return offset.thenCompose(off -> val.thenCompose(v -> {
|
||||
long lOff = traceState.getAddressArithmetic().toLong(off, Purpose.STORE);
|
||||
return doSetTargetVar(space, lOff, size, quantize, v);
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<byte[]> doGetVar(AddressSpace space,
|
||||
CompletableFuture<byte[]> offset, int size, boolean quantize) {
|
||||
if (!isTargetSpace(space)) {
|
||||
return super.doGetVar(space, offset, size, quantize);
|
||||
}
|
||||
return offset.thenCompose(off -> {
|
||||
TraceMemoryState ms = traceMemState.getVar(space, off, size, quantize);
|
||||
if (ms == TraceMemoryState.KNOWN) {
|
||||
return super.doGetVar(space, offset, size, quantize);
|
||||
}
|
||||
long lOff = traceState.getAddressArithmetic().toLong(off, Purpose.LOAD);
|
||||
return doGetTargetVar(space, lOff, size, quantize);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -21,8 +21,7 @@ import ghidra.app.plugin.core.debug.service.emulation.*;
|
|||
import ghidra.pcode.emu.PcodeThread;
|
||||
import ghidra.pcode.emu.auxiliary.AuxEmulatorPartsFactory;
|
||||
import ghidra.pcode.emu.auxiliary.AuxPcodeEmulator;
|
||||
import ghidra.pcode.exec.trace.PairedTracePcodeExecutorStatePiece;
|
||||
import ghidra.pcode.exec.trace.TracePcodeExecutorState;
|
||||
import ghidra.pcode.exec.trace.*;
|
||||
import ghidra.pcode.exec.trace.auxiliary.AuxTraceEmulatorPartsFactory;
|
||||
import ghidra.pcode.exec.trace.auxiliary.AuxTracePcodeEmulator;
|
||||
|
||||
|
@ -70,7 +69,9 @@ public interface AuxDebuggerEmulatorPartsFactory<U> extends AuxTraceEmulatorPart
|
|||
* given concrete piece is already capable of doing that for concrete values. The auxiliary
|
||||
* piece can, at its discretion, delegate to the concrete piece in order to derive its values.
|
||||
* It should be able to independently load its state from the trace and mapped static program,
|
||||
* since this is one way a user expects to initialize the auxiliary values.
|
||||
* since this is one way a user expects to initialize the auxiliary values. It ought to use the
|
||||
* same data-access shim as the given concrete state. See
|
||||
* {@link TracePcodeExecutorStatePiece#getData()}.
|
||||
*
|
||||
* @param emulator the emulator
|
||||
* @param concrete the concrete piece
|
||||
|
@ -78,14 +79,14 @@ public interface AuxDebuggerEmulatorPartsFactory<U> extends AuxTraceEmulatorPart
|
|||
*/
|
||||
TracePcodeExecutorState<Pair<byte[], U>> createDebuggerSharedState(
|
||||
AuxDebuggerPcodeEmulator<U> emulator,
|
||||
ReadsTargetMemoryPcodeExecutorStatePiece concrete);
|
||||
RWTargetMemoryPcodeExecutorStatePiece concrete);
|
||||
|
||||
/**
|
||||
* Create the local (register) state of a new Debugger-integrated thread
|
||||
*
|
||||
* <p>
|
||||
* Like
|
||||
* {@link #createDebuggerSharedState(AuxDebuggerPcodeEmulator, ReadsTargetMemoryPcodeExecutorStatePiece)}
|
||||
* {@link #createDebuggerSharedState(AuxDebuggerPcodeEmulator, RWTargetMemoryPcodeExecutorStatePiece)}
|
||||
* this state must also be capable of lazily loading state from a trace and from a live target.
|
||||
* Static programs can't be mapped into register space, so they do not apply here.
|
||||
*
|
||||
|
@ -96,5 +97,5 @@ public interface AuxDebuggerEmulatorPartsFactory<U> extends AuxTraceEmulatorPart
|
|||
*/
|
||||
TracePcodeExecutorState<Pair<byte[], U>> createDebuggerLocalState(
|
||||
AuxDebuggerPcodeEmulator<U> emulator, PcodeThread<Pair<byte[], U>> thread,
|
||||
ReadsTargetRegistersPcodeExecutorStatePiece concrete);
|
||||
RWTargetRegistersPcodeExecutorStatePiece concrete);
|
||||
}
|
||||
|
|
|
@ -18,14 +18,12 @@ package ghidra.pcode.exec.debug.auxiliary;
|
|||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import ghidra.app.plugin.core.debug.service.emulation.*;
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerAccess;
|
||||
import ghidra.pcode.emu.PcodeThread;
|
||||
import ghidra.pcode.emu.auxiliary.AuxEmulatorPartsFactory;
|
||||
import ghidra.pcode.exec.trace.TracePcodeExecutorState;
|
||||
import ghidra.pcode.exec.trace.auxiliary.AuxTraceEmulatorPartsFactory;
|
||||
import ghidra.pcode.exec.trace.auxiliary.AuxTracePcodeEmulator;
|
||||
import ghidra.trace.model.Trace;
|
||||
|
||||
/**
|
||||
* An Debugger-integrated emulator whose parts are manufactured by a
|
||||
|
@ -43,49 +41,33 @@ import ghidra.trace.model.Trace;
|
|||
*/
|
||||
public abstract class AuxDebuggerPcodeEmulator<U> extends AuxTracePcodeEmulator<U>
|
||||
implements DebuggerPcodeMachine<Pair<byte[], U>> {
|
||||
protected final PluginTool tool;
|
||||
protected final TraceRecorder recorder;
|
||||
|
||||
protected final PcodeDebuggerAccess access;
|
||||
|
||||
/**
|
||||
* Create a new emulator
|
||||
*
|
||||
* @param tool the user's tool where the emulator is integrated
|
||||
* @param trace the user's current trace from which the emulator loads state
|
||||
* @param snap the user's current snapshot from which the emulator loads state
|
||||
* @param recorder if applicable, the trace's recorder for its live target
|
||||
* @param access the trace-and-debugger access shim
|
||||
*/
|
||||
public AuxDebuggerPcodeEmulator(PluginTool tool, Trace trace, long snap,
|
||||
TraceRecorder recorder) {
|
||||
super(trace, snap);
|
||||
this.tool = tool;
|
||||
this.recorder = recorder;
|
||||
public AuxDebuggerPcodeEmulator(PcodeDebuggerAccess access) {
|
||||
super(access);
|
||||
this.access = access;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected abstract AuxDebuggerEmulatorPartsFactory<U> getPartsFactory();
|
||||
|
||||
@Override
|
||||
public PluginTool getTool() {
|
||||
return tool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceRecorder getRecorder() {
|
||||
return recorder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TracePcodeExecutorState<Pair<byte[], U>> createSharedState() {
|
||||
return getPartsFactory().createDebuggerSharedState(this,
|
||||
new ReadsTargetMemoryPcodeExecutorStatePiece(tool, trace, snap, null, 0, recorder));
|
||||
new RWTargetMemoryPcodeExecutorStatePiece(access.getDataForSharedState(), Mode.RO));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TracePcodeExecutorState<Pair<byte[], U>> createLocalState(
|
||||
PcodeThread<Pair<byte[], U>> thread) {
|
||||
return getPartsFactory().createDebuggerLocalState(this, thread,
|
||||
new ReadsTargetRegistersPcodeExecutorStatePiece(tool, trace, snap,
|
||||
getTraceThread(thread), 0,
|
||||
recorder));
|
||||
new RWTargetRegistersPcodeExecutorStatePiece(access.getDataForLocalState(thread, 0),
|
||||
Mode.RO));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -284,7 +284,7 @@ public class DebuggerCopyPlanTests extends AbstractGhidraHeadedDebuggerGUITest {
|
|||
Register contextReg = tb.language.getContextBaseRegister();
|
||||
Register longMode = tb.language.getRegister("longMode");
|
||||
RegisterValue rv = tb.trace.getRegisterContextManager()
|
||||
.getValueWithDefault(tb.language, contextReg, 0, tb.addr(0x55550000));
|
||||
.getValueWithDefault(tb.host, contextReg, 0, tb.addr(0x55550000));
|
||||
rv = rv.assign(longMode, BigInteger.ZERO);
|
||||
Instruction checkCtx;
|
||||
try (UndoableTransaction tid = tb.startTransaction()) {
|
||||
|
|
|
@ -835,17 +835,20 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
|||
assertTrue(listingProvider.actionGoTo.isEnabled());
|
||||
performAction(listingProvider.actionGoTo, false);
|
||||
DebuggerGoToDialog dialog1 = waitForDialogComponent(DebuggerGoToDialog.class);
|
||||
|
||||
dialog1.setExpression("r0");
|
||||
runSwing(() -> dialog1.okCallback());
|
||||
runSwing(() -> {
|
||||
dialog1.setExpression("r0");
|
||||
dialog1.okCallback();
|
||||
});
|
||||
|
||||
waitForPass(
|
||||
() -> assertEquals(tb.addr(0x00401234), listingProvider.getLocation().getAddress()));
|
||||
|
||||
performAction(listingProvider.actionGoTo, false);
|
||||
DebuggerGoToDialog dialog2 = waitForDialogComponent(DebuggerGoToDialog.class);
|
||||
dialog2.setExpression("*:4 r0");
|
||||
runSwing(() -> dialog2.okCallback());
|
||||
runSwing(() -> {
|
||||
dialog2.setExpression("*:4 r0");
|
||||
dialog2.okCallback();
|
||||
});
|
||||
|
||||
waitForPass(
|
||||
() -> assertEquals(tb.addr(0x00404321), listingProvider.getLocation().getAddress()));
|
||||
|
|
|
@ -627,18 +627,21 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
|
|||
|
||||
assertTrue(memBytesProvider.actionGoTo.isEnabled());
|
||||
performAction(memBytesProvider.actionGoTo, false);
|
||||
DebuggerGoToDialog dialog = waitForDialogComponent(DebuggerGoToDialog.class);
|
||||
|
||||
dialog.setExpression("r0");
|
||||
dialog.okCallback();
|
||||
DebuggerGoToDialog dialog1 = waitForDialogComponent(DebuggerGoToDialog.class);
|
||||
runSwing(() -> {
|
||||
dialog1.setExpression("r0");
|
||||
dialog1.okCallback();
|
||||
});
|
||||
|
||||
waitForPass(
|
||||
() -> assertEquals(tb.addr(0x00401234), memBytesProvider.getLocation().getAddress()));
|
||||
|
||||
performAction(memBytesProvider.actionGoTo, false);
|
||||
dialog = waitForDialogComponent(DebuggerGoToDialog.class);
|
||||
dialog.setExpression("*:4 r0");
|
||||
dialog.okCallback();
|
||||
DebuggerGoToDialog dialog2 = waitForDialogComponent(DebuggerGoToDialog.class);
|
||||
runSwing(() -> {
|
||||
dialog2.setExpression("*:4 r0");
|
||||
dialog2.okCallback();
|
||||
});
|
||||
|
||||
waitForPass(
|
||||
() -> assertEquals(tb.addr(0x00404321), memBytesProvider.getLocation().getAddress()));
|
||||
|
@ -1093,16 +1096,20 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
|
|||
mb.testProcess1.addRegion("exe:.text", mb.rng(0x55550000, 0x5555ffff), "rx");
|
||||
waitFor(() -> !trace.getMemoryManager().getAllRegions().isEmpty());
|
||||
|
||||
goToDyn(addr(trace, 0x55550800));
|
||||
performAction(actionEdit);
|
||||
triggerText(memBytesProvider.getByteViewerPanel().getCurrentComponent(), "42");
|
||||
performAction(actionEdit);
|
||||
waitForSwing();
|
||||
waitRecorder(recorder);
|
||||
waitForPass(noExc(() -> {
|
||||
traceManager.activateTrace(trace);
|
||||
goToDyn(addr(trace, 0x55550800));
|
||||
triggerText(memBytesProvider.getByteViewerPanel().getCurrentComponent(), "42");
|
||||
waitForSwing();
|
||||
waitRecorder(recorder);
|
||||
|
||||
byte[] data = new byte[4];
|
||||
mb.testProcess1.memory.getMemory(mb.addr(0x55550800), data);
|
||||
assertArrayEquals(mb.arr(0x42, 0, 0, 0), data);
|
||||
byte[] data = new byte[4];
|
||||
mb.testProcess1.memory.getMemory(mb.addr(0x55550800), data);
|
||||
assertArrayEquals(mb.arr(0x42, 0, 0, 0), data);
|
||||
}));
|
||||
|
||||
performAction(actionEdit);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -18,10 +18,15 @@ package ghidra.app.plugin.core.debug.gui.stack;
|
|||
import java.io.IOException;
|
||||
|
||||
import ghidra.dbg.target.schema.SchemaContext;
|
||||
import ghidra.dbg.target.schema.XmlSchemaContext;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema.SchemaName;
|
||||
import ghidra.dbg.target.schema.XmlSchemaContext;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.TraceObjectKeyPath;
|
||||
import ghidra.trace.model.target.TraceObject.ConflictResolution;
|
||||
import ghidra.trace.model.thread.TraceObjectThread;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.database.UndoableTransaction;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class DebuggerStackProviderObjectTest extends DebuggerStackProviderTest {
|
||||
|
||||
|
@ -51,46 +56,69 @@ public class DebuggerStackProviderObjectTest extends DebuggerStackProviderTest {
|
|||
|
||||
public void activateObjectsMode() throws Exception {
|
||||
// NOTE the use of index='1' allowing object-based managers to ID unique path
|
||||
ctx = XmlSchemaContext.deserialize("" + //
|
||||
"<context>" + //
|
||||
" <schema name='Session' elementResync='NEVER' attributeResync='ONCE'>" + //
|
||||
" <attribute name='Processes' schema='ProcessContainer' />" + //
|
||||
" </schema>" + //
|
||||
" <schema name='ProcessContainer' canonical='yes' elementResync='NEVER' " + //
|
||||
" attributeResync='ONCE'>" + //
|
||||
" <element index='1' schema='Process' />" + // <---- NOTE HERE
|
||||
" </schema>" + //
|
||||
" <schema name='Process' elementResync='NEVER' attributeResync='ONCE'>" + //
|
||||
" <attribute name='Threads' schema='ThreadContainer' />" + //
|
||||
" <attribute name='Memory' schema='RegionContainer' />" + //
|
||||
" </schema>" + //
|
||||
" <schema name='ThreadContainer' canonical='yes' elementResync='NEVER' " + //
|
||||
" attributeResync='ONCE'>" + //
|
||||
" <element schema='Thread' />" + //
|
||||
" </schema>" + //
|
||||
" <schema name='Thread' elementResync='NEVER' attributeResync='NEVER'>" + //
|
||||
" <interface name='Thread' />" + //
|
||||
" <attribute name='Stack' schema='Stack' />" + //
|
||||
" </schema>" + //
|
||||
" <schema name='Stack' canonical='yes' elementResync='NEVER' " + //
|
||||
" attributeResync='ONCE'>" + //
|
||||
" <interface name='Stack' />" + //
|
||||
" <element schema='Frame' />" + //
|
||||
" </schema>" + //
|
||||
" <schema name='Frame' elementResync='NEVER' attributeResync='NEVER'>" + //
|
||||
" <interface name='StackFrame' />" + //
|
||||
" </schema>" + //
|
||||
" <schema name='RegionContainer' canonical='yes' elementResync='NEVER' " + //
|
||||
" attributeResync='ONCE'>" + //
|
||||
" <element schema='Region' />" + //
|
||||
" </schema>" + //
|
||||
" <schema name='Region' elementResync='NEVER' attributeResync='NEVER'>" + //
|
||||
" <interface name='MemoryRegion' />" + //
|
||||
" </schema>" + //
|
||||
"</context>");
|
||||
ctx = XmlSchemaContext.deserialize("""
|
||||
<context>
|
||||
<schema name='Session' elementResync='NEVER' attributeResync='ONCE'>
|
||||
<attribute name='Processes' schema='ProcessContainer' />
|
||||
</schema>
|
||||
<schema name='ProcessContainer' canonical='yes' elementResync='NEVER'
|
||||
attributeResync='ONCE'>
|
||||
<element index='1' schema='Process' />
|
||||
</schema>
|
||||
<schema name='Process' elementResync='NEVER' attributeResync='ONCE'>
|
||||
<attribute name='Threads' schema='ThreadContainer' />
|
||||
<attribute name='Memory' schema='RegionContainer' />
|
||||
</schema>
|
||||
<schema name='ThreadContainer' canonical='yes' elementResync='NEVER'
|
||||
attributeResync='ONCE'>
|
||||
<element schema='Thread' />
|
||||
</schema>
|
||||
<schema name='Thread' elementResync='NEVER' attributeResync='NEVER'>
|
||||
<interface name='Thread' />
|
||||
<interface name='Aggregate' />
|
||||
<attribute name='Stack' schema='Stack' />
|
||||
<attribute name='Registers' schema='RegisterContainer' />
|
||||
</schema>
|
||||
<schema name='Stack' canonical='yes' elementResync='NEVER'
|
||||
attributeResync='ONCE'>
|
||||
<interface name='Stack' />
|
||||
<element schema='Frame' />
|
||||
</schema>
|
||||
<schema name='Frame' elementResync='NEVER' attributeResync='NEVER'>
|
||||
<interface name='StackFrame' />
|
||||
</schema>
|
||||
<schema name='RegisterContainer' canonical='yes' elementResync='NEVER'
|
||||
attributeResync='NEVER'>
|
||||
<interface name='RegisterContainer' />
|
||||
<element schema='Register' />
|
||||
</schema>
|
||||
<schema name='Register' elementResync='NEVER' attributeResync='NEVER'>
|
||||
<interface name='Register' />
|
||||
</schema>
|
||||
<schema name='RegionContainer' canonical='yes' elementResync='NEVER'
|
||||
attributeResync='ONCE'>
|
||||
<element schema='Region' />
|
||||
</schema>
|
||||
<schema name='Region' elementResync='NEVER' attributeResync='NEVER'>
|
||||
<interface name='MemoryRegion' />
|
||||
</schema>
|
||||
</context>
|
||||
""");
|
||||
|
||||
try (UndoableTransaction tid = tb.startTransaction()) {
|
||||
tb.trace.getObjectManager().createRootObject(ctx.getSchema(new SchemaName("Session")));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceThread addThread(String n) throws DuplicateNameException {
|
||||
try (UndoableTransaction tid = tb.startTransaction()) {
|
||||
TraceObjectThread thread = (TraceObjectThread) super.addThread(n);
|
||||
TraceObjectKeyPath regsPath = thread.getObject().getCanonicalPath().extend("Registers");
|
||||
tb.trace.getObjectManager()
|
||||
.createObject(regsPath)
|
||||
.insert(thread.getLifespan(), ConflictResolution.DENY);
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -405,7 +405,7 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
|||
TraceMemorySpace regVals =
|
||||
tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, false);
|
||||
|
||||
row.setRawValueString("0x1234");
|
||||
runSwing(() -> row.setRawValueString("0x1234"));
|
||||
waitForPass(() -> {
|
||||
long viewSnap = traceManager.getCurrent().getViewSnap();
|
||||
assertTrue(DBTraceUtils.isScratch(viewSnap));
|
||||
|
@ -414,7 +414,7 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
|||
assertEquals("0x1234", row.getRawValueString());
|
||||
});
|
||||
|
||||
row.setRawValueString("1234"); // Decimal this time
|
||||
runSwing(() -> row.setRawValueString("1234")); // Decimal this time
|
||||
waitForPass(() -> {
|
||||
long viewSnap = traceManager.getCurrent().getViewSnap();
|
||||
assertTrue(DBTraceUtils.isScratch(viewSnap));
|
||||
|
@ -436,7 +436,7 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
|||
TraceMemorySpace regVals =
|
||||
tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, false);
|
||||
|
||||
row.setValueString("1234");
|
||||
runSwing(() -> row.setValueString("1234"));
|
||||
waitForPass(() -> {
|
||||
long viewSnap = traceManager.getCurrent().getViewSnap();
|
||||
assertTrue(DBTraceUtils.isScratch(viewSnap));
|
||||
|
@ -456,7 +456,7 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
|||
|
||||
// Wait for row to settle. TODO: Why is this necessary?
|
||||
waitForPass(() -> assertEquals(tb.addr(0x00400000), row.getAddress()));
|
||||
row.setRawValueString("0x1234");
|
||||
runSwing(() -> row.setRawValueString("0x1234"));
|
||||
waitForPass(() -> {
|
||||
long viewSnap = traceManager.getCurrent().getViewSnap();
|
||||
assertTrue(DBTraceUtils.isScratch(viewSnap));
|
||||
|
@ -468,7 +468,7 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
|||
|
||||
// Wait for row to settle. TODO: Why is this necessary?
|
||||
waitForPass(() -> assertEquals(tb.addr(0x00400000), row.getAddress()));
|
||||
row.setRawValueString("{ 12 34 56 78 9a bc de f0 }");
|
||||
runSwing(() -> row.setRawValueString("{ 12 34 56 78 9a bc de f0 }"));
|
||||
waitForPass(() -> {
|
||||
long viewSnap = traceManager.getCurrent().getViewSnap();
|
||||
assertTrue(DBTraceUtils.isScratch(viewSnap));
|
||||
|
@ -491,7 +491,9 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
|||
TraceMemoryOperations mem = tb.trace.getMemoryManager();
|
||||
ByteBuffer buf = ByteBuffer.allocate(8);
|
||||
|
||||
row.setValueString("1234");
|
||||
// Wait for row to settle. TODO: Why is this necessary?
|
||||
waitForPass(() -> assertEquals(tb.addr(0x00400000), row.getAddress()));
|
||||
runSwing(() -> row.setValueString("1234"));
|
||||
waitForPass(() -> {
|
||||
long viewSnap = traceManager.getCurrent().getViewSnap();
|
||||
assertTrue(DBTraceUtils.isScratch(viewSnap));
|
||||
|
@ -516,7 +518,7 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
|||
TraceMemoryOperations mem = tb.trace.getMemoryManager();
|
||||
ByteBuffer buf = ByteBuffer.allocate(14);
|
||||
|
||||
row.setValueString("\"Hello, World!\"");
|
||||
runSwing(() -> row.setValueString("\"Hello, World!\""));
|
||||
waitForPass(() -> {
|
||||
long viewSnap = traceManager.getCurrent().getViewSnap();
|
||||
assertTrue(DBTraceUtils.isScratch(viewSnap));
|
||||
|
@ -561,7 +563,7 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
|||
public void testEditRegisterTarget() throws Throwable {
|
||||
WatchRow row = prepareTestEditTarget("r0");
|
||||
|
||||
row.setRawValueString("0x1234");
|
||||
runSwing(() -> row.setRawValueString("0x1234"));
|
||||
retryVoid(() -> {
|
||||
assertArrayEquals(mb.arr(0, 0, 0, 0, 0, 0, 0x12, 0x34), bank.regVals.get("r0"));
|
||||
}, List.of(AssertionError.class));
|
||||
|
@ -573,7 +575,7 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
|||
// Wait for the async reads to settle.
|
||||
waitForPass(() -> assertEquals(tb.addr(0x00400000), row.getAddress()));
|
||||
|
||||
row.setRawValueString("0x1234");
|
||||
runSwing(() -> row.setRawValueString("0x1234"));
|
||||
retryVoid(() -> {
|
||||
assertArrayEquals(tb.arr(0, 0, 0, 0, 0, 0, 0x12, 0x34),
|
||||
waitOn(mb.testProcess1.memory.readMemory(mb.addr(0x00400000), 8)));
|
||||
|
@ -588,7 +590,7 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
|||
assertFalse(recorder.isRegisterOnTarget(thread, r1));
|
||||
|
||||
assertFalse(row.isRawValueEditable());
|
||||
row.setRawValueString("0x1234");
|
||||
runSwingWithException(() -> row.setRawValueString("0x1234"));
|
||||
}
|
||||
|
||||
protected void setupUnmappedDataSection() throws Throwable {
|
||||
|
|
|
@ -32,8 +32,8 @@ import ghidra.app.services.DebuggerStateEditingService.StateEditingMode;
|
|||
import ghidra.app.services.DebuggerStateEditingService.StateEditor;
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.dbg.target.TargetRegisterBank;
|
||||
import ghidra.pcode.exec.AsyncPcodeExecutor;
|
||||
import ghidra.pcode.exec.DebuggerPcodeUtils;
|
||||
import ghidra.pcode.exec.PcodeExecutor;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.trace.database.DBTraceUtils;
|
||||
|
@ -143,8 +143,8 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
|
|||
try (UndoableTransaction tid = tb.startTransaction()) {
|
||||
// NB. TraceManager should automatically activate the first thread
|
||||
TraceThread thread = tb.getOrAddThread("Threads[0]", 0);
|
||||
AsyncPcodeExecutor<byte[]> executor = DebuggerPcodeUtils
|
||||
.executorForCoordinates(DebuggerCoordinates.NOWHERE.thread(thread));
|
||||
PcodeExecutor<byte[]> executor = DebuggerPcodeUtils.executorForCoordinates(
|
||||
env.getTool(), DebuggerCoordinates.NOWHERE.thread(thread));
|
||||
|
||||
Assembler asm = Assemblers.getAssembler(tb.trace.getFixedProgramView(0));
|
||||
asm.assemble(tb.addr(0x00400000), "imm r0,#123");
|
||||
|
@ -181,8 +181,8 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
|
|||
try (UndoableTransaction tid = tb.startTransaction()) {
|
||||
// NB. TraceManager should automatically activate the first thread
|
||||
thread = tb.getOrAddThread("Threads[0]", 0);
|
||||
AsyncPcodeExecutor<byte[]> executor = DebuggerPcodeUtils
|
||||
.executorForCoordinates(DebuggerCoordinates.NOWHERE.thread(thread));
|
||||
PcodeExecutor<byte[]> executor = DebuggerPcodeUtils.executorForCoordinates(
|
||||
env.getTool(), DebuggerCoordinates.NOWHERE.thread(thread));
|
||||
|
||||
Assembler asm = Assemblers.getAssembler(tb.trace.getFixedProgramView(0));
|
||||
asm.assemble(tb.addr(0x00400000), "imm r0,#123");
|
||||
|
|
|
@ -18,24 +18,29 @@ package ghidra.app.plugin.core.debug.service.emulation;
|
|||
import static org.junit.Assert.*;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.experimental.categories.Category;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import generic.Unique;
|
||||
import generic.test.category.NightlyCategory;
|
||||
import ghidra.app.plugin.assembler.Assembler;
|
||||
import ghidra.app.plugin.assembler.Assemblers;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
|
||||
import ghidra.app.services.DebuggerStaticMappingService;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.*;
|
||||
import ghidra.trace.model.memory.TraceMemoryManager;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
|
@ -202,22 +207,75 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerGU
|
|||
Trace trace = traceManager.getCurrentTrace();
|
||||
assertNotNull(trace);
|
||||
|
||||
TraceThread thread = Unique.assertOne(trace.getThreadManager().getAllThreads());
|
||||
TraceMemorySpace regs = trace.getMemoryManager().getMemoryRegisterSpace(thread, false);
|
||||
TraceMemoryManager mem = trace.getMemoryManager();
|
||||
assertEquals(new BigInteger("000100", 16),
|
||||
regs.getViewValue(0, regPC).getUnsignedValue());
|
||||
assertEquals(new BigInteger("0000", 16), regs.getViewValue(0, regW0).getUnsignedValue());
|
||||
mem.getViewValue(0, regPC).getUnsignedValue());
|
||||
assertEquals(new BigInteger("0000", 16),
|
||||
mem.getViewValue(0, regW0).getUnsignedValue());
|
||||
assertEquals(new BigInteger("0800", 16),
|
||||
regs.getViewValue(0, regW1).getUnsignedValue());
|
||||
mem.getViewValue(0, regW1).getUnsignedValue());
|
||||
|
||||
long scratch =
|
||||
emulationPlugin.emulate(trace, TraceSchedule.parse("0:t0-1"), TaskMonitor.DUMMY);
|
||||
|
||||
assertEquals(new BigInteger("000102", 16),
|
||||
regs.getViewValue(scratch, regPC).getUnsignedValue());
|
||||
mem.getViewValue(scratch, regPC).getUnsignedValue());
|
||||
assertEquals(new BigInteger("1234", 16),
|
||||
regs.getViewValue(scratch, regW0).getUnsignedValue());
|
||||
mem.getViewValue(scratch, regW0).getUnsignedValue());
|
||||
assertEquals(new BigInteger("0800", 16),
|
||||
regs.getViewValue(scratch, regW1).getUnsignedValue());
|
||||
mem.getViewValue(scratch, regW1).getUnsignedValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPureEmulationRelocated() throws Throwable {
|
||||
createAndOpenTrace("x86:LE:64:default");
|
||||
createProgramFromTrace();
|
||||
|
||||
intoProject(program);
|
||||
intoProject(tb.trace);
|
||||
|
||||
Assembler asm = Assemblers.getAssembler(program);
|
||||
Memory memory = program.getMemory();
|
||||
Address addrText = addr(program, 0x00400000);
|
||||
Address addrData = addr(program, 0x00600000);
|
||||
try (UndoableTransaction tid = UndoableTransaction.start(program, "Initialize")) {
|
||||
MemoryBlock blockText = memory.createInitializedBlock("text", addrText, 0x1000,
|
||||
(byte) 0, TaskMonitor.DUMMY, false);
|
||||
blockText.setExecute(true);
|
||||
memory.createInitializedBlock(".data", addrData, 0x1000, (byte) 0, TaskMonitor.DUMMY,
|
||||
false);
|
||||
|
||||
// NOTE: qword ptr [0x00600800] is RIP-relative
|
||||
asm.assemble(addrText, "MOV RAX, qword ptr [0x00600800]");
|
||||
memory.setLong(addr(program, 0x00600800), 0xdeadbeefcafebabeL);
|
||||
}
|
||||
|
||||
programManager.openProgram(program);
|
||||
waitForSwing();
|
||||
|
||||
DebuggerStaticMappingService mappings =
|
||||
tool.getService(DebuggerStaticMappingService.class);
|
||||
CompletableFuture<Void> settled;
|
||||
TraceThread thread;
|
||||
TraceMemorySpace regs;
|
||||
try (UndoableTransaction tid = tb.startTransaction()) {
|
||||
thread = tb.getOrAddThread("Threads[0]", 0);
|
||||
regs = tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, true);
|
||||
regs.setValue(0, new RegisterValue(program.getLanguage().getProgramCounter(),
|
||||
BigInteger.valueOf(0x55550000)));
|
||||
settled = mappings.changesSettled();
|
||||
mappings.addMapping(new DefaultTraceLocation(tb.trace, null, Range.atLeast(0L),
|
||||
tb.addr(0x55550000)), new ProgramLocation(program, addrText), 0x1000, false);
|
||||
mappings.addMapping(new DefaultTraceLocation(tb.trace, null, Range.atLeast(0L),
|
||||
tb.addr(0x55750000)), new ProgramLocation(program, addrData), 0x1000, false);
|
||||
}
|
||||
waitForSwing();
|
||||
waitOn(settled);
|
||||
|
||||
long scratch =
|
||||
emulationPlugin.emulate(tb.trace, TraceSchedule.parse("0:t0-1"), TaskMonitor.DUMMY);
|
||||
|
||||
assertEquals("deadbeefcafebabe",
|
||||
regs.getViewValue(scratch, tb.reg("RAX")).getUnsignedValue().toString(16));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -448,6 +448,7 @@ public class DebuggerTraceManagerServiceTest extends AbstractGhidraHeadedDebugge
|
|||
|
||||
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
|
||||
createTargetTraceMapper(mb.testProcess1), ActionSource.AUTOMATIC);
|
||||
waitRecorder(recorder);
|
||||
Trace trace = recorder.getTrace();
|
||||
|
||||
traceManager.openTrace(trace);
|
||||
|
@ -464,6 +465,7 @@ public class DebuggerTraceManagerServiceTest extends AbstractGhidraHeadedDebugge
|
|||
waitOn(mb.testModel.session.requestFocus(mb.testThread1));
|
||||
|
||||
TraceThread thread1 = recorder.getTraceThread(mb.testThread1);
|
||||
assertNotNull(thread1);
|
||||
waitForPass(() -> assertEquals(thread1, traceManager.getCurrentThread()));
|
||||
|
||||
TestTargetStack stack = mb.testThread1.addStack();
|
||||
|
|
|
@ -22,18 +22,25 @@ import java.util.Map;
|
|||
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
|
||||
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
|
||||
import ghidra.app.plugin.core.debug.mapping.DebuggerRegisterMapper;
|
||||
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerAccess;
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.app.services.ActionSource;
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.dbg.model.TestTargetRegisterBankInThread;
|
||||
import ghidra.pcode.exec.trace.DirectBytesTracePcodeExecutorState;
|
||||
import ghidra.pcode.utils.Utils;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
||||
public class TraceRecorderAsyncPcodeExecTest extends AbstractGhidraHeadedDebuggerGUITest {
|
||||
/**
|
||||
* Test the {@link DirectBytesTracePcodeExecutorState} in combination with
|
||||
* {@link PcodeDebuggerAccess} to ensure it read and writes the target when appropriate.
|
||||
*/
|
||||
public class TraceRecorderPcodeExecTest extends AbstractGhidraHeadedDebuggerGUITest {
|
||||
|
||||
@Test
|
||||
public void testExecutorEval() throws Throwable {
|
||||
|
@ -70,11 +77,11 @@ public class TraceRecorderAsyncPcodeExecTest extends AbstractGhidraHeadedDebugge
|
|||
assertTrue(rm.getRegistersOnTarget().contains(r1));
|
||||
});
|
||||
|
||||
AsyncPcodeExecutor<byte[]> executor =
|
||||
new AsyncPcodeExecutor<>(language, AsyncWrappedPcodeArithmetic.forLanguage(language),
|
||||
new TraceRecorderAsyncPcodeExecutorState(recorder, recorder.getSnap(), thread, 0));
|
||||
PcodeExecutor<byte[]> executor = DebuggerPcodeUtils.executorForCoordinates(tool,
|
||||
DebuggerCoordinates.NOWHERE.recorder(recorder).thread(thread));
|
||||
|
||||
byte[] result = waitOn(expr.evaluate(executor));
|
||||
// In practice, this should be backgrounded, but we're in a test thread
|
||||
byte[] result = expr.evaluate(executor);
|
||||
assertEquals(11, Utils.bytesToLong(result, result.length, language.isBigEndian()));
|
||||
}
|
||||
|
||||
|
@ -92,6 +99,7 @@ public class TraceRecorderAsyncPcodeExecTest extends AbstractGhidraHeadedDebugge
|
|||
|
||||
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
|
||||
createTargetTraceMapper(mb.testProcess1), ActionSource.AUTOMATIC);
|
||||
waitRecorder(recorder);
|
||||
|
||||
TraceThread thread = waitForValue(() -> recorder.getTraceThread(mb.testThread1));
|
||||
Trace trace = recorder.getTrace();
|
||||
|
@ -113,13 +121,12 @@ public class TraceRecorderAsyncPcodeExecTest extends AbstractGhidraHeadedDebugge
|
|||
assertTrue(rm.getRegistersOnTarget().contains(r1));
|
||||
});
|
||||
|
||||
TraceRecorderAsyncPcodeExecutorState asyncState =
|
||||
new TraceRecorderAsyncPcodeExecutorState(recorder, recorder.getSnap(), thread, 0);
|
||||
AsyncPcodeExecutor<byte[]> executor = new AsyncPcodeExecutor<>(
|
||||
language, AsyncWrappedPcodeArithmetic.forLanguage(language), asyncState);
|
||||
PcodeExecutor<byte[]> executor = DebuggerPcodeUtils.executorForCoordinates(tool,
|
||||
DebuggerCoordinates.NOWHERE.recorder(recorder).thread(thread));
|
||||
|
||||
waitOn(executor.executeAsync(prog, PcodeUseropLibrary.nil()));
|
||||
waitOn(asyncState.getVar(language.getRegister("r2")));
|
||||
executor.execute(prog, PcodeUseropLibrary.nil());
|
||||
// Ignore return value. We'll assert that it got written to the trace
|
||||
executor.state.getVar(language.getRegister("r2"));
|
||||
|
||||
assertEquals(BigInteger.valueOf(11), new BigInteger(1, regs.regVals.get("r2")));
|
||||
}
|
|
@ -21,8 +21,7 @@ import java.util.concurrent.CompletableFuture;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
import ghidra.dbg.agent.DefaultTargetObject;
|
||||
import ghidra.dbg.target.TargetAggregate;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.schema.DefaultTargetObjectSchema.DefaultAttributeSchema;
|
||||
import ghidra.dbg.util.*;
|
||||
import ghidra.dbg.util.CollectionUtils.Delta;
|
||||
|
@ -906,4 +905,82 @@ public interface TargetObjectSchema {
|
|||
schema.validateTypeAndInterfaces(element, parentPath, ent.getKey(), strict);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for a suitable register container
|
||||
*
|
||||
* <p>
|
||||
* This will try with and without considerations for frames. If the schema indicates that
|
||||
* register containers are not contained within frames, then frameLevel must be 0, otherwise
|
||||
* this will return empty. If dependent on frameLevel, this will return two singleton paths: one
|
||||
* for a decimal index and another for a hexadecimal index. If not, this will return a singleton
|
||||
* path. If it fails to find a unique container, this will return empty.
|
||||
*
|
||||
* <p>
|
||||
* <b>NOTE:</b> This must be used at the top of the search scope, probably the root schema. For
|
||||
* example, to search the entire model for a register container related to {@code myObject}:
|
||||
*
|
||||
* <pre>
|
||||
* for (PathPattern regPattern : myObject.getModel()
|
||||
* .getSchema()
|
||||
* .searchForRegisterContainer(0, myObject.getPath())) {
|
||||
* TargetObject objRegs = myObject.getModel().getModelObject(regPattern.getSingletonPath());
|
||||
* if (objRegs != null) {
|
||||
* // found it
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* This places some conventional restrictions / expectations on models where registers are given
|
||||
* on a frame-by-frame basis. The schema should present the {@link TargetRegisterContainer} as
|
||||
* the same object or a successor to {@link TargetStackFrame}, which must in turn be a successor
|
||||
* to {@link TargetThread}. The frame level (usually an index) must be in the path from thread
|
||||
* to stack frame. There can be no wild cards between the frame and the register container. For
|
||||
* example, the container for {@code Threads[1]} may be {@code Threads[1].Stack[n].Registers},
|
||||
* where {@code n} is the frame level. {@code Threads[1]} would have the {@link TargetThread}
|
||||
* interface, {@code Threads[1].Stack[0]} would have the {@link TargetStackFrame} interface, and
|
||||
* {@code Threads[1].Stack[0].Registers} would have the {@link TargetRegisterContainer}
|
||||
* interface. Note it is not sufficient for {@link TargetRegisterContainer} to be a successor of
|
||||
* {@link TargetThread} with a single index between. There <em>must</em> be an intervening
|
||||
* {@link TargetStackFrame}, and the frame level (index) must precede it.
|
||||
*
|
||||
* @param frameLevel the frameLevel, must be 0 if not applicable
|
||||
* @path the path of the seed object relative to the root
|
||||
* @return the predicates where the register container should be found, possibly empty
|
||||
*/
|
||||
default PathPredicates searchForRegisterContainer(int frameLevel, List<String> path) {
|
||||
List<String> simple = searchForSuitable(TargetRegisterContainer.class, path);
|
||||
if (simple != null) {
|
||||
return frameLevel == 0 ? PathPredicates.pattern(simple) : PathPredicates.EMPTY;
|
||||
}
|
||||
List<String> threadPath = searchForAncestor(TargetThread.class, path);
|
||||
if (threadPath == null) {
|
||||
return PathPredicates.EMPTY;
|
||||
}
|
||||
PathPattern framePatternRelThread =
|
||||
getSuccessorSchema(threadPath).searchFor(TargetStackFrame.class, false)
|
||||
.getSingletonPattern();
|
||||
if (framePatternRelThread == null) {
|
||||
return PathPredicates.EMPTY;
|
||||
}
|
||||
|
||||
if (framePatternRelThread.countWildcards() != 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PathMatcher result = new PathMatcher();
|
||||
for (String index : List.of(Integer.toString(frameLevel),
|
||||
"0x" + Integer.toHexString(frameLevel))) {
|
||||
List<String> framePathRelThread =
|
||||
framePatternRelThread.applyKeys(index).getSingletonPath();
|
||||
List<String> framePath = PathUtils.extend(threadPath, framePathRelThread);
|
||||
List<String> regsPath =
|
||||
searchForSuitable(TargetRegisterContainer.class, framePath);
|
||||
if (regsPath != null) {
|
||||
result.addPattern(regsPath);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -118,6 +118,11 @@ public class PathMatcher implements PathPredicates {
|
|||
return patterns.iterator().next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<PathPattern> getPatterns() {
|
||||
return patterns;
|
||||
}
|
||||
|
||||
protected void coalesceWilds(Set<String> result) {
|
||||
if (result.contains("")) {
|
||||
result.removeIf(PathUtils::isName);
|
||||
|
@ -179,10 +184,10 @@ public class PathMatcher implements PathPredicates {
|
|||
}
|
||||
|
||||
@Override
|
||||
public PathMatcher applyKeys(List<String> indices) {
|
||||
public PathMatcher applyKeys(Align align, List<String> indices) {
|
||||
PathMatcher result = new PathMatcher();
|
||||
for (PathPattern pat : patterns) {
|
||||
result.addPattern(pat.applyKeys(indices));
|
||||
result.addPattern(pat.applyKeys(align, indices));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -199,6 +199,11 @@ public class PathPattern implements PathPredicates {
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<PathPattern> getPatterns() {
|
||||
return List.of(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getNextKeys(List<String> path) {
|
||||
if (path.size() >= pattern.size()) {
|
||||
|
@ -257,22 +262,26 @@ public class PathPattern implements PathPredicates {
|
|||
}
|
||||
|
||||
@Override
|
||||
public PathPattern applyKeys(List<String> indices) {
|
||||
List<String> result = new ArrayList<>(pattern.size());
|
||||
Iterator<String> it = indices.iterator();
|
||||
for (String pat : pattern) {
|
||||
if (it.hasNext() && isWildcard(pat)) {
|
||||
String index = it.next();
|
||||
public PathPattern applyKeys(Align align, List<String> indices) {
|
||||
List<String> result = Arrays.asList(new String[pattern.size()]);
|
||||
ListIterator<String> iit = align.iterator(indices);
|
||||
ListIterator<String> pit = align.iterator(pattern);
|
||||
|
||||
while (pit.hasNext()) {
|
||||
int i = pit.nextIndex();
|
||||
String pat = pit.next();
|
||||
if (iit.hasNext() && isWildcard(pat)) {
|
||||
String index = iit.next();
|
||||
if (PathUtils.isIndex(pat)) {
|
||||
result.add(PathUtils.makeKey(index));
|
||||
result.set(i, PathUtils.makeKey(index));
|
||||
}
|
||||
else {
|
||||
// NB. Rare for attribute wildcards, but just in case
|
||||
result.add(index);
|
||||
result.set(i, index);
|
||||
}
|
||||
}
|
||||
else {
|
||||
result.add(pat);
|
||||
result.set(i, pat);
|
||||
}
|
||||
}
|
||||
return new PathPattern(result);
|
||||
|
|
|
@ -17,13 +17,108 @@ package ghidra.dbg.util;
|
|||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import ghidra.async.AsyncFence;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.util.PathUtils.PathComparator;
|
||||
import ghidra.util.ReversedListIterator;
|
||||
|
||||
public interface PathPredicates {
|
||||
PathPredicates EMPTY = new PathPredicates() {
|
||||
@Override
|
||||
public PathPredicates or(PathPredicates that) {
|
||||
return that;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(List<String> path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean successorCouldMatch(List<String> path, boolean strict) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean ancestorMatches(List<String> path, boolean strict) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean ancestorCouldMatchRight(List<String> path, boolean strict) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getNextKeys(List<String> path) {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getNextNames(List<String> path) {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getNextIndices(List<String> path) {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getPrevKeys(List<String> path) {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSingletonPath() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathPattern getSingletonPattern() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<PathPattern> getPatterns() {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathPredicates removeRight(int count) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathPredicates applyKeys(Align align, List<String> keys) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
enum Align {
|
||||
LEFT {
|
||||
@Override
|
||||
<T> ListIterator<T> iterator(List<T> list) {
|
||||
return list.listIterator();
|
||||
}
|
||||
},
|
||||
RIGHT {
|
||||
@Override
|
||||
<T> ListIterator<T> iterator(List<T> list) {
|
||||
return new ReversedListIterator<>(list.listIterator(list.size()));
|
||||
}
|
||||
};
|
||||
|
||||
abstract <T> ListIterator<T> iterator(List<T> list);
|
||||
}
|
||||
|
||||
static boolean keyMatches(String pat, String key) {
|
||||
if (key.equals(pat)) {
|
||||
|
@ -163,6 +258,13 @@ public interface PathPredicates {
|
|||
*/
|
||||
PathPattern getSingletonPattern();
|
||||
|
||||
/**
|
||||
* Get the patterns of this predicate
|
||||
*
|
||||
* @return the patterns
|
||||
*/
|
||||
Collection<PathPattern> getPatterns();
|
||||
|
||||
/**
|
||||
* Remove count elements from the right
|
||||
*
|
||||
|
@ -313,33 +415,44 @@ public interface PathPredicates {
|
|||
* Substitute wildcards from left to right for the given list of keys
|
||||
*
|
||||
* <p>
|
||||
* Takes each pattern and substitutes its wildcards for the given indices, starting from the
|
||||
* left and working right. This object is unmodified, and the result is returned.
|
||||
* Takes each pattern and substitutes its wildcards for the given indices, according to the
|
||||
* given alignment. This object is unmodified, and the result is returned.
|
||||
*
|
||||
* <p>
|
||||
* If there are fewer wildcards in a pattern than given, only the left-most keys are taken. If
|
||||
* there are fewer keys than wildcards in a pattern, then the right-most wildcards are left in
|
||||
* the resulting pattern.
|
||||
* If there are fewer wildcards in a pattern than given, only the first keys are taken. If there
|
||||
* are fewer keys than wildcards in a pattern, then the remaining wildcards are left in the
|
||||
* resulting pattern. In this manner, the left-most wildcards are substituted for the left-most
|
||||
* indices, or the right-most wildcards are substituted for the right-most indices, depending on
|
||||
* the alignment.
|
||||
*
|
||||
* @param align the end to align
|
||||
* @param keys the keys to substitute
|
||||
* @return the pattern or matcher with the applied substitutions
|
||||
*/
|
||||
PathPredicates applyKeys(List<String> keys);
|
||||
PathPredicates applyKeys(Align align, List<String> keys);
|
||||
|
||||
default PathPredicates applyKeys(Stream<String> keys) {
|
||||
return applyKeys(keys.collect(Collectors.toList()));
|
||||
default PathPredicates applyKeys(Align align, String... keys) {
|
||||
return applyKeys(align, List.of(keys));
|
||||
}
|
||||
|
||||
default PathPredicates applyKeys(String... keys) {
|
||||
return applyKeys(List.of(keys));
|
||||
return applyKeys(Align.LEFT, keys);
|
||||
}
|
||||
|
||||
default PathPredicates applyIntKeys(int radix, List<Integer> keys) {
|
||||
return applyKeys(keys.stream().map(k -> Integer.toString(k, radix)));
|
||||
default PathPredicates applyIntKeys(int radix, Align align, List<Integer> keys) {
|
||||
return applyKeys(align,
|
||||
keys.stream().map(k -> Integer.toString(k, radix)).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
default PathPredicates applyIntKeys(int radix, Align align, int... keys) {
|
||||
return applyKeys(align,
|
||||
IntStream.of(keys)
|
||||
.mapToObj(k -> Integer.toString(k, radix))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
default PathPredicates applyIntKeys(int... keys) {
|
||||
return applyKeys(IntStream.of(keys).mapToObj(k -> Integer.toString(k)));
|
||||
return applyIntKeys(10, Align.LEFT, keys);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,11 +18,9 @@ package ghidra.pcode.exec.trace;
|
|||
import com.google.common.collect.RangeSet;
|
||||
import com.google.common.primitives.UnsignedLong;
|
||||
|
||||
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
||||
/**
|
||||
* A state piece which can check for uninitialized reads
|
||||
|
@ -30,16 +28,16 @@ import ghidra.trace.model.thread.TraceThread;
|
|||
* <p>
|
||||
* Depending on the use case, it may be desirable to ensure all reads through the course of
|
||||
* emulation are from initialized parts of memory. For traces, there's an additional consideration
|
||||
* as to whether the values are present, but state. Again, depending on the use case, that may be
|
||||
* as to whether the values are present, but stale. Again, depending on the use case, that may be
|
||||
* acceptable. See the extensions of this class for "stock" implementations.
|
||||
*/
|
||||
public abstract class AbstractCheckedTraceCachedWriteBytesPcodeExecutorStatePiece
|
||||
extends BytesTracePcodeExecutorStatePiece {
|
||||
|
||||
protected class CheckedCachedSpace extends CachedSpace {
|
||||
public CheckedCachedSpace(Language language, AddressSpace space, TraceMemorySpace source,
|
||||
long snap) {
|
||||
super(language, space, source, snap);
|
||||
public CheckedCachedSpace(Language language, AddressSpace space,
|
||||
PcodeTraceDataAccess backing) {
|
||||
super(language, space, backing);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -54,31 +52,35 @@ public abstract class AbstractCheckedTraceCachedWriteBytesPcodeExecutorStatePiec
|
|||
}
|
||||
}
|
||||
|
||||
public AbstractCheckedTraceCachedWriteBytesPcodeExecutorStatePiece(Trace trace, long snap,
|
||||
TraceThread thread, int frame) {
|
||||
super(trace, snap, thread, frame);
|
||||
/**
|
||||
* Construct a piece
|
||||
*
|
||||
* @param data the trace-data access shim
|
||||
*/
|
||||
public AbstractCheckedTraceCachedWriteBytesPcodeExecutorStatePiece(PcodeTraceDataAccess data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractSpaceMap<CachedSpace> newSpaceMap() {
|
||||
return new TraceBackedSpaceMap() {
|
||||
@Override
|
||||
protected CachedSpace newSpace(AddressSpace space, TraceMemorySpace backing) {
|
||||
return new CheckedCachedSpace(language, space, backing, snap);
|
||||
protected CachedSpace newSpace(AddressSpace space, PcodeTraceDataAccess backing) {
|
||||
return new CheckedCachedSpace(language, space, backing);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide what to do, give that a portion of a read is uninitialized
|
||||
* Decide what to do, given that a portion of a read is uninitialized
|
||||
*
|
||||
* @param backing the object backing the address space that was read
|
||||
* @param backing the shim backing the address space that was read
|
||||
* @param start the starting address of the requested read
|
||||
* @param size the size of the requested read
|
||||
* @param uninitialized the portion of the read that is uninitialized
|
||||
* @return the adjusted size of the read
|
||||
* @throws Exception to interrupt the emulator
|
||||
*/
|
||||
protected abstract int checkUninitialized(TraceMemorySpace backing, Address start, int size,
|
||||
AddressSet uninitialized);
|
||||
protected abstract int checkUninitialized(PcodeTraceDataAccess backing, Address start,
|
||||
int size, AddressSet uninitialized);
|
||||
}
|
||||
|
|
|
@ -15,51 +15,54 @@
|
|||
*/
|
||||
package ghidra.pcode.exec.trace;
|
||||
|
||||
import ghidra.pcode.emu.PcodeEmulator;
|
||||
import ghidra.pcode.emu.PcodeThread;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.pcode.emu.*;
|
||||
import ghidra.pcode.exec.trace.data.*;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
|
||||
/**
|
||||
* An emulator that can read initial state from a trace and record its state back into it
|
||||
*/
|
||||
public class BytesTracePcodeEmulator extends PcodeEmulator implements TracePcodeMachine<byte[]> {
|
||||
protected final Trace trace;
|
||||
protected final long snap;
|
||||
protected final PcodeTraceAccess access;
|
||||
|
||||
/**
|
||||
* Create a trace-bound emulator
|
||||
*
|
||||
* @param trace the trace
|
||||
* @param snap the snap from which it lazily reads its state
|
||||
* @param access the trace access shim
|
||||
*/
|
||||
public BytesTracePcodeEmulator(Trace trace, long snap) {
|
||||
super(trace.getBaseLanguage());
|
||||
this.trace = trace;
|
||||
this.snap = snap;
|
||||
public BytesTracePcodeEmulator(PcodeTraceAccess access) {
|
||||
super(access.getLanguage());
|
||||
this.access = access;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a trace-bound emulator
|
||||
*
|
||||
* @param platform the platform to emulate
|
||||
* @param snap the source snap
|
||||
*/
|
||||
public BytesTracePcodeEmulator(TracePlatform platform, long snap) {
|
||||
this(new DefaultPcodeTraceAccess(platform, snap));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trace getTrace() {
|
||||
return trace;
|
||||
protected BytesPcodeThread createThread(String name) {
|
||||
BytesPcodeThread thread = super.createThread(name);
|
||||
access.getDataForLocalState(thread, 0).initializeThreadContext(thread);
|
||||
return thread;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSnap() {
|
||||
return snap;
|
||||
}
|
||||
|
||||
protected TracePcodeExecutorState<byte[]> newState(TraceThread thread) {
|
||||
return new BytesTracePcodeExecutorState(trace, snap, thread, 0);
|
||||
protected TracePcodeExecutorState<byte[]> newState(PcodeTraceDataAccess data) {
|
||||
return new BytesTracePcodeExecutorState(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TracePcodeExecutorState<byte[]> createSharedState() {
|
||||
return newState(null);
|
||||
return newState(access.getDataForSharedState());
|
||||
}
|
||||
|
||||
@Override
|
||||
public TracePcodeExecutorState<byte[]> createLocalState(PcodeThread<byte[]> thread) {
|
||||
return newState(getTraceThread(thread));
|
||||
return newState(access.getDataForLocalState(thread, 0));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,22 +15,18 @@
|
|||
*/
|
||||
package ghidra.pcode.exec.trace;
|
||||
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
|
||||
|
||||
/**
|
||||
* A state composing a single {@link BytesTracePcodeExecutorStatePiece}
|
||||
*/
|
||||
class BytesTracePcodeExecutorState extends DefaultTracePcodeExecutorState<byte[]> {
|
||||
public class BytesTracePcodeExecutorState extends DefaultTracePcodeExecutorState<byte[]> {
|
||||
/**
|
||||
* Create the state
|
||||
*
|
||||
* @param trace the trace from which bytes are loaded
|
||||
* @param snap the snap from which bytes are loaded
|
||||
* @param thread if applicable, the thread identifying the register space
|
||||
* @param frame if applicable, the frame identifying the register space
|
||||
* @param data the trace-data access shim
|
||||
*/
|
||||
public BytesTracePcodeExecutorState(Trace trace, long snap, TraceThread thread, int frame) {
|
||||
super(new BytesTracePcodeExecutorStatePiece(trace, snap, thread, frame));
|
||||
public BytesTracePcodeExecutorState(PcodeTraceDataAccess data) {
|
||||
super(new BytesTracePcodeExecutorStatePiece(data));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,17 +17,16 @@ package ghidra.pcode.exec.trace;
|
|||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import com.google.common.collect.*;
|
||||
import com.google.common.collect.Range;
|
||||
import com.google.common.collect.RangeSet;
|
||||
import com.google.common.primitives.UnsignedLong;
|
||||
|
||||
import ghidra.pcode.exec.AbstractBytesPcodeExecutorStatePiece;
|
||||
import ghidra.pcode.exec.BytesPcodeExecutorStateSpace;
|
||||
import ghidra.pcode.exec.trace.BytesTracePcodeExecutorStatePiece.CachedSpace;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.MathUtilities;
|
||||
|
||||
/**
|
||||
|
@ -42,35 +41,20 @@ public class BytesTracePcodeExecutorStatePiece
|
|||
extends AbstractBytesPcodeExecutorStatePiece<CachedSpace>
|
||||
implements TracePcodeExecutorStatePiece<byte[], byte[]> {
|
||||
|
||||
protected final Trace trace;
|
||||
protected final long snap;
|
||||
protected final TraceThread thread;
|
||||
protected final int frame;
|
||||
|
||||
public BytesTracePcodeExecutorStatePiece(Trace trace, long snap, TraceThread thread,
|
||||
int frame) {
|
||||
super(trace.getBaseLanguage());
|
||||
this.trace = trace;
|
||||
this.snap = snap;
|
||||
this.thread = thread;
|
||||
this.frame = frame;
|
||||
}
|
||||
|
||||
protected static class CachedSpace extends BytesPcodeExecutorStateSpace<TraceMemorySpace> {
|
||||
protected static class CachedSpace
|
||||
extends BytesPcodeExecutorStateSpace<PcodeTraceDataAccess> {
|
||||
protected final AddressSet written = new AddressSet();
|
||||
protected final long snap;
|
||||
|
||||
public CachedSpace(Language language, AddressSpace space, TraceMemorySpace backing,
|
||||
long snap) {
|
||||
public CachedSpace(Language language, AddressSpace space, PcodeTraceDataAccess backing) {
|
||||
// Backing could be null, so we need language parameter
|
||||
super(language, space, backing);
|
||||
this.snap = snap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(long offset, byte[] val, int srcOffset, int length) {
|
||||
super.write(offset, val, srcOffset, length);
|
||||
Address loc = space.getAddress(offset);
|
||||
Address end = loc.addWrap(length);
|
||||
Address end = loc.addWrap(length - 1);
|
||||
if (loc.compareTo(end) <= 0) {
|
||||
written.add(loc, end);
|
||||
}
|
||||
|
@ -91,7 +75,7 @@ public class BytesTracePcodeExecutorStatePiece
|
|||
long lower = lower(toRead);
|
||||
long upper = upper(toRead);
|
||||
ByteBuffer buf = ByteBuffer.allocate((int) (upper - lower + 1));
|
||||
backing.getBytes(snap, space.getAddress(lower), buf);
|
||||
backing.getBytes(space.getAddress(lower), buf);
|
||||
for (Range<UnsignedLong> rng : uninitialized.asRanges()) {
|
||||
long l = lower(rng);
|
||||
long u = upper(rng);
|
||||
|
@ -100,19 +84,17 @@ public class BytesTracePcodeExecutorStatePiece
|
|||
}
|
||||
}
|
||||
|
||||
protected void warnUnknown(AddressSet unknown) {
|
||||
protected void warnUnknown(AddressSetView unknown) {
|
||||
warnAddressSet("Emulator state initialized from UNKNOWN", unknown);
|
||||
}
|
||||
|
||||
// Must already have started a transaction
|
||||
protected void writeDown(Trace trace, long snap, TraceThread thread, int frame) {
|
||||
protected void writeDown(PcodeTraceDataAccess into) {
|
||||
if (space.isUniqueSpace()) {
|
||||
return;
|
||||
}
|
||||
byte[] data = new byte[4096];
|
||||
ByteBuffer buf = ByteBuffer.wrap(data);
|
||||
TraceMemorySpace mem =
|
||||
TraceSleighUtils.getSpaceForExecution(space, trace, thread, frame, true);
|
||||
for (AddressRange range : written) {
|
||||
long lower = range.getMinAddress().getOffset();
|
||||
long fullLen = range.getLength();
|
||||
|
@ -121,7 +103,7 @@ public class BytesTracePcodeExecutorStatePiece
|
|||
bytes.getData(lower, data, 0, len);
|
||||
buf.position(0);
|
||||
buf.limit(len);
|
||||
mem.putBytes(snap, space.getAddress(lower), buf);
|
||||
into.putBytes(space.getAddress(lower), buf);
|
||||
|
||||
lower += len;
|
||||
fullLen -= len;
|
||||
|
@ -130,64 +112,47 @@ public class BytesTracePcodeExecutorStatePiece
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the state's source trace
|
||||
*
|
||||
* @return the trace
|
||||
*/
|
||||
public Trace getTrace() {
|
||||
return trace;
|
||||
}
|
||||
protected final PcodeTraceDataAccess data;
|
||||
|
||||
/**
|
||||
* Get the source snap
|
||||
* Create a concrete state piece backed by a trace
|
||||
*
|
||||
* @return the snap
|
||||
* @param data the trace-data access shim
|
||||
*/
|
||||
public long getSnap() {
|
||||
return snap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the source thread, if a local state
|
||||
*
|
||||
* @return the thread
|
||||
*/
|
||||
public TraceThread getThread() {
|
||||
return thread;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the source frame, if a local state
|
||||
*
|
||||
* @return the frame, probably 0
|
||||
*/
|
||||
public int getFrame() {
|
||||
return frame;
|
||||
public BytesTracePcodeExecutorStatePiece(PcodeTraceDataAccess data) {
|
||||
super(data.getLanguage());
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDown(Trace trace, long snap, TraceThread thread, int frame) {
|
||||
if (trace.getBaseLanguage() != language) {
|
||||
throw new IllegalArgumentException("Destination trace must be same language as source");
|
||||
public PcodeTraceDataAccess getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDown(PcodeTraceDataAccess into) {
|
||||
if (into.getLanguage() != language) {
|
||||
throw new IllegalArgumentException(
|
||||
"Destination platform must be same language as source");
|
||||
}
|
||||
for (CachedSpace cached : spaceMap.values()) {
|
||||
cached.writeDown(trace, snap, thread, frame);
|
||||
cached.writeDown(into);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A space map which binds spaces to corresponding spaces in the trace
|
||||
*/
|
||||
protected class TraceBackedSpaceMap extends CacheingSpaceMap<TraceMemorySpace, CachedSpace> {
|
||||
protected class TraceBackedSpaceMap
|
||||
extends CacheingSpaceMap<PcodeTraceDataAccess, CachedSpace> {
|
||||
@Override
|
||||
protected TraceMemorySpace getBacking(AddressSpace space) {
|
||||
return TraceSleighUtils.getSpaceForExecution(space, trace, thread, frame, false);
|
||||
protected PcodeTraceDataAccess getBacking(AddressSpace space) {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CachedSpace newSpace(AddressSpace space, TraceMemorySpace backing) {
|
||||
return new CachedSpace(language, space, backing, snap);
|
||||
protected CachedSpace newSpace(AddressSpace space, PcodeTraceDataAccess backing) {
|
||||
return new CachedSpace(language, space, backing);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,8 +16,7 @@
|
|||
package ghidra.pcode.exec.trace;
|
||||
|
||||
import ghidra.pcode.exec.DefaultPcodeExecutorState;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
|
||||
|
||||
/**
|
||||
* An adapter that implements {@link TracePcodeExecutorState} given a
|
||||
|
@ -41,7 +40,12 @@ public class DefaultTracePcodeExecutorState<T> extends DefaultPcodeExecutorState
|
|||
}
|
||||
|
||||
@Override
|
||||
public void writeDown(Trace trace, long snap, TraceThread thread, int frame) {
|
||||
piece.writeDown(trace, snap, thread, frame);
|
||||
public PcodeTraceDataAccess getData() {
|
||||
return piece.getData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDown(PcodeTraceDataAccess into) {
|
||||
piece.writeDown(into);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,42 +17,60 @@ package ghidra.pcode.exec.trace;
|
|||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.pcode.exec.PairedPcodeExecutorState;
|
||||
import ghidra.pcode.exec.PcodeExecutorState;
|
||||
import ghidra.pcode.exec.trace.data.DefaultPcodeTraceAccess;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.memory.TraceMemoryState;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
||||
/**
|
||||
* A state composing a single {@link DirectBytesTracePcodeExecutorStatePiece}
|
||||
*
|
||||
* <p>
|
||||
* Note this does not implement {@link DefaultTracePcodeExecutorState} because it treats the trace
|
||||
* as if it were a stand-alone state. The interface expects implementations to lazily load into a
|
||||
* cache and write it back down later. This does not do that.
|
||||
*
|
||||
* @see TraceSleighUtils
|
||||
*/
|
||||
public class DirectBytesTracePcodeExecutorState extends DefaultPcodeExecutorState<byte[]> {
|
||||
private final Trace trace;
|
||||
private final long snap;
|
||||
private final TraceThread thread;
|
||||
private final int frame;
|
||||
public class DirectBytesTracePcodeExecutorState extends DefaultTracePcodeExecutorState<byte[]> {
|
||||
|
||||
/**
|
||||
* Get a trace-data access shim suitable for evaluating Sleigh expressions with thread context
|
||||
*
|
||||
* <p>
|
||||
* Do not use the returned shim for emulation, but only for one-off p-code execution, e.g.,
|
||||
* Sleigh expression evaluation.
|
||||
*
|
||||
* @param platform the platform whose language and address mappings to use
|
||||
* @param snap the source snap
|
||||
* @param thread the thread for register context
|
||||
* @param frame the frame for register context, 0 if not applicable
|
||||
* @return the trace-data access shim
|
||||
*/
|
||||
public static PcodeTraceDataAccess getDefaultThreadAccess(TracePlatform platform, long snap,
|
||||
TraceThread thread, int frame) {
|
||||
return new DefaultPcodeTraceAccess(platform, snap).getDataForThreadState(thread, frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the state
|
||||
*
|
||||
* @param trace the trace the executor will access
|
||||
* @param data the trace-data access shim
|
||||
*/
|
||||
public DirectBytesTracePcodeExecutorState(PcodeTraceDataAccess data) {
|
||||
super(new DirectBytesTracePcodeExecutorStatePiece(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the state
|
||||
*
|
||||
* @param platform the platform whose language and address mappings to use
|
||||
* @param snap the snap the executor will access
|
||||
* @param thread the thread for reading and writing registers
|
||||
* @param frame the frame for reading and writing registers
|
||||
*/
|
||||
public DirectBytesTracePcodeExecutorState(Trace trace, long snap, TraceThread thread,
|
||||
public DirectBytesTracePcodeExecutorState(TracePlatform platform, long snap, TraceThread thread,
|
||||
int frame) {
|
||||
super(new DirectBytesTracePcodeExecutorStatePiece(trace, snap, thread, frame));
|
||||
this.trace = trace;
|
||||
this.snap = snap;
|
||||
this.thread = thread;
|
||||
this.frame = frame;
|
||||
this(getDefaultThreadAccess(platform, snap, thread, frame));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -63,6 +81,6 @@ public class DirectBytesTracePcodeExecutorState extends DefaultPcodeExecutorStat
|
|||
*/
|
||||
public PcodeExecutorState<Pair<byte[], TraceMemoryState>> withMemoryState() {
|
||||
return new PairedPcodeExecutorState<>(this,
|
||||
new TraceMemoryStatePcodeExecutorStatePiece(trace, snap, thread, frame));
|
||||
new TraceMemoryStatePcodeExecutorStatePiece(getData()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,20 +17,18 @@ package ghidra.pcode.exec.trace;
|
|||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import javax.help.UnsupportedOperationException;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import ghidra.generic.util.datastruct.SemisparseByteArray;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
import ghidra.trace.model.memory.TraceMemoryState;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.DefaultTraceTimeViewport;
|
||||
|
||||
/**
|
||||
* An executor state piece that operates directly on trace memory and registers
|
||||
|
@ -45,37 +43,37 @@ import ghidra.trace.util.DefaultTraceTimeViewport;
|
|||
* @see TraceSleighUtils
|
||||
*/
|
||||
public class DirectBytesTracePcodeExecutorStatePiece
|
||||
extends AbstractLongOffsetPcodeExecutorStatePiece<byte[], byte[], TraceMemorySpace> {
|
||||
extends AbstractLongOffsetPcodeExecutorStatePiece<byte[], byte[], AddressSpace>
|
||||
implements TracePcodeExecutorStatePiece<byte[], byte[]> {
|
||||
|
||||
protected final PcodeTraceDataAccess data;
|
||||
|
||||
protected final SemisparseByteArray unique = new SemisparseByteArray();
|
||||
private final Trace trace;
|
||||
private long snap;
|
||||
private TraceThread thread;
|
||||
private int frame;
|
||||
|
||||
private final DefaultTraceTimeViewport viewport;
|
||||
|
||||
protected DirectBytesTracePcodeExecutorStatePiece(Language language,
|
||||
PcodeArithmetic<byte[]> arithmetic, Trace trace, long snap, TraceThread thread,
|
||||
int frame) {
|
||||
super(language, arithmetic, arithmetic);
|
||||
this.trace = trace;
|
||||
this.snap = snap;
|
||||
this.thread = thread;
|
||||
this.frame = frame;
|
||||
|
||||
this.viewport = new DefaultTraceTimeViewport(trace);
|
||||
this.viewport.setSnap(snap);
|
||||
/**
|
||||
* Construct a piece
|
||||
*
|
||||
* @param arithmetic the arithmetic for byte arrays
|
||||
* @param data the trace-data access shim
|
||||
*/
|
||||
protected DirectBytesTracePcodeExecutorStatePiece(PcodeArithmetic<byte[]> arithmetic,
|
||||
PcodeTraceDataAccess data) {
|
||||
super(data.getLanguage(), arithmetic, arithmetic);
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
protected DirectBytesTracePcodeExecutorStatePiece(Language language, Trace trace, long snap,
|
||||
TraceThread thread, int frame) {
|
||||
this(language, BytesPcodeArithmetic.forLanguage(language), trace, snap, thread, frame);
|
||||
/**
|
||||
* Construct a piece
|
||||
*
|
||||
* @param data the trace-data access shim
|
||||
*/
|
||||
protected DirectBytesTracePcodeExecutorStatePiece(PcodeTraceDataAccess data) {
|
||||
this(BytesPcodeArithmetic.forLanguage(data.getLanguage()), data);
|
||||
}
|
||||
|
||||
public DirectBytesTracePcodeExecutorStatePiece(Trace trace, long snap, TraceThread thread,
|
||||
int frame) {
|
||||
this(trace.getBaseLanguage(), trace, snap, thread, frame);
|
||||
@Override
|
||||
public PcodeTraceDataAccess getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -91,74 +89,7 @@ public class DirectBytesTracePcodeExecutorStatePiece
|
|||
*/
|
||||
public PcodeExecutorStatePiece<byte[], Pair<byte[], TraceMemoryState>> withMemoryState() {
|
||||
return new PairedPcodeExecutorStatePiece<>(this,
|
||||
new TraceMemoryStatePcodeExecutorStatePiece(trace, snap, thread, frame));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the trace
|
||||
*
|
||||
* @return the trace
|
||||
*/
|
||||
public Trace getTrace() {
|
||||
return trace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-bind this state to another snap
|
||||
*
|
||||
* @param snap the new snap
|
||||
*/
|
||||
public void setSnap(long snap) {
|
||||
this.snap = snap;
|
||||
this.viewport.setSnap(snap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current snap
|
||||
*
|
||||
* @return the snap
|
||||
*/
|
||||
public long getSnap() {
|
||||
return snap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-bind this state to another thread
|
||||
*
|
||||
* @param thread the new thread
|
||||
*/
|
||||
public void setThread(TraceThread thread) {
|
||||
if (thread != null & thread.getTrace() != trace) {
|
||||
throw new IllegalArgumentException("Thread, if given, must be part of the same trace");
|
||||
}
|
||||
this.thread = thread;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current thread
|
||||
*
|
||||
* @return the thread
|
||||
*/
|
||||
public TraceThread getThread() {
|
||||
return thread;
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-bind this state to another frame
|
||||
*
|
||||
* @param frame the new frame
|
||||
*/
|
||||
public void setFrame(int frame) {
|
||||
this.frame = frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current frame
|
||||
*
|
||||
* @return the frame
|
||||
*/
|
||||
public int getFrame() {
|
||||
return frame;
|
||||
new TraceMemoryStatePcodeExecutorStatePiece(data));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -175,24 +106,23 @@ public class DirectBytesTracePcodeExecutorStatePiece
|
|||
}
|
||||
|
||||
@Override
|
||||
protected TraceMemorySpace getForSpace(AddressSpace space, boolean toWrite) {
|
||||
return TraceSleighUtils.getSpaceForExecution(space, trace, thread, frame, toWrite);
|
||||
protected AddressSpace getForSpace(AddressSpace space, boolean toWrite) {
|
||||
return space;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setInSpace(TraceMemorySpace space, long offset, int size, byte[] val) {
|
||||
protected void setInSpace(AddressSpace space, long offset, int size, byte[] val) {
|
||||
assert size == val.length;
|
||||
int wrote =
|
||||
space.putBytes(snap, space.getAddressSpace().getAddress(offset), ByteBuffer.wrap(val));
|
||||
int wrote = data.putBytes(space.getAddress(offset), ByteBuffer.wrap(val));
|
||||
if (wrote != size) {
|
||||
throw new RuntimeException("Could not write full value to trace");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] getFromSpace(TraceMemorySpace space, long offset, int size) {
|
||||
protected byte[] getFromSpace(AddressSpace space, long offset, int size) {
|
||||
ByteBuffer buf = ByteBuffer.allocate(size);
|
||||
int read = space.getViewBytes(snap, space.getAddressSpace().getAddress(offset), buf);
|
||||
int read = data.getBytes(space.getAddress(offset), buf);
|
||||
if (read != size) {
|
||||
throw new RuntimeException("Could not read full value from trace");
|
||||
}
|
||||
|
@ -201,6 +131,11 @@ public class DirectBytesTracePcodeExecutorStatePiece
|
|||
|
||||
@Override
|
||||
public MemBuffer getConcreteBuffer(Address address, Purpose purpose) {
|
||||
return trace.getMemoryManager().getBufferAt(snap, address);
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDown(PcodeTraceDataAccess into) {
|
||||
// Writes directly, so just ignore
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,8 +18,7 @@ package ghidra.pcode.exec.trace;
|
|||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import ghidra.pcode.exec.PairedPcodeExecutorState;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
|
||||
|
||||
/**
|
||||
* A trace-bound state composed of another trace-bound state and a piece
|
||||
|
@ -48,8 +47,13 @@ public class PairedTracePcodeExecutorState<L, R> extends PairedPcodeExecutorStat
|
|||
}
|
||||
|
||||
@Override
|
||||
public void writeDown(Trace trace, long snap, TraceThread thread, int frame) {
|
||||
left.writeDown(trace, snap, thread, frame);
|
||||
right.writeDown(trace, snap, thread, frame);
|
||||
public PcodeTraceDataAccess getData() {
|
||||
return left.getData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDown(PcodeTraceDataAccess into) {
|
||||
left.writeDown(into);
|
||||
right.writeDown(into);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,8 +19,7 @@ import org.apache.commons.lang3.tuple.Pair;
|
|||
|
||||
import ghidra.pcode.exec.PairedPcodeExecutorStatePiece;
|
||||
import ghidra.pcode.exec.PcodeArithmetic;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
|
||||
|
||||
/**
|
||||
* A trace-bound state piece composed of two other trace-bound pieces sharing the same address type
|
||||
|
@ -53,9 +52,14 @@ public class PairedTracePcodeExecutorStatePiece<A, L, R>
|
|||
}
|
||||
|
||||
@Override
|
||||
public void writeDown(Trace trace, long snap, TraceThread thread, int frame) {
|
||||
left.writeDown(trace, snap, thread, frame);
|
||||
right.writeDown(trace, snap, thread, frame);
|
||||
public PcodeTraceDataAccess getData() {
|
||||
return left.getData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDown(PcodeTraceDataAccess into) {
|
||||
left.writeDown(into);
|
||||
right.writeDown(into);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,8 +15,7 @@
|
|||
*/
|
||||
package ghidra.pcode.exec.trace;
|
||||
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
|
||||
|
||||
/**
|
||||
* A state composing a single {@link RequireHasKnownTraceCachedWriteBytesPcodeExecutorStatePiece}
|
||||
|
@ -27,14 +26,9 @@ public class RequireHasKnownTraceCachedWriteBytesPcodeExecutorState
|
|||
/**
|
||||
* Create the state
|
||||
*
|
||||
* @param trace the trace from which to load state
|
||||
* @param snap the snap from which to load state
|
||||
* @param thread if applicable, the thread identifying the register space
|
||||
* @param frame if applicable, the frame identifying the register space
|
||||
* @param data the trace-data access shim
|
||||
*/
|
||||
public RequireHasKnownTraceCachedWriteBytesPcodeExecutorState(Trace trace, long snap,
|
||||
TraceThread thread, int frame) {
|
||||
super(new RequireHasKnownTraceCachedWriteBytesPcodeExecutorStatePiece(trace, snap, thread,
|
||||
frame));
|
||||
public RequireHasKnownTraceCachedWriteBytesPcodeExecutorState(PcodeTraceDataAccess data) {
|
||||
super(new RequireHasKnownTraceCachedWriteBytesPcodeExecutorStatePiece(data));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,14 +15,10 @@
|
|||
*/
|
||||
package ghidra.pcode.exec.trace;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.pcode.exec.AccessPcodeExecutionException;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
import ghidra.trace.model.memory.TraceMemoryState;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
||||
/**
|
||||
* A relaxation of {@link RequireIsKnownTraceCachedWriteBytesPcodeExecutorStatePiece} that permits
|
||||
|
@ -35,15 +31,18 @@ import ghidra.trace.model.thread.TraceThread;
|
|||
public class RequireHasKnownTraceCachedWriteBytesPcodeExecutorStatePiece
|
||||
extends RequireIsKnownTraceCachedWriteBytesPcodeExecutorStatePiece {
|
||||
|
||||
public RequireHasKnownTraceCachedWriteBytesPcodeExecutorStatePiece(Trace trace, long snap,
|
||||
TraceThread thread, int frame) {
|
||||
super(trace, snap, thread, frame);
|
||||
/**
|
||||
* Construct a piece
|
||||
*
|
||||
* @param data the trace-data access shim
|
||||
*/
|
||||
public RequireHasKnownTraceCachedWriteBytesPcodeExecutorStatePiece(PcodeTraceDataAccess data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AddressSetView getKnown(TraceMemorySpace source) {
|
||||
return source.getAddressesWithState(Range.closed(0L, snap),
|
||||
s -> s == TraceMemoryState.KNOWN);
|
||||
protected AddressSetView getKnown(PcodeTraceDataAccess backing) {
|
||||
return backing.getKnownBefore();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,8 +15,7 @@
|
|||
*/
|
||||
package ghidra.pcode.exec.trace;
|
||||
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
|
||||
|
||||
/**
|
||||
* A state composing a single {@link RequireIsKnownTraceCachedWriteBytesPcodeExecutorState}
|
||||
|
@ -27,14 +26,9 @@ public class RequireIsKnownTraceCachedWriteBytesPcodeExecutorState
|
|||
/**
|
||||
* Create the state
|
||||
*
|
||||
* @param trace the trace from which to load state
|
||||
* @param snap the snap from which to load state
|
||||
* @param thread if applicable, the thread identifying the register space
|
||||
* @param frame if applicable, the frame identifying the register space
|
||||
* @param data the trace-data access shim
|
||||
*/
|
||||
public RequireIsKnownTraceCachedWriteBytesPcodeExecutorState(Trace trace, long snap,
|
||||
TraceThread thread, int frame) {
|
||||
super(new RequireIsKnownTraceCachedWriteBytesPcodeExecutorStatePiece(trace, snap, thread,
|
||||
frame));
|
||||
public RequireIsKnownTraceCachedWriteBytesPcodeExecutorState(PcodeTraceDataAccess data) {
|
||||
super(new RequireIsKnownTraceCachedWriteBytesPcodeExecutorStatePiece(data));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,11 +16,9 @@
|
|||
package ghidra.pcode.exec.trace;
|
||||
|
||||
import ghidra.pcode.exec.AccessPcodeExecutionException;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
import ghidra.trace.model.memory.TraceMemoryState;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
||||
/**
|
||||
* A space which requires reads to be completely {@link TraceMemorySpace#KNOWN} memory.
|
||||
|
@ -32,13 +30,17 @@ import ghidra.trace.model.thread.TraceThread;
|
|||
public class RequireIsKnownTraceCachedWriteBytesPcodeExecutorStatePiece
|
||||
extends AbstractCheckedTraceCachedWriteBytesPcodeExecutorStatePiece {
|
||||
|
||||
public RequireIsKnownTraceCachedWriteBytesPcodeExecutorStatePiece(Trace trace, long snap,
|
||||
TraceThread thread, int frame) {
|
||||
super(trace, snap, thread, frame);
|
||||
public RequireIsKnownTraceCachedWriteBytesPcodeExecutorStatePiece(PcodeTraceDataAccess data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
protected AddressSetView getKnown(TraceMemorySpace source) {
|
||||
return source.getAddressesWithState(snap, s -> s == TraceMemoryState.KNOWN);
|
||||
/**
|
||||
* Construct a piece
|
||||
*
|
||||
* @param data the trace-data access shim
|
||||
*/
|
||||
protected AddressSetView getKnown(PcodeTraceDataAccess backing) {
|
||||
return backing.getKnownNow();
|
||||
}
|
||||
|
||||
protected AccessPcodeExecutionException excFor(AddressSetView unknown) {
|
||||
|
@ -46,7 +48,7 @@ public class RequireIsKnownTraceCachedWriteBytesPcodeExecutorStatePiece
|
|||
}
|
||||
|
||||
@Override
|
||||
protected int checkUninitialized(TraceMemorySpace backing, Address start, int size,
|
||||
protected int checkUninitialized(PcodeTraceDataAccess backing, Address start, int size,
|
||||
AddressSet uninitialized) {
|
||||
if (backing == null) {
|
||||
if (!uninitialized.contains(start)) {
|
||||
|
|
|
@ -22,13 +22,11 @@ import com.google.common.primitives.UnsignedLong;
|
|||
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
import ghidra.trace.model.memory.TraceMemoryState;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.DefaultTraceTimeViewport;
|
||||
|
||||
/**
|
||||
* The p-code execute state piece for {@link TraceMemoryState}
|
||||
|
@ -43,60 +41,21 @@ import ghidra.trace.util.DefaultTraceTimeViewport;
|
|||
* though it's also exemplified in {@link #getFromSpace(TraceMemorySpace, long, int)}.
|
||||
*/
|
||||
public class TraceMemoryStatePcodeExecutorStatePiece extends
|
||||
AbstractLongOffsetPcodeExecutorStatePiece<byte[], TraceMemoryState, TraceMemorySpace> {
|
||||
AbstractLongOffsetPcodeExecutorStatePiece<byte[], TraceMemoryState, AddressSpace> {
|
||||
|
||||
private final RangeMap<UnsignedLong, TraceMemoryState> unique = TreeRangeMap.create();
|
||||
private final Trace trace;
|
||||
private long snap;
|
||||
private TraceThread thread;
|
||||
private int frame;
|
||||
protected final RangeMap<UnsignedLong, TraceMemoryState> unique = TreeRangeMap.create();
|
||||
protected final PcodeTraceDataAccess data;
|
||||
|
||||
private final DefaultTraceTimeViewport viewport;
|
||||
|
||||
public TraceMemoryStatePcodeExecutorStatePiece(Trace trace, long snap, TraceThread thread,
|
||||
int frame) {
|
||||
super(trace.getBaseLanguage(),
|
||||
BytesPcodeArithmetic.forLanguage(trace.getBaseLanguage()),
|
||||
/**
|
||||
* Construct a piece
|
||||
*
|
||||
* @param data the trace-data access shim
|
||||
*/
|
||||
public TraceMemoryStatePcodeExecutorStatePiece(PcodeTraceDataAccess data) {
|
||||
super(data.getLanguage(),
|
||||
BytesPcodeArithmetic.forLanguage(data.getLanguage()),
|
||||
TraceMemoryStatePcodeArithmetic.INSTANCE);
|
||||
this.trace = trace;
|
||||
this.snap = snap;
|
||||
this.thread = thread;
|
||||
this.frame = frame;
|
||||
|
||||
this.viewport = new DefaultTraceTimeViewport(trace);
|
||||
this.viewport.setSnap(snap);
|
||||
}
|
||||
|
||||
public Trace getTrace() {
|
||||
return trace;
|
||||
}
|
||||
|
||||
public void setSnap(long snap) {
|
||||
this.snap = snap;
|
||||
this.viewport.setSnap(snap);
|
||||
}
|
||||
|
||||
public long getSnap() {
|
||||
return snap;
|
||||
}
|
||||
|
||||
public void setThread(TraceThread thread) {
|
||||
if (thread != null & thread.getTrace() != trace) {
|
||||
throw new IllegalArgumentException("Thread, if given, must be part of the same trace");
|
||||
}
|
||||
this.thread = thread;
|
||||
}
|
||||
|
||||
public TraceThread getThread() {
|
||||
return thread;
|
||||
}
|
||||
|
||||
public void setFrame(int frame) {
|
||||
this.frame = frame;
|
||||
}
|
||||
|
||||
public int getFrame() {
|
||||
return frame;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
protected Range<UnsignedLong> range(long offset, int size) {
|
||||
|
@ -136,24 +95,19 @@ public class TraceMemoryStatePcodeExecutorStatePiece extends
|
|||
}
|
||||
|
||||
@Override
|
||||
protected TraceMemorySpace getForSpace(AddressSpace space, boolean toWrite) {
|
||||
return TraceSleighUtils.getSpaceForExecution(space, trace, thread, frame, toWrite);
|
||||
protected AddressSpace getForSpace(AddressSpace space, boolean toWrite) {
|
||||
return space;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setInSpace(TraceMemorySpace space, long offset, int size, TraceMemoryState val) {
|
||||
protected void setInSpace(AddressSpace space, long offset, int size, TraceMemoryState val) {
|
||||
// NB. Will ensure writes with unknown state are still marked unknown
|
||||
space.setState(size, space.getAddressSpace().getAddress(offset), val);
|
||||
data.setState(range(space, offset, size), val);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceMemoryState getFromSpace(TraceMemorySpace space, long offset, int size) {
|
||||
AddressSet set = new AddressSet(range(space.getAddressSpace(), offset, size));
|
||||
for (long snap : viewport.getOrderedSnaps()) {
|
||||
set.delete(
|
||||
space.getAddressesWithState(snap, set, state -> state == TraceMemoryState.KNOWN));
|
||||
}
|
||||
return set.isEmpty() ? TraceMemoryState.KNOWN : TraceMemoryState.UNKNOWN;
|
||||
protected TraceMemoryState getFromSpace(AddressSpace space, long offset, int size) {
|
||||
return data.getViewportState(range(space, offset, size));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,15 +16,14 @@
|
|||
package ghidra.pcode.exec.trace;
|
||||
|
||||
import ghidra.pcode.exec.PcodeExecutorState;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
|
||||
|
||||
/**
|
||||
* An interface for trace-bound states
|
||||
*
|
||||
* <p>
|
||||
* In particular, because this derives from {@link TracePcodeExecutorStatePiece}, such states are
|
||||
* required to implement {@link #writeDown(Trace, long, TraceThread, int)}. This interface also
|
||||
* required to implement {@link #writeDown(PcodeTraceDataAccess)}. This interface also
|
||||
* derives from {@link PcodeExecutorState} so that, as the name implies, they can be used where a
|
||||
* state is required.
|
||||
*
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
package ghidra.pcode.exec.trace;
|
||||
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTraceAccess;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
|
||||
|
||||
/**
|
||||
* A state piece which knows how to write its values back into a trace
|
||||
|
@ -26,6 +26,18 @@ import ghidra.trace.model.thread.TraceThread;
|
|||
* @param <T> the type of values
|
||||
*/
|
||||
public interface TracePcodeExecutorStatePiece<A, T> extends PcodeExecutorStatePiece<A, T> {
|
||||
|
||||
/**
|
||||
* Get the state's trace-data access shim
|
||||
*
|
||||
* <p>
|
||||
* This method is meant for auxiliary state pieces, so that it can access the same trace data as
|
||||
* this piece.
|
||||
*
|
||||
* @return the trace-data access shim
|
||||
*/
|
||||
PcodeTraceDataAccess getData();
|
||||
|
||||
/**
|
||||
* Write the accumulated values (cache) into the given trace
|
||||
*
|
||||
|
@ -33,11 +45,8 @@ public interface TracePcodeExecutorStatePiece<A, T> extends PcodeExecutorStatePi
|
|||
* <b>NOTE:</b> This method requires a transaction to have already been started on the
|
||||
* destination trace.
|
||||
*
|
||||
* @param trace the trace to modify
|
||||
* @param snap the snap within the trace
|
||||
* @param thread the thread to take register writes
|
||||
* @param frame the frame for register writes
|
||||
* @see TracePcodeMachine#writeDown(Trace, long, long)
|
||||
* @param into the destination data-access shim
|
||||
* @see TracePcodeMachine#writeDown(PcodeTraceAccess)
|
||||
*/
|
||||
void writeDown(Trace trace, long snap, TraceThread thread, int frame);
|
||||
void writeDown(PcodeTraceDataAccess into);
|
||||
}
|
||||
|
|
|
@ -15,51 +15,17 @@
|
|||
*/
|
||||
package ghidra.pcode.exec.trace;
|
||||
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.pcode.emu.PcodeMachine;
|
||||
import ghidra.pcode.emu.PcodeThread;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
import ghidra.trace.model.memory.TraceMemoryState;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.model.thread.TraceThreadManager;
|
||||
import ghidra.pcode.exec.trace.data.*;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
|
||||
/**
|
||||
* A p-code machine which sources its state from a trace and can record back into it
|
||||
*
|
||||
* <p>
|
||||
* This is a "mix in" interface. It is part of the SPI, but not the API. That is, emulator
|
||||
* developers should use this interface, but emulator clients should not. Clients should use
|
||||
* {@link PcodeMachine} instead.
|
||||
*
|
||||
* @param <T> the type of values manipulated by the machine
|
||||
*/
|
||||
public interface TracePcodeMachine<T> extends PcodeMachine<T> {
|
||||
/**
|
||||
* Get the trace from which this emulator reads its initial state
|
||||
*
|
||||
* @return the trace
|
||||
*/
|
||||
Trace getTrace();
|
||||
|
||||
/**
|
||||
* Get the snapshot from which this emulator reads its initial state
|
||||
*
|
||||
* @return the snapshot key
|
||||
*/
|
||||
long getSnap();
|
||||
|
||||
/**
|
||||
* Get the trace thread corresponding to the given p-code thread
|
||||
*
|
||||
* @param thread the p-code thread
|
||||
* @return the trace thread
|
||||
*/
|
||||
default TraceThread getTraceThread(PcodeThread<T> thread) {
|
||||
return getTrace().getThreadManager().getLiveThreadByPath(getSnap(), thread.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a shared state
|
||||
|
@ -77,44 +43,7 @@ public interface TracePcodeMachine<T> extends PcodeMachine<T> {
|
|||
TracePcodeExecutorState<T> createLocalState(PcodeThread<T> thread);
|
||||
|
||||
/**
|
||||
* Check if a register has a {@link TraceMemoryState#KNOWN} value for the given thread
|
||||
*
|
||||
* @param thread the thread
|
||||
* @param register the register
|
||||
* @return true if known
|
||||
*/
|
||||
default boolean isRegisterKnown(PcodeThread<T> thread, Register register) {
|
||||
Trace trace = getTrace();
|
||||
long snap = getSnap();
|
||||
TraceThread traceThread =
|
||||
trace.getThreadManager().getLiveThreadByPath(snap, thread.getName());
|
||||
TraceMemorySpace space =
|
||||
trace.getMemoryManager().getMemoryRegisterSpace(traceThread, false);
|
||||
if (space == null) {
|
||||
return false;
|
||||
}
|
||||
return space.getState(snap, register) == TraceMemoryState.KNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the given thread using context from the trace at its program counter
|
||||
*
|
||||
* @param thread the thread to initialize
|
||||
*/
|
||||
default void initializeThreadContext(PcodeThread<T> thread) {
|
||||
SleighLanguage language = getLanguage();
|
||||
Register contextreg = language.getContextBaseRegister();
|
||||
if (contextreg != Register.NO_CONTEXT && !isRegisterKnown(thread, contextreg)) {
|
||||
RegisterValue context = getTrace().getRegisterContextManager()
|
||||
.getValueWithDefault(language, contextreg, getSnap(), thread.getCounter());
|
||||
if (context != null) { // TODO: Why does this happen?
|
||||
thread.overrideContext(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the accumulated emulator state into the given trace at the given snap
|
||||
* Write the accumulated emulator state via the given trace access shim
|
||||
*
|
||||
* <p>
|
||||
* <b>NOTE:</b> This method requires a transaction to have already been started on the
|
||||
|
@ -122,26 +51,32 @@ public interface TracePcodeMachine<T> extends PcodeMachine<T> {
|
|||
* threadsSnap. When using scratch space, threadsSnap should be the source snap. If populating a
|
||||
* new trace, threadsSnap should probably be the destination snap.
|
||||
*
|
||||
* @param trace the trace to modify
|
||||
* @param into the destination trace-data access shim
|
||||
*/
|
||||
default void writeDown(PcodeTraceAccess into) {
|
||||
TracePcodeExecutorState<T> sharedState = (TracePcodeExecutorState<T>) getSharedState();
|
||||
sharedState.writeDown(into.getDataForSharedState());
|
||||
for (PcodeThread<T> emuThread : getAllThreads()) {
|
||||
PcodeTraceDataAccess localInto = into.getDataForLocalState(emuThread, 0);
|
||||
if (localInto == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Given trace does not have thread with name/path '" + emuThread.getName() +
|
||||
"' at source snap");
|
||||
}
|
||||
TracePcodeExecutorState<T> localState =
|
||||
(TracePcodeExecutorState<T>) emuThread.getState().getLocalState();
|
||||
localState.writeDown(localInto);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #writeDown(PcodeTraceAccess)
|
||||
* @param platform the platform whose trace to modify
|
||||
* @param destSnap the destination snap within the trace
|
||||
* @param threadsSnap the snap at which to find corresponding threads, usually the same as
|
||||
* {@link #getSnap()}
|
||||
*/
|
||||
default void writeDown(Trace trace, long destSnap, long threadsSnap) {
|
||||
TracePcodeExecutorState<T> ss = (TracePcodeExecutorState<T>) getSharedState();
|
||||
ss.writeDown(trace, destSnap, null, 0);
|
||||
TraceThreadManager threadManager = trace.getThreadManager();
|
||||
for (PcodeThread<T> emuThread : getAllThreads()) {
|
||||
TracePcodeExecutorState<T> ls =
|
||||
(TracePcodeExecutorState<T>) emuThread.getState().getLocalState();
|
||||
TraceThread traceThread =
|
||||
threadManager.getLiveThreadByPath(threadsSnap, emuThread.getName());
|
||||
if (traceThread == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Given trace does not have thread with name/path '" + emuThread.getName() +
|
||||
"' at snap " + destSnap);
|
||||
}
|
||||
ls.writeDown(trace, destSnap, traceThread, 0);
|
||||
}
|
||||
default void writeDown(TracePlatform platform, long destSnap, long threadsSnap) {
|
||||
writeDown(new DefaultPcodeTraceAccess(platform, destSnap, threadsSnap));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ import ghidra.program.model.address.AddressRange;
|
|||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.memory.TraceMemoryState;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
||||
|
@ -38,32 +38,6 @@ import ghidra.trace.model.thread.TraceThread;
|
|||
public enum TraceSleighUtils {
|
||||
;
|
||||
|
||||
/**
|
||||
* Get the trace memory space for the given "coordinates"
|
||||
*
|
||||
* <p>
|
||||
* This is used to find "backing" objects for a p-code executor state bound to a trace, whether
|
||||
* direct or cached.
|
||||
*
|
||||
* @param space the address space
|
||||
* @param trace the trace
|
||||
* @param thread the thread, if a register space
|
||||
* @param frame the frame, if a register space
|
||||
* @param toWrite true if the state intends to write to the space, i.e., the space must exist
|
||||
* @return the space, or null if it doesn't exist
|
||||
*/
|
||||
public static TraceMemorySpace getSpaceForExecution(AddressSpace space, Trace trace,
|
||||
TraceThread thread, int frame, boolean toWrite) {
|
||||
if (space.isRegisterSpace()) {
|
||||
if (thread == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot access register unless a thread is given.");
|
||||
}
|
||||
return trace.getMemoryManager().getMemoryRegisterSpace(thread, frame, toWrite);
|
||||
}
|
||||
return trace.getMemoryManager().getMemorySpace(space, toWrite);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a p-code executor that operates directly on bytes of the given trace
|
||||
*
|
||||
|
@ -72,7 +46,27 @@ public enum TraceSleighUtils {
|
|||
* for manipulating or initializing variables using Sleigh code. It is generally not suitable
|
||||
* for use in an emulator. For that, consider {@link BytesTracePcodeEmulator}.
|
||||
*
|
||||
* @param trace the trace
|
||||
* @param platform the platform
|
||||
* @param snap the snap
|
||||
* @param thread the thread, required if register space is used
|
||||
* @param frame the frame, for when register space is used
|
||||
* @return the executor
|
||||
*/
|
||||
public static PcodeExecutor<byte[]> buildByteExecutor(TracePlatform platform, long snap,
|
||||
TraceThread thread, int frame) {
|
||||
DirectBytesTracePcodeExecutorState state =
|
||||
new DirectBytesTracePcodeExecutorState(platform, snap, thread, frame);
|
||||
Language language = platform.getLanguage();
|
||||
if (!(language instanceof SleighLanguage)) {
|
||||
throw new IllegalArgumentException("TracePlatform must use a SLEIGH language");
|
||||
}
|
||||
return new PcodeExecutor<>((SleighLanguage) language,
|
||||
BytesPcodeArithmetic.forLanguage(language), state);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #buildByteExecutor(TracePlatform, long, TraceThread, int)
|
||||
* @param trace the trace whose host platform to use
|
||||
* @param snap the snap
|
||||
* @param thread the thread, required if register space is used
|
||||
* @param frame the frame, for when register space is used
|
||||
|
@ -80,14 +74,7 @@ public enum TraceSleighUtils {
|
|||
*/
|
||||
public static PcodeExecutor<byte[]> buildByteExecutor(Trace trace, long snap,
|
||||
TraceThread thread, int frame) {
|
||||
DirectBytesTracePcodeExecutorState state =
|
||||
new DirectBytesTracePcodeExecutorState(trace, snap, thread, frame);
|
||||
Language language = trace.getBaseLanguage();
|
||||
if (!(language instanceof SleighLanguage)) {
|
||||
throw new IllegalArgumentException("Trace must use a SLEIGH language");
|
||||
}
|
||||
return new PcodeExecutor<>((SleighLanguage) language,
|
||||
BytesPcodeArithmetic.forLanguage(language), state);
|
||||
return buildByteExecutor(trace.getPlatformManager().getHostPlatform(), snap, thread, frame);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -98,7 +85,29 @@ public enum TraceSleighUtils {
|
|||
* when the client would also like to know if all variables involved are
|
||||
* {@link TraceMemoryState#KNOWN}.
|
||||
*
|
||||
* @param trace the trace
|
||||
* @param platform the platform
|
||||
* @param snap the snap
|
||||
* @param thread the thread, required if register space is used
|
||||
* @param frame the frame, for when register space is used
|
||||
* @return the executor
|
||||
*/
|
||||
public static PcodeExecutor<Pair<byte[], TraceMemoryState>> buildByteWithStateExecutor(
|
||||
TracePlatform platform, long snap, TraceThread thread, int frame) {
|
||||
DirectBytesTracePcodeExecutorState state =
|
||||
new DirectBytesTracePcodeExecutorState(platform, snap, thread, frame);
|
||||
PcodeExecutorState<Pair<byte[], TraceMemoryState>> paired = state.withMemoryState();
|
||||
Language language = platform.getLanguage();
|
||||
if (!(language instanceof SleighLanguage)) {
|
||||
throw new IllegalArgumentException("TracePlatform must use a SLEIGH language");
|
||||
}
|
||||
return new PcodeExecutor<>((SleighLanguage) language, new PairedPcodeArithmetic<>(
|
||||
BytesPcodeArithmetic.forLanguage(language), TraceMemoryStatePcodeArithmetic.INSTANCE),
|
||||
paired);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #buildByteWithStateExecutor(TracePlatform, long, TraceThread, int)
|
||||
* @param trace the trace whose host platform to use
|
||||
* @param snap the snap
|
||||
* @param thread the thread, required if register space is used
|
||||
* @param frame the frame, for when register space is used
|
||||
|
@ -106,16 +115,8 @@ public enum TraceSleighUtils {
|
|||
*/
|
||||
public static PcodeExecutor<Pair<byte[], TraceMemoryState>> buildByteWithStateExecutor(
|
||||
Trace trace, long snap, TraceThread thread, int frame) {
|
||||
DirectBytesTracePcodeExecutorState state =
|
||||
new DirectBytesTracePcodeExecutorState(trace, snap, thread, frame);
|
||||
PcodeExecutorState<Pair<byte[], TraceMemoryState>> paired = state.withMemoryState();
|
||||
Language language = trace.getBaseLanguage();
|
||||
if (!(language instanceof SleighLanguage)) {
|
||||
throw new IllegalArgumentException("Trace must use a SLEIGH language");
|
||||
}
|
||||
return new PcodeExecutor<>((SleighLanguage) language, new PairedPcodeArithmetic<>(
|
||||
BytesPcodeArithmetic.forLanguage(language), TraceMemoryStatePcodeArithmetic.INSTANCE),
|
||||
paired);
|
||||
return buildByteWithStateExecutor(trace.getPlatformManager().getHostPlatform(), snap,
|
||||
thread, frame);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -42,7 +42,9 @@ public interface AuxTraceEmulatorPartsFactory<U> extends AuxEmulatorPartsFactory
|
|||
* capable of lazily loading state from a trace and later writing its cache back into the trace
|
||||
* at another snapshot. The given concrete piece is already capable of doing that for concrete
|
||||
* values. The auxiliary piece should be able to independently load its state from the trace,
|
||||
* since this is one way a user expects to initialize the auxiliary values.
|
||||
* since this is one way a user expects to initialize the auxiliary values. It ought to use the
|
||||
* same data-access shim as the given concrete state. See
|
||||
* {@link TracePcodeExecutorStatePiece#getData()}.
|
||||
*
|
||||
* @param emulator the emulator
|
||||
* @param concrete the concrete piece
|
||||
|
|
|
@ -21,7 +21,8 @@ import ghidra.pcode.emu.PcodeThread;
|
|||
import ghidra.pcode.emu.auxiliary.AuxEmulatorPartsFactory;
|
||||
import ghidra.pcode.emu.auxiliary.AuxPcodeEmulator;
|
||||
import ghidra.pcode.exec.trace.*;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.pcode.exec.trace.data.*;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
|
||||
/**
|
||||
* An trace-integrated emulator whose parts are manufactured by a
|
||||
|
@ -39,51 +40,48 @@ import ghidra.trace.model.Trace;
|
|||
public abstract class AuxTracePcodeEmulator<U> extends AuxPcodeEmulator<U>
|
||||
implements TracePcodeMachine<Pair<byte[], U>> {
|
||||
|
||||
protected final Trace trace;
|
||||
protected final long snap;
|
||||
protected final PcodeTraceAccess access;
|
||||
|
||||
/**
|
||||
* Create a new emulator
|
||||
*
|
||||
* @param trace the trace from which the emulator loads state
|
||||
* @param snap the snap from which the emulator loads state
|
||||
* @param access the trace access shim
|
||||
*/
|
||||
public AuxTracePcodeEmulator(Trace trace, long snap) {
|
||||
super(trace.getBaseLanguage());
|
||||
this.trace = trace;
|
||||
this.snap = snap;
|
||||
public AuxTracePcodeEmulator(PcodeTraceAccess access) {
|
||||
super(access.getLanguage());
|
||||
this.access = access;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new emulator
|
||||
*
|
||||
* @param platform the platform to emulate
|
||||
* @param snap the source snap
|
||||
*/
|
||||
public AuxTracePcodeEmulator(TracePlatform platform, long snap) {
|
||||
this(new DefaultPcodeTraceAccess(platform, snap));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected abstract AuxTraceEmulatorPartsFactory<U> getPartsFactory();
|
||||
|
||||
@Override
|
||||
public Trace getTrace() {
|
||||
return trace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSnap() {
|
||||
return snap;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PcodeThread<Pair<byte[], U>> createThread(String name) {
|
||||
PcodeThread<Pair<byte[], U>> thread = super.createThread(name);
|
||||
initializeThreadContext(thread);
|
||||
access.getDataForLocalState(thread, 0).initializeThreadContext(thread);
|
||||
return thread;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TracePcodeExecutorState<Pair<byte[], U>> createSharedState() {
|
||||
return getPartsFactory().createTraceSharedState(this,
|
||||
new BytesTracePcodeExecutorStatePiece(trace, snap, null, 0));
|
||||
new BytesTracePcodeExecutorStatePiece(access.getDataForSharedState()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TracePcodeExecutorState<Pair<byte[], U>> createLocalState(
|
||||
PcodeThread<Pair<byte[], U>> thread) {
|
||||
return getPartsFactory().createTraceLocalState(this, thread,
|
||||
new BytesTracePcodeExecutorStatePiece(trace, snap, getTraceThread(thread), 0));
|
||||
new BytesTracePcodeExecutorStatePiece(access.getDataForLocalState(thread, 0)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
/* ###
|
||||
* 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.exec.trace.data;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import ghidra.pcode.emu.PcodeThread;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.DefaultTraceTimeViewport;
|
||||
import ghidra.trace.util.TraceTimeViewport;
|
||||
|
||||
/**
|
||||
* An abstract implementation of {@link PcodeTraceAccess}
|
||||
*
|
||||
* @param <S> the type of shared data-access shims provided
|
||||
* @param <L> the type of thread-local data-access shims provided
|
||||
*/
|
||||
public abstract class AbstractPcodeTraceAccess<S extends PcodeTraceMemoryAccess, L extends PcodeTraceRegistersAccess>
|
||||
implements PcodeTraceAccess {
|
||||
|
||||
protected final TracePlatform platform;
|
||||
protected final long threadsSnap;
|
||||
protected final long snap;
|
||||
|
||||
protected final TraceTimeViewport viewport;
|
||||
protected S dataForSharedState;
|
||||
protected final Map<Pair<TraceThread, Integer>, L> dataForLocalStateByThreadAndFrame =
|
||||
new HashMap<>();
|
||||
|
||||
/**
|
||||
* Construct a shim
|
||||
*
|
||||
* @param platform the associated platform
|
||||
* @param snap the associated snap
|
||||
* @param threadsSnap the snap to use when finding associated threads between trace and emulator
|
||||
*/
|
||||
public AbstractPcodeTraceAccess(TracePlatform platform, long snap, long threadsSnap) {
|
||||
this.platform = platform;
|
||||
this.snap = snap;
|
||||
this.threadsSnap = threadsSnap;
|
||||
|
||||
DefaultTraceTimeViewport viewport = new DefaultTraceTimeViewport(getTrace());
|
||||
viewport.setSnap(snap);
|
||||
this.viewport = viewport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a shim
|
||||
*
|
||||
* @param platform the associated platform
|
||||
* @param snap the associated snap
|
||||
*/
|
||||
public AbstractPcodeTraceAccess(TracePlatform platform, long snap) {
|
||||
this(platform, snap, snap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the associated trace
|
||||
*
|
||||
* @return the trace
|
||||
*/
|
||||
protected Trace getTrace() {
|
||||
return platform.getTrace();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the trace thread conventionally associated with the given p-code thread
|
||||
*
|
||||
* <p>
|
||||
* A p-code thread is conventionally associated with the trace thread whose path matches the
|
||||
* p-code thread's name.
|
||||
*
|
||||
* @param thread the p-code thread
|
||||
* @return the trace thread
|
||||
*/
|
||||
protected TraceThread getTraceThread(PcodeThread<?> thread) {
|
||||
return getTrace().getThreadManager().getLiveThreadByPath(threadsSnap, thread.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Language getLanguage() {
|
||||
return platform.getLanguage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method for the shared data-access shim
|
||||
*
|
||||
* @return the new data-access shim
|
||||
*/
|
||||
protected abstract S newDataForSharedState();
|
||||
|
||||
@Override
|
||||
public S getDataForSharedState() {
|
||||
synchronized (dataForLocalStateByThreadAndFrame) {
|
||||
if (dataForSharedState == null) {
|
||||
dataForSharedState = newDataForSharedState();
|
||||
}
|
||||
return dataForSharedState;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method for a thread's local data-access shim
|
||||
*
|
||||
* @param thread the associated trace thread
|
||||
* @param frame the frame, usually 0
|
||||
* @return the new data-access shim
|
||||
*/
|
||||
protected abstract L newDataForLocalState(TraceThread thread, int frame);
|
||||
|
||||
@Override
|
||||
public L getDataForLocalState(TraceThread thread, int frame) {
|
||||
if (thread == null) {
|
||||
return null;
|
||||
}
|
||||
synchronized (dataForLocalStateByThreadAndFrame) {
|
||||
return dataForLocalStateByThreadAndFrame.computeIfAbsent(Pair.of(thread, frame), p -> {
|
||||
return newDataForLocalState(p.getLeft(), p.getRight());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public L getDataForLocalState(PcodeThread<?> thread, int frame) {
|
||||
return getDataForLocalState(getTraceThread(thread), frame);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,193 @@
|
|||
/* ###
|
||||
* 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.exec.trace.data;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.memory.*;
|
||||
import ghidra.trace.util.TraceTimeViewport;
|
||||
|
||||
/**
|
||||
* An abstract data-access shim, for either memory or registers
|
||||
*/
|
||||
public abstract class AbstractPcodeTraceDataAccess implements InternalPcodeTraceDataAccess {
|
||||
protected final TracePlatform platform;
|
||||
protected final long snap;
|
||||
protected final TraceTimeViewport viewport;
|
||||
|
||||
protected final TraceMemoryManager mm;
|
||||
|
||||
/**
|
||||
* Construct a shim
|
||||
*
|
||||
* @param platform the associated platform
|
||||
* @param snap the associated snap
|
||||
* @param viewport the viewport, set to the same snapshot
|
||||
*/
|
||||
public AbstractPcodeTraceDataAccess(TracePlatform platform, long snap,
|
||||
TraceTimeViewport viewport) {
|
||||
this.platform = platform;
|
||||
this.snap = snap;
|
||||
this.viewport = viewport;
|
||||
|
||||
this.mm = platform.getTrace().getMemoryManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Language getLanguage() {
|
||||
return platform.getLanguage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TracePlatform getPlatform() {
|
||||
return platform;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSnap() {
|
||||
return snap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the interface for accessing trace memory or registers
|
||||
*
|
||||
* @param createIfAbsent in the case of registers, whether to create the missing space
|
||||
* @return the operations, or null
|
||||
*/
|
||||
protected abstract TraceMemoryOperations getMemoryOps(boolean createIfAbsent);
|
||||
|
||||
/**
|
||||
* If this shim is associated with a (register) overlay space, translate the given address into
|
||||
* it
|
||||
*
|
||||
* @param address the physical (register) address
|
||||
* @return the overlay address
|
||||
*/
|
||||
protected abstract Address toOverlay(Address address);
|
||||
|
||||
/**
|
||||
* @see #toOverlay(Address)
|
||||
* @param range the physical range
|
||||
* @return the overlay range
|
||||
*/
|
||||
protected abstract AddressRange toOverlay(AddressRange range);
|
||||
|
||||
/**
|
||||
* @see #toOverlay(Address)
|
||||
* @param set
|
||||
* @return
|
||||
*/
|
||||
protected abstract AddressSetView toOverlay(AddressSetView set);
|
||||
|
||||
@Override
|
||||
public void setState(AddressRange guestRange, TraceMemoryState state) {
|
||||
AddressRange hostRange = platform.mapGuestToHost(guestRange);
|
||||
if (hostRange == null) {
|
||||
return;
|
||||
}
|
||||
getMemoryOps(true).setState(snap, toOverlay(hostRange), state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceMemoryState getViewportState(AddressRange guestRange) {
|
||||
TraceMemoryOperations ops = getMemoryOps(false);
|
||||
if (ops == null) {
|
||||
return TraceMemoryState.UNKNOWN;
|
||||
}
|
||||
|
||||
AddressRange hostRange = platform.mapGuestToHost(guestRange);
|
||||
if (hostRange == null) {
|
||||
return TraceMemoryState.UNKNOWN;
|
||||
}
|
||||
|
||||
AddressSet hostSet = new AddressSet(toOverlay(hostRange));
|
||||
for (long snap : viewport.getOrderedSnaps()) {
|
||||
hostSet.delete(
|
||||
ops.getAddressesWithState(snap, hostSet, s -> s == TraceMemoryState.KNOWN));
|
||||
}
|
||||
return hostSet.isEmpty() ? TraceMemoryState.KNOWN : TraceMemoryState.UNKNOWN;
|
||||
}
|
||||
|
||||
protected AddressSetView doGetKnown(Range<Long> span) {
|
||||
TraceMemoryOperations ops = getMemoryOps(false);
|
||||
if (ops == null) {
|
||||
return new AddressSet();
|
||||
}
|
||||
return platform.mapHostToGuest(ops.getAddressesWithState(span,
|
||||
s -> s == TraceMemoryState.KNOWN));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSetView getKnownNow() {
|
||||
return doGetKnown(Range.singleton(snap));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSetView getKnownBefore() {
|
||||
return doGetKnown(Range.closed(0L, snap));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSetView intersectUnknown(AddressSetView guestView) {
|
||||
TraceMemoryOperations ops = getMemoryOps(false);
|
||||
if (ops == null) {
|
||||
return guestView;
|
||||
}
|
||||
|
||||
AddressSetView hostView = toOverlay(platform.mapGuestToHost(guestView));
|
||||
AddressSetView hostKnown = ops.getAddressesWithState(snap, hostView,
|
||||
s -> s != null && s != TraceMemoryState.UNKNOWN);
|
||||
AddressSetView hostResult = hostView.subtract(hostKnown);
|
||||
return platform.mapHostToGuest(hostResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putBytes(Address start, ByteBuffer buf) {
|
||||
// TODO: Truncate or verify range?
|
||||
Address hostStart = platform.mapGuestToHost(start);
|
||||
if (hostStart == null) {
|
||||
return 0;
|
||||
}
|
||||
return getMemoryOps(true).putBytes(snap, toOverlay(hostStart), buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBytes(Address start, ByteBuffer buf) {
|
||||
// TODO: Truncate or verify range?
|
||||
Address hostStart = platform.mapGuestToHost(start);
|
||||
if (hostStart == null) {
|
||||
return 0;
|
||||
}
|
||||
TraceMemoryOperations ops = getMemoryOps(false);
|
||||
if (ops == null) {
|
||||
// TODO: Write 0s?
|
||||
int length = buf.remaining();
|
||||
buf.position(buf.position() + length);
|
||||
return length;
|
||||
}
|
||||
return ops.getViewBytes(snap, toOverlay(hostStart), buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> PcodeTracePropertyAccess<T> getPropertyAccess(String name, Class<T> type) {
|
||||
return new DefaultPcodeTracePropertyAccess<>(this, name, type);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/* ###
|
||||
* 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.exec.trace.data;
|
||||
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
||||
/**
|
||||
* The default trace access shim for a session
|
||||
*/
|
||||
public class DefaultPcodeTraceAccess extends AbstractPcodeTraceAccess //
|
||||
<DefaultPcodeTraceMemoryAccess, DefaultPcodeTraceRegistersAccess> {
|
||||
|
||||
/**
|
||||
* Construct a shim
|
||||
*
|
||||
* @param platform the associated platform
|
||||
* @param snap the associated snap
|
||||
* @param threadsSnap the snap to use when finding associated threads between trace and emulator
|
||||
*/
|
||||
public DefaultPcodeTraceAccess(TracePlatform platform, long snap, long threadsSnap) {
|
||||
super(platform, snap, threadsSnap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a shim
|
||||
*
|
||||
* @param platform the associated platform
|
||||
* @param snap the associated snap
|
||||
*/
|
||||
public DefaultPcodeTraceAccess(TracePlatform platform, long snap) {
|
||||
super(platform, snap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultPcodeTraceMemoryAccess newDataForSharedState() {
|
||||
return new DefaultPcodeTraceMemoryAccess(platform, snap, viewport);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultPcodeTraceRegistersAccess newDataForLocalState(TraceThread thread, int frame) {
|
||||
return new DefaultPcodeTraceRegistersAccess(platform, snap, thread, frame, viewport);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/* ###
|
||||
* 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.exec.trace.data;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.memory.TraceMemoryOperations;
|
||||
import ghidra.trace.model.property.TracePropertyMapOperations;
|
||||
import ghidra.trace.util.TraceTimeViewport;
|
||||
|
||||
/**
|
||||
* The default data-access shim for trace memory
|
||||
*/
|
||||
public class DefaultPcodeTraceMemoryAccess extends AbstractPcodeTraceDataAccess
|
||||
implements PcodeTraceMemoryAccess {
|
||||
|
||||
/**
|
||||
* Construct a shim
|
||||
*
|
||||
* @param platform the associated platform
|
||||
* @param snap the associated snap
|
||||
* @param viewport the viewport, set to the same snapshot
|
||||
*/
|
||||
protected DefaultPcodeTraceMemoryAccess(TracePlatform platform, long snap,
|
||||
TraceTimeViewport viewport) {
|
||||
super(platform, snap, viewport);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceMemoryOperations getMemoryOps(boolean createIfAbsent) {
|
||||
return mm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> TracePropertyMapOperations<T> getPropertyOps(String name, Class<T> type,
|
||||
boolean createIfAbsent) {
|
||||
if (createIfAbsent) {
|
||||
return platform.getTrace()
|
||||
.getAddressPropertyManager()
|
||||
.getOrCreatePropertyMap(name, type);
|
||||
}
|
||||
return platform.getTrace().getAddressPropertyManager().getPropertyMap(name, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Address toOverlay(Address address) {
|
||||
return address;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AddressRange toOverlay(AddressRange range) {
|
||||
return range;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AddressSetView toOverlay(AddressSetView set) {
|
||||
return set;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
/* ###
|
||||
* 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.exec.trace.data;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.model.property.*;
|
||||
|
||||
/**
|
||||
* The default trace-property access shim
|
||||
*
|
||||
* @param <T> the type of the property's values
|
||||
*/
|
||||
public class DefaultPcodeTracePropertyAccess<T>
|
||||
implements PcodeTracePropertyAccess<T> {
|
||||
|
||||
protected final InternalPcodeTraceDataAccess data;
|
||||
protected final String name;
|
||||
protected final Class<T> type;
|
||||
|
||||
protected TracePropertyMapOperations<T> po;
|
||||
|
||||
/**
|
||||
* Construct the shim
|
||||
*
|
||||
* @param data the trace-data access shim providing this property access shim
|
||||
* @param name the name of the property
|
||||
* @param type the type of the property
|
||||
*/
|
||||
protected DefaultPcodeTracePropertyAccess(InternalPcodeTraceDataAccess data, String name,
|
||||
Class<T> type) {
|
||||
this.data = data;
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
|
||||
this.po = data.getPropertyOps(name, type, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the interface for accessing the trace property on memory or registers
|
||||
*
|
||||
* @param createIfAbsent whether to create the missing property (and space in the case of a
|
||||
* register property)
|
||||
* @return the operations, or null
|
||||
*/
|
||||
protected TracePropertyMapOperations<T> getPropertyOperations(boolean createIfAbsent) {
|
||||
if (po == null) {
|
||||
return po = data.getPropertyOps(name, type, createIfAbsent);
|
||||
}
|
||||
return po;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension point: Alternative logic when the trace property is null
|
||||
*
|
||||
* @param hostAddress the trace address (in the host platform)
|
||||
* @return the alternative value, or null
|
||||
*/
|
||||
protected T whenNull(Address hostAddress) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(Address address) {
|
||||
Address hostAddr = data.getPlatform().mapGuestToHost(address);
|
||||
if (hostAddr == null) {
|
||||
return null;
|
||||
}
|
||||
TracePropertyMapOperations<T> ops = getPropertyOperations(false);
|
||||
if (ops == null) {
|
||||
return whenNull(hostAddr);
|
||||
}
|
||||
Address overlayAddr = toOverlay(ops, hostAddr);
|
||||
return ops.get(data.getSnap(), overlayAddr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(Address address, T value) {
|
||||
Address hostAddr = data.getPlatform().mapGuestToHost(address);
|
||||
if (hostAddr == null) {
|
||||
// TODO: Warn?
|
||||
return;
|
||||
}
|
||||
Range<Long> span = DBTraceUtils.atLeastMaybeScratch(data.getSnap());
|
||||
TracePropertyMapOperations<T> ops = getPropertyOperations(true);
|
||||
ops.set(span, toOverlay(ops, hostAddr), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear(AddressRange range) {
|
||||
AddressRange hostRange = data.getPlatform().mapGuestToHost(range);
|
||||
if (hostRange == null) {
|
||||
// TODO: Warn?
|
||||
return;
|
||||
}
|
||||
Range<Long> span = DBTraceUtils.atLeastMaybeScratch(data.getSnap());
|
||||
TracePropertyMapOperations<T> ops = getPropertyOperations(false);
|
||||
if (ops == null) {
|
||||
return;
|
||||
}
|
||||
ops.clear(span, toOverlay(ops, hostRange));
|
||||
}
|
||||
|
||||
/**
|
||||
* If this provides access to an overlay space, translate the physical address to it
|
||||
*
|
||||
* @param ops the property operations
|
||||
* @param address the physical address
|
||||
* @return the overlay address, or the same address
|
||||
*/
|
||||
protected Address toOverlay(TracePropertyMapOperations<T> ops, Address address) {
|
||||
if (ops instanceof TracePropertyMap) {
|
||||
return address;
|
||||
}
|
||||
if (ops instanceof TracePropertyMapSpace<T> mapSpace) {
|
||||
return mapSpace.getAddressSpace().getOverlayAddress(address);
|
||||
}
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
/**
|
||||
* If this provides access to an overlay space, translate the physical range to it
|
||||
*
|
||||
* @param ops the property operations
|
||||
* @param range the physical range
|
||||
* @return the overlay range, or the same range
|
||||
*/
|
||||
protected AddressRange toOverlay(TracePropertyMapOperations<T> ops, AddressRange range) {
|
||||
if (ops instanceof TracePropertyMap) {
|
||||
return range;
|
||||
}
|
||||
if (ops instanceof TracePropertyMapSpace<T> mapSpace) {
|
||||
AddressSpace space = mapSpace.getAddressSpace();
|
||||
return new AddressRangeImpl(
|
||||
space.getOverlayAddress(range.getMinAddress()),
|
||||
space.getOverlayAddress(range.getMaxAddress()));
|
||||
}
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
/* ###
|
||||
* 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.exec.trace.data;
|
||||
|
||||
import ghidra.pcode.emu.PcodeThread;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
import ghidra.trace.model.memory.TraceMemoryState;
|
||||
import ghidra.trace.model.property.TracePropertyMap;
|
||||
import ghidra.trace.model.property.TracePropertyMapSpace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.TraceTimeViewport;
|
||||
|
||||
/**
|
||||
* The default data-access shim for trace registers
|
||||
*/
|
||||
public class DefaultPcodeTraceRegistersAccess extends AbstractPcodeTraceDataAccess
|
||||
implements PcodeTraceRegistersAccess {
|
||||
|
||||
protected final TraceThread thread;
|
||||
protected final int frame;
|
||||
|
||||
protected TraceMemorySpace ms;
|
||||
|
||||
/**
|
||||
* Construct a shim
|
||||
*
|
||||
* @param platform the associated platform
|
||||
* @param snap the associated snap
|
||||
* @param thread the associated thread whose registers to access
|
||||
* @param frame the associated frame, or 0 if not applicable
|
||||
* @param viewport the viewport, set to the same snapshot
|
||||
*/
|
||||
protected DefaultPcodeTraceRegistersAccess(TracePlatform platform, long snap,
|
||||
TraceThread thread, int frame, TraceTimeViewport viewport) {
|
||||
super(platform, snap, viewport);
|
||||
this.thread = thread;
|
||||
this.frame = frame;
|
||||
|
||||
this.ms = mm.getMemoryRegisterSpace(thread, frame, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceMemorySpace getMemoryOps(boolean createIfAbsent) {
|
||||
if (ms == null) {
|
||||
return ms = mm.getMemoryRegisterSpace(thread, frame, createIfAbsent);
|
||||
}
|
||||
return ms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> TracePropertyMapSpace<T> getPropertyOps(String name, Class<T> type,
|
||||
boolean createIfAbsent) {
|
||||
if (createIfAbsent) {
|
||||
return platform.getTrace()
|
||||
.getAddressPropertyManager()
|
||||
.getOrCreatePropertyMap(name, type)
|
||||
.getPropertyMapRegisterSpace(thread, frame, createIfAbsent);
|
||||
}
|
||||
TracePropertyMap<T> map = platform.getTrace()
|
||||
.getAddressPropertyManager()
|
||||
.getPropertyMap(name, type);
|
||||
return map == null ? null : map.getPropertyMapRegisterSpace(thread, frame, createIfAbsent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a register has a {@link TraceMemoryState#KNOWN} value for the given thread
|
||||
*
|
||||
* @param thread the thread
|
||||
* @param register the register
|
||||
* @return true if known
|
||||
*/
|
||||
protected boolean isRegisterKnown(PcodeThread<?> thread, Register register) {
|
||||
Trace trace = platform.getTrace();
|
||||
TraceThread traceThread =
|
||||
trace.getThreadManager().getLiveThreadByPath(snap, thread.getName());
|
||||
TraceMemorySpace space =
|
||||
trace.getMemoryManager().getMemoryRegisterSpace(traceThread, false);
|
||||
if (space == null) {
|
||||
return false;
|
||||
}
|
||||
return space.getState(platform, snap, register) == TraceMemoryState.KNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeThreadContext(PcodeThread<?> thread) {
|
||||
Trace trace = platform.getTrace();
|
||||
Language language = platform.getLanguage();
|
||||
Register contextreg = language.getContextBaseRegister();
|
||||
if (contextreg != Register.NO_CONTEXT && !isRegisterKnown(thread, contextreg)) {
|
||||
RegisterValue context = trace.getRegisterContextManager()
|
||||
.getValueWithDefault(platform, contextreg, snap, thread.getCounter());
|
||||
if (context != null) { // TODO: Why does this happen?
|
||||
thread.overrideContext(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Address toOverlay(Address address) {
|
||||
TraceMemorySpace ops = getMemoryOps(false);
|
||||
if (ops == null) {
|
||||
return null; // client should bail anyway
|
||||
}
|
||||
return ops.getAddressSpace().getOverlayAddress(address);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AddressRange toOverlay(AddressRange range) {
|
||||
TraceMemorySpace ops = getMemoryOps(false);
|
||||
if (ops == null) {
|
||||
return null; // client should bail anyway
|
||||
}
|
||||
AddressSpace space = ops.getAddressSpace();
|
||||
return new AddressRangeImpl(
|
||||
space.getOverlayAddress(range.getMinAddress()),
|
||||
space.getOverlayAddress(range.getMaxAddress()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AddressSetView toOverlay(AddressSetView set) {
|
||||
TraceMemorySpace ops = getMemoryOps(false);
|
||||
if (ops == null) {
|
||||
return null; // client should bail anyway
|
||||
}
|
||||
AddressSpace space = ops.getAddressSpace();
|
||||
AddressSet result = new AddressSet();
|
||||
for (AddressRange rng : set) {
|
||||
result.add(
|
||||
space.getOverlayAddress(rng.getMinAddress()),
|
||||
space.getOverlayAddress(rng.getMaxAddress()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
/* ###
|
||||
* 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.exec.trace.data;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import ghidra.pcode.emu.PcodeThread;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.trace.model.memory.TraceMemoryState;
|
||||
|
||||
/**
|
||||
* The default data-access shim, for both memory and registers
|
||||
*
|
||||
* <p>
|
||||
* This is not designed for use with the emulator, but rather with stand-alone p-code executors,
|
||||
* e.g., to evaluate a Sleigh expression. It multiplexes a given memory access shim and another
|
||||
* register access shim into a single shim for use in one state piece.
|
||||
*/
|
||||
public class DefaultPcodeTraceThreadAccess
|
||||
implements PcodeTraceMemoryAccess, PcodeTraceRegistersAccess {
|
||||
|
||||
protected final PcodeTraceMemoryAccess memory;
|
||||
protected final PcodeTraceRegistersAccess registers;
|
||||
|
||||
/**
|
||||
* Construct a shim
|
||||
*
|
||||
* @param memory the memory access shim
|
||||
* @param registers the regsiter access shim
|
||||
*/
|
||||
protected DefaultPcodeTraceThreadAccess(PcodeTraceMemoryAccess memory,
|
||||
PcodeTraceRegistersAccess registers) {
|
||||
this.memory = memory;
|
||||
this.registers = registers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Language getLanguage() {
|
||||
return memory.getLanguage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setState(AddressRange range, TraceMemoryState state) {
|
||||
if (range.getAddressSpace().isRegisterSpace()) {
|
||||
registers.setState(range, state);
|
||||
return;
|
||||
}
|
||||
memory.setState(range, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceMemoryState getViewportState(AddressRange range) {
|
||||
if (range.getAddressSpace().isRegisterSpace()) {
|
||||
return registers.getViewportState(range);
|
||||
}
|
||||
return memory.getViewportState(range);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSetView getKnownNow() {
|
||||
return memory.getKnownNow().union(registers.getKnownNow());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSetView getKnownBefore() {
|
||||
return memory.getKnownBefore().union(registers.getKnownBefore());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSetView intersectUnknown(AddressSetView view) {
|
||||
return memory.intersectUnknown(view).union(registers.intersectUnknown(view));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putBytes(Address start, ByteBuffer buf) {
|
||||
if (start.isRegisterAddress()) {
|
||||
return registers.putBytes(start, buf);
|
||||
}
|
||||
return memory.putBytes(start, buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBytes(Address start, ByteBuffer buf) {
|
||||
if (start.isRegisterAddress()) {
|
||||
return registers.getBytes(start, buf);
|
||||
}
|
||||
return memory.getBytes(start, buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> PcodeTracePropertyAccess<T> getPropertyAccess(String name, Class<T> type) {
|
||||
throw new UnsupportedOperationException("This is meant for p-code executor use");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeThreadContext(PcodeThread<?> thread) {
|
||||
registers.initializeThreadContext(thread);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/* ###
|
||||
* 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.exec.trace.data;
|
||||
|
||||
import ghidra.lifecycle.Internal;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.property.TracePropertyMapOperations;
|
||||
|
||||
@Internal
|
||||
public interface InternalPcodeTraceDataAccess extends PcodeTraceDataAccess {
|
||||
TracePlatform getPlatform();
|
||||
|
||||
long getSnap();
|
||||
|
||||
<T> TracePropertyMapOperations<T> getPropertyOps(String name, Class<T> type,
|
||||
boolean createIfAbsent);
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/* ###
|
||||
* 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.exec.trace.data;
|
||||
|
||||
import ghidra.pcode.emu.PcodeThread;
|
||||
import ghidra.pcode.exec.trace.TracePcodeMachine;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
||||
/**
|
||||
* A trace access shim
|
||||
*
|
||||
* <p>
|
||||
* This encapsulates the source or destination "coordinates" of a trace to simplify access to that
|
||||
* trace by p-code operations. This is also meant to encapsulate certain conventions, e.g., writes
|
||||
* are effective from the destination snapshot into the indefinite future, and meant to protect
|
||||
* p-code executor/emulator states from future re-factorings of the Trace API.
|
||||
*
|
||||
* <p>
|
||||
* While, technically anything can be behind the shim, the default implementations are backed by a
|
||||
* trace. The shim is associated with a chosen platform and snapshot. All methods are with respect
|
||||
* to that platform. In particular the addresses must all be in spaces of the platform's language.
|
||||
* Note that the platform may be the trace's host platform.
|
||||
*
|
||||
* <p>
|
||||
* Typically, each component of an emulator and/or its state will accept a corresponding access
|
||||
* shim. Thus, each method in the chain of obtaining the shim is invoked during that piece's
|
||||
* construction or invoked and passed into the constructor by a factory method. A complete chain
|
||||
* starts with {@link DefaultPcodeTraceAccess}. Each method is listed with notes about where it is
|
||||
* typically invoked below:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Typically invoked by an overloaded constructor, which then passes it to {@code this(...)}.
|
||||
* Clients can also construct the shim and pass it to the shim-accepting constructor manually.
|
||||
* Similarly, {@link TracePcodeMachine#writeDown(TracePlatform, long, long)} will construct one and
|
||||
* pass it to the overloaded method, which can instead be done by the client.
|
||||
*
|
||||
* <pre>
|
||||
* PcodeTraceAccess access =
|
||||
* new DefaultPcodeTraceAccess(trace.getPlatformManager().getHostPlatform(), 0, 0);
|
||||
* </pre>
|
||||
*
|
||||
* <li>Typically invoked by a factory method for an emulator's shared executor state
|
||||
*
|
||||
* <pre>
|
||||
* PcodeTraceMemoryAccess sharedData = access.getDataForSharedState();
|
||||
* </pre>
|
||||
*
|
||||
* <li>Typically invoked by a factory method for an emulator thread's local executor state
|
||||
*
|
||||
* <pre>
|
||||
* PcodeTraceRegisterAccess localData = access.getDataForLocalState(thread, 0);
|
||||
* </pre>
|
||||
*
|
||||
* <li>Typically invoked by an auxiliary emulator state piece
|
||||
*
|
||||
* <pre>
|
||||
* PcodeTracePropertyAccess<String> property = data.getPropertyAccess("MyProperty", String.class);
|
||||
* </pre>
|
||||
* </ul>
|
||||
*/
|
||||
public interface PcodeTraceAccess {
|
||||
/**
|
||||
* Get the language of the associated platform
|
||||
*
|
||||
* @return the langauge
|
||||
*/
|
||||
Language getLanguage();
|
||||
|
||||
/**
|
||||
* Get the data-access shim for use in an emulator's shared state
|
||||
*
|
||||
* @return the shim
|
||||
*/
|
||||
PcodeTraceMemoryAccess getDataForSharedState();
|
||||
|
||||
/**
|
||||
* Get the data-access shim for use in an emulator thread's local state
|
||||
*
|
||||
* @param thread the emulator's thread
|
||||
* @param frame the frame, usually 0
|
||||
* @return the shim
|
||||
*/
|
||||
PcodeTraceRegistersAccess getDataForLocalState(PcodeThread<?> thread, int frame);
|
||||
|
||||
/**
|
||||
* Get the data-access shim for use in an emulator thread's local state
|
||||
*
|
||||
* @param thread the trace thread associated with the emulator's thread
|
||||
* @param frame the frame, usually 0
|
||||
* @return the shim
|
||||
*/
|
||||
PcodeTraceRegistersAccess getDataForLocalState(TraceThread thread, int frame);
|
||||
|
||||
/**
|
||||
* Get the data-access shim for use in an executor having thread context
|
||||
*
|
||||
* <p>
|
||||
* <b>NOTE:</b> Do not use this shim for an emulator thread's local state. Use
|
||||
* {@link #getDataForLocalState(PcodeThread, int)} instead. This shim is meant for use in
|
||||
* stand-alone executors, e.g., for evaluating Sleigh expressions. Most likely, the thread is
|
||||
* the active thread in the UI.
|
||||
*
|
||||
* @param thread the trace thread for context, if applicable, or null
|
||||
* @param frame the frame
|
||||
* @return the shim
|
||||
*/
|
||||
default PcodeTraceDataAccess getDataForThreadState(TraceThread thread, int frame) {
|
||||
if (thread == null) {
|
||||
return getDataForSharedState();
|
||||
}
|
||||
return new DefaultPcodeTraceThreadAccess(getDataForSharedState(),
|
||||
getDataForLocalState(thread, frame));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
/* ###
|
||||
* 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.exec.trace.data;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.trace.model.memory.TraceMemoryState;
|
||||
|
||||
/**
|
||||
* A data-access shim for a trace
|
||||
*
|
||||
* @see PcodeTraceAccess
|
||||
*/
|
||||
public interface PcodeTraceDataAccess {
|
||||
/**
|
||||
* Get the language of the associated platform
|
||||
*
|
||||
* @return the language
|
||||
*/
|
||||
Language getLanguage();
|
||||
|
||||
/**
|
||||
* Set the memory state of an address range
|
||||
*
|
||||
* <p>
|
||||
* The state is set only for the destination snapshot. It is <em>not</em> effective for the
|
||||
* indefinite future.
|
||||
*
|
||||
* @param range the range
|
||||
* @param state the desired state
|
||||
*/
|
||||
void setState(AddressRange range, TraceMemoryState state);
|
||||
|
||||
/**
|
||||
* Get the composite state of an address range, using the snapshot's viewport
|
||||
*
|
||||
* <p>
|
||||
* Typically, the viewport is at most 2 snapshots deep. When reading from a captured snapshot,
|
||||
* the viewport includes only the source snapshot. When reading from scratch snapshot (usually
|
||||
* generated by emulation), the viewport includes that scratch snapshot and the original source
|
||||
* snapshot. The {@link TraceMemoryState#KNOWN} address set is the union of known address sets
|
||||
* among all snapshots in the viewport. If all addresses in the given range are
|
||||
* {@link TraceMemoryState#KNOWN}, then the composite state is known. Otherwise, the composite
|
||||
* state is {@link TraceMemoryState#UNKNOWN}.
|
||||
*
|
||||
* @param range the range to check
|
||||
* @return the composite state of the range
|
||||
*/
|
||||
TraceMemoryState getViewportState(AddressRange range);
|
||||
|
||||
/**
|
||||
* Get the address set of {@link TraceMemoryState#KNOWN} memory in the source snapshot
|
||||
*
|
||||
* <p>
|
||||
* Note, this does not consider the snapshot's viewport.
|
||||
*
|
||||
* @implNote This can be an expensive operation when the platform is a guest, since what would
|
||||
* ordinarily be a lazy address set must be computed and translated to the guest
|
||||
* address spaces.
|
||||
*
|
||||
* @return the address set
|
||||
*/
|
||||
AddressSetView getKnownNow();
|
||||
|
||||
/**
|
||||
* Get the address set of {@link TraceMemoryState#KNOWN} memory among all snapshots from 0 to
|
||||
* the source snapshot
|
||||
*
|
||||
* <p>
|
||||
* Note, this does not consider the snapshot's viewport.
|
||||
*
|
||||
* @implNote This can be an expensive operation when the platform is a guest, since what would
|
||||
* ordinarily be a lazy address set must be computed and translated to the guest
|
||||
* address spaces.
|
||||
*
|
||||
* @return the address set
|
||||
*/
|
||||
AddressSetView getKnownBefore();
|
||||
|
||||
/**
|
||||
* Compute the intersection of the given address set and the set of
|
||||
* {@link TraceMemoryState#UNKNOWN} memory
|
||||
*
|
||||
* @param view the address set
|
||||
* @return the intersection
|
||||
*/
|
||||
AddressSetView intersectUnknown(AddressSetView view);
|
||||
|
||||
/**
|
||||
* Write bytes into the trace
|
||||
*
|
||||
* <p>
|
||||
* Each written byte is effective for future snapshots up to but excluding the next snapshot
|
||||
* where another byte is written at the same address.
|
||||
*
|
||||
* @param start the address of the first byte to write
|
||||
* @param buf a buffer of bytes to write
|
||||
* @return the number of bytes written
|
||||
*/
|
||||
int putBytes(Address start, ByteBuffer buf);
|
||||
|
||||
/**
|
||||
* Read bytes from the trace
|
||||
*
|
||||
* @param start the address of the first byte to read
|
||||
* @param buf a buffer to receive the bytes
|
||||
* @return the number of bytes read
|
||||
*/
|
||||
int getBytes(Address start, ByteBuffer buf);
|
||||
|
||||
/**
|
||||
* Get a property-access shim for the named property
|
||||
*
|
||||
* @param <T> the type of the property's values
|
||||
* @param name the name of the property
|
||||
* @param type the class of the property's values
|
||||
* @return the access shim
|
||||
*/
|
||||
<T> PcodeTracePropertyAccess<T> getPropertyAccess(String name, Class<T> type);
|
||||
}
|
|
@ -13,18 +13,11 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.exec;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
package ghidra.pcode.exec.trace.data;
|
||||
|
||||
/**
|
||||
* The state for a {@link AsyncWrappedPcodeExecutorStatePiece}
|
||||
*
|
||||
* @param <T> the type of wrapped values
|
||||
* A data-access shim for a trace's memory
|
||||
*/
|
||||
public class AsyncWrappedPcodeExecutorState<T>
|
||||
extends DefaultPcodeExecutorState<CompletableFuture<T>> {
|
||||
public AsyncWrappedPcodeExecutorState(PcodeExecutorStatePiece<T, T> piece) {
|
||||
super(new AsyncWrappedPcodeExecutorStatePiece<>(piece));
|
||||
}
|
||||
public interface PcodeTraceMemoryAccess extends PcodeTraceDataAccess {
|
||||
// Nothing to add
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/* ###
|
||||
* 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.exec.trace.data;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
|
||||
/**
|
||||
* A trace-property access shim for a specific property
|
||||
*
|
||||
* @see PcodeTraceAccess
|
||||
* @see PcodeTraceDataAccess
|
||||
*
|
||||
* @param <T> the type of the property's values
|
||||
*/
|
||||
public interface PcodeTracePropertyAccess<T> {
|
||||
/**
|
||||
* Get the property's value at the given address
|
||||
*
|
||||
* <p>
|
||||
* This may search for the same property from other related data sources, e.g., from mapped
|
||||
* static images.
|
||||
*
|
||||
* @param address the address
|
||||
* @return the value, or null if not set
|
||||
*/
|
||||
T get(Address address);
|
||||
|
||||
/**
|
||||
* Set the property's value at the given address
|
||||
*
|
||||
* <p>
|
||||
* The value is affective for future snapshots up to but excluding the next snapshot where
|
||||
* another value is set at the same address.
|
||||
*
|
||||
* @param address the address
|
||||
* @param value the value to set
|
||||
*/
|
||||
void put(Address address, T value);
|
||||
|
||||
/**
|
||||
* Clear the property's value across a range
|
||||
*
|
||||
* @param range the range
|
||||
*/
|
||||
void clear(AddressRange range);
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/* ###
|
||||
* 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.exec.trace.data;
|
||||
|
||||
import ghidra.pcode.emu.PcodeThread;
|
||||
|
||||
/**
|
||||
* A data-access shim for a trace's registers
|
||||
*/
|
||||
public interface PcodeTraceRegistersAccess extends PcodeTraceDataAccess {
|
||||
|
||||
/**
|
||||
* Initialize the given p-code thread's context register using register context from the trace
|
||||
* at the thread's program counter
|
||||
*
|
||||
* <p>
|
||||
* This is called during thread construction, after the program counter is initialized from the
|
||||
* same trace thread. This will ensure that the instruction decoder starts in the same mode as
|
||||
* the disassembler was for the trace.
|
||||
*
|
||||
* @param thread the thread to initialize
|
||||
*/
|
||||
void initializeThreadContext(PcodeThread<?> thread);
|
||||
}
|
|
@ -38,6 +38,7 @@ import ghidra.trace.database.breakpoint.DBTraceBreakpointManager;
|
|||
import ghidra.trace.database.context.DBTraceRegisterContextManager;
|
||||
import ghidra.trace.database.data.DBTraceDataSettingsAdapter;
|
||||
import ghidra.trace.database.data.DBTraceDataTypeManager;
|
||||
import ghidra.trace.database.guest.DBTraceObjectRegisterSupport;
|
||||
import ghidra.trace.database.guest.DBTracePlatformManager;
|
||||
import ghidra.trace.database.listing.DBTraceCodeManager;
|
||||
import ghidra.trace.database.listing.DBTraceCommentAdapter;
|
||||
|
@ -567,6 +568,7 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
|||
@Override
|
||||
public void setChanged(TraceChangeRecord<?, ?> event) {
|
||||
changed = true;
|
||||
DBTraceObjectRegisterSupport.INSTANCE.processEvent(event);
|
||||
fireEvent(event);
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,8 @@ import db.*;
|
|||
import ghidra.program.model.address.*;
|
||||
import ghidra.trace.database.DBTrace;
|
||||
import ghidra.trace.database.DBTraceManager;
|
||||
import ghidra.trace.model.Trace.TraceOverlaySpaceChangeType;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.database.*;
|
||||
import ghidra.util.database.DBCachedObjectStoreFactory.AbstractDBFieldCodec;
|
||||
|
@ -241,6 +243,8 @@ public class DBTraceOverlaySpaceAdapter implements DBTraceManager {
|
|||
DBTraceOverlaySpaceEntry ent = overlayStore.create();
|
||||
ent.set(space.getName(), base.getName());
|
||||
trace.updateViewsAddSpaceBlock(space);
|
||||
trace.setChanged(new TraceChangeRecord<>(TraceOverlaySpaceChangeType.ADDED, null,
|
||||
trace, null, space));
|
||||
return space;
|
||||
}
|
||||
}
|
||||
|
@ -258,6 +262,8 @@ public class DBTraceOverlaySpaceAdapter implements DBTraceManager {
|
|||
assert space != null;
|
||||
factory.removeOverlaySpace(name);
|
||||
trace.updateViewsDeleteSpaceBlock(space);
|
||||
trace.setChanged(new TraceChangeRecord<>(TraceOverlaySpaceChangeType.DELETED, null,
|
||||
trace, space, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,12 @@ public class TraceAddressFactory extends ProgramAddressFactory {
|
|||
super(language, compilerSpec);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean validateOriginalSpace(AddressSpace originalSpace) {
|
||||
return (originalSpace.isMemorySpace() || originalSpace.isRegisterSpace()) &&
|
||||
!originalSpace.isOverlaySpace();
|
||||
}
|
||||
|
||||
@Override // for peer access
|
||||
protected OverlayAddressSpace addOverlayAddressSpace(String name, boolean preserveName,
|
||||
AddressSpace originalSpace, long minOffset, long maxOffset) {
|
||||
|
|
|
@ -39,6 +39,7 @@ import ghidra.trace.database.space.DBTraceDelegatingManager;
|
|||
import ghidra.trace.database.thread.DBTraceThreadManager;
|
||||
import ghidra.trace.model.TraceAddressSnapRange;
|
||||
import ghidra.trace.model.context.TraceRegisterContextManager;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.database.*;
|
||||
import ghidra.util.database.annot.*;
|
||||
|
@ -181,10 +182,15 @@ public class DBTraceRegisterContextManager
|
|||
}
|
||||
|
||||
@Override
|
||||
public RegisterValue getValueWithDefault(Language language, Register register, long snap,
|
||||
public RegisterValue getValueWithDefault(TracePlatform platform, Register register, long snap,
|
||||
Address address) {
|
||||
return delegateReadOr(address.getAddressSpace(),
|
||||
m -> m.getValueWithDefault(language, register, snap, address),
|
||||
Address hostAddress = platform.mapGuestToHost(address);
|
||||
Language language = platform.getLanguage();
|
||||
if (hostAddress == null) {
|
||||
return getDefaultValue(language, register, address);
|
||||
}
|
||||
return delegateReadOr(hostAddress.getAddressSpace(),
|
||||
m -> m.getValueWithDefault(language, register, snap, hostAddress, address),
|
||||
() -> getDefaultValue(language, register, address));
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ import ghidra.trace.database.space.DBTraceSpaceBased;
|
|||
import ghidra.trace.model.ImmutableTraceAddressSnapRange;
|
||||
import ghidra.trace.model.TraceAddressSnapRange;
|
||||
import ghidra.trace.model.context.TraceRegisterContextSpace;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.database.*;
|
||||
|
@ -369,20 +370,30 @@ public class DBTraceRegisterContextSpace implements TraceRegisterContextSpace, D
|
|||
}
|
||||
}
|
||||
|
||||
protected RegisterValue getValueWithDefault(Language language, Register register,
|
||||
long snap, Address hostAddress, Address langAddress) {
|
||||
Register base = register.getBaseRegister();
|
||||
RegisterValue baseValue = doGetBaseValue(language, base, snap, hostAddress);
|
||||
if (baseValue == null) {
|
||||
return getDefaultValue(language, register, langAddress);
|
||||
}
|
||||
RegisterValue defaultBaseValue = getDefaultValue(language, base, langAddress);
|
||||
if (defaultBaseValue == null) {
|
||||
return baseValue.getRegisterValue(register);
|
||||
}
|
||||
return defaultBaseValue.combineValues(baseValue).getRegisterValue(register);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegisterValue getValueWithDefault(Language language, Register register, long snap,
|
||||
Address address) {
|
||||
public RegisterValue getValueWithDefault(TracePlatform platform, Register register, long snap,
|
||||
Address guestAddress) {
|
||||
Language language = platform.getLanguage();
|
||||
try (LockHold hold = LockHold.lock(lock.readLock())) {
|
||||
Register base = register.getBaseRegister();
|
||||
RegisterValue baseValue = doGetBaseValue(language, base, snap, address);
|
||||
if (baseValue == null) {
|
||||
return getDefaultValue(language, register, address);
|
||||
Address hostAddress = platform.mapGuestToHost(guestAddress);
|
||||
if (hostAddress == null) {
|
||||
return getDefaultValue(language, register, guestAddress);
|
||||
}
|
||||
RegisterValue defaultBaseValue = getDefaultValue(language, base, address);
|
||||
if (defaultBaseValue == null) {
|
||||
return baseValue.getRegisterValue(register);
|
||||
}
|
||||
return defaultBaseValue.combineValues(baseValue).getRegisterValue(register);
|
||||
return getValueWithDefault(language, register, snap, hostAddress, guestAddress);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@ import java.io.IOException;
|
|||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import db.DBRecord;
|
||||
|
@ -35,6 +37,9 @@ import ghidra.trace.database.DBTraceUtils.LanguageIDDBFieldCodec;
|
|||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.Trace.TracePlatformChangeType;
|
||||
import ghidra.trace.model.guest.TraceGuestPlatform;
|
||||
import ghidra.trace.model.guest.TraceGuestPlatformMappedRange;
|
||||
import ghidra.trace.util.OverlappingObjectIterator;
|
||||
import ghidra.trace.util.OverlappingObjectIterator.Ranger;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.database.*;
|
||||
|
@ -48,6 +53,33 @@ public class DBTraceGuestPlatform extends DBAnnotatedObject
|
|||
implements TraceGuestPlatform, InternalTracePlatform {
|
||||
public static final String TABLE_NAME = "Platforms";
|
||||
|
||||
private static enum MappedRangeRanger implements Ranger<DBTraceGuestPlatformMappedRange> {
|
||||
HOST {
|
||||
@Override
|
||||
AddressRange getRange(DBTraceGuestPlatformMappedRange t) {
|
||||
return t.getHostRange();
|
||||
}
|
||||
},
|
||||
GUEST {
|
||||
@Override
|
||||
AddressRange getRange(DBTraceGuestPlatformMappedRange t) {
|
||||
return t.getGuestRange();
|
||||
}
|
||||
};
|
||||
|
||||
abstract AddressRange getRange(DBTraceGuestPlatformMappedRange t);
|
||||
|
||||
@Override
|
||||
public Address getMinAddress(DBTraceGuestPlatformMappedRange t) {
|
||||
return getRange(t).getMinAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getMaxAddress(DBTraceGuestPlatformMappedRange t) {
|
||||
return getRange(t).getMaxAddress();
|
||||
}
|
||||
}
|
||||
|
||||
@DBAnnotatedObjectInfo(version = 0)
|
||||
public static class DBTraceGuestLanguage extends DBAnnotatedObject {
|
||||
public static final String TABLE_NAME = "Languages";
|
||||
|
@ -239,6 +271,36 @@ public class DBTraceGuestPlatform extends DBAnnotatedObject
|
|||
return mappedRange;
|
||||
}
|
||||
|
||||
protected Address computeNextRegisterMin() {
|
||||
Address regMax = manager.hostPlatform.getLanguage()
|
||||
.getAddressFactory()
|
||||
.getRegisterSpace()
|
||||
.getMaxAddress();
|
||||
AddressRangeIterator rit = hostAddressSet.getAddressRanges(regMax, false);
|
||||
if (!rit.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
AddressRange next = rit.next();
|
||||
if (!next.getAddressSpace().isRegisterSpace()) {
|
||||
return null;
|
||||
}
|
||||
return next.getMaxAddress().add(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceGuestPlatformMappedRange addMappedRegisterRange()
|
||||
throws AddressOverflowException {
|
||||
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
|
||||
AddressRange guestRange = getRegistersRange();
|
||||
if (guestRange == null) {
|
||||
return null; // No registers, so we're mapped!
|
||||
}
|
||||
Address hostMin = manager.computeNextRegisterMin();
|
||||
long size = guestRange.getLength();
|
||||
return addMappedRange(hostMin, guestRange.getMinAddress(), size);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSetView getHostAddressSet() {
|
||||
return new AddressSet(hostAddressSet);
|
||||
|
@ -261,6 +323,34 @@ public class DBTraceGuestPlatform extends DBAnnotatedObject
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange mapHostToGuest(AddressRange hostRange) {
|
||||
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
|
||||
Entry<Address, DBTraceGuestPlatformMappedRange> floorEntry =
|
||||
rangesByHostAddress.floorEntry(hostRange.getMinAddress());
|
||||
if (floorEntry == null) {
|
||||
return null;
|
||||
}
|
||||
return floorEntry.getValue().mapHostToGuest(hostRange);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSetView mapHostToGuest(AddressSetView hostSet) {
|
||||
Iterator<Pair<DBTraceGuestPlatformMappedRange, AddressRange>> it =
|
||||
new OverlappingObjectIterator<DBTraceGuestPlatformMappedRange, AddressRange>(
|
||||
rangesByHostAddress.values().iterator(), MappedRangeRanger.HOST,
|
||||
hostSet.iterator(), OverlappingObjectIterator.ADDRESS_RANGE);
|
||||
AddressSet result = new AddressSet();
|
||||
while (it.hasNext()) {
|
||||
Pair<DBTraceGuestPlatformMappedRange, AddressRange> next = it.next();
|
||||
DBTraceGuestPlatformMappedRange entry = next.getLeft();
|
||||
AddressRange hostRange = next.getRight();
|
||||
result.add(entry.mapHostToGuest(hostRange));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address mapGuestToHost(Address guestAddress) {
|
||||
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
|
||||
|
@ -273,6 +363,34 @@ public class DBTraceGuestPlatform extends DBAnnotatedObject
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange mapGuestToHost(AddressRange guestRange) {
|
||||
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
|
||||
Entry<Address, DBTraceGuestPlatformMappedRange> floorEntry =
|
||||
rangesByGuestAddress.floorEntry(guestRange.getMinAddress());
|
||||
if (floorEntry == null) {
|
||||
return null;
|
||||
}
|
||||
return floorEntry.getValue().mapGuestToHost(guestRange);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSetView mapGuestToHost(AddressSetView guestSet) {
|
||||
Iterator<Pair<DBTraceGuestPlatformMappedRange, AddressRange>> it =
|
||||
new OverlappingObjectIterator<DBTraceGuestPlatformMappedRange, AddressRange>(
|
||||
rangesByGuestAddress.values().iterator(), MappedRangeRanger.GUEST,
|
||||
guestSet.iterator(), OverlappingObjectIterator.ADDRESS_RANGE);
|
||||
AddressSet result = new AddressSet();
|
||||
while (it.hasNext()) {
|
||||
Pair<DBTraceGuestPlatformMappedRange, AddressRange> next = it.next();
|
||||
DBTraceGuestPlatformMappedRange entry = next.getLeft();
|
||||
AddressRange hostRange = next.getRight();
|
||||
result.add(entry.mapGuestToHost(hostRange));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the an address only if the entire range is contained in a single mapped range
|
||||
*
|
||||
|
|
|
@ -19,8 +19,6 @@ import java.io.IOException;
|
|||
|
||||
import db.DBRecord;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.CompilerSpec;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.trace.model.guest.TraceGuestPlatformMappedRange;
|
||||
import ghidra.util.database.*;
|
||||
import ghidra.util.database.annot.*;
|
||||
|
@ -53,13 +51,13 @@ public class DBTraceGuestPlatformMappedRange extends DBAnnotatedObject
|
|||
static DBObjectColumn LENGTH_COLUMN;
|
||||
|
||||
@DBAnnotatedField(column = HOST_SPACE_COLUMN_NAME)
|
||||
private int hostSpace;
|
||||
private int hostSpaceID;
|
||||
@DBAnnotatedField(column = HOST_OFFSET_COLUMN_NAME)
|
||||
private long hostOffset;
|
||||
@DBAnnotatedField(column = GUEST_LANGUAGE_COLUMN_NAME)
|
||||
int guestPlatformKey;
|
||||
@DBAnnotatedField(column = GUEST_SPACE_COLUMN_NAME)
|
||||
private int guestSpace;
|
||||
private int guestSpaceID;
|
||||
@DBAnnotatedField(column = GUEST_OFFSET_COLUMN_NAME)
|
||||
private long guestOffset;
|
||||
@DBAnnotatedField(column = LENGTH_COLUMN_NAME)
|
||||
|
@ -70,6 +68,9 @@ public class DBTraceGuestPlatformMappedRange extends DBAnnotatedObject
|
|||
private AddressRangeImpl hostRange;
|
||||
private DBTraceGuestPlatform platform;
|
||||
private AddressRangeImpl guestRange;
|
||||
private AddressSpace hostSpace;
|
||||
private AddressSpace guestSpace;
|
||||
private long shiftHostToGuest;
|
||||
|
||||
public DBTraceGuestPlatformMappedRange(DBTracePlatformManager manager, DBCachedObjectStore<?> s,
|
||||
DBRecord r) {
|
||||
|
@ -83,10 +84,8 @@ public class DBTraceGuestPlatformMappedRange extends DBAnnotatedObject
|
|||
if (created) {
|
||||
return;
|
||||
}
|
||||
Address hostStart =
|
||||
manager.trace.getBaseLanguage()
|
||||
.getAddressFactory()
|
||||
.getAddress(hostSpace, hostOffset);
|
||||
this.hostSpace = manager.trace.getBaseAddressFactory().getAddressSpace(hostSpaceID);
|
||||
Address hostStart = hostSpace.getAddress(hostOffset);
|
||||
Address hostEnd = hostStart.addWrap(length - 1);
|
||||
this.hostRange = new AddressRangeImpl(hostStart, hostEnd);
|
||||
|
||||
|
@ -95,16 +94,21 @@ public class DBTraceGuestPlatformMappedRange extends DBAnnotatedObject
|
|||
throw new IOException("Table is corrupt. Got host platform in guest mapping.");
|
||||
}
|
||||
this.platform = (DBTraceGuestPlatform) platform;
|
||||
Address guestStart = platform.getAddressFactory().getAddress(guestSpace, guestOffset);
|
||||
this.guestSpace = platform.getAddressFactory().getAddressSpace(guestSpaceID);
|
||||
Address guestStart = guestSpace.getAddress(guestOffset);
|
||||
Address guestEnd = guestStart.addWrap(length - 1);
|
||||
this.guestRange = new AddressRangeImpl(guestStart, guestEnd);
|
||||
|
||||
this.shiftHostToGuest = guestStart.getOffset() - hostStart.getOffset();
|
||||
}
|
||||
|
||||
void set(Address hostStart, DBTraceGuestPlatform platform, Address guestStart, long length) {
|
||||
this.hostSpace = hostStart.getAddressSpace().getSpaceID();
|
||||
this.hostSpace = hostStart.getAddressSpace();
|
||||
this.hostSpaceID = hostSpace.getSpaceID();
|
||||
this.hostOffset = hostStart.getOffset();
|
||||
this.guestPlatformKey = (int) platform.getKey();
|
||||
this.guestSpace = guestStart.getAddressSpace().getSpaceID();
|
||||
this.guestSpace = guestStart.getAddressSpace();
|
||||
this.guestSpaceID = guestSpace.getSpaceID();
|
||||
this.guestOffset = guestStart.getOffset();
|
||||
this.length = length;
|
||||
update(HOST_SPACE_COLUMN, HOST_OFFSET_COLUMN, GUEST_LANGUAGE_COLUMN, GUEST_SPACE_COLUMN,
|
||||
|
@ -114,16 +118,12 @@ public class DBTraceGuestPlatformMappedRange extends DBAnnotatedObject
|
|||
this.platform = platform;
|
||||
this.guestRange = new AddressRangeImpl(guestStart, guestStart.addWrap(length - 1));
|
||||
|
||||
this.shiftHostToGuest = guestStart.getOffset() - hostStart.getOffset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Language getHostLanguage() {
|
||||
return manager.trace.getBaseLanguage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompilerSpec getHostCompilerSpec() {
|
||||
return manager.trace.getBaseCompilerSpec();
|
||||
public InternalTracePlatform getHostPlatform() {
|
||||
return manager.hostPlatform;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -141,13 +141,31 @@ public class DBTraceGuestPlatformMappedRange extends DBAnnotatedObject
|
|||
return guestRange;
|
||||
}
|
||||
|
||||
protected static Address doMapTo(Address address, AddressSpace space, long shift) {
|
||||
return space.getAddress(address.getOffset() + shift);
|
||||
}
|
||||
|
||||
protected static AddressRange doMapTo(AddressRange range, AddressSpace space, long shift) {
|
||||
return new AddressRangeImpl(
|
||||
doMapTo(range.getMinAddress(), space, shift),
|
||||
doMapTo(range.getMaxAddress(), space, shift));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address mapHostToGuest(Address hostAddress) {
|
||||
if (!hostRange.contains(hostAddress)) {
|
||||
return null;
|
||||
}
|
||||
long offset = hostAddress.subtract(hostRange.getMinAddress());
|
||||
return guestRange.getMinAddress().add(offset);
|
||||
return doMapTo(hostAddress, guestSpace, shiftHostToGuest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange mapHostToGuest(AddressRange hostRange) {
|
||||
if (!this.hostRange.contains(hostRange.getMinAddress()) ||
|
||||
!this.hostRange.contains(hostRange.getMaxAddress())) {
|
||||
return null;
|
||||
}
|
||||
return doMapTo(hostRange, guestSpace, shiftHostToGuest);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -155,8 +173,16 @@ public class DBTraceGuestPlatformMappedRange extends DBAnnotatedObject
|
|||
if (!guestRange.contains(guestAddress)) {
|
||||
return null;
|
||||
}
|
||||
long offset = guestAddress.subtract(guestRange.getMinAddress());
|
||||
return hostRange.getMinAddress().add(offset);
|
||||
return doMapTo(guestAddress, hostSpace, -shiftHostToGuest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange mapGuestToHost(AddressRange guestRange) {
|
||||
if (!this.guestRange.contains(guestRange.getMinAddress()) ||
|
||||
!this.guestRange.contains(guestRange.getMaxAddress())) {
|
||||
return null;
|
||||
}
|
||||
return doMapTo(guestRange, hostSpace, -shiftHostToGuest);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,492 @@
|
|||
/* ###
|
||||
* 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.trace.database.guest;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.dbg.target.TargetRegister;
|
||||
import ghidra.dbg.target.TargetRegisterContainer;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||
import ghidra.dbg.util.PathMatcher;
|
||||
import ghidra.dbg.util.PathPredicates.Align;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
import ghidra.pcode.utils.Utils;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.Trace.*;
|
||||
import ghidra.trace.model.TraceDomainObjectListener;
|
||||
import ghidra.trace.model.guest.*;
|
||||
import ghidra.trace.model.memory.TraceMemoryManager;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
import ghidra.trace.model.symbol.*;
|
||||
import ghidra.trace.model.target.*;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
public enum DBTraceObjectRegisterSupport {
|
||||
// TODO: Could/should this be done by an analyzer, instead of being internal trace logic?
|
||||
INSTANCE;
|
||||
|
||||
private static final TraceDomainObjectListener HANDLER = new TraceDomainObjectListener() {
|
||||
{
|
||||
listenFor(TraceObjectChangeType.VALUE_CREATED, INSTANCE::objectValueCreated);
|
||||
listenFor(TraceSymbolChangeType.ADDED, INSTANCE::symbolAdded);
|
||||
listenFor(TraceOverlaySpaceChangeType.ADDED, INSTANCE::spaceAdded);
|
||||
listenFor(TracePlatformChangeType.MAPPING_ADDED, INSTANCE::guestMappingAdded);
|
||||
}
|
||||
};
|
||||
|
||||
static class RegisterValueException extends Exception {
|
||||
public RegisterValueException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
static class LazyValues {
|
||||
private final TraceObjectValue registerValue;
|
||||
private BigInteger value;
|
||||
private int length = -1;
|
||||
private byte[] be;
|
||||
private byte[] le;
|
||||
|
||||
public LazyValues(TraceObjectValue registerValue) {
|
||||
this.registerValue = registerValue;
|
||||
}
|
||||
|
||||
BigInteger convertRegisterValueToBigInteger() throws RegisterValueException {
|
||||
Object val = registerValue.getValue();
|
||||
if (val instanceof String s) {
|
||||
try {
|
||||
return new BigInteger(s, 16);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
throw new RegisterValueException(
|
||||
"Invalid register value " + s + ". Must be hex digits only.");
|
||||
}
|
||||
}
|
||||
if (val instanceof Byte b) {
|
||||
return BigInteger.valueOf(b);
|
||||
}
|
||||
else if (val instanceof Short s) {
|
||||
return BigInteger.valueOf(s);
|
||||
}
|
||||
else if (val instanceof Integer i) {
|
||||
return BigInteger.valueOf(i);
|
||||
}
|
||||
else if (val instanceof Long l) {
|
||||
return BigInteger.valueOf(l);
|
||||
}
|
||||
else if (val instanceof Address a) {
|
||||
return a.getOffsetAsBigInteger();
|
||||
}
|
||||
throw new RegisterValueException(
|
||||
"Cannot convert register value: (" + registerValue.getValue().getClass() + ") '" +
|
||||
registerValue.getValue() +
|
||||
"'");
|
||||
}
|
||||
|
||||
int getRegisterValueLength() throws RegisterValueException {
|
||||
Object objLength = registerValue.getParent()
|
||||
.getValue(registerValue.getMinSnap(), TargetRegister.LENGTH_ATTRIBUTE_NAME)
|
||||
.getValue();
|
||||
if (!(objLength instanceof Number)) {
|
||||
throw new RegisterValueException(
|
||||
"Register length is not numeric: (" + objLength.getClass() + ") '" + objLength +
|
||||
"'");
|
||||
}
|
||||
return ((Number) objLength).intValue();
|
||||
}
|
||||
|
||||
BigInteger getValue() throws RegisterValueException {
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
return value = convertRegisterValueToBigInteger();
|
||||
}
|
||||
|
||||
int getLength() throws RegisterValueException {
|
||||
if (length != -1) {
|
||||
return length;
|
||||
}
|
||||
return length = getRegisterValueLength();
|
||||
}
|
||||
|
||||
byte[] getBytesBigEndian() throws RegisterValueException {
|
||||
if (be != null) {
|
||||
return be;
|
||||
}
|
||||
return be = Utils.bigIntegerToBytes(getValue(), getLength(), true);
|
||||
}
|
||||
|
||||
byte[] getBytesLittleEndian() throws RegisterValueException {
|
||||
if (le != null) {
|
||||
return le;
|
||||
}
|
||||
return le = Utils.bigIntegerToBytes(getValue(), getLength(), false);
|
||||
}
|
||||
|
||||
public byte[] getBytes(boolean isBigEndian) throws RegisterValueException {
|
||||
return isBigEndian ? getBytesBigEndian() : getBytesLittleEndian();
|
||||
}
|
||||
}
|
||||
|
||||
protected AddressSpace findRegisterOverlay(TraceObject object) {
|
||||
TraceObject container = object
|
||||
.queryCanonicalAncestorsTargetInterface(TargetRegisterContainer.class)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (container == null) {
|
||||
return null;
|
||||
}
|
||||
String pathStr = container.getCanonicalPath().toString();
|
||||
AddressSpace space = object.getTrace().getBaseAddressFactory().getAddressSpace(pathStr);
|
||||
if (space == null) {
|
||||
return null;
|
||||
}
|
||||
if (!space.isRegisterSpace()) {
|
||||
return null;
|
||||
}
|
||||
return space;
|
||||
}
|
||||
|
||||
protected AddressSpace findRegisterOverlay(TraceObjectValue objectValue) {
|
||||
return findRegisterOverlay(objectValue.getParent());
|
||||
}
|
||||
|
||||
protected void onValueCreatedTransferToPlatformRegister(TraceObjectValue registerValue,
|
||||
TracePlatform platform, String name, LazyValues lazy) throws RegisterValueException {
|
||||
Register register = platform.getLanguage().getRegister(name);
|
||||
if (register == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Address hostAddr = platform.mapGuestToHost(register.getAddress());
|
||||
if (hostAddr == null) {
|
||||
return;
|
||||
}
|
||||
AddressSpace hostSpace = hostAddr.getAddressSpace();
|
||||
TraceMemoryManager mem = registerValue.getTrace().getMemoryManager();
|
||||
long minSnap = registerValue.getMinSnap();
|
||||
if (hostSpace.isMemorySpace()) {
|
||||
mem.getMemorySpace(hostSpace, true)
|
||||
.setValue(platform, minSnap, new RegisterValue(register, lazy.getValue()));
|
||||
}
|
||||
else if (hostSpace.isRegisterSpace()) {
|
||||
AddressSpace overlay = findRegisterOverlay(registerValue);
|
||||
if (overlay == null) {
|
||||
return;
|
||||
}
|
||||
mem.getMemorySpace(overlay, true)
|
||||
.setValue(platform, minSnap, new RegisterValue(register, lazy.getValue()));
|
||||
}
|
||||
else {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
protected void transferValueToPlatformRegister(TraceObjectValue registerValue,
|
||||
TracePlatform platform, TraceMemorySpace mem, Register register) {
|
||||
LazyValues lazy = new LazyValues(registerValue);
|
||||
try {
|
||||
mem.setValue(platform, registerValue.getMinSnap(),
|
||||
new RegisterValue(register, lazy.getValue()));
|
||||
}
|
||||
catch (RegisterValueException e) {
|
||||
Msg.error(this, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
protected String getRegisterName(TraceObject registerObject) {
|
||||
String name = registerObject.getCanonicalPath().key();
|
||||
if (PathUtils.isIndex(name)) {
|
||||
return PathUtils.parseIndex(name);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
protected void onSpaceAddedCheckTransferObjectToPlatformRegister(TraceObject registerObject,
|
||||
TracePlatform platform, TraceMemorySpace mem) {
|
||||
String name = getRegisterName(registerObject);
|
||||
Register register = platform.getLanguage().getRegister(name);
|
||||
if (register == null || !register.getAddressSpace().isRegisterSpace()) {
|
||||
return;
|
||||
}
|
||||
for (TraceObjectValue registerValue : it(registerObject.getOrderedValues(Range.all(),
|
||||
TargetRegister.VALUE_ATTRIBUTE_NAME, true))) {
|
||||
transferValueToPlatformRegister(registerValue, platform, mem, register);
|
||||
}
|
||||
}
|
||||
|
||||
protected void onSpaceAddedCheckTransferToPlatformRegisters(TracePlatform platform,
|
||||
TraceObject regContainer, TraceMemorySpace mem) {
|
||||
for (TraceObjectValPath path : it(
|
||||
regContainer.querySuccessorsTargetInterface(Range.all(), TargetRegister.class))) {
|
||||
TraceObject registerObject =
|
||||
path.getDestination(platform.getTrace().getObjectManager().getRootObject());
|
||||
onSpaceAddedCheckTransferObjectToPlatformRegister(registerObject, platform, mem);
|
||||
}
|
||||
}
|
||||
|
||||
protected TraceMemorySpace getMemorySpace(TraceObject object, TraceLabelSymbol label) {
|
||||
Address hostAddr = label.getAddress();
|
||||
AddressSpace hostSpace = hostAddr.getAddressSpace();
|
||||
TraceMemoryManager mem = label.getTrace().getMemoryManager();
|
||||
if (hostSpace.isMemorySpace()) {
|
||||
return mem.getMemorySpace(hostSpace, true);
|
||||
}
|
||||
else if (hostSpace.isRegisterSpace()) {
|
||||
AddressSpace overlay = findRegisterOverlay(object);
|
||||
return mem.getMemorySpace(overlay, true);
|
||||
}
|
||||
else {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
protected void transferRegisterValueToLabel(TraceObjectValue registerValue,
|
||||
TraceLabelSymbol label, byte[] value) {
|
||||
TraceMemorySpace mem = getMemorySpace(registerValue.getParent(), label);
|
||||
Address hostAddr = label.getAddress();
|
||||
long minSnap = registerValue.getMinSnap();
|
||||
Address address = mem.getAddressSpace().getOverlayAddress(hostAddr);
|
||||
mem.putBytes(minSnap, address, ByteBuffer.wrap(value));
|
||||
}
|
||||
|
||||
protected static <T> Iterable<T> it(Stream<T> stream) {
|
||||
return () -> stream.iterator();
|
||||
}
|
||||
|
||||
protected void transferRegisterObjectToLabel(TraceObject registerObject, TraceLabelSymbol label,
|
||||
boolean isBigEndian) {
|
||||
TraceMemorySpace mem = getMemorySpace(registerObject, label);
|
||||
Address address = mem.getAddressSpace().getOverlayAddress(label.getAddress());
|
||||
for (TraceObjectValue registerValue : it(registerObject.getOrderedValues(
|
||||
label.getLifespan(), TargetRegister.VALUE_ATTRIBUTE_NAME, true))) {
|
||||
LazyValues lazy = new LazyValues(registerValue);
|
||||
try {
|
||||
long minSnap = registerValue.getMinSnap();
|
||||
mem.putBytes(minSnap, address, ByteBuffer.wrap(lazy.getBytes(isBigEndian)));
|
||||
}
|
||||
catch (RegisterValueException e) {
|
||||
Msg.error(this, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onValueCreatedTransfer(TraceObjectValue registerValue)
|
||||
throws RegisterValueException {
|
||||
TraceObject registerObject = registerValue.getParent();
|
||||
Trace trace = registerValue.getTrace();
|
||||
LazyValues lazy = new LazyValues(registerValue);
|
||||
|
||||
String name = getRegisterName(registerObject);
|
||||
|
||||
TracePlatformManager platformManager = trace.getPlatformManager();
|
||||
onValueCreatedTransferToPlatformRegister(registerValue, platformManager.getHostPlatform(),
|
||||
name, lazy);
|
||||
for (TracePlatform platform : platformManager.getGuestPlatforms()) {
|
||||
onValueCreatedTransferToPlatformRegister(registerValue, platform, name, lazy);
|
||||
}
|
||||
|
||||
TraceNamespaceSymbolView namespaces = trace.getSymbolManager().namespaces();
|
||||
TraceNamespaceSymbol nsRegMapBE =
|
||||
namespaces.getGlobalNamed(InternalTracePlatform.REG_MAP_BE);
|
||||
if (nsRegMapBE != null) {
|
||||
for (TraceLabelSymbol label : trace.getSymbolManager()
|
||||
.labels()
|
||||
.getChildrenNamed(name, nsRegMapBE)) {
|
||||
transferRegisterValueToLabel(registerValue, label, lazy.getBytesBigEndian());
|
||||
}
|
||||
}
|
||||
TraceNamespaceSymbol nsRegMapLE =
|
||||
namespaces.getGlobalNamed(InternalTracePlatform.REG_MAP_LE);
|
||||
if (nsRegMapLE != null) {
|
||||
for (TraceLabelSymbol label : trace.getSymbolManager()
|
||||
.labels()
|
||||
.getChildrenNamed(name, nsRegMapLE)) {
|
||||
transferRegisterValueToLabel(registerValue, label, lazy.getBytesLittleEndian());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isRegisterValue(TraceObjectValue objectValue) {
|
||||
TraceObject parent = objectValue.getParent();
|
||||
return parent != null &&
|
||||
parent.getTargetSchema().getInterfaces().contains(TargetRegister.class) &&
|
||||
TargetRegister.VALUE_ATTRIBUTE_NAME.equals(objectValue.getEntryKey());
|
||||
}
|
||||
|
||||
public void onValueCreatedCheckTransfer(TraceObjectValue objectValue) {
|
||||
if (isRegisterValue(objectValue)) {
|
||||
try {
|
||||
onValueCreatedTransfer(objectValue);
|
||||
}
|
||||
catch (RegisterValueException e) {
|
||||
Msg.error(this, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onSymbolAddedCheckTransferToLabel(TraceLabelSymbol label, boolean isBigEndian) {
|
||||
TraceObjectManager objectManager = label.getTrace().getObjectManager();
|
||||
TargetObjectSchema schema = objectManager.getRootSchema();
|
||||
if (schema == null) {
|
||||
return;
|
||||
}
|
||||
PathMatcher matcher = schema.searchFor(TargetRegister.class, true);
|
||||
matcher = matcher.applyKeys(Align.RIGHT, List.of(label.getName()));
|
||||
for (TraceObjectValPath path : it(
|
||||
objectManager.getValuePaths(label.getLifespan(), matcher))) {
|
||||
Object regRaw = path.getDestinationValue(objectManager.getRootObject());
|
||||
if (regRaw instanceof TraceObject regObj) {
|
||||
transferRegisterObjectToLabel(regObj, label, isBigEndian);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onSymbolAddedCheckTransfer(TraceSymbol symbol) {
|
||||
TraceObject root = symbol.getTrace().getObjectManager().getRootObject();
|
||||
if (root == null) {
|
||||
return;
|
||||
}
|
||||
if (symbol instanceof TraceLabelSymbol label) {
|
||||
TraceNamespaceSymbolView namespaces = label.getTrace().getSymbolManager().namespaces();
|
||||
TraceNamespaceSymbol regMapBE =
|
||||
namespaces.getGlobalNamed(InternalTracePlatform.REG_MAP_BE);
|
||||
TraceNamespaceSymbol regMapLE =
|
||||
namespaces.getGlobalNamed(InternalTracePlatform.REG_MAP_LE);
|
||||
if (label.getParentNamespace() == regMapBE) {
|
||||
onSymbolAddedCheckTransferToLabel(label, true);
|
||||
}
|
||||
else if (label.getParentNamespace() == regMapLE) {
|
||||
onSymbolAddedCheckTransferToLabel(label, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onSpaceAddedCheckTransfer(Trace trace, AddressSpace space) {
|
||||
TraceObject root = trace.getObjectManager().getRootObject();
|
||||
if (root == null) {
|
||||
return;
|
||||
}
|
||||
assert space.isOverlaySpace();
|
||||
if (!space.isRegisterSpace()) {
|
||||
return;
|
||||
}
|
||||
TraceMemorySpace mem = trace.getMemoryManager().getMemorySpace(space, true);
|
||||
TraceObject regContainer = trace.getObjectManager()
|
||||
.getObjectByCanonicalPath(
|
||||
TraceObjectKeyPath.parse(mem.getAddressSpace().getName()));
|
||||
if (regContainer == null || !regContainer.getTargetSchema()
|
||||
.getInterfaces()
|
||||
.contains(TargetRegisterContainer.class)) {
|
||||
return;
|
||||
}
|
||||
TracePlatformManager platformManager = trace.getPlatformManager();
|
||||
onSpaceAddedCheckTransferToPlatformRegisters(platformManager.getHostPlatform(),
|
||||
regContainer, mem);
|
||||
for (TraceGuestPlatform platform : platformManager.getGuestPlatforms()) {
|
||||
onSpaceAddedCheckTransferToPlatformRegisters(platform, regContainer, mem);
|
||||
}
|
||||
}
|
||||
|
||||
protected void onMappingAddedCheckTransferRegisterObjectMemoryMapped(TraceObject registerObject,
|
||||
TraceGuestPlatformMappedRange mapped) {
|
||||
String name = getRegisterName(registerObject);
|
||||
TraceGuestPlatform guest = mapped.getGuestPlatform();
|
||||
Register register = guest.getLanguage().getRegister(name);
|
||||
// TODO: Permit overlay spaces?
|
||||
if (register == null || mapped.getGuestRange().contains(register.getAddress())) {
|
||||
return;
|
||||
}
|
||||
Address hostAddr = mapped.mapGuestToHost(register.getAddress());
|
||||
if (hostAddr == null) {
|
||||
return;
|
||||
}
|
||||
TraceMemorySpace mem = registerObject.getTrace()
|
||||
.getMemoryManager()
|
||||
.getMemorySpace(hostAddr.getAddressSpace(), true);
|
||||
for (TraceObjectValue registerValue : it(registerObject.getOrderedValues(Range.all(),
|
||||
TargetRegister.VALUE_ATTRIBUTE_NAME, true))) {
|
||||
transferValueToPlatformRegister(registerValue, guest, mem, register);
|
||||
}
|
||||
}
|
||||
|
||||
public void onMappingAddedCheckTransferMemoryMapped(TraceObject root,
|
||||
TraceGuestPlatformMappedRange mapped) {
|
||||
for (TraceObjectValPath path : it(
|
||||
root.querySuccessorsTargetInterface(Range.all(), TargetRegister.class))) {
|
||||
TraceObject registerObject = path.getDestination(root);
|
||||
onMappingAddedCheckTransferRegisterObjectMemoryMapped(registerObject, mapped);
|
||||
}
|
||||
}
|
||||
|
||||
public void onMappingAddedCheckTransfer(TraceGuestPlatformMappedRange mapped) {
|
||||
Trace trace = mapped.getHostPlatform().getTrace();
|
||||
TraceObject root = trace.getObjectManager().getRootObject();
|
||||
if (root == null) {
|
||||
return;
|
||||
}
|
||||
AddressSpace guestSpace = mapped.getGuestRange().getAddressSpace();
|
||||
if (guestSpace.isRegisterSpace()) {
|
||||
// TODO: Optimize: create/use TraceAddressFactory.getOverlaySpaces()
|
||||
for (AddressSpace space : trace.getBaseAddressFactory().getAllAddressSpaces()) {
|
||||
if (!space.isOverlaySpace()) {
|
||||
continue;
|
||||
}
|
||||
onSpaceAddedCheckTransfer(trace, space);
|
||||
}
|
||||
}
|
||||
else if (guestSpace.isMemorySpace()) {
|
||||
if (guestSpace.isOverlaySpace()) {
|
||||
return;
|
||||
}
|
||||
onMappingAddedCheckTransferMemoryMapped(root, mapped);
|
||||
}
|
||||
else {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
public void processEvent(TraceChangeRecord<?, ?> event) {
|
||||
HANDLER.handleTraceChangeRecord(event);
|
||||
}
|
||||
|
||||
private void objectValueCreated(TraceObjectValue objectValue) {
|
||||
onValueCreatedCheckTransfer(objectValue);
|
||||
}
|
||||
|
||||
private void symbolAdded(TraceSymbol symbol) {
|
||||
onSymbolAddedCheckTransfer(symbol);
|
||||
}
|
||||
|
||||
private void spaceAdded(Trace trace, AddressSpace isNull, AddressSpace space) {
|
||||
onSpaceAddedCheckTransfer(trace, space);
|
||||
}
|
||||
|
||||
private void guestMappingAdded(TraceGuestPlatform guest, TraceGuestPlatformMappedRange isNull,
|
||||
TraceGuestPlatformMappedRange mapped) {
|
||||
onMappingAddedCheckTransfer(mapped);
|
||||
}
|
||||
}
|
|
@ -21,8 +21,7 @@ import java.util.concurrent.locks.ReadWriteLock;
|
|||
|
||||
import db.DBHandle;
|
||||
import ghidra.lifecycle.Internal;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.trace.database.DBTrace;
|
||||
|
@ -106,11 +105,31 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana
|
|||
return hostAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange mapHostToGuest(AddressRange hostRange) {
|
||||
return hostRange;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSetView mapHostToGuest(AddressSetView hostSet) {
|
||||
return hostSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address mapGuestToHost(Address guestAddress) {
|
||||
return guestAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange mapGuestToHost(AddressRange guestRange) {
|
||||
return guestRange;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSetView mapGuestToHost(AddressSetView guestSet) {
|
||||
return guestSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemBuffer getMappedMemBuffer(long snap, Address guestAddress) {
|
||||
return trace.getMemoryManager().getBufferAt(snap, guestAddress);
|
||||
|
@ -342,4 +361,18 @@ public class DBTracePlatformManager implements DBTraceManager, TracePlatformMana
|
|||
}
|
||||
return dbPlatform;
|
||||
}
|
||||
|
||||
protected Address computeNextRegisterMin() {
|
||||
AddressRange hostRegsRange = hostPlatform.getRegistersRange();
|
||||
Address next = hostRegsRange == null
|
||||
? hostPlatform.getAddressFactory().getRegisterSpace().getAddress(0)
|
||||
: hostRegsRange.getMaxAddress().add(1);
|
||||
for (DBTraceGuestPlatform guest : platformStore.asMap().values()) {
|
||||
Address candidateNext = guest.computeNextRegisterMin();
|
||||
if (candidateNext != null && next.compareTo(candidateNext) < 0) {
|
||||
next = candidateNext;
|
||||
}
|
||||
}
|
||||
return next;
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue