Merge remote-tracking branch 'origin/GP-5879_Dan_reworkEmuUiIntegration--SQUASHED'

This commit is contained in:
Ryan Kurtz
2025-08-21 11:04:12 -04:00
162 changed files with 5298 additions and 5887 deletions
@@ -19,9 +19,10 @@ import java.io.IOException;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
import ghidra.debug.api.emulation.DebuggerPcodeEmulatorFactory;
import ghidra.debug.api.emulation.DebuggerPcodeMachine;
import ghidra.debug.api.emulation.EmulatorFactory;
import ghidra.framework.plugintool.ServiceInfo;
import ghidra.pcode.emu.PcodeMachine;
import ghidra.pcode.exec.trace.TraceEmulationIntegration.Writer;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.trace.model.Trace;
@@ -68,11 +69,12 @@ public interface DebuggerEmulationService {
*
* @param trace the trace the emulator is bound to
* @param emulator the emulator itself
* @param writer the callbacks with delayed writes for trace/UI integration
* @param version the cache version. See {@link #isValid()}.
*/
record CachedEmulator(Trace trace, DebuggerPcodeMachine<?> emulator, long version) {
public CachedEmulator(Trace trace, DebuggerPcodeMachine<?> emulator) {
this(trace, emulator, trace.getEmulatorCacheVersion());
record CachedEmulator(Trace trace, PcodeMachine<?> emulator, Writer writer, long version) {
public CachedEmulator(Trace trace, PcodeMachine<?> emulator, Writer writer) {
this(trace, emulator, writer, trace.getEmulatorCacheVersion());
}
/**
@@ -96,7 +98,7 @@ public interface DebuggerEmulationService {
* @return the emulator
*/
@Override
public DebuggerPcodeMachine<?> emulator() {
public PcodeMachine<?> emulator() {
return emulator;
}
@@ -136,7 +138,7 @@ public interface DebuggerEmulationService {
*
* @return the collection of factories
*/
Collection<DebuggerPcodeEmulatorFactory> getEmulatorFactories();
Collection<EmulatorFactory> getEmulatorFactories();
/**
* Set the current emulator factory
@@ -153,14 +155,14 @@ public interface DebuggerEmulationService {
*
* @param factory the chosen factory
*/
void setEmulatorFactory(DebuggerPcodeEmulatorFactory factory);
void setEmulatorFactory(EmulatorFactory factory);
/**
* Get the current emulator factory
*
* @return the factory
*/
DebuggerPcodeEmulatorFactory getEmulatorFactory();
EmulatorFactory getEmulatorFactory();
/**
* Load the given program into a trace suitable for emulation in the UI, starting at the given
@@ -290,7 +292,7 @@ public interface DebuggerEmulationService {
* @param time the time coordinates, including initial snap, steps, and p-code steps
* @return the copied p-code frame
*/
DebuggerPcodeMachine<?> getCachedEmulator(Trace trace, TraceSchedule time);
PcodeMachine<?> getCachedEmulator(Trace trace, TraceSchedule time);
/**
* Get the emulators which are current executing
@@ -1,26 +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.debug.api.emulation;
import ghidra.pcode.exec.trace.TracePcodeMachine;
/**
* A Debugger-integrated emulator (or p-code machine)
*
* @param <T> the type of values in the machine's memory and registers
*/
public interface DebuggerPcodeMachine<T> extends TracePcodeMachine<T> {
}
@@ -15,15 +15,14 @@
*/
package ghidra.debug.api.emulation;
import ghidra.debug.api.target.Target;
import ghidra.framework.plugintool.PluginTool;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.pcode.emu.PcodeMachine;
import ghidra.pcode.exec.trace.TraceEmulationIntegration.Writer;
import ghidra.util.classfinder.ExtensionPoint;
/**
* A factory for configuring and creating a Debugger-integrated emulator
*/
public interface DebuggerPcodeEmulatorFactory extends ExtensionPoint {
public interface EmulatorFactory extends ExtensionPoint {
// TODO: Config options, use ModelFactory as a model
/**
@@ -33,23 +32,12 @@ public interface DebuggerPcodeEmulatorFactory extends ExtensionPoint {
*/
String getTitle();
/**
* Create the emulator
*
* @param tool the tool creating the emulator
* @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 target if applicable, the live target
* @return the emulator
*/
DebuggerPcodeMachine<?> create(PluginTool tool, TracePlatform platform, long snap,
Target target);
/**
* Create the emulator
*
* @param access the trace-and-debugger access shim
* @return the emulator
* @param writer the Debugger's emulation callbacks for UI integration
* @return the emulator with callbacks installed
*/
DebuggerPcodeMachine<?> create(PcodeDebuggerAccess access);
PcodeMachine<?> create(PcodeDebuggerAccess access, Writer writer);
}
@@ -4,9 +4,9 @@
* 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.
@@ -17,7 +17,7 @@ package ghidra.debug.api.emulation;
import java.util.concurrent.CompletableFuture;
import ghidra.generic.util.datastruct.SemisparseByteArray;
import ghidra.pcode.exec.PcodeExecutorStatePiece;
import ghidra.pcode.exec.trace.data.PcodeTraceMemoryAccess;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
@@ -53,11 +53,12 @@ public interface PcodeDebuggerMemoryAccess
* 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 piece the destination state piece
* @param unknown the address set to read
* @return true if any bytes were read, false if there was no effect
* @return the parts of {@code unknown} that <em>still haven't</em> been read
*/
boolean readFromStaticImages(SemisparseByteArray bytes, AddressSetView unknown);
AddressSetView readFromStaticImages(PcodeExecutorStatePiece<byte[], byte[]> piece,
AddressSetView unknown);
/**
* Instruct the associated recorder to write target memory
@@ -2,9 +2,9 @@ AutoMapSpec
AutoReadMemorySpecFactory
DebuggerMappingOpinion
DebuggerModelFactory
DebuggerPcodeEmulatorFactory
DebuggerPlatformOpinion
DebuggerProgramLaunchOpinion
DebuggerRegisterColumnFactory
DisassemblyInject
EmulatorFactory
LocationTrackingSpecFactory
@@ -37,7 +37,6 @@ import ghidra.app.services.DebuggerEmulationService.EmulatorStateListener;
import ghidra.app.services.DebuggerTraceManagerService.ActivationCause;
import ghidra.async.AsyncUtils;
import ghidra.debug.api.control.ControlMode;
import ghidra.debug.api.emulation.DebuggerPcodeMachine;
import ghidra.debug.api.model.DebuggerObjectActionContext;
import ghidra.debug.api.target.ActionName;
import ghidra.debug.api.target.Target;
@@ -47,6 +46,7 @@ import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.pcode.emu.PcodeMachine;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.trace.model.*;
@@ -382,7 +382,7 @@ public class DebuggerControlPlugin extends AbstractDebuggerPlugin
return true;
}
private DebuggerPcodeMachine<?> getBusyEmulator() {
private PcodeMachine<?> getBusyEmulator() {
/**
* NOTE: Could search for current trace, but task manager will only allow one to actually
* run at a time. Best not let the user queue a bunch up if another trace's emulator is
@@ -430,7 +430,7 @@ public class DebuggerControlPlugin extends AbstractDebuggerPlugin
if (emulationService == null) {
return;
}
DebuggerPcodeMachine<?> emu = getBusyEmulator();
PcodeMachine<?> emu = getBusyEmulator();
emu.setSuspended(true);
}
@@ -44,11 +44,11 @@ import ghidra.app.util.pcode.AbstractAppender;
import ghidra.app.util.pcode.AbstractPcodeFormatter;
import ghidra.async.SwingExecutorService;
import ghidra.base.widgets.table.DataTypeTableCellEditor;
import ghidra.debug.api.emulation.DebuggerPcodeMachine;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.docking.settings.Settings;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.pcode.emu.PcodeMachine;
import ghidra.pcode.emu.PcodeThread;
import ghidra.pcode.exec.*;
import ghidra.program.model.address.AddressSpace;
@@ -853,7 +853,7 @@ public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter {
populateSingleton(EnumPcodeRow.DECODE);
return;
}
DebuggerPcodeMachine<?> emu = emulationService.getCachedEmulator(trace, time);
PcodeMachine<?> emu = emulationService.getCachedEmulator(trace, time);
if (emu != null) {
clear();
doLoadPcodeFrameFromEmulator(emu);
@@ -868,7 +868,7 @@ public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter {
}, SwingExecutorService.LATER);
}
protected <T> void doLoadPcodeFrameFromEmulator(DebuggerPcodeMachine<T> emu) {
protected <T> void doLoadPcodeFrameFromEmulator(PcodeMachine<T> emu) {
PcodeThread<T> thread = emu.getThread(current.getThread().getPath(), false);
if (thread == null) {
/**
@@ -1,31 +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.plugin.core.debug.service.emulation.data.DefaultPcodeDebuggerAccess;
import ghidra.debug.api.emulation.DebuggerPcodeEmulatorFactory;
import ghidra.debug.api.emulation.DebuggerPcodeMachine;
import ghidra.debug.api.target.Target;
import ghidra.framework.plugintool.PluginTool;
import ghidra.trace.model.guest.TracePlatform;
public abstract class AbstractDebuggerPcodeEmulatorFactory implements DebuggerPcodeEmulatorFactory {
@Override
public DebuggerPcodeMachine<?> create(PluginTool tool, TracePlatform platform, long snap,
Target target) {
return create(new DefaultPcodeDebuggerAccess(tool, target, platform, snap));
}
}
@@ -1,119 +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.Map;
import java.util.concurrent.*;
import generic.ULongSpan.ULongSpanSet;
import ghidra.debug.api.emulation.PcodeDebuggerDataAccess;
import ghidra.generic.util.datastruct.SemisparseByteArray;
import ghidra.pcode.exec.AccessPcodeExecutionException;
import ghidra.pcode.exec.trace.BytesTracePcodeExecutorStatePiece;
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
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 AbstractRWTargetCachedSpace(Language language, AddressSpace space,
PcodeTraceDataAccess backing, SemisparseByteArray bytes, AddressSet written) {
super(language, space, backing, bytes, written);
}
protected abstract ULongSpanSet readUninitializedFromTarget(ULongSpanSet uninitialized);
@Override
protected ULongSpanSet readUninitializedFromBacking(ULongSpanSet uninitialized) {
uninitialized = readUninitializedFromTarget(uninitialized);
if (uninitialized.isEmpty()) {
return uninitialized;
}
return super.readUninitializedFromBacking(uninitialized);
// TODO: What to flush when bytes in the trace change?
}
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> {
public TargetBackedSpaceMap() {
super();
}
protected TargetBackedSpaceMap(Map<AddressSpace, CachedSpace> spaces) {
super(spaces);
}
@Override
public CachedSpace fork(CachedSpace s) {
return s.fork();
}
@Override
protected PcodeDebuggerDataAccess getBacking(AddressSpace space) {
return data;
}
}
}
@@ -1,64 +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.debug.api.emulation.DebuggerPcodeMachine;
import ghidra.debug.api.emulation.PcodeDebuggerAccess;
import ghidra.pcode.emu.PcodeEmulator;
import ghidra.pcode.emu.PcodeThread;
import ghidra.pcode.exec.trace.BytesTracePcodeEmulator;
import ghidra.pcode.exec.trace.TracePcodeExecutorState;
/**
* A trace emulator that knows how to read target memory when necessary
*
* <p>
* This is the default emulator used by the Debugger UI to perform interpolation and extrapolation.
* For standalone scripting, consider using {@link BytesTracePcodeEmulator} or {@link PcodeEmulator}
* instead. The former readily reads and records its state to traces, while the latter is the
* simplest use case. See scripts ending in {@code EmuExampleScript} for example uses.
*
* <p>
* This emulator must always be run in its own thread, or at least a thread that can never lock the
* UI. It blocks on target reads so that execution can proceed synchronously. Probably the most
* suitable option is to use a background task.
*/
public class BytesDebuggerPcodeEmulator extends BytesTracePcodeEmulator
implements DebuggerPcodeMachine<byte[]> {
protected final PcodeDebuggerAccess access;
/**
* Create the emulator
*
* @param access the trace-and-debugger access shim
*/
public BytesDebuggerPcodeEmulator(PcodeDebuggerAccess access) {
super(access);
this.access = access;
}
@Override
public TracePcodeExecutorState<byte[]> createSharedState() {
return new RWTargetMemoryPcodeExecutorState(access.getDataForSharedState(), Mode.RO);
}
@Override
public TracePcodeExecutorState<byte[]> createLocalState(PcodeThread<byte[]> emuThread) {
return new RWTargetRegistersPcodeExecutorState(access.getDataForLocalState(emuThread, 0),
Mode.RO);
}
}
@@ -0,0 +1,196 @@
/* ###
* 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.debug.api.emulation.*;
import ghidra.pcode.emu.PcodeEmulator;
import ghidra.pcode.emu.PcodeThread;
import ghidra.pcode.exec.*;
import ghidra.pcode.exec.trace.TraceEmulationIntegration;
import ghidra.pcode.exec.trace.TraceEmulationIntegration.*;
import ghidra.pcode.exec.trace.data.*;
import ghidra.program.model.address.*;
import ghidra.trace.model.thread.TraceThread;
/**
* A collection of static methods for integrating an emulator with a trace and target.
*/
public enum DebuggerEmulationIntegration {
;
private static <T> Writer createDelayedWrite(PcodeDebuggerAccess acc, Mode mode) {
Writer writer = new TraceWriter(acc);
writer.putHandler(new TargetBytesPieceHandler(mode));
return writer;
}
/**
* Create a writer (callbacks) that lazily loads data from the given access shim.
*
* <p>
* Reads may be redirected to the target. Writes are logged, but <em>never</em> sent to the
* target. This is used for forking emulation from a chosen snapshot and saving the results into
* (usually scratch) snapshots. This is the pattern used by the UI when emulation schedules are
* requested.
*
* @see TraceEmulationIntegration#bytesDelayedWrite(PcodeTraceAccess)
* @param from the access shim for lazy loads
* @return the writer
*/
public static Writer bytesDelayedWriteTrace(PcodeDebuggerAccess from) {
return createDelayedWrite(from, Mode.RO);
}
/**
* Create a writer (callbacks) that lazily loads data and immediately writes changes to the
* given access shim.
*
* <p>
* Reads may be redirected to the target. If redirected, writes are immediately sent to the
* target and presumably stored into the trace at the same snapshot as state is sourced.
*
* @see TraceEmulationIntegration#bytesImmediateWrite(PcodeTraceAccess)
* @param access the access shim for loads and stores
* @return the writer
*/
public static Writer bytesImmediateWriteTarget(PcodeDebuggerAccess access) {
return createDelayedWrite(access, Mode.RW);
}
/**
* Create state callbacks that lazily load data and immediately write changes to the given
* access shim.
*
* <p>
* Reads may be redirected to the target. If redirected, writes are immediately sent to the
* target and presumably stored into the trace at the same snapshot as state is sourced.
*
* <p>
* Use this instead of {@link #bytesImmediateWriteTarget(PcodeDebuggerAccess)} when interfacing
* directly with a {@link PcodeExecutorState} vice a {@link PcodeEmulator}.
*
* @see TraceEmulationIntegration#bytesImmediateWrite(PcodeTraceAccess, TraceThread, int)
* @param access the access shim for loads and stores
* @param thread the trace thread for register accesses
* @param frame the frame for register accesses, usually 0
* @return the callbacks
*/
public static PcodeStateCallbacks bytesImmediateWriteTarget(PcodeDebuggerAccess access,
TraceThread thread, int frame) {
PcodeDebuggerRegistersAccess regAcc = access.getDataForLocalState(thread, frame);
Writer writer = new TraceWriter(access) {
@Override
protected PcodeTraceRegistersAccess getRegAccess(PcodeThread<?> ignored) {
return regAcc;
}
};
writer.putHandler(new TargetBytesPieceHandler(Mode.RW));
return writer.wrapFor(null);
}
protected static <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);
}
}
/**
* An extension/replacement of the {@link BytesPieceHandler} that may redirect reads and writes
* to/from the target.
*
* @implNote Because piece handlers are keyed by (address-domain, value-domain), adding this to
* a writer will replace the default handler.
*/
public static class TargetBytesPieceHandler extends BytesPieceHandler {
protected final Mode mode;
public TargetBytesPieceHandler(Mode mode) {
this.mode = mode;
}
@Override
public AddressSetView readUninitialized(PcodeTraceDataAccess acc, PcodeThread<?> thread,
PcodeExecutorStatePiece<byte[], byte[]> piece, AddressSetView set) {
AddressSetView unknown = acc.intersectUnknown(set);
if (unknown.isEmpty()) {
return super.readUninitialized(acc, thread, piece, set);
}
if (acc instanceof PcodeDebuggerRegistersAccess regsAcc) {
if (regsAcc.isLive()) {
waitTimeout(regsAcc.readFromTargetRegisters(unknown));
}
/**
* Pass `set` to super, because even if regsAcc has just read from target into
* trace, we have yet to read from trace into state piece.
*/
return super.readUninitialized(acc, thread, piece, set);
}
if (acc instanceof PcodeDebuggerMemoryAccess memAcc) {
if (memAcc.isLive() && waitTimeout(memAcc.readFromTargetMemory(unknown))) {
unknown = memAcc.intersectUnknown(set);
if (unknown.isEmpty()) {
return super.readUninitialized(acc, thread, piece, set);
}
}
AddressSetView remains = memAcc.readFromStaticImages(piece, unknown);
/**
* In this case, readFromStaticImages has in fact modified the state piece, so we to
* compute what that was and remove it from the original request. The rest still
* needs to be read from the trace into the piece, which is done by the super call.
*/
AddressSetView readFromStatic = unknown.subtract(remains);
AddressSetView toReadFromTraceToPiece = set.subtract(readFromStatic);
return super.readUninitialized(memAcc, thread, piece, toReadFromTraceToPiece);
}
throw new AssertionError();
}
@Override
public boolean dataWritten(PcodeTraceDataAccess acc, AddressSet written,
PcodeThread<?> thread, PcodeExecutorStatePiece<byte[], byte[]> piece,
Address address, int length, byte[] value) {
if (!mode.isWriteTarget()) {
return false; // Log it as written, so it goes to the trace
}
if (address.isUniqueAddress()) {
return true;
}
if (acc instanceof PcodeDebuggerRegistersAccess regsAcc) {
if (!regsAcc.isLive()) {
return true;
}
waitTimeout(regsAcc.writeTargetRegister(address, value));
// Change should get recorded by back-end, if successful
}
else if (acc instanceof PcodeDebuggerMemoryAccess memAcc) {
if (!memAcc.isLive()) {
return true;
}
waitTimeout(memAcc.writeTargetMemory(address, value));
// Change should get recorded by back-end, if successful
}
return true;
}
}
}
@@ -40,19 +40,24 @@ import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.event.TraceClosedPluginEvent;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.service.emulation.data.DefaultPcodeDebuggerAccess;
import ghidra.app.services.*;
import ghidra.async.AsyncLazyMap;
import ghidra.debug.api.control.ControlMode;
import ghidra.debug.api.emulation.DebuggerPcodeEmulatorFactory;
import ghidra.debug.api.emulation.DebuggerPcodeMachine;
import ghidra.debug.api.emulation.EmulatorFactory;
import ghidra.debug.api.modules.DebuggerStaticMappingChangeListener;
import ghidra.debug.api.target.Target;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.pcode.emu.PcodeMachine;
import ghidra.pcode.emu.PcodeMachine.AccessKind;
import ghidra.pcode.emu.PcodeMachine.SwiMode;
import ghidra.pcode.exec.InjectionErrorPcodeExecutionException;
import ghidra.pcode.exec.trace.TraceEmulationIntegration.Writer;
import ghidra.pcode.exec.trace.data.DefaultPcodeTraceAccess;
import ghidra.pcode.exec.trace.data.PcodeTraceAccess;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
@@ -64,7 +69,8 @@ import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.time.TraceSnapshot;
import ghidra.trace.model.time.schedule.*;
import ghidra.trace.model.time.schedule.Scheduler.RunResult;
import ghidra.util.*;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.datastruct.ListenerSet;
import ghidra.util.exception.CancelledException;
@@ -286,8 +292,8 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
}
}
protected DebuggerPcodeEmulatorFactory emulatorFactory =
new BytesDebuggerPcodeEmulatorFactory();
protected EmulatorFactory emulatorFactory =
new DefaultEmulatorFactory();
protected final Set<CacheKey> eldest = new LinkedHashSet<>();
protected final NavigableMap<CacheKey, CachedEmulator> cache = new TreeMap<>();
@@ -359,8 +365,8 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
DockingAction actionEmulateProgram;
DockingAction actionEmulateAddThread;
DockingAction actionInvalidateCache;
Map<Class<? extends DebuggerPcodeEmulatorFactory>, ToggleDockingAction> //
actionsChooseEmulatorFactory = new HashMap<>();
Map<Class<? extends EmulatorFactory>, ToggleDockingAction> actionsChooseEmulatorFactory =
new HashMap<>();
final ChangeListener classChangeListener = this::classesChanged;
@@ -400,7 +406,7 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
updateConfigureEmulatorStates();
}
private ToggleDockingAction createActionChooseEmulator(DebuggerPcodeEmulatorFactory factory) {
private ToggleDockingAction createActionChooseEmulator(EmulatorFactory factory) {
ToggleDockingAction action = ConfigureEmulatorAction.builder(this)
.menuPath(DebuggerPluginPackage.NAME, ConfigureEmulatorAction.NAME,
factory.getTitle())
@@ -412,21 +418,18 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
}
private void updateConfigureEmulatorStates() {
Map<Class<? extends DebuggerPcodeEmulatorFactory>, DebuggerPcodeEmulatorFactory> byClass =
Map<Class<? extends EmulatorFactory>, EmulatorFactory> byClass =
getEmulatorFactories().stream()
.collect(Collectors.toMap(DebuggerPcodeEmulatorFactory::getClass,
Objects::requireNonNull));
Iterator<Entry<Class<? extends DebuggerPcodeEmulatorFactory>, ToggleDockingAction>> it =
.collect(Collectors.toMap(EmulatorFactory::getClass, Objects::requireNonNull));
Iterator<Entry<Class<? extends EmulatorFactory>, ToggleDockingAction>> it =
actionsChooseEmulatorFactory.entrySet().iterator();
while (it.hasNext()) {
Entry<Class<? extends DebuggerPcodeEmulatorFactory>, ToggleDockingAction> ent =
it.next();
Entry<Class<? extends EmulatorFactory>, ToggleDockingAction> ent = it.next();
if (!byClass.keySet().contains(ent.getKey())) {
tool.removeAction(ent.getValue());
}
}
for (Entry<Class<? extends DebuggerPcodeEmulatorFactory>, DebuggerPcodeEmulatorFactory> ent : byClass
.entrySet()) {
for (Entry<Class<? extends EmulatorFactory>, EmulatorFactory> ent : byClass.entrySet()) {
if (!actionsChooseEmulatorFactory.containsKey(ent.getKey())) {
ToggleDockingAction action = createActionChooseEmulator(ent.getValue());
action.setSelected(ent.getKey() == emulatorFactory.getClass());
@@ -557,7 +560,7 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
invalidateCache();
}
private void configureEmulatorActivated(DebuggerPcodeEmulatorFactory factory) {
private void configureEmulatorActivated(EmulatorFactory factory) {
// TODO: Pull up config page. Tool Options? Program/Trace Options?
setEmulatorFactory(factory);
}
@@ -578,12 +581,12 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
}
@Override
public Collection<DebuggerPcodeEmulatorFactory> getEmulatorFactories() {
return ClassSearcher.getInstances(DebuggerPcodeEmulatorFactory.class);
public Collection<EmulatorFactory> getEmulatorFactories() {
return ClassSearcher.getInstances(EmulatorFactory.class);
}
@Override
public synchronized void setEmulatorFactory(DebuggerPcodeEmulatorFactory factory) {
public synchronized void setEmulatorFactory(EmulatorFactory factory) {
emulatorFactory = Objects.requireNonNull(factory);
for (ToggleDockingAction toggle : actionsChooseEmulatorFactory.values()) {
toggle.setSelected(false);
@@ -598,7 +601,7 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
}
@Override
public synchronized DebuggerPcodeEmulatorFactory getEmulatorFactory() {
public synchronized EmulatorFactory getEmulatorFactory() {
return emulatorFactory;
}
@@ -640,7 +643,7 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
return task.future;
}
protected void installBreakpoints(Trace trace, long snap, DebuggerPcodeMachine<?> emu) {
protected void installBreakpoints(Trace trace, long snap, PcodeMachine<?> emu) {
Lifespan span = Lifespan.at(snap);
TraceBreakpointManager bm = trace.getBreakpointManager();
for (AddressSpace as : trace.getBaseAddressFactory().getAddressSpaces()) {
@@ -696,7 +699,7 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
// TODO: Handle errors, and add to proper place in cache?
// TODO: Finish partially-executed instructions?
try (BusyEmu be = new BusyEmu(ancestor.getValue())) {
DebuggerPcodeMachine<?> emu = be.ce.emulator();
PcodeMachine<?> emu = be.ce.emulator();
emu.clearAllInjects();
emu.clearAccessBreakpoints();
@@ -710,9 +713,13 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
return be.dup();
}
}
DebuggerPcodeMachine<?> emu = emulatorFactory.create(tool, platform, time.getSnap(),
targetService == null ? null : targetService.getTarget(trace));
try (BusyEmu be = new BusyEmu(new CachedEmulator(key.trace, emu))) {
Target target = targetService == null ? null : targetService.getTarget(trace);
DefaultPcodeDebuggerAccess from =
new DefaultPcodeDebuggerAccess(tool, target, platform, time.getSnap());
Writer writer = DebuggerEmulationIntegration.bytesDelayedWriteTrace(from);
PcodeMachine<?> emu = emulatorFactory.create(from, writer);
try (BusyEmu be = new BusyEmu(new CachedEmulator(key.trace, emu, writer))) {
installBreakpoints(key.trace, key.time.getSnap(), be.ce.emulator());
monitor.initialize(time.totalTickCount());
createRegisterSpaces(trace, time, monitor);
@@ -741,7 +748,9 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
destSnap = key.trace.getTimeManager().findScratchSnapshot(key.time);
destSnap.setDescription("Emulated");
try {
ce.emulator().writeDown(key.platform, destSnap.getKey(), key.time.getSnap());
PcodeTraceAccess into = new DefaultPcodeTraceAccess(key.platform, destSnap.getKey(),
key.time.getSnap());
ce.writer().writeDown(into);
TraceThread lastThread = key.time.getLastThread(key.trace);
destSnap.setEventThread(lastThread);
}
@@ -870,7 +879,7 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
}
@Override
public DebuggerPcodeMachine<?> getCachedEmulator(Trace trace, TraceSchedule time) {
public PcodeMachine<?> getCachedEmulator(Trace trace, TraceSchedule time) {
CachedEmulator ce =
cache.get(new CacheKey(trace.getPlatformManager().getHostPlatform(), time));
return ce == null || !ce.isValid() ? null : ce.emulator();
@@ -4,9 +4,9 @@
* 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.
@@ -15,13 +15,16 @@
*/
package ghidra.app.plugin.core.debug.service.emulation;
import ghidra.debug.api.emulation.DebuggerPcodeMachine;
import ghidra.debug.api.emulation.EmulatorFactory;
import ghidra.debug.api.emulation.PcodeDebuggerAccess;
import ghidra.pcode.emu.PcodeEmulator;
import ghidra.pcode.emu.PcodeMachine;
import ghidra.pcode.exec.trace.TraceEmulationIntegration.Writer;
/**
* The Debugger's default emulator factory
*/
public class BytesDebuggerPcodeEmulatorFactory extends AbstractDebuggerPcodeEmulatorFactory {
public class DefaultEmulatorFactory implements EmulatorFactory {
// TODO: Config options:
// 1) userop library
@@ -31,7 +34,7 @@ public class BytesDebuggerPcodeEmulatorFactory extends AbstractDebuggerPcodeEmul
}
@Override
public DebuggerPcodeMachine<?> create(PcodeDebuggerAccess access) {
return new BytesDebuggerPcodeEmulator(access);
public PcodeMachine<?> create(PcodeDebuggerAccess access, Writer writer) {
return new PcodeEmulator(access.getLanguage(), writer.callbacks());
}
}
@@ -1,44 +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.debug.api.emulation.PcodeDebuggerMemoryAccess;
import ghidra.pcode.exec.trace.*;
/**
* 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));
}
protected RWTargetMemoryPcodeExecutorState(
TracePcodeExecutorStatePiece<byte[], byte[]> piece) {
super(piece);
}
@Override
public RWTargetMemoryPcodeExecutorState fork() {
return new RWTargetMemoryPcodeExecutorState(piece.fork());
}
}
@@ -1,158 +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.Map;
import java.util.concurrent.CompletableFuture;
import generic.ULongSpan;
import generic.ULongSpan.ULongSpanSet;
import ghidra.debug.api.emulation.PcodeDebuggerDataAccess;
import ghidra.debug.api.emulation.PcodeDebuggerMemoryAccess;
import ghidra.generic.util.datastruct.SemisparseByteArray;
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;
}
protected RWTargetMemoryCachedSpace(Language language, AddressSpace space,
PcodeDebuggerMemoryAccess backing, SemisparseByteArray bytes, AddressSet written) {
super(language, space, backing, bytes, written);
this.backing = backing;
}
@Override
public RWTargetMemoryCachedSpace fork() {
return new RWTargetMemoryCachedSpace(language, space, backing, bytes.fork(),
new AddressSet(written));
}
@Override
protected ULongSpanSet readUninitializedFromTarget(ULongSpanSet uninitialized) {
if (space.isUniqueSpace()) {
return uninitialized;
}
AddressSetView unknown;
AddressSet addrsUninit = addrSet(uninitialized);
unknown = backing.intersectUnknown(addrsUninit);
if (unknown.isEmpty()) {
return uninitialized;
}
if (backing.isLive() && waitTimeout(backing.readFromTargetMemory(unknown))) {
unknown = backing.intersectUnknown(addrsUninit);
if (unknown.isEmpty()) {
return uninitialized;
}
}
if (backing.readFromStaticImages(bytes, unknown)) {
ULongSpan bound = uninitialized.bound();
return bytes.getUninitialized(bound.min(), bound.max());
}
return uninitialized;
}
@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;
}
class WRTargetMemorySpaceMap extends TargetBackedSpaceMap {
public WRTargetMemorySpaceMap() {
super();
}
protected WRTargetMemorySpaceMap(Map<AddressSpace, CachedSpace> spaceMap) {
super(spaceMap);
}
@Override
public AbstractSpaceMap<CachedSpace> fork() {
return new WRTargetMemorySpaceMap(fork(spaces));
}
@Override
protected CachedSpace newSpace(AddressSpace space, PcodeDebuggerDataAccess data) {
return new RWTargetMemoryCachedSpace(language, space,
(PcodeDebuggerMemoryAccess) data);
}
}
@Override
protected AbstractSpaceMap<CachedSpace> newSpaceMap() {
return new WRTargetMemorySpaceMap();
}
}
@@ -1,44 +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.debug.api.emulation.PcodeDebuggerRegistersAccess;
import ghidra.pcode.exec.trace.*;
/**
* 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));
}
protected RWTargetRegistersPcodeExecutorState(
TracePcodeExecutorStatePiece<byte[], byte[]> piece) {
super(piece);
}
@Override
public RWTargetRegistersPcodeExecutorState fork() {
return new RWTargetRegistersPcodeExecutorState(piece.fork());
}
}
@@ -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.Map;
import java.util.concurrent.CompletableFuture;
import generic.ULongSpan.ULongSpanSet;
import ghidra.debug.api.emulation.PcodeDebuggerDataAccess;
import ghidra.debug.api.emulation.PcodeDebuggerRegistersAccess;
import ghidra.generic.util.datastruct.SemisparseByteArray;
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;
}
protected RWTargetRegistersCachedSpace(Language language, AddressSpace space,
PcodeDebuggerRegistersAccess backing, SemisparseByteArray bytes,
AddressSet written) {
super(language, space, backing, bytes, written);
this.backing = backing;
}
@Override
public RWTargetRegistersCachedSpace fork() {
return new RWTargetRegistersCachedSpace(language, uniqueSpace, backing, bytes.fork(),
new AddressSet(written));
}
@Override
protected ULongSpanSet readUninitializedFromTarget(ULongSpanSet uninitialized) {
if (space.isUniqueSpace() || !backing.isLive()) {
return uninitialized;
}
AddressSet addrsUninit = addrSet(uninitialized);
AddressSetView unknown = backing.intersectUnknown(addrsUninit);
waitTimeout(backing.readFromTargetRegisters(unknown));
return uninitialized;
}
@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;
}
class WRTargetRegistersSpaceMap extends TargetBackedSpaceMap {
public WRTargetRegistersSpaceMap() {
super();
}
protected WRTargetRegistersSpaceMap(Map<AddressSpace, CachedSpace> spaceMap) {
super(spaceMap);
}
@Override
public AbstractSpaceMap<CachedSpace> fork() {
return new WRTargetRegistersSpaceMap(fork(spaces));
}
@Override
protected CachedSpace newSpace(AddressSpace space, PcodeDebuggerDataAccess data) {
return new RWTargetRegistersCachedSpace(language, space,
(PcodeDebuggerRegistersAccess) data);
}
}
@Override
protected AbstractSpaceMap<CachedSpace> newSpaceMap() {
return new WRTargetRegistersSpaceMap();
}
}
@@ -4,9 +4,9 @@
* 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.
@@ -43,10 +43,25 @@ public abstract class AbstractPcodeDebuggerAccess<S extends PcodeDebuggerMemoryA
* @param snap the associated snap
*/
public AbstractPcodeDebuggerAccess(ServiceProvider provider, Target target,
TracePlatform platform,
long snap) {
TracePlatform platform, long snap) {
super(platform, snap);
this.provider = provider;
this.target = target;
}
/**
* Construct a shim
*
* @param provider the service provider (usually the tool)
* @param target the target
* @param platform the associated platform, having the same trace as the recorder
* @param snap the associated snap
* @param threadsSnap the snap to use when finding associated threads between trace and emulator
*/
public AbstractPcodeDebuggerAccess(ServiceProvider provider, Target target,
TracePlatform platform, long snap, long threadsSnap) {
super(platform, snap, threadsSnap);
this.provider = provider;
this.target = target;
}
}
@@ -4,9 +4,9 @@
* 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.
@@ -17,6 +17,8 @@ package ghidra.app.plugin.core.debug.service.emulation.data;
import ghidra.debug.api.target.Target;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.pcode.exec.trace.data.DefaultPcodeTraceAccess;
import ghidra.pcode.exec.trace.data.PcodeTraceAccess;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.thread.TraceThread;
@@ -40,6 +42,31 @@ public class DefaultPcodeDebuggerAccess extends
super(provider, target, platform, snap);
}
/**
* Construct a shim
*
* @param provider the service provider (usually the tool)
* @param target the target
* @param platform the associated platform, having the same trace as the recorder
* @param snap the associated snap
* @param threadsSnap the snap to use when finding associated threads between trace and emulator
*/
public DefaultPcodeDebuggerAccess(ServiceProvider provider, Target target,
TracePlatform platform, long snap, long threadsSnap) {
super(provider, target, platform, snap, threadsSnap);
}
/**
* {@inheritDoc}
*
* @implNote This does <em>not</em> return a Debugger access shim, but a Trace one, since we
* never expect a delayed write to affect the target.
*/
@Override
public PcodeTraceAccess deriveForWrite(long snap) {
return new DefaultPcodeTraceAccess(platform, snap, threadsSnap);
}
@Override
protected DefaultPcodeDebuggerMemoryAccess newDataForSharedState() {
return new DefaultPcodeDebuggerMemoryAccess(provider, target, platform, snap, viewport);
@@ -4,9 +4,9 @@
* 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.
@@ -24,7 +24,7 @@ import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
import ghidra.debug.api.emulation.PcodeDebuggerMemoryAccess;
import ghidra.debug.api.target.Target;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.generic.util.datastruct.SemisparseByteArray;
import ghidra.pcode.exec.PcodeExecutorStatePiece;
import ghidra.pcode.exec.trace.data.DefaultPcodeTraceMemoryAccess;
import ghidra.pcode.exec.trace.data.PcodeTracePropertyAccess;
import ghidra.program.model.address.*;
@@ -94,16 +94,18 @@ public class DefaultPcodeDebuggerMemoryAccess extends DefaultPcodeTraceMemoryAcc
}
@Override
public boolean readFromStaticImages(SemisparseByteArray bytes, AddressSetView guestView) {
// TODO: Expand to block? DON'T OVERWRITE KNOWN!
public AddressSetView readFromStaticImages(PcodeExecutorStatePiece<byte[], byte[]> piece,
AddressSetView guestView) {
// NOTE: If we expand to block, DON'T OVERWRITE KNOWN!
DebuggerStaticMappingService mappingService =
provider.getService(DebuggerStaticMappingService.class);
if (mappingService == null) {
return false;
return guestView;
}
AddressSet remains = new AddressSet(guestView);
try {
return new AbstractMappedMemoryBytesVisitor(mappingService, new byte[4096]) {
boolean result = new AbstractMappedMemoryBytesVisitor(mappingService, new byte[4096]) {
@Override
protected int read(Memory memory, Address addr, byte[] dest, int size)
throws MemoryAccessException {
@@ -128,9 +130,12 @@ public class DefaultPcodeDebuggerMemoryAccess extends DefaultPcodeTraceMemoryAcc
@Override
protected void visitData(Address hostAddr, byte[] data, int size) {
Address guestAddr = platform.mapHostToGuest(hostAddr);
bytes.putData(guestAddr.getOffset(), data, 0, size);
piece.setVarInternal(guestAddr.getAddressSpace(), guestAddr.getOffset(), size,
data);
remains.delete(guestAddr, guestAddr.add(size));
}
}.visit(platform.getTrace(), snap, platform.mapGuestToHost(guestView));
return result ? remains : guestView;
}
catch (MemoryAccessException e) {
throw new AssertionError(e);
@@ -42,6 +42,11 @@ class SymPcodeArithmetic implements PcodeArithmetic<Sym> {
this.language = cSpec.getLanguage();
}
@Override
public Class<Sym> getDomain() {
return Sym.class;
}
@Override
public Endian getEndian() {
return language.isBigEndian() ? Endian.BIG : Endian.LITTLE;
@@ -16,12 +16,12 @@
package ghidra.app.plugin.core.debug.stack;
import java.util.*;
import java.util.stream.Stream;
import ghidra.app.plugin.core.debug.stack.Sym.*;
import ghidra.app.plugin.core.debug.stack.SymStateSpace.SymEntry;
import ghidra.pcode.exec.PcodeArithmetic;
import ghidra.pcode.exec.*;
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
import ghidra.pcode.exec.PcodeExecutorState;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.lang.*;
@@ -53,6 +53,8 @@ public class SymPcodeExecutorState implements PcodeExecutorState<Sym> {
/**
* Construct a new state for the given program
*
* @param program the program under analysis
*/
public SymPcodeExecutorState(Program program) {
this.program = program;
@@ -101,6 +103,11 @@ public class SymPcodeExecutorState implements PcodeExecutorState<Sym> {
return arithmetic;
}
@Override
public Stream<PcodeExecutorStatePiece<?, ?>> streamPieces() {
return Stream.of(this);
}
@Override
public void setVar(AddressSpace space, Sym offset, int size, boolean quantize,
Sym val) {
@@ -123,6 +130,11 @@ public class SymPcodeExecutorState implements PcodeExecutorState<Sym> {
}
}
@Override
public void setVarInternal(AddressSpace space, Sym offset, int size, Sym val) {
setVar(space, offset, size, false, val);
}
@Override
public Sym getVar(AddressSpace space, Sym offset, int size, boolean quantize,
Reason reason) {
@@ -142,6 +154,11 @@ public class SymPcodeExecutorState implements PcodeExecutorState<Sym> {
return Sym.opaque();
}
@Override
public Sym getVarInternal(AddressSpace space, Sym offset, int size, Reason reason) {
return getVar(space, offset, size, false, reason);
}
@Override
public Map<Register, Sym> getRegisterValues() {
return Map.of();
@@ -159,13 +176,15 @@ public class SymPcodeExecutorState implements PcodeExecutorState<Sym> {
}
@Override
public SymPcodeExecutorState fork() {
public SymPcodeExecutorState fork(PcodeStateCallbacks cb) {
return new SymPcodeExecutorState(program, arithmetic, stackSpace.fork(),
registerSpace.fork(), uniqueSpace.fork());
}
/**
* Create a new state whose registers are forked from those of this state
*
* @return this fork
*/
public SymPcodeExecutorState forkRegs() {
return new SymPcodeExecutorState(program, arithmetic, new SymStateSpace(),
@@ -281,7 +300,7 @@ public class SymPcodeExecutorState implements PcodeExecutorState<Sym> {
* Any entry of the form (reg, v:Deref) is collected as (reg, [Stack]:v.offset). Note that the
* size of the stack entry is implied by the size of the register.
*
* @return
* @return the map from register to stack address
*/
public Map<Register, Address> computeMapUsingRegisters() {
Map<Register, Address> result = new HashMap<>();
@@ -18,16 +18,16 @@ package ghidra.pcode.exec;
import java.math.BigInteger;
import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Stream;
import ghidra.app.nav.NavigationUtils;
import ghidra.app.plugin.core.debug.service.emulation.*;
import ghidra.app.plugin.core.debug.service.emulation.DebuggerEmulationIntegration;
import ghidra.app.plugin.core.debug.service.emulation.data.DefaultPcodeDebuggerAccess;
import ghidra.app.plugin.processors.sleigh.SleighException;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.pcode.emu.ThreadPcodeExecutorState;
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
import ghidra.pcode.exec.SleighProgramCompiler.ErrorCollectingPcodeParser;
@@ -241,28 +241,15 @@ public enum DebuggerPcodeUtils {
throw new IllegalArgumentException("Coordinates have no trace");
}
TracePlatform platform = coordinates.getPlatform();
Language language = platform.getLanguage();
if (!(language instanceof SleighLanguage)) {
if (!(platform.getLanguage() instanceof SleighLanguage language)) {
throw new IllegalArgumentException(
"Given trace or platform does not use a Sleigh language");
}
DefaultPcodeDebuggerAccess access = new DefaultPcodeDebuggerAccess(provider,
coordinates.getTarget(), platform, coordinates.getViewSnap());
PcodeExecutorState<byte[]> shared =
new RWTargetMemoryPcodeExecutorState(access.getDataForSharedState(), Mode.RW);
if (coordinates.getThread() == null) {
return shared;
}
PcodeExecutorState<byte[]> local = new RWTargetRegistersPcodeExecutorState(
access.getDataForLocalState(coordinates.getThread(), coordinates.getFrame()),
Mode.RW);
return new ThreadPcodeExecutorState<>(shared, local) {
@Override
public void clear() {
shared.clear();
local.clear();
}
};
PcodeStateCallbacks cb = DebuggerEmulationIntegration.bytesImmediateWriteTarget(access,
coordinates.getThread(), coordinates.getFrame());
return new BytesPcodeExecutorState(language, cb);
}
/**
@@ -279,9 +266,8 @@ public enum DebuggerPcodeUtils {
public static PcodeExecutor<byte[]> executorForCoordinates(ServiceProvider provider,
DebuggerCoordinates coordinates) {
PcodeExecutorState<byte[]> state = executorStateForCoordinates(provider, coordinates);
SleighLanguage slang = (SleighLanguage) state.getLanguage();
return new PcodeExecutor<>(slang, BytesPcodeArithmetic.forLanguage(slang), state,
SleighLanguage language = (SleighLanguage) state.getLanguage();
return new PcodeExecutor<>(language, BytesPcodeArithmetic.forLanguage(language), state,
Reason.INSPECT);
}
@@ -475,6 +461,11 @@ public enum DebuggerPcodeUtils {
this.location = location;
}
@Override
public Class<WatchValue> getDomain() {
return WatchValue.class;
}
@Override
public Endian getEndian() {
return bytes.getEndian();
@@ -590,9 +581,25 @@ public enum DebuggerPcodeUtils {
}
@Override
public WatchValuePcodeExecutorStatePiece fork() {
public Stream<PcodeExecutorStatePiece<?, ?>> streamPieces() {
return Stream.of(bytesPiece, statePiece, locationPiece, readsPiece);
}
@Override
public WatchValuePcodeExecutorStatePiece fork(PcodeStateCallbacks cb) {
return new WatchValuePcodeExecutorStatePiece(
bytesPiece.fork(), statePiece.fork(), locationPiece.fork(), readsPiece.fork());
bytesPiece.fork(cb),
statePiece.fork(cb),
locationPiece.fork(cb),
readsPiece.fork(cb));
}
@Override
public void setVarInternal(AddressSpace space, byte[] offset, int size, WatchValue val) {
bytesPiece.setVarInternal(space, offset, size, val.bytes.bytes);
statePiece.setVarInternal(space, offset, size, val.state);
locationPiece.setVarInternal(space, offset, size, val.location);
readsPiece.setVarInternal(space, offset, size, val.reads);
}
@Override
@@ -615,6 +622,17 @@ public enum DebuggerPcodeUtils {
readsPiece.getVar(space, offset, size, quantize, reason));
}
@Override
public WatchValue getVarInternal(AddressSpace space, byte[] offset, int size,
Reason reason) {
return new WatchValue(
new PrettyBytes(getLanguage().isBigEndian(),
bytesPiece.getVarInternal(space, offset, size, reason)),
statePiece.getVarInternal(space, offset, size, reason),
locationPiece.getVarInternal(space, offset, size, reason),
readsPiece.getVarInternal(space, offset, size, reason));
}
@Override
public Map<Register, WatchValue> getRegisterValues() {
Map<Register, WatchValue> result = new HashMap<>();
@@ -646,54 +664,21 @@ public enum DebuggerPcodeUtils {
}
}
public static class WatchValuePcodeExecutorState implements PcodeExecutorState<WatchValue> {
private WatchValuePcodeExecutorStatePiece piece;
public static class WatchValuePcodeExecutorState
extends AbstractPcodeExecutorState<byte[], WatchValue> {
public WatchValuePcodeExecutorState(WatchValuePcodeExecutorStatePiece piece) {
this.piece = piece;
public WatchValuePcodeExecutorState(PcodeExecutorStatePiece<byte[], WatchValue> piece) {
super(piece);
}
@Override
public Language getLanguage() {
return piece.getLanguage();
protected byte[] extractAddress(WatchValue value) {
return value.bytes.bytes;
}
@Override
public PcodeArithmetic<WatchValue> getArithmetic() {
return piece.arithmetic;
}
@Override
public WatchValuePcodeExecutorState fork() {
return new WatchValuePcodeExecutorState(piece.fork());
}
@Override
public void setVar(AddressSpace space, WatchValue offset, int size, boolean quantize,
WatchValue val) {
piece.setVar(space, offset.bytes.bytes, size, quantize, val);
}
@Override
public WatchValue getVar(AddressSpace space, WatchValue offset, int size,
boolean quantize,
Reason reason) {
return piece.getVar(space, offset.bytes.bytes, size, quantize, reason);
}
@Override
public Map<Register, WatchValue> getRegisterValues() {
return piece.getRegisterValues();
}
@Override
public MemBuffer getConcreteBuffer(Address address, Purpose purpose) {
return piece.getConcreteBuffer(address, purpose);
}
@Override
public void clear() {
piece.clear();
public WatchValuePcodeExecutorState fork(PcodeStateCallbacks cb) {
return new WatchValuePcodeExecutorState(piece.fork(cb));
}
}
@@ -1,102 +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.debug.auxiliary;
import org.apache.commons.lang3.tuple.Pair;
import ghidra.app.plugin.core.debug.service.emulation.*;
import ghidra.debug.api.emulation.DebuggerPcodeEmulatorFactory;
import ghidra.pcode.emu.PcodeThread;
import ghidra.pcode.emu.auxiliary.AuxEmulatorPartsFactory;
import ghidra.pcode.emu.auxiliary.AuxPcodeEmulator;
import ghidra.pcode.exec.trace.*;
import ghidra.pcode.exec.trace.auxiliary.AuxTraceEmulatorPartsFactory;
import ghidra.pcode.exec.trace.auxiliary.AuxTracePcodeEmulator;
/**
* The most capable auxiliary emulator parts factory
*
* <p>
* This can manufacture parts for an emulator that is fully integrated with the Debugger UI, as well
* as all the parts for the less integrated forms of the same emulator. The pattern of use is
* generally to implement {@link DebuggerPcodeEmulatorFactory}, allowing the UI to discover and
* instantiate the emulator, though they could also be created directly by scripts or plugins.
*
* <p>
* For an example of a fully-integrated solution using this interface, see the Taint Analyzer. Its
* project serves as an archetype for similar dynamic analysis employing p-code emulation.
*
* <p>
* We recommend implementors start with the methods declared in {@link AuxEmulatorPartsFactory} with
* the aim of creating a derivative of {@link AuxPcodeEmulator}. Note that one Debugger-integrated
* emulator parts factory can be used with all three of {@link AuxPcodeEmulator},
* {@link AuxTracePcodeEmulator}, {@link AuxTraceEmulatorPartsFactory}. Once the stand-alone
* emulator has been tested, proceed to the methods in {@link AuxTraceEmulatorPartsFactory} with the
* aim of creating a derivative of {@link AuxTracePcodeEmulator}. Most of the work here is in
* factoring the state objects and pieces to reduce code duplication among the stand-alone and
* trace-integrated states. Once the trace-integrated emulator is tested, then proceed to the
* methods declared here in {@link AuxDebuggerEmulatorPartsFactory} with the aim of creating a
* derivative of {@link AuxDebuggerPcodeEmulator}. Again, most of the work is in factoring the
* states to avoid code duplication. Once the Debugger-integrated emulator is tested, the final bit
* is to implement a {@link DebuggerPcodeEmulatorFactory} so that users can configure and create the
* emulator. Other UI pieces, e.g., actions, fields, and table columns, may be needed to facilitate
* user access to the emulator's auxiliary state. Furthermore, a userop library for accessing the
* auxiliary state is recommended, since Sleigh code can be executed by the user.
*
* @param <U> the type of auxiliary values
*/
public interface AuxDebuggerEmulatorPartsFactory<U> extends AuxTraceEmulatorPartsFactory<U> {
/**
* Create the shared (memory) state of a new Debugger-integrated emulator
*
* <p>
* This state is usually composed of pieces using {@link PairedTracePcodeExecutorStatePiece},
* but it does not have to be. It must incorporate the concrete piece provided. The state must
* be capable of lazily loading state from a trace, from a live target, and from mapped static
* programs. It must also be able to write its cache into the trace at another snapshot. The
* 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. 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
* @return the composed state
*/
TracePcodeExecutorState<Pair<byte[], U>> createDebuggerSharedState(
AuxDebuggerPcodeEmulator<U> emulator,
RWTargetMemoryPcodeExecutorStatePiece concrete);
/**
* Create the local (register) state of a new Debugger-integrated thread
*
* <p>
* Like
* {@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.
*
* @param emulator the emulator
* @param thread the new thread
* @param concrete the concrete piece
* @return the composed state
*/
TracePcodeExecutorState<Pair<byte[], U>> createDebuggerLocalState(
AuxDebuggerPcodeEmulator<U> emulator, PcodeThread<Pair<byte[], U>> thread,
RWTargetRegistersPcodeExecutorStatePiece concrete);
}
@@ -1,74 +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.debug.auxiliary;
import org.apache.commons.lang3.tuple.Pair;
import ghidra.app.plugin.core.debug.service.emulation.*;
import ghidra.debug.api.emulation.DebuggerPcodeMachine;
import ghidra.debug.api.emulation.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;
/**
* An Debugger-integrated emulator whose parts are manufactured by a
* {@link AuxDebuggerEmulatorPartsFactory}
*
* <p>
* See the parts factory interface and its super interfaces:
* <ul>
* <li>{@link AuxDebuggerEmulatorPartsFactory}</li>
* <li>{@link AuxTraceEmulatorPartsFactory}</li>
* <li>{@link AuxEmulatorPartsFactory}</li>
* </ul>
*
* @param <U> the type of auxiliary values
*/
public abstract class AuxDebuggerPcodeEmulator<U> extends AuxTracePcodeEmulator<U>
implements DebuggerPcodeMachine<Pair<byte[], U>> {
protected final PcodeDebuggerAccess access;
/**
* Create a new emulator
*
* @param access the trace-and-debugger access shim
*/
public AuxDebuggerPcodeEmulator(PcodeDebuggerAccess access) {
super(access);
this.access = access;
}
@Override
protected abstract AuxDebuggerEmulatorPartsFactory<U> getPartsFactory();
@Override
public TracePcodeExecutorState<Pair<byte[], U>> createSharedState() {
return getPartsFactory().createDebuggerSharedState(this,
new RWTargetMemoryPcodeExecutorStatePiece(access.getDataForSharedState(), Mode.RO));
}
@Override
public TracePcodeExecutorState<Pair<byte[], U>> createLocalState(
PcodeThread<Pair<byte[], U>> thread) {
return getPartsFactory().createDebuggerLocalState(this, thread,
new RWTargetRegistersPcodeExecutorStatePiece(access.getDataForLocalState(thread, 0),
Mode.RO));
}
}
@@ -33,16 +33,17 @@ import ghidra.app.plugin.assembler.Assemblers;
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerTest;
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin;
import ghidra.app.plugin.core.debug.gui.pcode.DebuggerPcodeStepperProvider.PcodeRowHtmlFormatter;
import ghidra.app.plugin.core.debug.service.emulation.BytesDebuggerPcodeEmulator;
import ghidra.app.plugin.core.debug.service.emulation.BytesDebuggerPcodeEmulatorFactory;
import ghidra.app.plugin.core.debug.service.emulation.DefaultEmulatorFactory;
import ghidra.app.plugin.core.debug.service.tracemgr.DebuggerTraceManagerServicePlugin;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.app.services.DebuggerEmulationService;
import ghidra.app.services.DebuggerTraceManagerService;
import ghidra.debug.api.emulation.DebuggerPcodeMachine;
import ghidra.debug.api.emulation.PcodeDebuggerAccess;
import ghidra.pcode.emu.PcodeEmulator;
import ghidra.pcode.emu.PcodeMachine;
import ghidra.pcode.exec.*;
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
import ghidra.pcode.exec.trace.TraceEmulationIntegration.Writer;
import ghidra.pcode.exec.trace.TraceSleighUtils;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Instruction;
@@ -103,7 +104,7 @@ public class DebuggerPcodeStepperProviderTest extends AbstractGhidraHeadedDebugg
tb.createObjectsFramesAndRegs(thread, Lifespan.nowOn(0), tb.host, 1);
PcodeExecutor<byte[]> init = TraceSleighUtils.buildByteExecutor(tb.trace, 0, thread, 0);
init.executeSleigh("pc = 0x00400000;");
init.executeSleigh("pc = 0x%s;".formatted(start));
Assembler asm = Assemblers.getAssembler(tb.trace.getFixedProgramView(0));
iit = asm.assemble(start, "imm r0, #0x123");
@@ -154,10 +155,10 @@ public class DebuggerPcodeStepperProviderTest extends AbstractGhidraHeadedDebugg
public void testCustomUseropDisplay() throws Exception {
populateTrace();
emuService.setEmulatorFactory(new BytesDebuggerPcodeEmulatorFactory() {
emuService.setEmulatorFactory(new DefaultEmulatorFactory() {
@Override
public DebuggerPcodeMachine<?> create(PcodeDebuggerAccess access) {
BytesDebuggerPcodeEmulator emu = new BytesDebuggerPcodeEmulator(access) {
public PcodeMachine<?> create(PcodeDebuggerAccess access, Writer writer) {
PcodeEmulator emu = new PcodeEmulator(access.getLanguage(), writer.callbacks()) {
@Override
protected PcodeUseropLibrary<byte[]> createUseropLibrary() {
return new AnnotatedPcodeUseropLibrary<byte[]>() {
@@ -38,11 +38,11 @@ import ghidra.app.plugin.core.debug.service.platform.DebuggerPlatformServicePlug
import ghidra.app.services.DebuggerEmulationService.EmulationResult;
import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.app.services.DebuggerTraceManagerService.ActivationCause;
import ghidra.debug.api.emulation.DebuggerPcodeMachine;
import ghidra.debug.api.platform.DebuggerPlatformMapper;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.pcode.emu.PcodeThread;
import ghidra.pcode.emu.*;
import ghidra.pcode.exec.*;
import ghidra.pcode.exec.trace.TraceEmulationIntegration.Writer;
import ghidra.pcode.utils.Utils;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
@@ -533,7 +533,7 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe
assertTrue(result1.error() instanceof InterruptPcodeExecutionException);
// Save this for comparison later
DebuggerPcodeMachine<?> emu = Unique.assertOne(emulationPlugin.cache.values()).emulator();
PcodeMachine<?> emu = Unique.assertOne(emulationPlugin.cache.values()).emulator();
// This will test if the one just hit gets ignored
EmulationResult result2 = emulationPlugin.run(trace.getPlatformManager().getHostPlatform(),
@@ -604,7 +604,7 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe
assertTrue(result1.error() instanceof InterruptPcodeExecutionException);
// Save this for comparison later
DebuggerPcodeMachine<?> emu = Unique.assertOne(emulationPlugin.cache.values()).emulator();
PcodeMachine<?> emu = Unique.assertOne(emulationPlugin.cache.values()).emulator();
// Now, step it forward to complete the instruction
emulationPlugin.emulate(trace.getPlatformManager().getHostPlatform(),
@@ -671,7 +671,7 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe
assertTrue(result1.error() instanceof PcodeExecutionException);
// Save this for comparison later
DebuggerPcodeMachine<?> emu = Unique.assertOne(emulationPlugin.cache.values()).emulator();
PcodeMachine<?> emu = Unique.assertOne(emulationPlugin.cache.values()).emulator();
// We shouldn't get any further
EmulationResult result2 = emulationPlugin.run(trace.getPlatformManager().getHostPlatform(),
@@ -979,12 +979,13 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe
TracePlatform host = tb.trace.getPlatformManager().getHostPlatform();
DefaultPcodeDebuggerAccess access =
new DefaultPcodeDebuggerAccess(tool, null, host, restartEmuSnap);
BytesDebuggerPcodeEmulator emulator = new BytesDebuggerPcodeEmulator(access);
Writer writer = DebuggerEmulationIntegration.bytesDelayedWriteTrace(access);
PcodeEmulator emulator = new PcodeEmulator(access.getLanguage(), writer.callbacks());
TraceSnapshot snapshot =
tb.trace.getTimeManager().createSnapshot("created new emulator thread");
long newSnap = snapshot.getKey();
emulator.writeDown(host, newSnap, newSnap);
writer.writeDown(newSnap);
TraceThread newTraceThread = ProgramEmulationUtils.doLaunchEmulationThread(tb.trace,
newSnap, program, tb.addr(0x00400000), addr(program, 0x00400000));
@@ -1,117 +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.trace;
import java.util.Map;
import generic.ULongSpan.ULongSpanSet;
import ghidra.generic.util.datastruct.SemisparseByteArray;
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.Language;
/**
* A state piece which can check for uninitialized reads
*
* <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 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,
PcodeTraceDataAccess backing) {
super(language, space, backing);
}
protected CheckedCachedSpace(Language language, AddressSpace space,
PcodeTraceDataAccess backing, SemisparseByteArray bytes, AddressSet written) {
super(language, space, backing, bytes, written);
}
@Override
public CachedSpace fork() {
return new CheckedCachedSpace(language, space, backing, bytes.fork(),
new AddressSet(written));
}
@Override
public byte[] read(long offset, int size, Reason reason) {
ULongSpanSet uninitialized =
bytes.getUninitialized(offset, offset + size - 1);
if (!uninitialized.isEmpty()) {
size = checkUninitialized(backing, space.getAddress(offset), size,
addrSet(uninitialized));
}
return super.read(offset, size, reason);
}
}
/**
* Construct a piece
*
* @param data the trace-data access shim
*/
public AbstractCheckedTraceCachedWriteBytesPcodeExecutorStatePiece(PcodeTraceDataAccess data) {
super(data);
}
protected AbstractCheckedTraceCachedWriteBytesPcodeExecutorStatePiece(PcodeTraceDataAccess data,
AbstractSpaceMap<CachedSpace> spaceMap) {
super(data, spaceMap);
}
protected class CheckedCachedSpaceMap extends TraceBackedSpaceMap {
public CheckedCachedSpaceMap() {
super();
}
protected CheckedCachedSpaceMap(Map<AddressSpace, CachedSpace> spaces) {
super(spaces);
}
@Override
protected CachedSpace newSpace(AddressSpace space, PcodeTraceDataAccess backing) {
return new CheckedCachedSpace(language, space, backing);
}
@Override
public CheckedCachedSpaceMap fork() {
return new CheckedCachedSpaceMap(fork(spaces));
}
}
@Override
protected AbstractSpaceMap<CachedSpace> newSpaceMap() {
return new CheckedCachedSpaceMap();
}
/**
* Decide what to do, given that a portion of a read is uninitialized
*
* @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
*/
protected abstract int checkUninitialized(PcodeTraceDataAccess backing, Address start,
int size, AddressSet uninitialized);
}
@@ -17,8 +17,6 @@ package ghidra.pcode.exec.trace;
import java.util.*;
import javax.help.UnsupportedOperationException;
import ghidra.pcode.exec.*;
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
@@ -40,29 +38,31 @@ import ghidra.program.model.mem.MemBuffer;
*/
public class AddressesReadTracePcodeExecutorStatePiece
extends AbstractLongOffsetPcodeExecutorStatePiece<byte[], AddressSetView, AddressSpace>
implements TracePcodeExecutorStatePiece<byte[], AddressSetView> {
implements PcodeExecutorStatePiece<byte[], AddressSetView> {
protected final PcodeTraceDataAccess data;
private final Map<Long, AddressSetView> unique;
protected AddressesReadTracePcodeExecutorStatePiece(PcodeTraceDataAccess data,
Map<Long, AddressSetView> unique) {
super(data.getLanguage(), BytesPcodeArithmetic.forLanguage(data.getLanguage()),
AddressesReadPcodeArithmetic.INSTANCE, PcodeStateCallbacks.NONE);
this.data = data;
this.unique = unique;
}
/**
* Construct the state piece
*
* @param data the trace data access shim
*/
public AddressesReadTracePcodeExecutorStatePiece(PcodeTraceDataAccess data) {
super(data.getLanguage(), BytesPcodeArithmetic.forLanguage(data.getLanguage()),
AddressesReadPcodeArithmetic.INSTANCE);
this.data = data;
this.unique = new HashMap<>();
this(data, new HashMap<>());
}
protected AddressesReadTracePcodeExecutorStatePiece(PcodeTraceDataAccess data,
Map<Long, AddressSetView> unique) {
super(data.getLanguage(), BytesPcodeArithmetic.forLanguage(data.getLanguage()),
AddressesReadPcodeArithmetic.INSTANCE);
this.data = data;
this.unique = unique;
@Override
protected AddressSetView checkSize(int size, AddressSetView val) {
return val;
}
@Override
@@ -71,20 +71,10 @@ public class AddressesReadTracePcodeExecutorStatePiece
}
@Override
public PcodeTraceDataAccess getData() {
return data;
}
@Override
public AddressesReadTracePcodeExecutorStatePiece fork() {
public AddressesReadTracePcodeExecutorStatePiece fork(PcodeStateCallbacks cb) {
return new AddressesReadTracePcodeExecutorStatePiece(data, new HashMap<>(unique));
}
@Override
public void writeDown(PcodeTraceDataAccess into) {
throw new UnsupportedOperationException();
}
@Override
protected Map<Register, AddressSetView> getRegisterValuesFromSpace(AddressSpace s,
List<Register> registers) {
@@ -102,7 +92,8 @@ public class AddressesReadTracePcodeExecutorStatePiece
}
@Override
protected void setInSpace(AddressSpace space, long offset, int size, AddressSetView val) {
protected void setInSpace(AddressSpace space, long offset, int size, AddressSetView val,
PcodeStateCallbacks cb) {
if (!space.isUniqueSpace()) {
return;
}
@@ -112,7 +103,7 @@ public class AddressesReadTracePcodeExecutorStatePiece
@Override
protected AddressSetView getFromSpace(AddressSpace space, long offset, int size,
Reason reason) {
Reason reason, PcodeStateCallbacks cb) {
if (space.isUniqueSpace()) {
AddressSetView result = unique.get(offset);
if (result == null) {
@@ -1,68 +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.trace;
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 PcodeTraceAccess access;
/**
* Create a trace-bound emulator
*
* @param access the trace access shim
*/
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
protected BytesPcodeThread createThread(String name) {
BytesPcodeThread thread = super.createThread(name);
access.getDataForLocalState(thread, 0).initializeThreadContext(thread);
return thread;
}
protected TracePcodeExecutorState<byte[]> newState(PcodeTraceDataAccess data) {
return new BytesTracePcodeExecutorState(data);
}
@Override
public TracePcodeExecutorState<byte[]> createSharedState() {
return newState(access.getDataForSharedState());
}
@Override
public TracePcodeExecutorState<byte[]> createLocalState(PcodeThread<byte[]> thread) {
return newState(access.getDataForLocalState(thread, 0));
}
}
@@ -1,41 +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.trace;
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
/**
* A state composing a single {@link BytesTracePcodeExecutorStatePiece}
*/
public class BytesTracePcodeExecutorState extends DefaultTracePcodeExecutorState<byte[]> {
/**
* Create the state
*
* @param data the trace-data access shim
*/
public BytesTracePcodeExecutorState(PcodeTraceDataAccess data) {
super(new BytesTracePcodeExecutorStatePiece(data));
}
protected BytesTracePcodeExecutorState(TracePcodeExecutorStatePiece<byte[], byte[]> piece) {
super(piece);
}
@Override
public BytesTracePcodeExecutorState fork() {
return new BytesTracePcodeExecutorState(piece.fork());
}
}
@@ -1,214 +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.trace;
import java.nio.ByteBuffer;
import java.util.Map;
import generic.ULongSpan;
import generic.ULongSpan.ULongSpanSet;
import ghidra.generic.util.datastruct.SemisparseByteArray;
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.util.MathUtilities;
/**
* A state piece which reads bytes from a trace, but caches writes internally.
*
* <p>
* This provides for "read-only" emulation on a trace. Writes do not affect the source trace, but
* rather are cached in this state. If desired, those cached writes can be written back out at a
* later time.
*/
public class BytesTracePcodeExecutorStatePiece
extends AbstractBytesPcodeExecutorStatePiece<CachedSpace>
implements TracePcodeExecutorStatePiece<byte[], byte[]> {
protected static class CachedSpace
extends BytesPcodeExecutorStateSpace<PcodeTraceDataAccess> {
protected final AddressSet written;
public CachedSpace(Language language, AddressSpace space, PcodeTraceDataAccess backing) {
// Backing could be null, so we need language parameter
super(language, space, backing);
this.written = new AddressSet();
}
protected CachedSpace(Language language, AddressSpace space, PcodeTraceDataAccess backing,
SemisparseByteArray bytes, AddressSet written) {
super(language, space, backing, bytes);
this.written = written;
}
@Override
public CachedSpace fork() {
return new CachedSpace(language, space, backing, bytes.fork(), new AddressSet(written));
}
@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 - 1);
if (loc.compareTo(end) <= 0) {
written.add(loc, end);
}
else {
written.add(loc, space.getMaxAddress());
written.add(space.getMinAddress(), end);
}
}
protected AddressSetView intersectViewKnown(AddressSetView set) {
return backing.intersectViewKnown(set, true);
}
@Override
protected ULongSpanSet readUninitializedFromBacking(ULongSpanSet uninitialized) {
if (uninitialized.isEmpty()) {
return uninitialized;
}
// TODO: Warn or bail when reading UNKNOWN bytes
// NOTE: Read without regard to gaps
// NOTE: Cannot write those gaps, though!!!
AddressSetView knownButUninit = intersectViewKnown(addrSet(uninitialized));
if (knownButUninit.isEmpty()) {
return uninitialized;
}
AddressRange knownBound = new AddressRangeImpl(
knownButUninit.getMinAddress(),
knownButUninit.getMaxAddress());
ByteBuffer buf = ByteBuffer.allocate((int) knownBound.getLength());
backing.getBytes(knownBound.getMinAddress(), buf);
for (AddressRange range : knownButUninit) {
bytes.putData(range.getMinAddress().getOffset(), buf.array(),
(int) (range.getMinAddress().subtract(knownBound.getMinAddress())),
(int) range.getLength());
}
ULongSpan uninitBound = uninitialized.bound();
return bytes.getUninitialized(uninitBound.min(), uninitBound.max());
}
protected void warnUnknown(AddressSetView unknown) {
warnAddressSet("Emulator state initialized from UNKNOWN", unknown);
}
// Must already have started a transaction
protected void writeDown(PcodeTraceDataAccess into) {
if (space.isUniqueSpace()) {
return;
}
byte[] data = new byte[4096];
ByteBuffer buf = ByteBuffer.wrap(data);
for (AddressRange range : written) {
long lower = range.getMinAddress().getOffset();
long fullLen = range.getLength();
while (fullLen > 0) {
int len = MathUtilities.unsignedMin(data.length, fullLen);
bytes.getData(lower, data, 0, len);
buf.position(0);
buf.limit(len);
into.putBytes(space.getAddress(lower), buf);
lower += len;
fullLen -= len;
}
}
}
}
protected final PcodeTraceDataAccess data;
/**
* Create a concrete state piece backed by a trace
*
* @param data the trace-data access shim
*/
public BytesTracePcodeExecutorStatePiece(PcodeTraceDataAccess data) {
super(data.getLanguage());
this.data = data;
}
protected BytesTracePcodeExecutorStatePiece(PcodeTraceDataAccess data,
AbstractSpaceMap<CachedSpace> spaceMap) {
super(data.getLanguage(), spaceMap);
this.data = data;
}
@Override
public PcodeTraceDataAccess getData() {
return data;
}
@Override
public BytesTracePcodeExecutorStatePiece fork() {
return new BytesTracePcodeExecutorStatePiece(data, spaceMap.fork());
}
@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(into);
}
}
/**
* A space map which binds spaces to corresponding spaces in the trace
*/
protected class TraceBackedSpaceMap
extends CacheingSpaceMap<PcodeTraceDataAccess, CachedSpace> {
public TraceBackedSpaceMap() {
super();
}
protected TraceBackedSpaceMap(Map<AddressSpace, CachedSpace> spaces) {
super(spaces);
}
@Override
protected PcodeTraceDataAccess getBacking(AddressSpace space) {
return data;
}
@Override
protected CachedSpace newSpace(AddressSpace space, PcodeTraceDataAccess backing) {
return new CachedSpace(language, space, backing);
}
@Override
public TraceBackedSpaceMap fork() {
return new TraceBackedSpaceMap(fork(spaces));
}
@Override
public CachedSpace fork(CachedSpace s) {
return s.fork();
}
}
@Override
protected AbstractSpaceMap<CachedSpace> newSpaceMap() {
return new TraceBackedSpaceMap();
}
}
@@ -1,56 +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.trace;
import ghidra.pcode.exec.DefaultPcodeExecutorState;
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
/**
* An adapter that implements {@link TracePcodeExecutorState} given a
* {@link TracePcodeExecutorStatePiece} whose address and value types already match
*
* @param <T> the type of values
*/
public class DefaultTracePcodeExecutorState<T> extends DefaultPcodeExecutorState<T>
implements TracePcodeExecutorState<T> {
protected final TracePcodeExecutorStatePiece<T, T> piece;
/**
* Wrap a state piece
*
* @param piece the piece
*/
public DefaultTracePcodeExecutorState(TracePcodeExecutorStatePiece<T, T> piece) {
super(piece);
this.piece = piece;
}
@Override
public PcodeTraceDataAccess getData() {
return piece.getData();
}
@Override
public DefaultTracePcodeExecutorState<T> fork() {
return new DefaultTracePcodeExecutorState<>(piece.fork());
}
@Override
public void writeDown(PcodeTraceDataAccess into) {
piece.writeDown(into);
}
}
@@ -1,96 +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.trace;
import org.apache.commons.lang3.tuple.Pair;
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}
*
* @see TraceSleighUtils
*/
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 data the trace-data access shim
*/
public DirectBytesTracePcodeExecutorState(PcodeTraceDataAccess data) {
super(new DirectBytesTracePcodeExecutorStatePiece(data));
}
protected DirectBytesTracePcodeExecutorState(
TracePcodeExecutorStatePiece<byte[], byte[]> piece) {
super(piece);
}
/**
* 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(TracePlatform platform, long snap, TraceThread thread,
int frame) {
this(getDefaultThreadAccess(platform, snap, thread, frame));
}
/**
* Pair this state with an auxiliary {@link TraceMemoryState} piece
*
* @return the new state, composing this state with the new piece
* @see TraceSleighUtils#buildByteWithStateExecutor(Trace, long, TraceThread, int)
*/
public PcodeExecutorState<Pair<byte[], TraceMemoryState>> withMemoryState() {
return new PairedPcodeExecutorState<>(this,
new TraceMemoryStatePcodeExecutorStatePiece(getData()));
}
@Override
public DirectBytesTracePcodeExecutorState fork() {
return new DirectBytesTracePcodeExecutorState(piece.fork());
}
}
@@ -1,166 +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.trace;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Map;
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.Register;
import ghidra.program.model.mem.MemBuffer;
import ghidra.trace.model.memory.TraceMemoryState;
/**
* An executor state piece that operates directly on trace memory and registers
*
* <p>
* This differs from {@link BytesTracePcodeExecutorStatePiece} in that writes performed by the
* emulator immediately affect the trace. There is no caching. In effect, the trace <em>is</em> the
* state. This is used primarily in testing to initialize trace state using Sleigh, which is more
* succinct than accessing trace memory and registers via the trace API. It may also be incorporated
* into the UI at a later time.
*
* @see TraceSleighUtils
*/
public class DirectBytesTracePcodeExecutorStatePiece
extends AbstractLongOffsetPcodeExecutorStatePiece<byte[], byte[], AddressSpace>
implements TracePcodeExecutorStatePiece<byte[], byte[]> {
protected final PcodeTraceDataAccess data;
protected final SemisparseByteArray unique;
/**
* Construct a piece
*
* @param arithmetic the arithmetic for byte arrays
* @param data the trace-data access shim
*/
protected DirectBytesTracePcodeExecutorStatePiece(PcodeArithmetic<byte[]> arithmetic,
PcodeTraceDataAccess data, SemisparseByteArray unique) {
super(data.getLanguage(), arithmetic, arithmetic);
this.data = data;
this.unique = unique;
}
/**
* Construct a piece
*
* @param data the trace-data access shim
*/
public DirectBytesTracePcodeExecutorStatePiece(PcodeTraceDataAccess data) {
this(BytesPcodeArithmetic.forLanguage(data.getLanguage()), data, new SemisparseByteArray());
}
@Override
public PcodeTraceDataAccess getData() {
return data;
}
@Override
public DirectBytesTracePcodeExecutorStatePiece fork() {
return new DirectBytesTracePcodeExecutorStatePiece(arithmetic, data, unique.fork());
}
/**
* Create a state which computes an expression's {@link TraceMemoryState} as an auxiliary
* attribute
*
* <p>
* If every part of every input to the expression is {@link TraceMemoryState#KNOWN}, then the
* expression's value will be marked {@link TraceMemoryState#KNOWN}. Otherwise, it's marked
* {@link TraceMemoryState#UNKNOWN}.
*
* @return the paired executor state
*/
public PcodeExecutorStatePiece<byte[], Pair<byte[], TraceMemoryState>> withMemoryState() {
return new PairedPcodeExecutorStatePiece<>(this,
new TraceMemoryStatePcodeExecutorStatePiece(data));
}
@Override
protected void setUnique(long offset, int size, byte[] val) {
assert size == val.length;
unique.putData(offset, val);
}
@Override
protected byte[] getUnique(long offset, int size, Reason reason) {
byte[] data = new byte[size];
unique.getData(offset, data);
return data;
}
@Override
protected AddressSpace getForSpace(AddressSpace space, boolean toWrite) {
return space;
}
@Override
protected void setInSpace(AddressSpace space, long offset, int size, byte[] val) {
assert size == val.length;
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(AddressSpace space, long offset, int size, Reason reason) {
ByteBuffer buf = ByteBuffer.allocate(size);
int read = data.getBytes(space.getAddress(offset), buf);
if (read != size) {
throw new RuntimeException("Could not read full value from trace");
}
return buf.array();
}
@Override
protected Map<Register, byte[]> getRegisterValuesFromSpace(AddressSpace s,
List<Register> registers) {
return Map.of();
}
@Override
public Map<Register, byte[]> getRegisterValues() {
return Map.of();
}
@Override
public MemBuffer getConcreteBuffer(Address address, Purpose purpose) {
throw new UnsupportedOperationException();
}
@Override
public void writeDown(PcodeTraceDataAccess into) {
// Writes directly, so just ignore
}
@Override
public void clear() {
unique.clear();
}
}
@@ -1,60 +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.trace;
import org.apache.commons.lang3.tuple.Pair;
import ghidra.pcode.exec.IndependentPairedPcodeExecutorState;
import ghidra.pcode.exec.PairedPcodeExecutorState;
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
/**
* A trace-bound state composed of another trace-bound state and a piece
*
* @param <L> the type of values for the left state
* @param <R> the type of values for the right piece
* @see PairedPcodeExecutorState
*/
public class IndependentPairedTracePcodeExecutorState<L, R>
extends IndependentPairedPcodeExecutorState<L, R>
implements TracePcodeExecutorState<Pair<L, R>> {
private final TracePcodeExecutorStatePiece<L, L> left;
private final TracePcodeExecutorStatePiece<R, R> right;
public IndependentPairedTracePcodeExecutorState(TracePcodeExecutorStatePiece<L, L> left,
TracePcodeExecutorStatePiece<R, R> right) {
super(left, right);
this.left = left;
this.right = right;
}
@Override
public PcodeTraceDataAccess getData() {
return left.getData();
}
@Override
public void writeDown(PcodeTraceDataAccess into) {
left.writeDown(into);
right.writeDown(into);
}
@Override
public IndependentPairedTracePcodeExecutorState<L, R> fork() {
return new IndependentPairedTracePcodeExecutorState<>(left.fork(), right.fork());
}
}
@@ -1,59 +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.trace;
import org.apache.commons.lang3.tuple.Pair;
import ghidra.pcode.exec.PairedPcodeExecutorState;
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
/**
* A trace-bound state composed of another trace-bound state and a piece
*
* @param <L> the type of values for the left state
* @param <R> the type of values for the right piece
* @see PairedPcodeExecutorState
*/
public class PairedTracePcodeExecutorState<L, R> extends PairedPcodeExecutorState<L, R>
implements TracePcodeExecutorState<Pair<L, R>> {
private final PairedTracePcodeExecutorStatePiece<L, L, R> piece;
public PairedTracePcodeExecutorState(PairedTracePcodeExecutorStatePiece<L, L, R> piece) {
super(piece);
this.piece = piece;
}
public PairedTracePcodeExecutorState(TracePcodeExecutorState<L> left,
TracePcodeExecutorStatePiece<L, R> right) {
this(new PairedTracePcodeExecutorStatePiece<>(left, right));
}
@Override
public PcodeTraceDataAccess getData() {
return piece.getData();
}
@Override
public PairedTracePcodeExecutorState<L, R> fork() {
return new PairedTracePcodeExecutorState<>(piece.fork());
}
@Override
public void writeDown(PcodeTraceDataAccess into) {
piece.writeDown(into);
}
}
@@ -1,80 +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.trace;
import org.apache.commons.lang3.tuple.Pair;
import ghidra.pcode.exec.PairedPcodeExecutorStatePiece;
import ghidra.pcode.exec.PcodeArithmetic;
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
/**
* A trace-bound state piece composed of two other trace-bound pieces sharing the same address type
*
* @see PairedPcodeExecutorStatePiece
* @param <A> the type of addresses
* @param <L> the type of values for the left piece
* @param <R> the type of values for the right piece
*/
public class PairedTracePcodeExecutorStatePiece<A, L, R>
extends PairedPcodeExecutorStatePiece<A, L, R>
implements TracePcodeExecutorStatePiece<A, Pair<L, R>> {
protected final TracePcodeExecutorStatePiece<A, L> left;
protected final TracePcodeExecutorStatePiece<A, R> right;
public PairedTracePcodeExecutorStatePiece(TracePcodeExecutorStatePiece<A, L> left,
TracePcodeExecutorStatePiece<A, R> right) {
super(left, right);
this.left = left;
this.right = right;
}
public PairedTracePcodeExecutorStatePiece(TracePcodeExecutorStatePiece<A, L> left,
TracePcodeExecutorStatePiece<A, R> right, PcodeArithmetic<A> addressArithmetic,
PcodeArithmetic<Pair<L, R>> arithmetic) {
super(left, right, addressArithmetic, arithmetic);
this.left = left;
this.right = right;
}
@Override
public PcodeTraceDataAccess getData() {
return left.getData();
}
@Override
public PairedTracePcodeExecutorStatePiece<A, L, R> fork() {
return new PairedTracePcodeExecutorStatePiece<>(left.fork(), right.fork(),
getAddressArithmetic(), getArithmetic());
}
@Override
public void writeDown(PcodeTraceDataAccess into) {
left.writeDown(into);
right.writeDown(into);
}
@Override
public TracePcodeExecutorStatePiece<A, L> getLeft() {
return left;
}
@Override
public TracePcodeExecutorStatePiece<A, R> getRight() {
return right;
}
}
@@ -38,6 +38,11 @@ public enum TraceMemoryStatePcodeArithmetic implements PcodeArithmetic<TraceMemo
/** The singleton instance */
INSTANCE;
@Override
public Class<TraceMemoryState> getDomain() {
return TraceMemoryState.class;
}
@Override
public Endian getEndian() {
return null;
@@ -33,14 +33,16 @@ import ghidra.trace.model.memory.TraceMemoryState;
* The p-code execute state piece for {@link TraceMemoryState}
*
* <p>
* This state piece is meant to be used as an auxiliary to a concrete trace-bound state. See
* {@link DirectBytesTracePcodeExecutorState#withMemoryState()}. It should be used with
* {@link TraceMemoryStatePcodeArithmetic} as a means of computing the "state" of a Sleigh
* expression's value. It essentially works like a rudimentary taint analyzer: If any part of any
* input to the expression in tainted, i.e., not {@link TraceMemoryState#KNOWN}, then the result is
* {@link TraceMemoryState#UNKNOWN}. This is best exemplified in
* {@link #getUnique(long, int, Reason)}, though it's also exemplified in
* {@link #getFromSpace(AddressSpace, long, int, Reason)}.
* This state piece is meant to be used as an auxiliary to a concrete trace-bound state. It should
* be used with {@link TraceMemoryStatePcodeArithmetic} as a means of computing the "state" of a
* Sleigh expression's value. It essentially works like a rudimentary taint analyzer: If any part of
* any input to the expression in tainted, i.e., not {@link TraceMemoryState#KNOWN}, then the result
* is {@link TraceMemoryState#UNKNOWN}. This is best exemplified in
* {@link #getUnique(long, int, Reason, PcodeStateCallbacks)}, though it's also exemplified in
* {@link #getFromSpace(AddressSpace, long, int, Reason, PcodeStateCallbacks)}.
*
* <p>
* NOTE: This is backed directly by the trace rather than using {@link PcodeStateCallbacks}.
*/
public class TraceMemoryStatePcodeExecutorStatePiece extends
AbstractLongOffsetPcodeExecutorStatePiece<byte[], TraceMemoryState, AddressSpace> {
@@ -48,29 +50,30 @@ public class TraceMemoryStatePcodeExecutorStatePiece extends
protected final MutableULongSpanMap<TraceMemoryState> unique;
protected final PcodeTraceDataAccess data;
protected TraceMemoryStatePcodeExecutorStatePiece(PcodeTraceDataAccess data,
MutableULongSpanMap<TraceMemoryState> unique) {
super(data.getLanguage(), BytesPcodeArithmetic.forLanguage(data.getLanguage()),
TraceMemoryStatePcodeArithmetic.INSTANCE, PcodeStateCallbacks.NONE);
this.data = data;
this.unique = unique;
}
/**
* Construct a piece
*
* @param data the trace-data access shim
*/
public TraceMemoryStatePcodeExecutorStatePiece(PcodeTraceDataAccess data) {
super(data.getLanguage(),
BytesPcodeArithmetic.forLanguage(data.getLanguage()),
TraceMemoryStatePcodeArithmetic.INSTANCE);
this.data = data;
this.unique = new DefaultULongSpanMap<>();
}
protected TraceMemoryStatePcodeExecutorStatePiece(PcodeTraceDataAccess data,
MutableULongSpanMap<TraceMemoryState> unique) {
super(data.getLanguage(), BytesPcodeArithmetic.forLanguage(data.getLanguage()),
TraceMemoryStatePcodeArithmetic.INSTANCE);
this.data = data;
this.unique = unique;
this(data, new DefaultULongSpanMap<>());
}
@Override
public TraceMemoryStatePcodeExecutorStatePiece fork() {
protected TraceMemoryState checkSize(int size, TraceMemoryState val) {
return val;
}
@Override
public TraceMemoryStatePcodeExecutorStatePiece fork(PcodeStateCallbacks cb) {
MutableULongSpanMap<TraceMemoryState> copyUnique = new DefaultULongSpanMap<>();
copyUnique.putAll(unique);
return new TraceMemoryStatePcodeExecutorStatePiece(data, copyUnique);
@@ -86,12 +89,13 @@ public class TraceMemoryStatePcodeExecutorStatePiece extends
}
@Override
protected void setUnique(long offset, int size, TraceMemoryState val) {
protected void setUnique(long offset, int size, TraceMemoryState val, PcodeStateCallbacks cb) {
unique.put(ULongSpan.extent(offset, size), val);
}
@Override
protected TraceMemoryState getUnique(long offset, int size, Reason reason) {
protected TraceMemoryState getUnique(long offset, int size, Reason reason,
PcodeStateCallbacks cb) {
MutableULongSpanSet remains = new DefaultULongSpanSet();
ULongSpan span = ULongSpan.extent(offset, size);
remains.add(span);
@@ -110,19 +114,20 @@ public class TraceMemoryStatePcodeExecutorStatePiece extends
}
@Override
protected void setInSpace(AddressSpace space, long offset, int size, TraceMemoryState val) {
protected void setInSpace(AddressSpace space, long offset, int size, TraceMemoryState val,
PcodeStateCallbacks cb) {
// NB. Will ensure writes with unknown state are still marked unknown
data.setState(range(space, offset, size), val);
}
@Override
protected TraceMemoryState getFromSpace(AddressSpace space, long offset, int size,
Reason reason) {
Reason reason, PcodeStateCallbacks cb) {
return data.getViewportState(range(space, offset, size));
}
@Override
protected TraceMemoryState getFromNullSpace(int size, Reason reason) {
protected TraceMemoryState getFromNullSpace(int size, Reason reason, PcodeStateCallbacks cb) {
return TraceMemoryState.UNKNOWN;
}
@@ -1,36 +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.trace;
import ghidra.pcode.exec.PcodeExecutorState;
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(PcodeTraceDataAccess)}. This interface also derives from
* {@link PcodeExecutorState} so that, as the name implies, they can be used where a state is
* required.
*
* @param <T> the type of values
*/
public interface TracePcodeExecutorState<T>
extends PcodeExecutorState<T>, TracePcodeExecutorStatePiece<T, T> {
@Override
TracePcodeExecutorState<T> fork();
}
@@ -1,55 +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.trace;
import ghidra.pcode.exec.PcodeExecutorStatePiece;
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
*
* @param <A> the type of address offsets
* @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();
@Override
TracePcodeExecutorStatePiece<A, T> fork();
/**
* Write the accumulated values (cache) into the given trace
*
* <p>
* <b>NOTE:</b> This method requires a transaction to have already been started on the
* destination trace.
*
* @param into the destination data-access shim
* @see TracePcodeMachine#writeDown(PcodeTraceAccess)
*/
void writeDown(PcodeTraceDataAccess into);
}
@@ -1,81 +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.trace;
import ghidra.pcode.emu.PcodeMachine;
import ghidra.pcode.emu.PcodeThread;
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
*
* @param <T> the type of values manipulated by the machine
*/
public interface TracePcodeMachine<T> extends PcodeMachine<T> {
/**
* Create a shared state
*
* @return the shared state
*/
TracePcodeExecutorState<T> createSharedState();
/**
* Create a local state
*
* @param thread the thread whose state is being created
* @return the local state
*/
TracePcodeExecutorState<T> createLocalState(PcodeThread<T> thread);
/**
* 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
* destination trace. The destination threads must have equal names/paths at the given
* threadsSnap. When using scratch space, threadsSnap should be the source snap. If populating a
* new trace, threadsSnap should probably be the destination snap.
*
* @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
*/
default void writeDown(TracePlatform platform, long destSnap, long threadsSnap) {
writeDown(new DefaultPcodeTraceAccess(platform, destSnap, threadsSnap));
}
}
@@ -4,9 +4,9 @@
* 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.
@@ -22,8 +22,10 @@ import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.pcode.emu.PcodeEmulator;
import ghidra.pcode.exec.*;
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
import ghidra.pcode.exec.trace.data.DefaultPcodeTraceAccess;
import ghidra.pcode.utils.Utils;
import ghidra.program.model.address.AddressRange;
import ghidra.program.model.address.AddressSpace;
@@ -43,9 +45,10 @@ public enum TraceSleighUtils {
* Build a p-code executor that operates directly on bytes of the given trace
*
* <p>
* This execute is most suitable for evaluating Sleigh expression on a given trace snapshot, and
* for manipulating or initializing variables using Sleigh code. It is generally not suitable
* for use in an emulator. For that, consider {@link BytesTracePcodeEmulator}.
* This executor is most suitable for evaluating Sleigh expression on a given trace snapshot,
* and for manipulating or initializing variables using Sleigh code. It is generally not
* suitable for use in an emulator. For that, use {@link PcodeEmulator} with
* {@link TraceEmulationIntegration}.
*
* @param platform the platform
* @param snap the snap
@@ -55,14 +58,15 @@ public enum TraceSleighUtils {
*/
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)) {
if (!(platform.getLanguage() instanceof SleighLanguage language)) {
throw new IllegalArgumentException("TracePlatform must use a Sleigh language");
}
return new PcodeExecutor<>((SleighLanguage) language,
BytesPcodeArithmetic.forLanguage(language), state, Reason.INSPECT);
DefaultPcodeTraceAccess access = new DefaultPcodeTraceAccess(platform, snap);
PcodeStateCallbacks cb =
TraceEmulationIntegration.bytesImmediateWrite(access, thread, frame);
BytesPcodeExecutorState state = new BytesPcodeExecutorState(language, cb);
return new PcodeExecutor<>(language, BytesPcodeArithmetic.forLanguage(language), state,
Reason.INSPECT);
}
/**
@@ -94,14 +98,17 @@ public enum TraceSleighUtils {
*/
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)) {
if (!(platform.getLanguage() instanceof SleighLanguage language)) {
throw new IllegalArgumentException("TracePlatform must use a Sleigh language");
}
return new PcodeExecutor<>((SleighLanguage) language, new PairedPcodeArithmetic<>(
DefaultPcodeTraceAccess access = new DefaultPcodeTraceAccess(platform, snap);
PcodeStateCallbacks cb =
TraceEmulationIntegration.bytesImmediateWrite(access, thread, frame);
BytesPcodeExecutorState state = new BytesPcodeExecutorState(language, cb);
PcodeExecutorState<Pair<byte[], TraceMemoryState>> paired =
state.paired(new TraceMemoryStatePcodeExecutorStatePiece(
access.getDataForThreadState(thread, frame)));
return new PcodeExecutor<>(language, new PairedPcodeArithmetic<>(
BytesPcodeArithmetic.forLanguage(language), TraceMemoryStatePcodeArithmetic.INSTANCE),
paired, Reason.INSPECT);
}
@@ -1,71 +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.trace.auxiliary;
import org.apache.commons.lang3.tuple.Pair;
import ghidra.pcode.emu.PcodeThread;
import ghidra.pcode.emu.auxiliary.AuxEmulatorPartsFactory;
import ghidra.pcode.exec.trace.*;
/**
* An auxiliary emulator parts factory capable of integrating with a trace
*
* <p>
* This can manufacture parts for an emulator that reads and writes its state (concrete and
* auxiliary pieces) from and to a trace, as well as all the parts for the less integrated forms of
* the same emulator. The pattern of use is generally to read from a given "source" snap, execute
* some stepping schedule, then write the cache to a given "destination" snap.
*
* @param <U> the type of auxiliary values
*/
public interface AuxTraceEmulatorPartsFactory<U> extends AuxEmulatorPartsFactory<U> {
/**
* Create the shared (memory) state of a new trace-integrated emulator
*
* <p>
* This is usually composed of pieces using {@link PairedTracePcodeExecutorStatePiece}, but it
* does not have to be. It must incorporate the concrete piece provided. The state must be
* 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. 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
* @return the composed state
*/
TracePcodeExecutorState<Pair<byte[], U>> createTraceSharedState(
AuxTracePcodeEmulator<U> emulator, BytesTracePcodeExecutorStatePiece concrete);
/**
* Create the local (register) state of a new trace-integrated thread
*
* <p>
* This must have the same capabilities as
* {@link #createTraceSharedState(AuxTracePcodeEmulator, BytesTracePcodeExecutorStatePiece)}.
*
* @param emulator the emulator
* @param thread the new thread
* @param concrete the concrete piece
* @return the composed state
*/
TracePcodeExecutorState<Pair<byte[], U>> createTraceLocalState(
AuxTracePcodeEmulator<U> emulator, PcodeThread<Pair<byte[], U>> thread,
BytesTracePcodeExecutorStatePiece concrete);
}
@@ -1,87 +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.trace.auxiliary;
import org.apache.commons.lang3.tuple.Pair;
import ghidra.pcode.emu.PcodeThread;
import ghidra.pcode.emu.auxiliary.AuxEmulatorPartsFactory;
import ghidra.pcode.emu.auxiliary.AuxPcodeEmulator;
import ghidra.pcode.exec.trace.*;
import ghidra.pcode.exec.trace.data.*;
import ghidra.trace.model.guest.TracePlatform;
/**
* An trace-integrated emulator whose parts are manufactured by a
* {@link AuxTraceEmulatorPartsFactory}
*
* <p>
* See the parts factory interface and its super interfaces:
* <ul>
* <li>{@link AuxTraceEmulatorPartsFactory}</li>
* <li>{@link AuxEmulatorPartsFactory}</li>
* </ul>
*
* @param <U> the type of auxiliary values
*/
public abstract class AuxTracePcodeEmulator<U> extends AuxPcodeEmulator<U>
implements TracePcodeMachine<Pair<byte[], U>> {
protected final PcodeTraceAccess access;
/**
* Create a new emulator
*
* @param access the trace access shim
*/
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
protected PcodeThread<Pair<byte[], U>> createThread(String name) {
PcodeThread<Pair<byte[], U>> thread = super.createThread(name);
access.getDataForLocalState(thread, 0).initializeThreadContext(thread);
return thread;
}
@Override
public TracePcodeExecutorState<Pair<byte[], U>> createSharedState() {
return getPartsFactory().createTraceSharedState(this,
new BytesTracePcodeExecutorStatePiece(access.getDataForSharedState()));
}
@Override
public TracePcodeExecutorState<Pair<byte[], U>> createLocalState(
PcodeThread<Pair<byte[], U>> thread) {
return getPartsFactory().createTraceLocalState(this, thread,
new BytesTracePcodeExecutorStatePiece(access.getDataForLocalState(thread, 0)));
}
}
@@ -4,9 +4,9 @@
* 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.
@@ -45,6 +45,11 @@ public class DefaultPcodeTraceAccess extends AbstractPcodeTraceAccess //
super(platform, snap);
}
@Override
public PcodeTraceAccess deriveForWrite(long snap) {
return new DefaultPcodeTraceAccess(platform, snap, threadsSnap);
}
@Override
protected DefaultPcodeTraceMemoryAccess newDataForSharedState() {
return new DefaultPcodeTraceMemoryAccess(platform, snap, viewport);
@@ -15,9 +15,13 @@
*/
package ghidra.pcode.exec.trace.data;
import java.util.Map;
import java.util.Map.Entry;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.Language;
import ghidra.trace.model.Lifespan;
import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.property.*;
/**
@@ -93,31 +97,64 @@ public class DefaultPcodeTracePropertyAccess<T>
return ops.get(data.getSnap(), overlayAddr);
}
@Override
public Entry<AddressRange, T> getEntry(Address address) {
Address hostAddr = data.getPlatform().mapGuestToHost(address);
if (hostAddr == null) {
return null;
}
TracePropertyMapOperations<T> ops = getPropertyOperations(false);
if (ops == null) {
return null;
}
Address overlayAddr = toOverlay(ops, hostAddr);
Entry<TraceAddressSnapRange, T> entry = ops.getEntry(data.getSnap(), overlayAddr);
return entry == null ? null : Map.entry(entry.getKey().getRange(), entry.getValue());
}
@Override
public void put(Address address, T value) {
Address hostAddr = data.getPlatform().mapGuestToHost(address);
if (hostAddr == null) {
// TODO: Warn?
// Warn?
return;
}
Lifespan span = Lifespan.nowOnMaybeScratch(data.getSnap());
TracePropertyMapOperations<T> ops = getPropertyOperations(true);
ops.set(span, toOverlay(ops, hostAddr), value);
if (value == null) {
if (ops == null) {
return;
}
ops.clear(span, toOverlay(ops, new AddressRangeImpl(hostAddr, hostAddr)));
}
else {
ops.set(span, toOverlay(ops, hostAddr), value);
}
}
@Override
public void put(AddressRange range, T value) {
AddressRange hostRange = data.getPlatform().mapGuestToHost(range);
if (hostRange == null) {
// Warn?
return;
}
Lifespan span = Lifespan.nowOnMaybeScratch(data.getSnap());
TracePropertyMapOperations<T> ops = getPropertyOperations(true);
if (value == null) {
if (ops == null) {
return;
}
ops.clear(span, toOverlay(ops, hostRange));
}
else {
ops.set(span, toOverlay(ops, hostRange), value);
}
}
@Override
public void clear(AddressRange range) {
AddressRange hostRange = data.getPlatform().mapGuestToHost(range);
if (hostRange == null) {
// TODO: Warn?
return;
}
Lifespan span = Lifespan.nowOnMaybeScratch(data.getSnap());
TracePropertyMapOperations<T> ops = getPropertyOperations(false);
if (ops == null) {
return;
}
ops.clear(span, toOverlay(ops, hostRange));
put(range, null);
}
@Override
@@ -16,9 +16,7 @@
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;
/**
@@ -35,50 +33,17 @@ import ghidra.trace.model.thread.TraceThread;
* 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>
* <li>Typically invoked by a factory method for an emulator's shared executor state
*
* <pre>
* PcodeTraceMemoryAccess sharedData = access.getDataForSharedState();
* </pre>
*
* </li>
* <li>Typically invoked by a factory method for an emulator thread's local executor state
*
* <pre>
* PcodeTraceRegisterAccess localData = access.getDataForLocalState(thread, 0);
* </pre>
*
* </li>
* <li>Typically invoked by an auxiliary emulator state piece
*
* <pre>{@code
* PcodeTracePropertyAccess<String> property = data.getPropertyAccess("MyProperty", String.class);
* }</pre>
*
* </li>
* </ul>
*/
public interface PcodeTraceAccess {
/**
* Derive an access for writing a snapshot, where this access was the emulator's source
*
* @param snap the destination snapshot key
* @return the derived access shim
*/
PcodeTraceAccess deriveForWrite(long snap);
/**
* Get the language of the associated platform
*

Some files were not shown because too many files have changed in this diff Show More