mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-02 17:29:37 +02:00
GP-5879: Add callbacks to PcodeEmulator. Refactor for composition vice inheritance.
This commit is contained in:
parent
c0127326f8
commit
72001639a8
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,802 @@
|
|||
/* ###
|
||||
* 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.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import ghidra.generic.util.datastruct.SemisparseByteArray;
|
||||
import ghidra.pcode.emu.*;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||
import ghidra.pcode.exec.trace.data.*;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.trace.model.memory.TraceMemoryState;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.MathUtilities;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* A collection of static methods for integrating an emulator with a trace.
|
||||
*/
|
||||
public enum TraceEmulationIntegration {
|
||||
;
|
||||
|
||||
/**
|
||||
* Create a writer (callbacks) that lazily loads data from the given access shim.
|
||||
*
|
||||
* <p>
|
||||
* Writes are logged, but not written to the trace. Instead, the client should call
|
||||
* {@link Writer#writeDown(PcodeTraceAccess)} to write the logged changes to another given
|
||||
* snapshot. This is used for forking emulation from a chosen snapshot and saving the results
|
||||
* into (usually scratch) snapshots. Scripts might also use this pattern to save a series of
|
||||
* snapshots resulting from an emulation experiment.
|
||||
*
|
||||
* @param from the access shim for lazy loads
|
||||
* @return the writer
|
||||
*/
|
||||
public static Writer bytesDelayedWrite(PcodeTraceAccess from) {
|
||||
Writer writer = new TraceWriter(from);
|
||||
writer.putHandler(new BytesPieceHandler());
|
||||
return writer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a writer (callbacks) that lazily loads data and immediately writes changes to the
|
||||
* given access shim.
|
||||
*
|
||||
* <p>
|
||||
* Writes are immediately stored into the trace at the same snapshot as state is sourced.
|
||||
*
|
||||
* @param access the access shim for loads and stores
|
||||
* @return the writer
|
||||
*/
|
||||
public static Writer bytesImmediateWrite(PcodeTraceAccess access) {
|
||||
Writer writer = new TraceWriter(access);
|
||||
writer.putHandler(new ImmediateBytesPieceHandler());
|
||||
return writer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create state callbacks that lazily load data and immediately write changes to the given
|
||||
* access shim.
|
||||
*
|
||||
* <p>
|
||||
* Writes are immediately stored into the trace at the same snapshot as state is sourced.
|
||||
*
|
||||
* <p>
|
||||
* Use this instead of {@link #bytesImmediateWrite(PcodeTraceAccess)} when interfacing directly
|
||||
* with a {@link PcodeExecutorState} vice a {@link PcodeEmulator}.
|
||||
*
|
||||
* @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 bytesImmediateWrite(PcodeTraceAccess access,
|
||||
TraceThread thread, int frame) {
|
||||
Writer writer = new TraceWriter(access) {
|
||||
@Override
|
||||
protected PcodeTraceRegistersAccess getRegAccess(PcodeThread<?> ignored) {
|
||||
return access.getDataForLocalState(thread, frame);
|
||||
}
|
||||
};
|
||||
writer.putHandler(new ImmediateBytesPieceHandler());
|
||||
return writer.wrapFor(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* The key when selecting a handler for a given piece: (address-domain, value-domain)
|
||||
*
|
||||
* @param <A> the address domain
|
||||
* @param <T> the value domain
|
||||
*/
|
||||
record PieceType<A, T>(Class<A> addressDomain, Class<T> valueDomain) {
|
||||
/**
|
||||
* Get the key for a given piece
|
||||
*
|
||||
* @param <A> the address domain
|
||||
* @param <T> the value domain
|
||||
* @param piece the piece
|
||||
* @return the key
|
||||
*/
|
||||
public static <A, T> PieceType<A, T> forPiece(PcodeExecutorStatePiece<A, T> piece) {
|
||||
return new PieceType<>(piece.getAddressArithmetic().getDomain(),
|
||||
piece.getArithmetic().getDomain());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the key for a given handler
|
||||
*
|
||||
* @param <A> the address domain
|
||||
* @param <T> the value domain
|
||||
* @param handler the handler
|
||||
* @return the key
|
||||
*/
|
||||
public static <A, T> PieceType<A, T> forHandler(PieceHandler<A, T> handler) {
|
||||
return new PieceType<>(handler.getAddressDomain(), handler.getValueDomain());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The primary mechanism for integrating emulators and traces
|
||||
*
|
||||
* <p>
|
||||
* This implements callbacks for the emulator and provides a method for recording logged writes
|
||||
* after some number of emulation steps. The client must pass this writer in as the callbacks
|
||||
* and then later invoke {@link #writeDown(PcodeTraceAccess)}. This also permits the addition of
|
||||
* state piece handlers via {@link #putHandler(PieceHandler)}, should the emulator be operating
|
||||
* on other value domains.
|
||||
*/
|
||||
public interface Writer extends PcodeEmulationCallbacks<Object> {
|
||||
/**
|
||||
* Record state changes into the trace via the given access shim
|
||||
*
|
||||
* @param into the access shim
|
||||
*/
|
||||
void writeDown(PcodeTraceAccess into);
|
||||
|
||||
/**
|
||||
* Record state changes into the trace at the given snapshot.
|
||||
*
|
||||
* <p>
|
||||
* The destination trace is the same as from the source access shim.
|
||||
*
|
||||
* @param snap the destination snapshot key
|
||||
*/
|
||||
void writeDown(long snap);
|
||||
|
||||
/**
|
||||
* Add or replace a handler
|
||||
*
|
||||
* <p>
|
||||
* The handler must identify the address and value domains for which it is applicable. If
|
||||
* there is already a handler for the same domains, the old handler is replaced by this one.
|
||||
* Otherwise, this handler is added without removing any others. The handler is invoked if
|
||||
* and only if the emulator's state contains a piece for the same domains. Otherwise, the
|
||||
* handler may be silently ignored.
|
||||
*
|
||||
* @param handler the handler
|
||||
*/
|
||||
void putHandler(PieceHandler<?, ?> handler);
|
||||
|
||||
/**
|
||||
* Cast this writer to fit the emulator's value domain
|
||||
*
|
||||
* <p>
|
||||
* Use this as the callbacks parameter when constructing the trace-integrated emulator. We
|
||||
* assert this cast is safe, because none of the callbacks actually depend on the emulator's
|
||||
* value domain. Instead, the states are accessed generically and invocations doled out to
|
||||
* respective {@link PieceHandler}s based on their applicable domain types.
|
||||
*
|
||||
* @param <T> the emulator's value domain
|
||||
* @return this
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
default <T> PcodeEmulationCallbacks<T> callbacks() {
|
||||
return (PcodeEmulationCallbacks<T>) this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The handler for a specific piece within an emulator's (or executor's) state.
|
||||
*
|
||||
* @see PcodeExecutorStatePiece
|
||||
* @param <A> the address domain of pieces this can handle
|
||||
* @param <T> the value domain of pieces this can handle
|
||||
*/
|
||||
public interface PieceHandler<A, T> {
|
||||
/** A handler that does nothing */
|
||||
public static PieceHandler<?, ?> NONE = VoidPieceHandler.INSTANCE;
|
||||
|
||||
/**
|
||||
* Get the address domain this can handle
|
||||
*
|
||||
* @return the address domain
|
||||
*/
|
||||
Class<A> getAddressDomain();
|
||||
|
||||
/**
|
||||
* Get the value domain this can handle
|
||||
*
|
||||
* @return the value domain
|
||||
*/
|
||||
Class<T> getValueDomain();
|
||||
|
||||
/**
|
||||
* An uninitialized portion of a state piece is being read (concrete addressing).
|
||||
*
|
||||
* @param acc the trace access shim for the relevant state (shared or local)
|
||||
* @param thread the thread, if applicable. This is null if either the state being accessed
|
||||
* is the emulator's shared state, or if the state is bound to a plain
|
||||
* {@link PcodeExecutor}.
|
||||
* @param piece the state piece being handled
|
||||
* @param set the uninitialized portion required
|
||||
* @return the addresses in {@code set} that remain uninitialized
|
||||
* @see PcodeEmulationCallbacks#readUninitialized(PcodeThread, PcodeExecutorStatePiece,
|
||||
* AddressSetView)
|
||||
*/
|
||||
AddressSetView readUninitialized(PcodeTraceDataAccess acc, PcodeThread<?> thread,
|
||||
PcodeExecutorStatePiece<A, T> piece, AddressSetView set);
|
||||
|
||||
/**
|
||||
* An uninitialized portion of a state piece is being read (abstract addressing).
|
||||
*
|
||||
* @param acc the trace access shim for the relevant state (shared or local)
|
||||
* @param thread the thread, if applicable. This is null if either the state being accessed
|
||||
* is the emulator's shared state, or if the state is bound to a plain
|
||||
* {@link PcodeExecutor}.
|
||||
* @param piece the state piece being handled
|
||||
* @param space the address space
|
||||
* @param offset the offset at the start of the uninitialized portion
|
||||
* @param length the size in bytes of the uninitialized portion
|
||||
* @return the number of bytes just initialized, typically 0 or {@code length}
|
||||
* @see PcodeEmulationCallbacks#readUninitialized(PcodeThread, PcodeExecutorStatePiece,
|
||||
* AddressSpace, Object, int)
|
||||
*/
|
||||
default int abstractReadUninit(PcodeTraceDataAccess acc, PcodeThread<?> thread,
|
||||
PcodeExecutorStatePiece<A, T> piece, AddressSpace space, A offset, int length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data was written (concrete addressing).
|
||||
*
|
||||
* @param acc the trace access shim for the relevant state (shared or local)
|
||||
* @param written the {@link Writer}'s current log of written addresses (mutable).
|
||||
* Typically, this is not accessed but rather passed to delegate methods.
|
||||
* @param thread the thread, if applicable. This is null if either the state being accessed
|
||||
* is the emulator's shared state, or if the state is bound to a plain
|
||||
* {@link PcodeExecutor}.
|
||||
* @param piece the state piece being handled
|
||||
* @param address the start address of the write
|
||||
* @param length the size in bytes of the write
|
||||
* @param value the value written
|
||||
* @return true to prevent the {@link Writer} from updating its log.
|
||||
* @see PcodeEmulationCallbacks#dataWritten(PcodeThread, PcodeExecutorStatePiece, Address,
|
||||
* int, Object)
|
||||
*/
|
||||
default boolean dataWritten(PcodeTraceDataAccess acc, AddressSet written,
|
||||
PcodeThread<?> thread, PcodeExecutorStatePiece<A, T> piece, Address address,
|
||||
int length, T value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data was written (abstract addressing).
|
||||
*
|
||||
* @param acc the trace access shim for the relevant state (shared or local)
|
||||
* @param written the {@link Writer}'s current log of written addresses (mutable).
|
||||
* Typically, this is not accessed but rather passed to delegate methods.
|
||||
* @param thread the thread, if applicable. This is null if either the state being accessed
|
||||
* is the emulator's shared state, or if the state is bound to a plain
|
||||
* {@link PcodeExecutor}.
|
||||
* @param piece the state piece being handled
|
||||
* @param space the address space
|
||||
* @param offset the offset of the start of the write
|
||||
* @param length the size in bytes of the write
|
||||
* @param value the value written
|
||||
* @see PcodeEmulationCallbacks#dataWritten(PcodeThread, PcodeExecutorStatePiece,
|
||||
* AddressSpace, Object, int, Object)
|
||||
*/
|
||||
default void abstractWritten(PcodeTraceDataAccess acc, AddressSet written,
|
||||
PcodeThread<?> thread, PcodeExecutorStatePiece<A, T> piece, AddressSpace space,
|
||||
A offset, int length, T value) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize a given portion of the state to the trace database.
|
||||
*
|
||||
* <p>
|
||||
* The "given portion" refers to the address set provided in {@code written}. Pieces may
|
||||
* also have state assigned to abstract addresses. In such cases, it is up to the handler to
|
||||
* track what has been written.
|
||||
*
|
||||
* @param into the destination trace access
|
||||
* @param thread the thread associated with the piece's state
|
||||
* @param piece the source state piece
|
||||
* @param written the portion that is known to have been written
|
||||
*/
|
||||
void writeDown(PcodeTraceDataAccess into, PcodeThread<?> thread,
|
||||
PcodeExecutorStatePiece<A, T> piece, AddressSetView written);
|
||||
}
|
||||
|
||||
/**
|
||||
* An implementation of {@link PieceHandler} that does nothing.
|
||||
*
|
||||
* @implNote This is the object returned when a handler is not found for a given piece. It
|
||||
* removes the need for a null check.
|
||||
*/
|
||||
private enum VoidPieceHandler implements PieceHandler<Void, Void> {
|
||||
/** The handler that does nothing */
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public Class<Void> getAddressDomain() {
|
||||
return Void.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<Void> getValueDomain() {
|
||||
return Void.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSetView readUninitialized(PcodeTraceDataAccess acc, PcodeThread<?> thread,
|
||||
PcodeExecutorStatePiece<Void, Void> piece, AddressSetView set) {
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDown(PcodeTraceDataAccess into, PcodeThread<?> thread,
|
||||
PcodeExecutorStatePiece<Void, Void> piece, AddressSetView written) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A handler that implements the lazy-read-writer-later pattern of trace integration for a
|
||||
* concrete emulator's bytes.
|
||||
*/
|
||||
public static class BytesPieceHandler implements PieceHandler<byte[], byte[]> {
|
||||
/**
|
||||
* The maximum number of bytes to buffer at a time
|
||||
*/
|
||||
public static final int CHUNK_SIZE = 4096;
|
||||
|
||||
@Override
|
||||
public Class<byte[]> getAddressDomain() {
|
||||
return byte[].class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<byte[]> getValueDomain() {
|
||||
return byte[].class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSetView readUninitialized(PcodeTraceDataAccess acc, PcodeThread<?> thread,
|
||||
PcodeExecutorStatePiece<byte[], byte[]> piece, AddressSetView set) {
|
||||
// NOTE: For simplicity, read without regard to gaps
|
||||
// NOTE: We cannot write those gaps, though!!!
|
||||
AddressSetView knownButUninit = acc.intersectViewKnown(set, true);
|
||||
if (knownButUninit.isEmpty()) {
|
||||
return set;
|
||||
}
|
||||
AddressSet remains = new AddressSet(set);
|
||||
AddressRange knownBound = new AddressRangeImpl(
|
||||
knownButUninit.getMinAddress(),
|
||||
knownButUninit.getMaxAddress());
|
||||
ByteBuffer buf = ByteBuffer.allocate((int) knownBound.getLength());
|
||||
acc.getBytes(knownBound.getMinAddress(), buf);
|
||||
for (AddressRange range : knownButUninit) {
|
||||
piece.setVarInternal(range.getAddressSpace(), range.getMinAddress().getOffset(),
|
||||
(int) range.getLength(), buf.array());
|
||||
remains.delete(range);
|
||||
}
|
||||
return remains;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDown(PcodeTraceDataAccess into, PcodeThread<?> thread,
|
||||
PcodeExecutorStatePiece<byte[], byte[]> piece, AddressSetView written) {
|
||||
for (AddressRange range : written) {
|
||||
AddressSpace space = range.getAddressSpace();
|
||||
if (space.isUniqueSpace()) {
|
||||
continue;
|
||||
}
|
||||
long lower = range.getMinAddress().getOffset();
|
||||
long fullLen = range.getLength();
|
||||
while (fullLen > 0) {
|
||||
int len = MathUtilities.unsignedMin(CHUNK_SIZE, fullLen);
|
||||
// NOTE: Would prefer less copying and less heap garbage....
|
||||
byte[] bytes = piece.getVarInternal(space, lower, len, Reason.INSPECT);
|
||||
into.putBytes(space.getAddress(lower), ByteBuffer.wrap(bytes));
|
||||
|
||||
lower += bytes.length;
|
||||
fullLen -= bytes.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A handler that implements the lazy-read-write-immediately pattern of trace integration for a
|
||||
* concrete emulator's bytes.
|
||||
*/
|
||||
public static class ImmediateBytesPieceHandler extends BytesPieceHandler {
|
||||
@Override
|
||||
public boolean dataWritten(PcodeTraceDataAccess acc, AddressSet written,
|
||||
PcodeThread<?> thread, PcodeExecutorStatePiece<byte[], byte[]> piece,
|
||||
Address address, int length, byte[] value) {
|
||||
if (address.isUniqueAddress()) {
|
||||
return true;
|
||||
}
|
||||
acc.putBytes(address, ByteBuffer.wrap(value));
|
||||
return true; // Avoid any delayed write
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An abstract implementation of {@link PieceHandler} that seeks to simplify integration of
|
||||
* abstract domains where the state is serialized into a trace's property map.
|
||||
*
|
||||
* <p>
|
||||
* Generally, such abstract domains should follow a byte-wise access pattern. That is, it should
|
||||
* be capable of reading and writing to overlapping variables. This implementation is aimed at
|
||||
* that pattern. The state piece will need to implement at least
|
||||
* {@link PcodeExecutorStatePiece#getNextEntryInternal(AddressSpace, long)}. Each state entry
|
||||
* should be serialized as an entry at the same address and size in the property map.
|
||||
* Uninitialized reads should search the full range for any applicable entries. Entries may need
|
||||
* to be subpieced, depending on what part of the state is already initialized.
|
||||
*
|
||||
* <p>
|
||||
* If the address domain is also abstract, the recommended pattern is to attempt to concretize
|
||||
* it (see {@link PcodeArithmetic#toAddress(Object, AddressSpace, Purpose)}) and delegate to the
|
||||
* concrete callback. Failing that, you must choose some other means of storing the state. Our
|
||||
* current recommendation is to use {@link Address#NO_ADDRESS} in a string map, where you can
|
||||
* serialize any number of (address, value) pairs. This will not work for thread-local states,
|
||||
* but it is unlikely you should encounter non-concretizable addresses in a thread-local state.
|
||||
*
|
||||
* @param <A> the address domain
|
||||
* @param <T> the value domain
|
||||
* @param <P> the type of values in the property map, often {@link String}
|
||||
*/
|
||||
public static abstract class AbstractPropertyBasedPieceHandler<A, T, P>
|
||||
implements PieceHandler<A, T> {
|
||||
|
||||
/**
|
||||
* Get the name of the property map.
|
||||
*
|
||||
* <p>
|
||||
* This should be unique among all possible domains. Nor should it collide with map names
|
||||
* used for other purposes.
|
||||
*/
|
||||
protected abstract String getPropertyName();
|
||||
|
||||
/**
|
||||
* Get the type of values in the property map
|
||||
*
|
||||
* @return the type, often {@link String}{@code .class}
|
||||
*/
|
||||
protected abstract Class<P> getPropertyType();
|
||||
|
||||
/**
|
||||
* Decode a property entry and set appropriate variable(s) in the piece
|
||||
* <p>
|
||||
* The found property entry may cover more addresses than are actually required, either
|
||||
* because they've not been requested or because the value has already been set. Writing a
|
||||
* value that wasn't requested isn't too bad, but writing one that was already initialized
|
||||
* could be catastrophic.
|
||||
*
|
||||
* @param piece the piece with uninitialized variables to decode from a property
|
||||
* @param limit the portion that is requested and uninitialized. <b>DO NOT</b> initialize
|
||||
* any address outside of this set.
|
||||
* @param range the range covered by the found property entry
|
||||
* @param propertyValue the value of the property entry
|
||||
*/
|
||||
protected abstract void decodeFrom(PcodeExecutorStatePiece<A, T> piece,
|
||||
AddressSetView limit, AddressRange range, P propertyValue);
|
||||
|
||||
/**
|
||||
* Encode a variable's value into a property entry
|
||||
*
|
||||
* @param property the property map (access shim)
|
||||
* @param range the variable's address range (this may optionally be coalesced from several
|
||||
* variables by the piece's internals)
|
||||
* @param value the variable's value
|
||||
*/
|
||||
protected abstract void encodeInto(PcodeTracePropertyAccess<P> property, AddressRange range,
|
||||
T value);
|
||||
|
||||
@Override
|
||||
public AddressSetView readUninitialized(PcodeTraceDataAccess acc, PcodeThread<?> thread,
|
||||
PcodeExecutorStatePiece<A, T> piece, AddressSetView set) {
|
||||
PcodeTracePropertyAccess<P> property =
|
||||
acc.getPropertyAccess(getPropertyName(), getPropertyType());
|
||||
AddressSet remains = new AddressSet(set);
|
||||
AddressSet cursor = new AddressSet(set);
|
||||
boolean result = false;
|
||||
while (!cursor.isEmpty()) {
|
||||
Address cur = cursor.getMinAddress();
|
||||
Entry<AddressRange, P> entry = property.getEntry(cur);
|
||||
if (entry == null) {
|
||||
// Not the most efficient....
|
||||
cursor.delete(cur, cur);
|
||||
continue;
|
||||
}
|
||||
AddressRange physical = new AddressRangeImpl(
|
||||
entry.getKey().getMinAddress().getPhysicalAddress(),
|
||||
entry.getKey().getMaxAddress().getPhysicalAddress());
|
||||
decodeFrom(piece, set, physical, entry.getValue());
|
||||
result = true;
|
||||
remains.delete(physical);
|
||||
cursor.delete(physical);
|
||||
}
|
||||
return result ? remains : set;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* This should be overridden by developers needing to store abstract state into the trace.
|
||||
* Conventionally, if the address cannot be made concrete (see
|
||||
* {@link PcodeArithmetic#toLong(Object, Purpose)}), then it should be stored at
|
||||
* {@link Address#NO_ADDRESS}. It is up to the developer to determine how to (de)serialize
|
||||
* all of the abstract states.
|
||||
*/
|
||||
@Override
|
||||
public int abstractReadUninit(PcodeTraceDataAccess acc, PcodeThread<?> thread,
|
||||
PcodeExecutorStatePiece<A, T> piece, AddressSpace space, A offset, int length) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* This method handles serializing the concrete portion and associating the states to their
|
||||
* respective addresses in the property. Handlers needing to serialize abstracts portions
|
||||
* must both implement the means of tracking what has been written (see
|
||||
* {@link #abstractWritten(PcodeTraceDataAccess, AddressSet, PcodeThread, PcodeExecutorStatePiece, AddressSpace, Object, int, Object)}),
|
||||
* and the placement of that state information into the property. The latter is accomplished
|
||||
* by overriding this method, taking care to invoke the super method for the concrete
|
||||
* portion.
|
||||
*/
|
||||
@Override
|
||||
public void writeDown(PcodeTraceDataAccess into, PcodeThread<?> thread,
|
||||
PcodeExecutorStatePiece<A, T> piece, AddressSetView written) {
|
||||
PcodeTracePropertyAccess<P> property =
|
||||
into.getPropertyAccess(getPropertyName(), getPropertyType());
|
||||
AddressSet remains = new AddressSet(written);
|
||||
while (!remains.isEmpty()) {
|
||||
Address cur = remains.getMinAddress();
|
||||
AddressSpace space = cur.getAddressSpace();
|
||||
Entry<Long, T> entry = piece.getNextEntryInternal(space, cur.getOffset());
|
||||
if (entry == null) {
|
||||
remains.delete(space.getMinAddress(), space.getMaxAddress());
|
||||
continue;
|
||||
}
|
||||
if (Long.compareUnsigned(entry.getKey(), cur.getOffset()) < 0) {
|
||||
Msg.error(this, "getNextEntryInternal return an incorrect entry.");
|
||||
remains.delete(space.getMinAddress(), space.getMaxAddress());
|
||||
continue;
|
||||
}
|
||||
Address min = space.getAddress(entry.getKey());
|
||||
AddressRange range = new AddressRangeImpl(min,
|
||||
min.add(piece.getArithmetic().sizeOf(entry.getValue()) - 1));
|
||||
encodeInto(property, range, entry.getValue());
|
||||
|
||||
// Delete everything preceding and including the range, within the same space
|
||||
remains.delete(space.getMinAddress(), range.getMaxAddress());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abstractWritten(PcodeTraceDataAccess acc, AddressSet written,
|
||||
PcodeThread<?> thread, PcodeExecutorStatePiece<A, T> piece, AddressSpace space,
|
||||
A offset, int length, T value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A misguided simplification of {@link AbstractPropertyBasedPieceHandler} that reduces the
|
||||
* requirement to a simple codec.
|
||||
*
|
||||
* <p>
|
||||
* For cases where subpiecing of variables is not of concern, this simplification may suffice.
|
||||
* This is usually okay for proofs of concept or very simplistic architectures. However, once
|
||||
* you introduce structured/aliased registers (e.g., {@code EAX} is the lower 32 bits of
|
||||
* {@code RAX}), or you're dealing with off-cut memory references, you have to deal with
|
||||
* subpiecing and this simplification is no longer viable.
|
||||
*
|
||||
* @param <A> the address domain of the piece
|
||||
* @param <T> the value domain of the piece
|
||||
* @param <P> the type of the property map
|
||||
*/
|
||||
public static abstract class AbstractSimplePropertyBasedPieceHandler<A, T, P>
|
||||
extends AbstractPropertyBasedPieceHandler<A, T, P> {
|
||||
|
||||
/**
|
||||
* Decode a state value from the given property value
|
||||
*
|
||||
* @param propertyValue the property value
|
||||
* @return the decoded state value
|
||||
*/
|
||||
protected abstract T decode(P propertyValue);
|
||||
|
||||
@Override
|
||||
protected void decodeFrom(PcodeExecutorStatePiece<A, T> piece, AddressSetView limit,
|
||||
AddressRange range, P propertyValue) {
|
||||
piece.setVarInternal(range.getAddressSpace(), range.getMinAddress().getOffset(),
|
||||
(int) range.getLength(), decode(propertyValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a state value into a property value
|
||||
*
|
||||
* @param value the state value
|
||||
* @return the encoded property value
|
||||
*/
|
||||
protected abstract P encode(T value);
|
||||
|
||||
@Override
|
||||
protected void encodeInto(PcodeTracePropertyAccess<P> property, AddressRange range,
|
||||
T value) {
|
||||
property.put(range, encode(value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of {@link Writer} for traces.
|
||||
*
|
||||
* <p>
|
||||
* The interface is already somewhat trace-centric in that it requires
|
||||
* {@link Writer#writeDown(PcodeTraceAccess)}, but those may technically do nothing (as is the
|
||||
* case for the write-immediately implementations). NOTE: Perhaps we should replace the
|
||||
* interface with this class (renamed to {@link Writer}).
|
||||
*/
|
||||
public static class TraceWriter implements Writer {
|
||||
protected final PcodeTraceAccess access;
|
||||
protected final PcodeTraceMemoryAccess memAccess;
|
||||
protected final Map<PcodeThread<?>, PcodeTraceRegistersAccess> regAccess = new HashMap<>();
|
||||
|
||||
/**
|
||||
* An address set to track what has actually been written. It's not enough to just use the
|
||||
* {@link SemisparseByteArray}'s initialized set, as that may be caching bytes from the
|
||||
* trace which are still {@link TraceMemoryState#UNKNOWN}.
|
||||
*/
|
||||
protected final AddressSet memWritten = new AddressSet();
|
||||
protected final Map<PcodeThread<?>, AddressSet> regsWritten = new HashMap<>();
|
||||
|
||||
protected final Map<PieceType<?, ?>, PieceHandler<?, ?>> handlers = new HashMap<>();
|
||||
|
||||
private PcodeMachine<?> emulator;
|
||||
|
||||
/**
|
||||
* Construct a writer which sources state from the given access shim
|
||||
*
|
||||
* @param access the source access shim
|
||||
*/
|
||||
public TraceWriter(PcodeTraceAccess access) {
|
||||
this.access = access;
|
||||
this.memAccess = access.getDataForSharedState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putHandler(PieceHandler<?, ?> handler) {
|
||||
handlers.put(PieceType.forHandler(handler), handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emulatorCreated(PcodeMachine<Object> emulator) {
|
||||
this.emulator = emulator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void threadCreated(PcodeThread<Object> thread) {
|
||||
access.getDataForLocalState(thread, 0).initializeThreadContext(thread);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <B, U> PieceHandler<B, U> handlerFor(PcodeExecutorStatePiece<B, U> piece) {
|
||||
return (PieceHandler<B, U>) handlers.getOrDefault(PieceType.forPiece(piece),
|
||||
PieceHandler.NONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Record the given piece's state into the trace
|
||||
*
|
||||
* @param <B> the piece's address domain
|
||||
* @param <U> the piece's value domain
|
||||
* @param into the destination trace access shim
|
||||
* @param thread the thread, if applicable
|
||||
* @param piece the piece
|
||||
* @param written the logged portions written
|
||||
*/
|
||||
protected <B, U> void writePieceDown(PcodeTraceDataAccess into, PcodeThread<?> thread,
|
||||
PcodeExecutorStatePiece<B, U> piece, AddressSetView written) {
|
||||
PieceHandler<B, U> handler = handlerFor(piece);
|
||||
handler.writeDown(into, thread, piece, written);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDown(PcodeTraceAccess into) {
|
||||
PcodeTraceMemoryAccess memInto = into.getDataForSharedState();
|
||||
for (PcodeExecutorStatePiece<?, ?> piece : emulator.getSharedState()
|
||||
.streamPieces()
|
||||
.toList()) {
|
||||
writePieceDown(memInto, null, piece, memWritten);
|
||||
}
|
||||
for (PcodeThread<?> thread : emulator.getAllThreads()) {
|
||||
PcodeTraceRegistersAccess regInto = into.getDataForLocalState(thread, 0);
|
||||
AddressSetView written = regsWritten.getOrDefault(thread, new AddressSet());
|
||||
for (PcodeExecutorStatePiece<?, ?> piece : thread.getState()
|
||||
.streamPieces()
|
||||
.toList()) {
|
||||
writePieceDown(regInto, thread, piece, written);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDown(long snap) {
|
||||
writeDown(access.deriveForWrite(snap));
|
||||
}
|
||||
|
||||
protected PcodeTraceRegistersAccess getRegAccess(PcodeThread<?> thread) {
|
||||
// Always use frame 0
|
||||
return regAccess.computeIfAbsent(thread, t -> access.getDataForLocalState(t, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <B, U> void dataWritten(PcodeThread<Object> thread,
|
||||
PcodeExecutorStatePiece<B, U> piece,
|
||||
Address address, int length, U value) {
|
||||
PcodeTraceDataAccess acc = address.isRegisterAddress()
|
||||
? getRegAccess(thread)
|
||||
: memAccess;
|
||||
AddressSet written = address.isRegisterAddress()
|
||||
? regsWritten.computeIfAbsent(thread, t -> new AddressSet())
|
||||
: memWritten;
|
||||
if (handlerFor(piece).dataWritten(acc, written, thread, piece, address, length,
|
||||
value)) {
|
||||
return;
|
||||
}
|
||||
Address end = address.addWrap(length - 1);
|
||||
if (address.compareTo(end) <= 0) {
|
||||
written.add(address, end);
|
||||
}
|
||||
else {
|
||||
AddressSpace space = address.getAddressSpace();
|
||||
written.add(address, space.getMaxAddress());
|
||||
written.add(space.getMinAddress(), end);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <B, U> void dataWritten(PcodeThread<Object> thread,
|
||||
PcodeExecutorStatePiece<B, U> piece, AddressSpace space, B offset, int length,
|
||||
U value) {
|
||||
PcodeTraceDataAccess acc = space.isRegisterSpace() ? getRegAccess(thread) : memAccess;
|
||||
AddressSet written = space.isRegisterSpace()
|
||||
? regsWritten.computeIfAbsent(thread, t -> new AddressSet())
|
||||
: memWritten;
|
||||
handlerFor(piece).abstractWritten(acc, written, thread, piece, space, offset, length,
|
||||
value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <B, U> int readUninitialized(PcodeThread<Object> thread,
|
||||
PcodeExecutorStatePiece<B, U> piece, AddressSpace space, B offset, int length) {
|
||||
PcodeTraceDataAccess acc = space.isRegisterSpace() ? getRegAccess(thread) : memAccess;
|
||||
return handlerFor(piece).abstractReadUninit(acc, thread, piece, space, offset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <B, U> AddressSetView readUninitialized(PcodeThread<Object> thread,
|
||||
PcodeExecutorStatePiece<B, U> piece, AddressSetView set) {
|
||||
if (set.isEmpty()) {
|
||||
return set;
|
||||
}
|
||||
AddressSpace space = set.getMinAddress().getAddressSpace();
|
||||
PcodeTraceDataAccess acc = space.isRegisterSpace() ? getRegAccess(thread) : memAccess;
|
||||
return handlerFor(piece).readUninitialized(acc, thread, piece, set);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package ghidra.pcode.exec.trace.data;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Language;
|
||||
|
||||
|
@ -28,7 +30,7 @@ import ghidra.program.model.lang.Language;
|
|||
*/
|
||||
public interface PcodeTracePropertyAccess<T> {
|
||||
/**
|
||||
* @see PcodeTraceDataAccess#getLanguage()
|
||||
* {@return the language}
|
||||
*/
|
||||
Language getLanguage();
|
||||
|
||||
|
@ -44,11 +46,19 @@ public interface PcodeTracePropertyAccess<T> {
|
|||
*/
|
||||
T get(Address address);
|
||||
|
||||
/**
|
||||
* Get the property's entry at the given address
|
||||
*
|
||||
* @param address the address
|
||||
* @return the entry, or null if not set
|
||||
*/
|
||||
Entry<AddressRange, T> getEntry(Address address);
|
||||
|
||||
/**
|
||||
* Set the property's value at the given address
|
||||
*
|
||||
* <p>
|
||||
* The value is affective for future snapshots up to but excluding the next snapshot where
|
||||
* The value is effective for future snapshots up to but excluding the next snapshot where
|
||||
* another value is set at the same address.
|
||||
*
|
||||
* @param address the address
|
||||
|
@ -56,6 +66,18 @@ public interface PcodeTracePropertyAccess<T> {
|
|||
*/
|
||||
void put(Address address, T value);
|
||||
|
||||
/**
|
||||
* Set the property's value at the given range
|
||||
*
|
||||
* <p>
|
||||
* The value is effective for future snapshots up to but excluding the next snapshot where
|
||||
* another value is set at the same address.
|
||||
*
|
||||
* @param range the range
|
||||
* @param value the value to set
|
||||
*/
|
||||
void put(AddressRange range, T value);
|
||||
|
||||
/**
|
||||
* Clear the property's value across a range
|
||||
*
|
||||
|
|
|
@ -22,12 +22,16 @@ import ghidra.app.plugin.assembler.Assembler;
|
|||
import ghidra.app.plugin.assembler.Assemblers;
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.pcode.exec.*;
|
||||
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.AddressRange;
|
||||
import ghidra.program.model.listing.Instruction;
|
||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||
import ghidra.trace.database.ToyDBTraceBuilder;
|
||||
import ghidra.trace.database.ToyDBTraceBuilder.ToySchemaBuilder;
|
||||
import ghidra.trace.model.Lifespan;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.memory.TraceMemoryFlag;
|
||||
import ghidra.trace.model.memory.TraceMemoryManager;
|
||||
import ghidra.trace.model.target.schema.SchemaContext;
|
||||
|
@ -36,6 +40,14 @@ import ghidra.util.Msg;
|
|||
|
||||
public class AbstractTracePcodeEmulatorTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
|
||||
protected PcodeTraceAccess createAccess(TracePlatform platform, long snap) {
|
||||
return new DefaultPcodeTraceAccess(platform, snap);
|
||||
}
|
||||
|
||||
protected Writer createWriter(TracePlatform platform, long snap) {
|
||||
return TraceEmulationIntegration.bytesDelayedWrite(createAccess(platform, snap));
|
||||
}
|
||||
|
||||
public TraceThread initTrace(ToyDBTraceBuilder tb, String stateInit,
|
||||
List<String> assembly) throws Throwable {
|
||||
return initTrace(tb, tb.range(0x00400000, 0x0040ffff), tb.range(0x00100000, 0x0010ffff),
|
||||
|
|
|
@ -28,9 +28,11 @@ import org.junit.Test;
|
|||
import db.Transaction;
|
||||
import ghidra.app.plugin.assembler.*;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyPatternBlock;
|
||||
import ghidra.pcode.emu.PcodeEmulator;
|
||||
import ghidra.pcode.emu.PcodeThread;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||
import ghidra.pcode.exec.trace.TraceEmulationIntegration.Writer;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRangeImpl;
|
||||
import ghidra.program.model.lang.*;
|
||||
|
@ -39,6 +41,7 @@ import ghidra.trace.database.context.DBTraceRegisterContextManager;
|
|||
import ghidra.trace.database.target.DBTraceObjectManager;
|
||||
import ghidra.trace.model.Lifespan;
|
||||
import ghidra.trace.model.guest.TraceGuestPlatform;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.memory.*;
|
||||
import ghidra.trace.model.target.TraceObject.ConflictResolution;
|
||||
import ghidra.trace.model.target.path.KeyPath;
|
||||
|
@ -48,6 +51,10 @@ import ghidra.util.NumericUtilities;
|
|||
@SuppressWarnings("javadoc")
|
||||
public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest {
|
||||
|
||||
PcodeEmulator createEmulator(TracePlatform platform, Writer writer) {
|
||||
return new PcodeEmulator(platform.getLanguage(), writer.callbacks());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a single instruction
|
||||
*
|
||||
|
@ -66,7 +73,8 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
List.of(
|
||||
"PUSH 0x0dedbeef"));
|
||||
|
||||
BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
PcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
|
||||
emuThread.stepInstruction();
|
||||
|
||||
|
@ -77,7 +85,7 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
TraceSleighUtils.evaluate("*:8 0x0010fff8:8", tb.trace, 0, thread, 0));
|
||||
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(tb.host, 1, 1);
|
||||
writer.writeDown(1);
|
||||
}
|
||||
|
||||
assertEquals(BigInteger.valueOf(0x0010fff8),
|
||||
|
@ -105,13 +113,14 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
"PUSH 0x0dedbeef",
|
||||
"PUSH 0x0badf00d"));
|
||||
|
||||
BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
PcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
|
||||
emuThread.stepInstruction();
|
||||
emuThread.stepInstruction();
|
||||
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(tb.host, 1, 1);
|
||||
writer.writeDown(1);
|
||||
}
|
||||
|
||||
assertEquals(BigInteger.valueOf(0x0010fff0),
|
||||
|
@ -145,7 +154,8 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
"MOV EAX,0x0dedbeef", // 5 bytes
|
||||
"MOV ECX,0x0badf00d")); // 5 bytes
|
||||
|
||||
BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
PcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
|
||||
emuThread.stepInstruction();
|
||||
|
||||
|
@ -156,7 +166,7 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
emuThread.stepInstruction();
|
||||
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(tb.host, 1, 1);
|
||||
writer.writeDown(1);
|
||||
}
|
||||
|
||||
assertEquals(BigInteger.valueOf(0x00110000),
|
||||
|
@ -206,7 +216,8 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
asm.patchProgram(mov, tb.addr(0x00401000));
|
||||
}
|
||||
|
||||
BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
PcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
|
||||
emuThread.stepInstruction();
|
||||
emuThread.stepInstruction();
|
||||
|
@ -222,7 +233,7 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
emuThread.stepInstruction();
|
||||
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(tb.host, 1, 1);
|
||||
writer.writeDown(1);
|
||||
}
|
||||
|
||||
assertEquals(BigInteger.valueOf(0x00110000),
|
||||
|
@ -249,12 +260,13 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
List.of(
|
||||
"imm r0, #911")); // decimal
|
||||
|
||||
BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
PcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
|
||||
emuThread.stepInstruction();
|
||||
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(tb.host, 1, 1);
|
||||
writer.writeDown(1);
|
||||
}
|
||||
|
||||
assertEquals(BigInteger.valueOf(0x00110000),
|
||||
|
@ -282,13 +294,14 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
"imm r0, #860",
|
||||
"imm r1, #861"));
|
||||
|
||||
BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
PcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
|
||||
emuThread.stepInstruction(); // brds and 1st imm executed
|
||||
emuThread.stepInstruction(); // 3rd imm executed
|
||||
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(tb.host, 1, 1);
|
||||
writer.writeDown(1);
|
||||
}
|
||||
|
||||
assertEquals(BigInteger.valueOf(0x00400008),
|
||||
|
@ -325,14 +338,15 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
"XOR byte ptr [0x00400007], 0xcc", // 7 bytes
|
||||
"MOV EAX,0x0dedbeef")); // 5 bytes
|
||||
|
||||
BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
PcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
|
||||
|
||||
emuThread.stepInstruction();
|
||||
emuThread.stepInstruction();
|
||||
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(tb.host, 1, 1);
|
||||
writer.writeDown(1);
|
||||
}
|
||||
|
||||
assertEquals(BigInteger.valueOf(0x00110000),
|
||||
|
@ -362,7 +376,8 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
"PUSH 0x0dedbeef",
|
||||
"PUSH 0x0badf00d"));
|
||||
|
||||
BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
PcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
|
||||
assertNull(emuThread.getFrame());
|
||||
|
||||
|
@ -385,7 +400,7 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
assertNull(emuThread.getFrame());
|
||||
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(tb.host, 1, 1);
|
||||
writer.writeDown(1);
|
||||
}
|
||||
|
||||
assertEquals(BigInteger.valueOf(0x0040000a),
|
||||
|
@ -429,7 +444,8 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
"PUSH 0x0dedbeef",
|
||||
"PUSH 0x0badf00d"));
|
||||
|
||||
BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(tb.host, 0) {
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
PcodeEmulator emu = new PcodeEmulator(tb.language, writer.callbacks()) {
|
||||
@Override
|
||||
protected PcodeUseropLibrary<byte[]> createUseropLibrary() {
|
||||
return hexLib;
|
||||
|
@ -476,7 +492,8 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
"PUSH 0x0dedbeef",
|
||||
"PUSH 0x0badf00d"));
|
||||
|
||||
BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(tb.host, 0) {
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
PcodeEmulator emu = new PcodeEmulator(tb.language, writer.callbacks()) {
|
||||
@Override
|
||||
protected PcodeUseropLibrary<byte[]> createUseropLibrary() {
|
||||
return hexLib;
|
||||
|
@ -509,7 +526,7 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
dumped.delete(0, dumped.length());
|
||||
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(tb.host, 1, 1);
|
||||
writer.writeDown(1);
|
||||
}
|
||||
|
||||
assertEquals(BigInteger.valueOf(0x0badf00dL),
|
||||
|
@ -532,7 +549,8 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
"PUSH 0x0dedbeef",
|
||||
"PUSH 0x0badf00d"));
|
||||
|
||||
BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
PcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
emu.addBreakpoint(tb.addr(0x00400000), "RAX == 1");
|
||||
emu.addBreakpoint(tb.addr(0x00400005), "RAX == 0");
|
||||
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
|
||||
|
@ -564,12 +582,13 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
List.of(
|
||||
"clz r1, r0"));
|
||||
|
||||
BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
PcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
|
||||
emuThread.stepInstruction();
|
||||
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(tb.host, 1, 1);
|
||||
writer.writeDown(1);
|
||||
}
|
||||
|
||||
assertEquals(BigInteger.valueOf(16),
|
||||
|
@ -597,7 +616,8 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
List.of(
|
||||
"MOVAPS XMM0, xmmword ptr [0x00600000]"));
|
||||
|
||||
BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
PcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
|
||||
emuThread.stepInstruction();
|
||||
|
||||
|
@ -606,7 +626,7 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
emuThread.getState().getVar(pc, Reason.INSPECT));
|
||||
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(tb.host, 1, 1);
|
||||
writer.writeDown(1);
|
||||
}
|
||||
|
||||
assertEquals(new BigInteger("0123456789abcdeffedcba9876543210", 16),
|
||||
|
@ -634,7 +654,8 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
List.of(
|
||||
"SAR EAX, CL"));
|
||||
|
||||
BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
PcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
|
||||
emuThread.stepInstruction();
|
||||
|
||||
|
@ -643,7 +664,7 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
emuThread.getState().getVar(pc, Reason.INSPECT));
|
||||
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(tb.host, 1, 1);
|
||||
writer.writeDown(1);
|
||||
}
|
||||
|
||||
assertEquals(BigInteger.valueOf(0x7ffffff),
|
||||
|
@ -663,13 +684,14 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
"XOR AH, AH",
|
||||
"MOV RCX, RAX"));
|
||||
|
||||
BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
PcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
|
||||
emuThread.stepInstruction();
|
||||
emuThread.stepInstruction();
|
||||
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(tb.host, 1, 1);
|
||||
writer.writeDown(1);
|
||||
}
|
||||
|
||||
assertEquals(BigInteger.valueOf(0x12340078),
|
||||
|
@ -703,7 +725,8 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
tb.trace.getMemoryManager().getBytes(0, tb.addr(0x00400000), buf);
|
||||
assertArrayEquals(tb.arr(0x48, 0x89, 0xc1), buf.array());
|
||||
|
||||
BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
PcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
|
||||
// TODO: Seems the Trace-bound thread ought to know to do this in reInitialize()
|
||||
ctxVal = ctxManager.getValueWithDefault(tb.host, ctxReg, 0, tb.addr(0x00400000));
|
||||
|
@ -711,7 +734,7 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
emuThread.stepInstruction();
|
||||
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(tb.host, 1, 1);
|
||||
writer.writeDown(1);
|
||||
}
|
||||
|
||||
assertEquals(BigInteger.valueOf(0x00400003),
|
||||
|
@ -740,12 +763,13 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
List.of(
|
||||
"MOV EAX, dword ptr [RBP + -0x4]"));
|
||||
|
||||
BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
PcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
|
||||
emuThread.stepInstruction();
|
||||
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(tb.host, 1, 1);
|
||||
writer.writeDown(1);
|
||||
}
|
||||
|
||||
assertEquals(BigInteger.valueOf(0x12345678),
|
||||
|
@ -773,7 +797,8 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
List.of(
|
||||
"MOV EAX, dword ptr [RBP + -0x2]"));
|
||||
|
||||
BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
PcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
|
||||
emuThread.stepInstruction();
|
||||
}
|
||||
|
@ -795,7 +820,8 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
List.of(
|
||||
"MOV EAX, dword ptr [EBP + -0x2]"));
|
||||
|
||||
BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
PcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
|
||||
emuThread.stepInstruction();
|
||||
}
|
||||
|
@ -817,7 +843,8 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
List.of(
|
||||
"unimpl"));
|
||||
|
||||
BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
PcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
|
||||
emuThread.stepInstruction();
|
||||
}
|
||||
|
@ -834,7 +861,8 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
""",
|
||||
List.of()); // An empty, uninitialized program
|
||||
|
||||
BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
PcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
|
||||
emuThread.stepInstruction();
|
||||
}
|
||||
|
@ -856,12 +884,13 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
List.of(
|
||||
"mov.w [W1], W0"));
|
||||
|
||||
BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
PcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
|
||||
emuThread.stepInstruction();
|
||||
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(tb.host, 1, 1);
|
||||
writer.writeDown(1);
|
||||
}
|
||||
|
||||
assertEquals(BigInteger.valueOf(0x000102),
|
||||
|
@ -918,7 +947,8 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
mm.putBytes(0, tb.addr(0x00000000), ByteBuffer.wrap(buf.getBytes()));
|
||||
}
|
||||
|
||||
BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(x64, 0);
|
||||
Writer writer = createWriter(x64, 0);
|
||||
PcodeEmulator emu = createEmulator(x64, writer);
|
||||
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
|
||||
emuThread.stepInstruction();
|
||||
|
||||
|
@ -931,7 +961,7 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
TraceSleighUtils.evaluate(changedExpr, tb.trace, 0, thread, 0));
|
||||
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(x64, 1, 1);
|
||||
writer.writeDown(1);
|
||||
}
|
||||
|
||||
assertEquals(BigInteger.valueOf(0x0010fff8),
|
||||
|
@ -957,7 +987,8 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
"mov r0,r7", // Assembler doesn't handle context flow
|
||||
"mov r1,r7"));
|
||||
|
||||
BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
PcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
|
||||
emuThread.stepInstruction();
|
||||
emuThread.stepPcodeOp(); // decode second
|
||||
|
@ -968,7 +999,7 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
emuThread.finishInstruction();
|
||||
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(tb.host, 1, 1);
|
||||
writer.writeDown(1);
|
||||
}
|
||||
|
||||
assertEquals(BigInteger.valueOf(0x00400006),
|
||||
|
@ -997,7 +1028,8 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
"mov r1,r7", // "
|
||||
"mov r2,r7"));
|
||||
|
||||
BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
PcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
PcodeThread<byte[]> emuThread = emu.newThread(thread.getPath());
|
||||
emuThread.stepInstruction();
|
||||
emuThread.stepPcodeOp(); // decode second
|
||||
|
@ -1011,7 +1043,7 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
emuThread.finishInstruction();
|
||||
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(tb.host, 1, 1);
|
||||
writer.writeDown(1);
|
||||
}
|
||||
|
||||
assertEquals(BigInteger.valueOf(0x00400008),
|
||||
|
|
|
@ -27,7 +27,6 @@ import org.junit.Test;
|
|||
import db.Transaction;
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.util.DefaultLanguageService;
|
||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||
|
@ -241,10 +240,7 @@ public class TraceSleighUtilsTest extends AbstractGhidraHeadlessIntegrationTest
|
|||
thread = b.getOrAddThread("Threads[1]", 0);
|
||||
b.createObjectsFramesAndRegs(thread, Lifespan.nowOn(0), b.host, 1);
|
||||
PcodeExecutor<byte[]> executor =
|
||||
new PcodeExecutor<>(sp.getLanguage(),
|
||||
BytesPcodeArithmetic.forLanguage(b.language),
|
||||
new DirectBytesTracePcodeExecutorState(b.host, 0, thread, 0),
|
||||
Reason.EXECUTE_READ);
|
||||
TraceSleighUtils.buildByteExecutor(b.host, 0, thread, 0);
|
||||
sp.execute(executor, PcodeUseropLibrary.nil());
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
@ -18,8 +18,7 @@ package ghidra.trace.model.time.schedule;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.pcode.emu.AbstractPcodeMachine;
|
||||
import ghidra.pcode.emu.PcodeThread;
|
||||
import ghidra.pcode.emu.*;
|
||||
import ghidra.pcode.exec.*;
|
||||
|
||||
/**
|
||||
|
@ -34,7 +33,7 @@ class TestMachine extends AbstractPcodeMachine<Void> {
|
|||
protected final List<String> record = new ArrayList<>();
|
||||
|
||||
public TestMachine() {
|
||||
super(TraceScheduleTest.TOY_BE_64_LANG);
|
||||
super(TraceScheduleTest.TOY_BE_64_LANG, PcodeEmulationCallbacks.none());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,135 +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.emu.taint;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import ghidra.pcode.emu.taint.plain.TaintSpace;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.taint.model.TaintVec;
|
||||
|
||||
/**
|
||||
* An abstract taint state piece
|
||||
*
|
||||
* <p>
|
||||
* Because we want to reduce code repetition, we use the type hierarchy to increase the capabilities
|
||||
* of the state piece as we progress from stand-alone to Debugger-integrated. The framework-provided
|
||||
* class from which this derives, however, introduces the idea of a space map, whose values have
|
||||
* type {@code <S>}. We'll be using types derived from {@link TaintSpace}, which is where all the
|
||||
* taint storage logic is actually located. Because that logic is what we're actually extending with
|
||||
* each more capable state piece, we have to ensure that type can be substituted. Thus, we have to
|
||||
* create these abstract classes from which the actual state pieces are derived, leaving {@code <S>}
|
||||
* bounded, but unspecified.
|
||||
*
|
||||
* @param <S> the type of spaces
|
||||
*/
|
||||
public abstract class AbstractTaintPcodeExecutorStatePiece<S extends TaintSpace>
|
||||
extends AbstractLongOffsetPcodeExecutorStatePiece<byte[], TaintVec, S> {
|
||||
|
||||
/**
|
||||
* The map from address space to storage space
|
||||
*
|
||||
* <p>
|
||||
* While the concept is introduced in the super class, we're not required to actually use one.
|
||||
* We just have to implement {@link #getForSpace(AddressSpace, boolean)}. Nevertheless, the
|
||||
* provided map is probably the best way, so we'll follow the pattern.
|
||||
*/
|
||||
protected final AbstractSpaceMap<S> spaceMap = newSpaceMap();
|
||||
|
||||
/**
|
||||
* Create a state piece
|
||||
*
|
||||
* @param language the emulator's language
|
||||
* @param addressArithmetic the arithmetic for the address type
|
||||
* @param arithmetic the arithmetic for the value type
|
||||
*/
|
||||
public AbstractTaintPcodeExecutorStatePiece(Language language,
|
||||
PcodeArithmetic<byte[]> addressArithmetic, PcodeArithmetic<TaintVec> arithmetic) {
|
||||
super(language, addressArithmetic, arithmetic);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension point: Create the actual space map
|
||||
*
|
||||
* <p>
|
||||
* This will need to be implemented by each state piece, i.e., non-abstract derivative class.
|
||||
* The space map will provide instances of {@code <S>}, which will provide the actual (extended)
|
||||
* storage logic.
|
||||
*
|
||||
* @return the space map
|
||||
*/
|
||||
protected abstract AbstractSpaceMap<S> newSpaceMap();
|
||||
|
||||
@Override
|
||||
public MemBuffer getConcreteBuffer(Address address, Purpose purpose) {
|
||||
throw new ConcretionError("Cannot make Taint concrete", purpose);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* Here, we just follow the pattern: delegate to the space map.
|
||||
*/
|
||||
@Override
|
||||
protected S getForSpace(AddressSpace space, boolean toWrite) {
|
||||
return spaceMap.getForSpace(space, toWrite);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* Because the super class places no bound on {@code <S>}, we have to provide the delegation to
|
||||
* the storage space.
|
||||
*/
|
||||
@Override
|
||||
protected void setInSpace(S space, long offset, int size, TaintVec val) {
|
||||
space.set(offset, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* Because the super class places no bound on {@code <S>}, we have to provide the delegation to
|
||||
* the storage space.
|
||||
*/
|
||||
@Override
|
||||
protected TaintVec getFromSpace(S space, long offset, int size, Reason reason) {
|
||||
return space.get(offset, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<Register, TaintVec> getRegisterValuesFromSpace(S space,
|
||||
List<Register> registers) {
|
||||
return space.getRegisterValues(registers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
for (S space : spaceMap.values()) {
|
||||
space.clear();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/* ###
|
||||
* 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.emu.taint;
|
||||
|
||||
import ghidra.debug.api.emulation.EmulatorFactory;
|
||||
import ghidra.debug.api.emulation.PcodeDebuggerAccess;
|
||||
import ghidra.pcode.emu.PcodeMachine;
|
||||
import ghidra.pcode.emu.taint.state.TaintPieceHandler;
|
||||
import ghidra.pcode.exec.trace.TraceEmulationIntegration;
|
||||
import ghidra.pcode.exec.trace.TraceEmulationIntegration.Writer;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTraceAccess;
|
||||
|
||||
/**
|
||||
* An emulator factory for making the {@link TaintPcodeEmulator} discoverable to the UI
|
||||
*
|
||||
* <p>
|
||||
* This is the final class to create a full Debugger-integrated emulator. This class is what makes
|
||||
* it appear in the menu of possible emulators the user may configure.
|
||||
*/
|
||||
public class TaintEmulatorFactory implements EmulatorFactory {
|
||||
|
||||
/**
|
||||
* This is conventionally available for testing and for scripts that would like to create a
|
||||
* trace-integrated emulator without using the service.
|
||||
*
|
||||
* @param access the means of accessing the integrated trace
|
||||
* @return a writer with callbacks for trace integration
|
||||
*/
|
||||
public static Writer delayedWriteTrace(PcodeTraceAccess access) {
|
||||
Writer writer = TraceEmulationIntegration.bytesDelayedWrite(access);
|
||||
addHandlers(writer);
|
||||
return writer;
|
||||
}
|
||||
|
||||
/**
|
||||
* A common place to factor addition of the required handler.
|
||||
*
|
||||
* <p>
|
||||
* It is presumed something else has or will add the other handlers, e.g., for the bytes.
|
||||
*
|
||||
* @param writer the writer to add handlers to
|
||||
*/
|
||||
public static void addHandlers(Writer writer) {
|
||||
writer.putHandler(new TaintPieceHandler());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return "Taint Analyzer with Concrete Emulation";
|
||||
}
|
||||
|
||||
@Override
|
||||
public PcodeMachine<?> create(PcodeDebuggerAccess access, Writer writer) {
|
||||
addHandlers(writer);
|
||||
return new TaintPcodeEmulator(access.getLanguage(), writer.callbacks());
|
||||
}
|
||||
}
|
|
@ -17,19 +17,12 @@ package ghidra.pcode.emu.taint;
|
|||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import ghidra.app.plugin.core.debug.service.emulation.RWTargetMemoryPcodeExecutorStatePiece;
|
||||
import ghidra.app.plugin.core.debug.service.emulation.RWTargetRegistersPcodeExecutorStatePiece;
|
||||
import ghidra.pcode.emu.*;
|
||||
import ghidra.pcode.emu.DefaultPcodeThread.PcodeThreadExecutor;
|
||||
import ghidra.pcode.emu.auxiliary.AuxEmulatorPartsFactory;
|
||||
import ghidra.pcode.emu.auxiliary.AuxPcodeEmulator;
|
||||
import ghidra.pcode.emu.taint.plain.TaintPcodeExecutorState;
|
||||
import ghidra.pcode.emu.taint.trace.TaintTracePcodeExecutorState;
|
||||
import ghidra.pcode.emu.taint.state.TaintPcodeExecutorState;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.pcode.exec.debug.auxiliary.AuxDebuggerEmulatorPartsFactory;
|
||||
import ghidra.pcode.exec.debug.auxiliary.AuxDebuggerPcodeEmulator;
|
||||
import ghidra.pcode.exec.trace.BytesTracePcodeExecutorStatePiece;
|
||||
import ghidra.pcode.exec.trace.TracePcodeExecutorState;
|
||||
import ghidra.pcode.exec.trace.auxiliary.AuxTracePcodeEmulator;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.taint.model.TaintVec;
|
||||
|
||||
|
@ -51,22 +44,10 @@ import ghidra.taint.model.TaintVec;
|
|||
* <li>P-code Arithmetic: {@link TaintPcodeArithmetic}</li>
|
||||
* <li>Userop Library: {@link TaintPcodeUseropLibrary}</li>
|
||||
* <li>P-code Executor: {@link TaintPcodeThreadExecutor}</li>
|
||||
* <li>Machine State
|
||||
* <ul>
|
||||
* <li>Stand alone: {@link TaintPcodeExecutorState}</li>
|
||||
* <li>Trace integrated: {@link TaintTracePcodeExecutorState}</li>
|
||||
* <li>Debugger integrated: {@link TaintTracePcodeExecutorState} (same as Trace integrated)</li>
|
||||
* <li>Machine State: {@link TaintPcodeExecutorState}</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* If you're following from the {@link ghidra.taint} package documentation, you'll want to return to
|
||||
* {@link ghidra.pcode.emu.taint.plain} before you examine the trace-integrated state. Similarly,
|
||||
* you'll want to return to {@link ghidra.pcode.emu.taint.trace} before you examine the
|
||||
* Debugger-integrated state.
|
||||
*/
|
||||
public enum TaintPartsFactory implements AuxDebuggerEmulatorPartsFactory<TaintVec> {
|
||||
public enum TaintPartsFactory implements AuxEmulatorPartsFactory<TaintVec> {
|
||||
/** This singleton factory instance */
|
||||
INSTANCE;
|
||||
|
||||
|
@ -145,8 +126,9 @@ public enum TaintPartsFactory implements AuxDebuggerEmulatorPartsFactory<TaintVe
|
|||
*/
|
||||
@Override
|
||||
public PcodeExecutorState<Pair<byte[], TaintVec>> createSharedState(
|
||||
AuxPcodeEmulator<TaintVec> emulator, BytesPcodeExecutorStatePiece concrete) {
|
||||
return new TaintPcodeExecutorState(emulator.getLanguage(), concrete);
|
||||
AuxPcodeEmulator<TaintVec> emulator, BytesPcodeExecutorStatePiece concrete,
|
||||
PcodeStateCallbacks cb) {
|
||||
return new TaintPcodeExecutorState(emulator.getLanguage(), concrete, cb);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -159,79 +141,7 @@ public enum TaintPartsFactory implements AuxDebuggerEmulatorPartsFactory<TaintVe
|
|||
@Override
|
||||
public PcodeExecutorState<Pair<byte[], TaintVec>> createLocalState(
|
||||
AuxPcodeEmulator<TaintVec> emulator, PcodeThread<Pair<byte[], TaintVec>> thread,
|
||||
BytesPcodeExecutorStatePiece concrete) {
|
||||
return new TaintPcodeExecutorState(emulator.getLanguage(), concrete);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* If you're following the {@link ghidra.taint} package documentation, please finish reading
|
||||
* about the stand-alone emulator before proceeding to this trace-integrated part. This part
|
||||
* extends the shared state of the stand-alone emulator so that it can lazily deserialize taint
|
||||
* sets from a trace database. It can also serialize intermediate and/or final taint sets back
|
||||
* into a trace database.
|
||||
*/
|
||||
@Override
|
||||
public TracePcodeExecutorState<Pair<byte[], TaintVec>> createTraceSharedState(
|
||||
AuxTracePcodeEmulator<TaintVec> emulator, BytesTracePcodeExecutorStatePiece concrete) {
|
||||
return new TaintTracePcodeExecutorState(concrete);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* If you're following the {@link ghidra.taint} package documentation, please finish reading
|
||||
* about the stand-alone emulator before proceeding to this trace-integrated part. This part
|
||||
* extends the local state of the stand-alone emulator so that it can lazily deserialize taint
|
||||
* sets from a trace database. It can also serialize intermediate and/or final taint sets back
|
||||
* into a trace database.
|
||||
*/
|
||||
@Override
|
||||
public TracePcodeExecutorState<Pair<byte[], TaintVec>> createTraceLocalState(
|
||||
AuxTracePcodeEmulator<TaintVec> emulator, PcodeThread<Pair<byte[], TaintVec>> emuThread,
|
||||
BytesTracePcodeExecutorStatePiece concrete) {
|
||||
return new TaintTracePcodeExecutorState(concrete);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* If you're following the {@link ghidra.taint} package documentation, please finish reading
|
||||
* about the stand-alone and trace-integrated emulators before proceeding to this
|
||||
* Debugger-integrated part. This part uses the same shared state as the trace-integrated
|
||||
* emulator but takes a different data access shim, so that it can also deserialize taint sets
|
||||
* from mapped static programs. Since taint is not generally a concept understood by a live
|
||||
* debugger, we need not retrieve anything new from the target. Note that the shim is passed to
|
||||
* us implicitly via the {@code concrete} state.
|
||||
*/
|
||||
@Override
|
||||
public TracePcodeExecutorState<Pair<byte[], TaintVec>> createDebuggerSharedState(
|
||||
AuxDebuggerPcodeEmulator<TaintVec> emulator,
|
||||
RWTargetMemoryPcodeExecutorStatePiece concrete) {
|
||||
return new TaintTracePcodeExecutorState(concrete);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* If you're following the {@link ghidra.taint} package documentation, please finish reading
|
||||
* about the stand-alone and trace-integrated emulators before proceeding to this method. Since
|
||||
* taint is not generally a concept understood by a live debugger, we need not retrieve anything
|
||||
* new from the target. Furthermore, because static program mappings do not apply to registers,
|
||||
* we need not consider them. Thus, we can just re-use the trace-integrated local state. The
|
||||
* concrete piece given to us, which we just pass to our paired state, will handle retrieving
|
||||
* concrete values from the live target, if applicable.
|
||||
*/
|
||||
@Override
|
||||
public TracePcodeExecutorState<Pair<byte[], TaintVec>> createDebuggerLocalState(
|
||||
AuxDebuggerPcodeEmulator<TaintVec> emulator,
|
||||
PcodeThread<Pair<byte[], TaintVec>> emuThread,
|
||||
RWTargetRegistersPcodeExecutorStatePiece concrete) {
|
||||
return new TaintTracePcodeExecutorState(concrete);
|
||||
BytesPcodeExecutorStatePiece concrete, PcodeStateCallbacks cb) {
|
||||
return new TaintPcodeExecutorState(emulator.getLanguage(), concrete, cb);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
@ -23,6 +23,7 @@ import ghidra.program.model.address.AddressSpace;
|
|||
import ghidra.program.model.lang.Endian;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.taint.model.TaintSet;
|
||||
import ghidra.taint.model.TaintVec;
|
||||
import ghidra.taint.model.TaintVec.ShiftMode;
|
||||
|
||||
|
@ -73,6 +74,11 @@ public enum TaintPcodeArithmetic implements PcodeArithmetic<TaintVec> {
|
|||
this.endian = endian;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<TaintVec> getDomain() {
|
||||
return TaintVec.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Endian getEndian() {
|
||||
return endian;
|
||||
|
@ -94,20 +100,13 @@ public enum TaintPcodeArithmetic implements PcodeArithmetic<TaintVec> {
|
|||
*/
|
||||
@Override
|
||||
public TaintVec unaryOp(int opcode, int sizeout, int sizein1, TaintVec in1) {
|
||||
switch (opcode) {
|
||||
case PcodeOp.COPY:
|
||||
case PcodeOp.BOOL_NEGATE:
|
||||
case PcodeOp.INT_NEGATE:
|
||||
return in1;
|
||||
case PcodeOp.INT_ZEXT:
|
||||
return in1.extended(sizeout, endian.isBigEndian(), false);
|
||||
case PcodeOp.INT_SEXT:
|
||||
return in1.extended(sizeout, endian.isBigEndian(), true);
|
||||
case PcodeOp.INT_2COMP:
|
||||
return in1.copy().setCascade(endian.isBigEndian());
|
||||
default:
|
||||
return TaintVec.copies(in1.union(), sizeout);
|
||||
}
|
||||
return switch (opcode) {
|
||||
case PcodeOp.COPY, PcodeOp.BOOL_NEGATE, PcodeOp.INT_NEGATE -> in1;
|
||||
case PcodeOp.INT_ZEXT -> in1.extended(sizeout, endian.isBigEndian(), false);
|
||||
case PcodeOp.INT_SEXT -> in1.extended(sizeout, endian.isBigEndian(), true);
|
||||
case PcodeOp.INT_2COMP -> in1.copy().setCascade(endian.isBigEndian());
|
||||
default -> TaintVec.copies(in1.union(), sizeout);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -127,13 +126,11 @@ public enum TaintPcodeArithmetic implements PcodeArithmetic<TaintVec> {
|
|||
public TaintVec binaryOp(PcodeOp op, TaintVec in1, TaintVec in2) {
|
||||
// TODO: Detect immediate operands and be more precise
|
||||
switch (op.getOpcode()) {
|
||||
case PcodeOp.INT_XOR:
|
||||
case PcodeOp.INT_SUB:
|
||||
case PcodeOp.BOOL_XOR:
|
||||
case PcodeOp.INT_XOR, PcodeOp.INT_SUB, PcodeOp.BOOL_XOR -> {
|
||||
if (Objects.equals(op.getInput(0), op.getInput(1))) {
|
||||
return fromConst(0, op.getOutput().getSize());
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
return PcodeArithmetic.super.binaryOp(op, in1, in2);
|
||||
}
|
||||
|
@ -152,29 +149,33 @@ public enum TaintPcodeArithmetic implements PcodeArithmetic<TaintVec> {
|
|||
@Override
|
||||
public TaintVec binaryOp(int opcode, int sizeout, int sizein1, TaintVec in1,
|
||||
int sizein2, TaintVec in2) {
|
||||
switch (opcode) {
|
||||
case PcodeOp.BOOL_AND:
|
||||
case PcodeOp.BOOL_OR:
|
||||
case PcodeOp.BOOL_XOR:
|
||||
case PcodeOp.INT_AND:
|
||||
case PcodeOp.INT_OR:
|
||||
case PcodeOp.INT_XOR:
|
||||
return in1.zipUnion(in2);
|
||||
case PcodeOp.INT_ADD:
|
||||
case PcodeOp.INT_SUB: {
|
||||
TaintVec temp = in1.zipUnion(in2);
|
||||
return temp.setCascade(endian.isBigEndian());
|
||||
return switch (opcode) {
|
||||
case PcodeOp.BOOL_AND, PcodeOp.BOOL_OR, PcodeOp.BOOL_XOR, PcodeOp.INT_AND, //
|
||||
PcodeOp.INT_OR, PcodeOp.INT_XOR -> {
|
||||
yield in1.zipUnion(in2);
|
||||
}
|
||||
case PcodeOp.PIECE: {
|
||||
case PcodeOp.INT_ADD, PcodeOp.INT_SUB -> {
|
||||
TaintVec temp = in1.zipUnion(in2);
|
||||
yield temp.setCascade(endian.isBigEndian());
|
||||
}
|
||||
case PcodeOp.INT_SLESS, PcodeOp.INT_SLESSEQUAL, //
|
||||
PcodeOp.INT_LESS, PcodeOp.INT_LESSEQUAL, //
|
||||
PcodeOp.INT_EQUAL, PcodeOp.INT_NOTEQUAL, //
|
||||
PcodeOp.FLOAT_LESS, PcodeOp.FLOAT_LESSEQUAL, //
|
||||
PcodeOp.FLOAT_EQUAL, PcodeOp.FLOAT_NOTEQUAL -> {
|
||||
TaintSet temp = in1.union().union(in2.union());
|
||||
yield TaintVec.copies(temp, sizeout);
|
||||
}
|
||||
case PcodeOp.PIECE -> {
|
||||
TaintVec temp = in1.extended(sizeout, endian.isBigEndian(), false);
|
||||
temp.setShifted(endian.isBigEndian() ? -sizein2 : sizein2, ShiftMode.UNBOUNDED);
|
||||
return temp.set(endian.isBigEndian() ? sizeout - sizein2 : 0, in2);
|
||||
yield temp.set(endian.isBigEndian() ? sizeout - sizein2 : 0, in2);
|
||||
}
|
||||
default: {
|
||||
default -> {
|
||||
TaintVec temp = in1.zipUnion(in2);
|
||||
return temp.setCopies(temp.union());
|
||||
yield temp.setCopies(temp.union());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,34 +4,48 @@
|
|||
* 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.emu.taint.plain;
|
||||
package ghidra.pcode.emu.taint;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import ghidra.pcode.emu.PcodeEmulationCallbacks;
|
||||
import ghidra.pcode.emu.auxiliary.AuxEmulatorPartsFactory;
|
||||
import ghidra.pcode.emu.auxiliary.AuxPcodeEmulator;
|
||||
import ghidra.pcode.emu.taint.TaintPartsFactory;
|
||||
import ghidra.pcode.emu.taint.state.TaintPcodeExecutorState;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.taint.model.TaintVec;
|
||||
|
||||
/**
|
||||
* A stand-alone emulator with taint analysis
|
||||
* An emulator with taint analysis
|
||||
*/
|
||||
public class TaintPcodeEmulator extends AuxPcodeEmulator<TaintVec> {
|
||||
/**
|
||||
* Create an emulator
|
||||
*
|
||||
* @param language the language (processor model)
|
||||
* @param cb callbacks to receive emulation events
|
||||
*/
|
||||
public TaintPcodeEmulator(Language language,
|
||||
PcodeEmulationCallbacks<Pair<byte[], TaintVec>> cb) {
|
||||
super(language, cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an emulator
|
||||
*
|
||||
* @param language the language (processor model)
|
||||
*/
|
||||
public TaintPcodeEmulator(Language language) {
|
||||
super(language);
|
||||
this(language, PcodeEmulationCallbacks.none());
|
||||
}
|
||||
|
||||
/**
|
|
@ -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.
|
||||
|
@ -72,8 +72,8 @@ public class TaintPcodeUseropLibrary extends AnnotatedPcodeUseropLibrary<Pair<by
|
|||
* the Sleigh code {@code RAX = taint_arr(RAX)} will cause RAX to be tainted as
|
||||
* [arr_0_0][arr_0_1]...[arr_0_7].
|
||||
*
|
||||
* @param in
|
||||
* @return
|
||||
* @param in the input value
|
||||
* @return the same value, with the generated taint unioned in
|
||||
*/
|
||||
@PcodeUserop
|
||||
public Pair<byte[], TaintVec> taint_arr(Pair<byte[], TaintVec> in) {
|
||||
|
|
|
@ -1,51 +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.emu.taint.full;
|
||||
|
||||
import ghidra.debug.api.emulation.PcodeDebuggerAccess;
|
||||
import ghidra.pcode.emu.taint.TaintPartsFactory;
|
||||
import ghidra.pcode.emu.taint.plain.TaintPcodeEmulator;
|
||||
import ghidra.pcode.exec.debug.auxiliary.AuxDebuggerEmulatorPartsFactory;
|
||||
import ghidra.pcode.exec.debug.auxiliary.AuxDebuggerPcodeEmulator;
|
||||
import ghidra.taint.model.TaintVec;
|
||||
|
||||
/**
|
||||
* A Debugger-integrated emulator with taint analysis
|
||||
*/
|
||||
public class TaintDebuggerPcodeEmulator extends AuxDebuggerPcodeEmulator<TaintVec> {
|
||||
/**
|
||||
* Create an emulator
|
||||
*
|
||||
* @param data the trace-and-debugger access shim
|
||||
*/
|
||||
public TaintDebuggerPcodeEmulator(PcodeDebuggerAccess data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* Here, we just return the singleton parts factory. This appears simple because all the
|
||||
* complexity is encapsulated in the factory. See {@link TaintPartsFactory} to see everything
|
||||
* the implementation actually entails. Notice that this is the same parts factory used by
|
||||
* {@link TaintPcodeEmulator}.
|
||||
*/
|
||||
@Override
|
||||
protected AuxDebuggerEmulatorPartsFactory<TaintVec> getPartsFactory() {
|
||||
return TaintPartsFactory.INSTANCE;
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.taint.full;
|
||||
|
||||
import ghidra.app.plugin.core.debug.service.emulation.AbstractDebuggerPcodeEmulatorFactory;
|
||||
import ghidra.debug.api.emulation.DebuggerPcodeMachine;
|
||||
import ghidra.debug.api.emulation.PcodeDebuggerAccess;
|
||||
|
||||
/**
|
||||
* An emulator factory for making the {@link TaintDebuggerPcodeEmulator} discoverable to the UI
|
||||
*
|
||||
* <p>
|
||||
* This is the final class to create a full Debugger-integrated emulator. This class is what makes
|
||||
* it appear in the menu of possible emulators the user may configure.
|
||||
*/
|
||||
public class TaintDebuggerPcodeEmulatorFactory extends AbstractDebuggerPcodeEmulatorFactory {
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return "Taint Analyzer with Concrete Emulation";
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebuggerPcodeMachine<?> create(PcodeDebuggerAccess data) {
|
||||
return new TaintDebuggerPcodeEmulator(data);
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
@ -14,20 +14,16 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* The stand-alone Taint Emulator
|
||||
* The Taint Emulator
|
||||
*
|
||||
* <p>
|
||||
* This and the {@link ghidra.pcode.emu.taint} packages contain all the parts necessary to construct
|
||||
* a stand-alone emulator. Because this is a working solution, the state components already have
|
||||
* provisions in place for extension to support the fully-integrated solution. Generally, it's a bit
|
||||
* easier to just get the basic state components implemented, put tests in place, and then re-factor
|
||||
* them to permit extension as you address each more integrated emulator.
|
||||
* This and the {@link ghidra.pcode.emu.taint.state} packages contain all the parts necessary to
|
||||
* construct the emulator.
|
||||
*
|
||||
* <p>
|
||||
* For this package, I recommend a top-down approach, since the top component provides a flat
|
||||
* catalog of the lower components. That top piece is actually in a separate package. See
|
||||
* {@link ghidra.pcode.emu.taint.TaintPartsFactory}. That factory is then used in
|
||||
* {@link TaintPcodeEmulator} to realize the stand-alone emulator. When you get to the state pieces,
|
||||
* you may want to pause and read {@link TaintSpace} first.
|
||||
* {@link ghidra.pcode.emu.taint.TaintPcodeEmulator} to realize the emulator.
|
||||
*/
|
||||
package ghidra.pcode.emu.taint.plain;
|
||||
package ghidra.pcode.emu.taint;
|
|
@ -1,91 +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.emu.taint.plain;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import ghidra.pcode.emu.taint.AbstractTaintPcodeExecutorStatePiece;
|
||||
import ghidra.pcode.emu.taint.TaintPcodeArithmetic;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.pcode.exec.trace.TracePcodeExecutorState;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Language;
|
||||
|
||||
/**
|
||||
* The state piece for holding taint marks in the emulator's machine state
|
||||
*
|
||||
* <p>
|
||||
* Because this is already a working solution, most of the logic has already been abstracted into a
|
||||
* super class {@link AbstractTaintPcodeExecutorStatePiece}. This class serves only to choose the
|
||||
* type {@link TaintSpace}, which implements the real storage logic, and provide a map from address
|
||||
* space to that type. Note the concept of a space map is introduced by
|
||||
* {@link AbstractLongOffsetPcodeExecutorStatePiece}, which is provided by the p-code emulation
|
||||
* framework. This is suitable for state pieces with concrete addresses. This likely fits your
|
||||
* auxiliary piece, but may not. If you choose to use abstract addresses for your auxiliary piece,
|
||||
* then your implementation of state will not follow the archetype presented here. You'll instead
|
||||
* want to implement {@link TracePcodeExecutorState} directly, take the concrete piece provided, and
|
||||
* wrap it as you see fit. You may still benefit by referring to the implementation of
|
||||
* {@link PairedPcodeExecutorState}. When implementing your flavor of
|
||||
* {@link PairedPcodeExecutorState#getVar(AddressSpace, Pair, int, boolean, Reason)}, still consider
|
||||
* that you could benefit from the concrete element of the offset pair passed in.
|
||||
*/
|
||||
public class TaintPcodeExecutorStatePiece extends AbstractTaintPcodeExecutorStatePiece<TaintSpace> {
|
||||
/**
|
||||
* Create the taint piece
|
||||
*
|
||||
* @param language the language of the emulator
|
||||
* @param addressArithmetic the address arithmetic, likely taken from the concrete piece
|
||||
*/
|
||||
public TaintPcodeExecutorStatePiece(Language language,
|
||||
PcodeArithmetic<byte[]> addressArithmetic) {
|
||||
super(language, addressArithmetic, TaintPcodeArithmetic.forLanguage(language));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* Here we use the simplest scheme for creating a map of {@link TaintSpace}s. This is
|
||||
* essentially a lazy map from address space to some object for managing taint marks in that
|
||||
* address space. The space could be a memory space, register space, unique space, etc. This
|
||||
* piece will look up the space, creating it if necessary, and then delegate the get and set
|
||||
* methods.
|
||||
*/
|
||||
@Override
|
||||
protected AbstractSpaceMap<TaintSpace> newSpaceMap() {
|
||||
return new SimpleSpaceMap<TaintSpace>() {
|
||||
@Override
|
||||
protected TaintSpace newSpace(AddressSpace space) {
|
||||
return new TaintSpace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractSpaceMap<TaintSpace> fork() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaintSpace fork(TaintSpace s) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaintPcodeExecutorStatePiece fork() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
|
@ -4,16 +4,16 @@
|
|||
* 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.emu.taint.plain;
|
||||
package ghidra.pcode.emu.taint.state;
|
||||
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.program.model.lang.Language;
|
||||
|
@ -48,8 +48,11 @@ public class TaintPcodeExecutorState extends PairedPcodeExecutorState<byte[], Ta
|
|||
*
|
||||
* @param language the language for creating the taint piece
|
||||
* @param concrete the concrete piece
|
||||
* @param cb callbacks to receive emulation events
|
||||
*/
|
||||
public TaintPcodeExecutorState(Language language, BytesPcodeExecutorStatePiece concrete) {
|
||||
this(concrete, new TaintPcodeExecutorStatePiece(language, concrete.getAddressArithmetic()));
|
||||
public TaintPcodeExecutorState(Language language, BytesPcodeExecutorStatePiece concrete,
|
||||
PcodeStateCallbacks cb) {
|
||||
this(concrete,
|
||||
new TaintPcodeExecutorStatePiece(language, concrete.getAddressArithmetic(), cb));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
/* ###
|
||||
* 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.emu.taint.state;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import ghidra.pcode.emu.taint.TaintPcodeArithmetic;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.taint.model.TaintVec;
|
||||
|
||||
/**
|
||||
* The taint state piece
|
||||
*
|
||||
* <p>
|
||||
* The framework-provided class from which this derives expects us to implement state for each
|
||||
* address space using a separate storage object. We do this by providing {@link TaintSpace}, which
|
||||
* is where all the taint storage logic is actually located. We then use a map {@link #spaceMap} to
|
||||
* lazily create a keep each of those spaces.
|
||||
*/
|
||||
public class TaintPcodeExecutorStatePiece
|
||||
extends AbstractLongOffsetPcodeExecutorStatePiece<byte[], TaintVec, TaintSpace> {
|
||||
|
||||
/**
|
||||
* A lazily-populated map of address space to taint storage.
|
||||
*/
|
||||
protected final Map<AddressSpace, TaintSpace> spaceMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Create a state piece
|
||||
*
|
||||
* @param language the emulator's language
|
||||
* @param addressArithmetic the arithmetic for the address type
|
||||
* @param arithmetic the arithmetic for the value type
|
||||
* @param cb callbacks to receive emulation events
|
||||
*/
|
||||
public TaintPcodeExecutorStatePiece(Language language,
|
||||
PcodeArithmetic<byte[]> addressArithmetic, PcodeArithmetic<TaintVec> arithmetic,
|
||||
PcodeStateCallbacks cb) {
|
||||
super(language, addressArithmetic, arithmetic, cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the taint piece
|
||||
*
|
||||
* @param language the language of the emulator
|
||||
* @param addressArithmetic the address arithmetic, likely taken from the concrete piece
|
||||
* @param cb callbacks to receive emulation events
|
||||
*/
|
||||
public TaintPcodeExecutorStatePiece(Language language,
|
||||
PcodeArithmetic<byte[]> addressArithmetic, PcodeStateCallbacks cb) {
|
||||
super(language, addressArithmetic, TaintPcodeArithmetic.forLanguage(language), cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaintPcodeExecutorStatePiece fork(PcodeStateCallbacks cb) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemBuffer getConcreteBuffer(Address address, Purpose purpose) {
|
||||
throw new ConcretionError("Cannot make Taint concrete", purpose);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* Here, we just follow the pattern: delegate to the space map.
|
||||
*/
|
||||
@Override
|
||||
protected TaintSpace getForSpace(AddressSpace space, boolean toWrite) {
|
||||
if (toWrite) {
|
||||
return spaceMap.computeIfAbsent(space, s -> new TaintSpace(space, this));
|
||||
}
|
||||
return spaceMap.get(space);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* Because the super class places no bound on {@code <S>}, we have to provide the delegation to
|
||||
* the storage space.
|
||||
*/
|
||||
@Override
|
||||
protected void setInSpace(TaintSpace space, long offset, int size, TaintVec val,
|
||||
PcodeStateCallbacks cb) {
|
||||
space.set(offset, val, cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* Because the super class places no bound on {@code <S>}, we have to provide the delegation to
|
||||
* the storage space.
|
||||
*/
|
||||
@Override
|
||||
protected TaintVec getFromSpace(TaintSpace space, long offset, int size, Reason reason,
|
||||
PcodeStateCallbacks cb) {
|
||||
return space.get(offset, size, cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<Register, TaintVec> getRegisterValuesFromSpace(TaintSpace space,
|
||||
List<Register> registers) {
|
||||
return space.getRegisterValues(registers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
for (TaintSpace space : spaceMap.values()) {
|
||||
space.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<Long, TaintVec> getNextEntryInternal(AddressSpace space, long offset) {
|
||||
TaintSpace s = getForSpace(space, false);
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
return s.getNextEntry(offset);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
/* ###
|
||||
* 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.emu.taint.state;
|
||||
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece;
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||
import ghidra.pcode.exec.trace.TraceEmulationIntegration.AbstractPropertyBasedPieceHandler;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTracePropertyAccess;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.taint.model.TaintSet;
|
||||
import ghidra.taint.model.TaintVec;
|
||||
|
||||
/**
|
||||
* The piece handler for {@link TaintVec}
|
||||
*
|
||||
* <p>
|
||||
* This contains the logic for integrating the Taint emulator with traces. That is, it is the
|
||||
* mechanism that loads previous taint analysis from a trace and stores new results back into the
|
||||
* trace. The object passed into these methods as {@code piece} is almost certainly a
|
||||
* {@link TaintPcodeExecutorStatePiece}, but not necessarily. As a matter of best practice, it
|
||||
* should not be necessary to cast. The given {@link PcodeExecutorStatePiece} interface should be
|
||||
* sufficient as internals can often be reached via
|
||||
* {@link PcodeExecutorStatePiece#getVarInternal(AddressSpace, long, int, Reason)}.
|
||||
*/
|
||||
public class TaintPieceHandler extends AbstractPropertyBasedPieceHandler<byte[], TaintVec, String> {
|
||||
/**
|
||||
* The name we will use for the property map
|
||||
*/
|
||||
public static final String NAME = "Taint";
|
||||
|
||||
@Override
|
||||
public Class<byte[]> getAddressDomain() {
|
||||
return byte[].class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<TaintVec> getValueDomain() {
|
||||
return TaintVec.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getPropertyName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<String> getPropertyType() {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* The super class takes care of visiting each property map entry that may be involved. This
|
||||
* method gets invoked for each one found, identifying the range to which the property value
|
||||
* applies and the value itself. <b>IMPORTANT:</b> This implementation must still ensure it only
|
||||
* modifies addresses that are not yet initialized. The set of such addresses is given by
|
||||
* {@code limit}. Thus, we have the if-else to determine whether or not the found property entry
|
||||
* is wholly contained within that limit. If not, then we have to piecemeal it.
|
||||
* <p>
|
||||
* To insert each resulting entry into the state piece, we use
|
||||
* {@link PcodeExecutorStatePiece#setVarInternal(AddressSpace, long, int, Object)}, so that we
|
||||
* do not issue any follow-on callbacks.
|
||||
*/
|
||||
@Override
|
||||
protected void decodeFrom(PcodeExecutorStatePiece<byte[], TaintVec> piece, AddressSetView limit,
|
||||
AddressRange range, String propertyValue) {
|
||||
TaintVec vec = TaintVec.copies(TaintSet.parse(propertyValue), (int) range.getLength());
|
||||
if (limit.contains(range.getMaxAddress(), range.getMaxAddress())) {
|
||||
piece.setVarInternal(range.getAddressSpace(), range.getMinAddress().getOffset(),
|
||||
vec.length, vec);
|
||||
}
|
||||
else {
|
||||
for (AddressRange sub : limit.intersectRange(range.getMinAddress(),
|
||||
range.getMaxAddress())) {
|
||||
int offset = (int) sub.getMinAddress().subtract(range.getMinAddress());
|
||||
TaintVec sv = vec.sub(offset, (int) sub.getLength());
|
||||
piece.setVarInternal(sub.getAddressSpace(), sub.getMinAddress().getOffset(),
|
||||
sv.length, sv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* The super class takes care of iterating over the entries in the state piece, using
|
||||
* {@link PcodeExecutorStatePiece#getNextEntryInternal(AddressSpace, long)}. In our case, since
|
||||
* we coalesce identically-tainted contiguous bytes, serialization is fairly straightforward.
|
||||
* The one nuances is that we'd rather not waste entries for addresses without any taint, and so
|
||||
* we check for that and use {@code null} instead, which will cause the shim to clear the
|
||||
* property on those addresses.
|
||||
*/
|
||||
@Override
|
||||
protected void encodeInto(PcodeTracePropertyAccess<String> property, AddressRange range,
|
||||
TaintVec value) {
|
||||
Address min = range.getMinAddress();
|
||||
for (int i = 0; i < value.length; i++) {
|
||||
TaintSet s = value.get(i);
|
||||
Address address = min.add(i);
|
||||
if (s.isEmpty()) {
|
||||
property.clear(new AddressRangeImpl(address, address));
|
||||
}
|
||||
else {
|
||||
property.put(address, s.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,14 +13,17 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.taint.plain;
|
||||
package ghidra.pcode.emu.taint.state;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import ghidra.pcode.emu.taint.trace.TaintTraceSpace;
|
||||
import ghidra.pcode.exec.PcodeStateCallbacks;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.taint.model.TaintSet;
|
||||
import ghidra.taint.model.TaintVec;
|
||||
import ghidra.util.MathUtilities;
|
||||
|
||||
/**
|
||||
* The storage space for taint sets in a single address space (possibly the register space)
|
||||
|
@ -28,12 +31,19 @@ import ghidra.taint.model.TaintVec;
|
|||
* <p>
|
||||
* This is the actual implementation of the in-memory storage for taint marks. For a stand-alone
|
||||
* emulator, this is the full state. For a trace- or Debugger-integrated emulator, this is a cache
|
||||
* of taints loaded from a trace backing this emulator. Most likely, that trace is the user's
|
||||
* current trace.
|
||||
* of taints loaded from a trace backing this emulator. (See {@link TaintPieceHandler}.) Most
|
||||
* likely, that trace is the user's current trace.
|
||||
*/
|
||||
public class TaintSpace {
|
||||
protected final AddressSpace space;
|
||||
protected final TaintPcodeExecutorStatePiece piece;
|
||||
// TODO: There must be a better way. Similar to SemisparseByteArray?
|
||||
protected final Map<Long, TaintSet> taints = new HashMap<>();
|
||||
protected final NavigableMap<Long, TaintSet> taints = new TreeMap<>(Long::compareUnsigned);
|
||||
|
||||
public TaintSpace(AddressSpace space, TaintPcodeExecutorStatePiece piece) {
|
||||
this.space = space;
|
||||
this.piece = piece;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the variable at offset with the given taint sets
|
||||
|
@ -46,8 +56,9 @@ public class TaintSpace {
|
|||
*
|
||||
* @param offset the starting offset
|
||||
* @param val the vector of taint sets
|
||||
* @param cb callbacks to receive emulation events
|
||||
*/
|
||||
public void set(long offset, TaintVec val) {
|
||||
public void set(long offset, TaintVec val, PcodeStateCallbacks cb) {
|
||||
for (int i = 0; i < val.length; i++) {
|
||||
TaintSet s = val.get(i);
|
||||
/*
|
||||
|
@ -56,6 +67,7 @@ public class TaintSpace {
|
|||
*/
|
||||
taints.put(offset + i, s);
|
||||
}
|
||||
cb.dataWritten(piece, space.getAddress(offset), val.length, val);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -70,11 +82,21 @@ public class TaintSpace {
|
|||
*
|
||||
* @param offset the offset
|
||||
* @param buf the vector to receive taint sets
|
||||
* @param cb callbacks to receive emulation events
|
||||
*/
|
||||
public void getInto(long offset, TaintVec buf) {
|
||||
public void getInto(long offset, TaintVec buf, PcodeStateCallbacks cb) {
|
||||
for (int i = 0; i < buf.length; i++) {
|
||||
TaintSet s = taints.get(offset + i);
|
||||
buf.set(i, s == null ? whenNull(offset + i) : s);
|
||||
if (s == null) {
|
||||
if (cb.readUninitialized(piece, PcodeStateCallbacks.rngSet(space, offset + i, 1))
|
||||
.isEmpty()) {
|
||||
s = taints.get(offset + i);
|
||||
}
|
||||
}
|
||||
if (s == null) { // still
|
||||
s = TaintSet.EMPTY;
|
||||
}
|
||||
buf.set(i, s);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -82,33 +104,20 @@ public class TaintSpace {
|
|||
* Retrieve the taint sets for the variable at the given offset
|
||||
*
|
||||
* <p>
|
||||
* This works the same as {@link #getInto(long, TaintVec)}, but creates a new vector of the
|
||||
* given size, reads the taint sets, and returns the vector.
|
||||
* This works the same as {@link #getInto(long, TaintVec, PcodeStateCallbacks)}, but creates a
|
||||
* new vector of the given size, reads the taint sets, and returns the vector.
|
||||
*
|
||||
* @param offset the offset
|
||||
* @param size the size of the variable
|
||||
* @param cb callbacks to receive emulation events
|
||||
* @return the taint vector for that variable
|
||||
*/
|
||||
public TaintVec get(long offset, int size) {
|
||||
public TaintVec get(long offset, int size, PcodeStateCallbacks cb) {
|
||||
TaintVec vec = new TaintVec(size);
|
||||
getInto(offset, vec);
|
||||
getInto(offset, vec, cb);
|
||||
return vec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension point: Behavior when there is no in-memory taint set at the given offset
|
||||
*
|
||||
* <p>
|
||||
* This will be overridden by {@link TaintTraceSpace} to implement the lazy loading and
|
||||
* deserialization from a trace.
|
||||
*
|
||||
* @param offset the offset
|
||||
* @return the taint set to use
|
||||
*/
|
||||
protected TaintSet whenNull(long offset) {
|
||||
return TaintSet.EMPTY;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
taints.clear();
|
||||
}
|
||||
|
@ -128,4 +137,19 @@ public class TaintSpace {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Entry<Long, TaintVec> getNextEntry(long offset) {
|
||||
Long check = taints.ceilingKey(offset);
|
||||
if (check == null) {
|
||||
return null;
|
||||
}
|
||||
offset = check;
|
||||
long end = offset;
|
||||
while (taints.get(end) != null) {
|
||||
end++;
|
||||
}
|
||||
TaintVec vec = new TaintVec(MathUtilities.unsignedMin(1024, end - offset));
|
||||
getInto(offset, vec, PcodeStateCallbacks.NONE);
|
||||
return Map.entry(offset, vec);
|
||||
}
|
||||
}
|
|
@ -1,63 +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.emu.taint.trace;
|
||||
|
||||
import ghidra.pcode.emu.taint.TaintPartsFactory;
|
||||
import ghidra.pcode.emu.taint.plain.TaintPcodeEmulator;
|
||||
import ghidra.pcode.exec.trace.auxiliary.AuxTraceEmulatorPartsFactory;
|
||||
import ghidra.pcode.exec.trace.auxiliary.AuxTracePcodeEmulator;
|
||||
import ghidra.pcode.exec.trace.data.*;
|
||||
import ghidra.taint.model.TaintVec;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
|
||||
/**
|
||||
* A trace-integrated emulator with taint analysis
|
||||
*/
|
||||
public class TaintTracePcodeEmulator extends AuxTracePcodeEmulator<TaintVec> {
|
||||
/**
|
||||
* Create an emulator
|
||||
*
|
||||
* @param access the trace access shim
|
||||
*/
|
||||
public TaintTracePcodeEmulator(PcodeTraceAccess access) {
|
||||
super(access);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an emulator
|
||||
*
|
||||
* @param platform the platform to emulate
|
||||
* @param snap the source snap
|
||||
*/
|
||||
public TaintTracePcodeEmulator(TracePlatform platform, long snap) {
|
||||
super(platform, snap);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* Here, we just return the singleton parts factory. This appears simple because all the
|
||||
* complexity is encapsulated in the factory. See {@link TaintPartsFactory} to see everything
|
||||
* the implementation actually entails. Notice that this is the same parts factory used by
|
||||
* {@link TaintPcodeEmulator}. The {@link AuxTracePcodeEmulator} knows to use the more capable
|
||||
* state parts.
|
||||
*/
|
||||
@Override
|
||||
protected AuxTraceEmulatorPartsFactory<TaintVec> getPartsFactory() {
|
||||
return TaintPartsFactory.INSTANCE;
|
||||
}
|
||||
}
|
|
@ -1,54 +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.emu.taint.trace;
|
||||
|
||||
import ghidra.pcode.emu.taint.plain.TaintPcodeExecutorState;
|
||||
import ghidra.pcode.exec.trace.*;
|
||||
import ghidra.taint.model.TaintVec;
|
||||
|
||||
/**
|
||||
* A paired concrete-plus-taint trace-integrated state
|
||||
*
|
||||
* <p>
|
||||
* This contains the emulator's machine state along with the taint markings, just like
|
||||
* {@link TaintPcodeExecutorState}, except that it can read and write state from a trace. In
|
||||
* reality, this just composes concrete and taint state pieces, which actually do all the work.
|
||||
*/
|
||||
public class TaintTracePcodeExecutorState extends PairedTracePcodeExecutorState<byte[], TaintVec> {
|
||||
|
||||
/**
|
||||
* Create a state from the two given pieces
|
||||
*
|
||||
* @param concrete the concrete piece
|
||||
* @param taint the taint piece
|
||||
*/
|
||||
public TaintTracePcodeExecutorState(BytesTracePcodeExecutorStatePiece concrete,
|
||||
TaintTracePcodeExecutorStatePiece taint) {
|
||||
super(new PairedTracePcodeExecutorStatePiece<>(concrete, taint));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a state from the given concrete piece and an internally constructed taint piece
|
||||
*
|
||||
* <p>
|
||||
* We take the data access shim needed by the taint piece from the concrete piece.
|
||||
*
|
||||
* @param concrete the concrete piece
|
||||
*/
|
||||
public TaintTracePcodeExecutorState(BytesTracePcodeExecutorStatePiece concrete) {
|
||||
this(concrete, new TaintTracePcodeExecutorStatePiece(concrete.getData()));
|
||||
}
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.taint.trace;
|
||||
|
||||
import ghidra.pcode.emu.taint.AbstractTaintPcodeExecutorStatePiece;
|
||||
import ghidra.pcode.emu.taint.TaintPcodeArithmetic;
|
||||
import ghidra.pcode.exec.BytesPcodeArithmetic;
|
||||
import ghidra.pcode.exec.trace.TracePcodeExecutorStatePiece;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTracePropertyAccess;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.taint.model.TaintVec;
|
||||
import ghidra.trace.model.property.TracePropertyMapSpace;
|
||||
|
||||
/**
|
||||
* The trace-integrated state piece for holding taint marks
|
||||
*
|
||||
* <p>
|
||||
* See {@link AbstractTaintPcodeExecutorStatePiece} for framing. We'll store taint sets in the
|
||||
* trace's address property map, which is the recommended scheme for auxiliary state.
|
||||
*/
|
||||
public class TaintTracePcodeExecutorStatePiece
|
||||
extends AbstractTaintPcodeExecutorStatePiece<TaintTraceSpace>
|
||||
implements TracePcodeExecutorStatePiece<byte[], TaintVec> {
|
||||
public static final String NAME = "Taint";
|
||||
|
||||
protected final PcodeTraceDataAccess data;
|
||||
protected final PcodeTracePropertyAccess<String> property;
|
||||
|
||||
/**
|
||||
* Create a state piece
|
||||
*
|
||||
* @param data the trace-data access shim
|
||||
*/
|
||||
public TaintTracePcodeExecutorStatePiece(PcodeTraceDataAccess data) {
|
||||
super(data.getLanguage(),
|
||||
BytesPcodeArithmetic.forLanguage(data.getLanguage()),
|
||||
TaintPcodeArithmetic.forLanguage(data.getLanguage()));
|
||||
this.data = data;
|
||||
this.property = data.getPropertyAccess(NAME, String.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PcodeTraceDataAccess getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* Here we create a map that uses {@link TaintTraceSpace}s. The framework provides the concept
|
||||
* of a space map where storage is actually a cache backed by some other object. The backing
|
||||
* object we'll use here is {@link TracePropertyMapSpace}, which is provided by the
|
||||
* TraceModeling module. We'll need a little bit of extra logic for fetching a register space
|
||||
* vs. a plain memory space, but after that, we need not care which address space the backing
|
||||
* object is for.
|
||||
*/
|
||||
@Override
|
||||
protected AbstractSpaceMap<TaintTraceSpace> newSpaceMap() {
|
||||
return new CacheingSpaceMap<PcodeTracePropertyAccess<String>, TaintTraceSpace>() {
|
||||
@Override
|
||||
protected PcodeTracePropertyAccess<String> getBacking(AddressSpace space) {
|
||||
return property;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TaintTraceSpace newSpace(AddressSpace space,
|
||||
PcodeTracePropertyAccess<String> backing) {
|
||||
return new TaintTraceSpace(space, property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractSpaceMap<TaintTraceSpace> fork() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaintTraceSpace fork(TaintTraceSpace s) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaintTracePcodeExecutorStatePiece fork() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* This does the inverse of the lazy loading. Serialize the state and store it back into the
|
||||
* trace. Technically, it could be a different trace, but it must have identically-named
|
||||
* threads.
|
||||
*/
|
||||
@Override
|
||||
public void writeDown(PcodeTraceDataAccess into) {
|
||||
PcodeTracePropertyAccess<String> property = into.getPropertyAccess(NAME, String.class);
|
||||
for (TaintTraceSpace space : spaceMap.values()) {
|
||||
space.writeDown(property);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,91 +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.emu.taint.trace;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import ghidra.pcode.emu.taint.plain.TaintSpace;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTracePropertyAccess;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.taint.model.TaintSet;
|
||||
|
||||
/**
|
||||
* The storage space for taint sets in a trace's address space
|
||||
*
|
||||
* <p>
|
||||
* This adds to {@link TaintSpace} the ability to load taint sets from a trace and the ability to
|
||||
* save them back into a trace.
|
||||
*/
|
||||
public class TaintTraceSpace extends TaintSpace {
|
||||
protected final AddressSpace space;
|
||||
protected final PcodeTracePropertyAccess<String> property;
|
||||
|
||||
/**
|
||||
* Create the space
|
||||
*
|
||||
* @param space the address space
|
||||
* @param property the trace property backing this space
|
||||
*/
|
||||
public TaintTraceSpace(AddressSpace space, PcodeTracePropertyAccess<String> property) {
|
||||
this.space = space;
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* The taint space will call this when the cache misses, allowing us to populate it with a taint
|
||||
* set stored in the trace. Note that if the emulator writes to this offset <em>before</em>
|
||||
* reading it, this will not get called for that offset. Here we simply get the string property
|
||||
* and parse the taint set.
|
||||
*/
|
||||
@Override
|
||||
protected TaintSet whenNull(long offset) {
|
||||
String string = property.get(space.getAddress(offset));
|
||||
if (string == null) {
|
||||
return TaintSet.EMPTY;
|
||||
}
|
||||
return TaintSet.parse(string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write this cache back down into a trace
|
||||
*
|
||||
* <p>
|
||||
* Here we simply iterate over every entry in this space, serialize the taint, and put it into
|
||||
* the property at the entry's offset. If the taint set is empty, we clear the property rather
|
||||
* than putting the empty taint set into the property.
|
||||
*
|
||||
* @param into the trace-property access to write into
|
||||
*/
|
||||
public void writeDown(PcodeTracePropertyAccess<String> into) {
|
||||
if (space.isUniqueSpace()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Entry<Long, TaintSet> entry : taints.entrySet()) {
|
||||
TaintSet taint = entry.getValue();
|
||||
Address address = space.getAddress(entry.getKey());
|
||||
if (taint.isEmpty()) {
|
||||
into.clear(new AddressRangeImpl(address, address));
|
||||
}
|
||||
else {
|
||||
into.put(address, taint.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,35 +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.
|
||||
*/
|
||||
/**
|
||||
* The trace-integrated Taint Emulator
|
||||
*
|
||||
* <p>
|
||||
* This package builds on the emulation framework to construct a trace-integrated emulator. The
|
||||
* framework's state components were designed to accommodate the state components introduced by this
|
||||
* package.
|
||||
*
|
||||
* <p>
|
||||
* For this package, I recommend a bottom-up approach, since you should already be familiar with the
|
||||
* parts factory and the structure of the stand-alone state part.
|
||||
* {@link ghidra.pcode.emu.taint.trace.TaintTraceSpace} adds the ability to read and write taint
|
||||
* sets from a trace. {@link ghidra.pcode.emu.taint.trace.TaintTracePcodeExecutorStatePiece} works
|
||||
* that into a state piece derived from
|
||||
* {@link ghidra.pcode.emu.taint.plain.TaintPcodeExecutorStatePiece}. Then,
|
||||
* {@link ghidra.pcode.emu.taint.trace.TaintTracePcodeExecutorState} composes that with a given
|
||||
* concrete state piece. The factory creates that state for use by the
|
||||
* {@link ghidra.pcode.emu.taint.trace.TaintTracePcodeEmulator}.
|
||||
*/
|
||||
package ghidra.pcode.emu.taint.trace;
|
|
@ -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,7 +22,7 @@ import ghidra.app.plugin.core.debug.gui.register.RegisterRow;
|
|||
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.pcode.emu.taint.trace.TaintTracePcodeExecutorStatePiece;
|
||||
import ghidra.pcode.emu.taint.state.TaintPieceHandler;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Register;
|
||||
|
@ -38,7 +38,7 @@ import ghidra.trace.model.property.TracePropertyMapSpace;
|
|||
* screen.
|
||||
*/
|
||||
public class TaintDebuggerRegisterColumnFactory implements DebuggerRegisterColumnFactory {
|
||||
protected static final String PROP_NAME = TaintTracePcodeExecutorStatePiece.NAME;
|
||||
protected static final String PROP_NAME = TaintPieceHandler.NAME;
|
||||
public static final String COL_NAME = "Taint";
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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.
|
||||
|
@ -27,7 +27,7 @@ import ghidra.app.util.viewer.format.FieldFormatModel;
|
|||
import ghidra.app.util.viewer.proxy.ProxyObj;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.pcode.emu.taint.trace.TaintTracePcodeExecutorStatePiece;
|
||||
import ghidra.pcode.emu.taint.state.TaintPieceHandler;
|
||||
import ghidra.program.model.listing.CodeUnit;
|
||||
import ghidra.program.model.util.StringPropertyMap;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
|
@ -42,7 +42,7 @@ import ghidra.taint.model.TaintVec;
|
|||
* framework. I used the "sample" module's {@code EntropyFieldFactory} for reference.
|
||||
*/
|
||||
public class TaintFieldFactory extends FieldFactory {
|
||||
public static final String PROPERTY_NAME = TaintTracePcodeExecutorStatePiece.NAME;
|
||||
public static final String PROPERTY_NAME = TaintPieceHandler.NAME;
|
||||
public static final GColor COLOR = new GColor("color.fg.listing.taint");
|
||||
public static final String FIELD_NAME = "Taint";
|
||||
|
||||
|
@ -50,7 +50,8 @@ public class TaintFieldFactory extends FieldFactory {
|
|||
super(FIELD_NAME);
|
||||
}
|
||||
|
||||
protected TaintFieldFactory(FieldFormatModel formatModel, ListingHighlightProvider highlightProvider,
|
||||
protected TaintFieldFactory(FieldFormatModel formatModel,
|
||||
ListingHighlightProvider highlightProvider,
|
||||
Options displayOptions, Options fieldOptions) {
|
||||
super(FIELD_NAME, formatModel, highlightProvider, displayOptions, fieldOptions);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
@ -32,6 +32,11 @@ import java.util.stream.Stream;
|
|||
* do not (yet) have a {@code parse(String)} method.
|
||||
*/
|
||||
public class TaintVec {
|
||||
|
||||
public static TaintVec of(TaintSet... taints) {
|
||||
return new TaintVec(taints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a vector of empty taint sets
|
||||
*
|
||||
|
@ -75,15 +80,19 @@ public class TaintVec {
|
|||
private List<TaintSet> setsView;
|
||||
public final int length;
|
||||
|
||||
private TaintVec(TaintSet[] sets) {
|
||||
this.sets = sets;
|
||||
this.setsView = Collections.unmodifiableList(Arrays.asList(sets));
|
||||
this.length = sets.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new uninitialized taint vector of the given length
|
||||
*
|
||||
* @param length the length
|
||||
*/
|
||||
public TaintVec(int length) {
|
||||
this.sets = new TaintSet[length];
|
||||
this.setsView = Collections.unmodifiableList(Arrays.asList(sets));
|
||||
this.length = sets.length;
|
||||
this(new TaintSet[length]);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -356,7 +365,25 @@ public class TaintVec {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Common shifting behaviors
|
||||
*/
|
||||
public enum ShiftMode {
|
||||
/**
|
||||
* No bound is applied to the shift. Values that fall off the edge are dropped. Furthermore,
|
||||
* if the shift is greater than the length, all the values will fall off the edge and be
|
||||
* dropped.
|
||||
*
|
||||
* <pre>
|
||||
* +---+------+
|
||||
* | 0 | 1234 |
|
||||
* | 1 | _123 |
|
||||
* | 2 | __12 |
|
||||
* | 3 | ___1 |
|
||||
* | 4 | ____ |
|
||||
* +---+------+
|
||||
* </pre>
|
||||
*/
|
||||
UNBOUNDED {
|
||||
@Override
|
||||
int adjustRight(int right, int length) {
|
||||
|
@ -368,6 +395,20 @@ public class TaintVec {
|
|||
return src;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Only the lowest required bits are taken for the shift amount, i.e., the remainder when
|
||||
* divided by the length, often a power of 2. Values that fall off the edge are dropped.
|
||||
*
|
||||
* <pre>
|
||||
* +---+------+
|
||||
* | 0 | 1234 |
|
||||
* | 1 | _123 |
|
||||
* | 2 | __12 |
|
||||
* | 3 | ___1 |
|
||||
* | 4 | 1234 | (Only the lowest 2 bits of the shift amount are considered)
|
||||
* +---+------+
|
||||
* </pre>
|
||||
*/
|
||||
REMAINDER {
|
||||
@Override
|
||||
int adjustRight(int right, int length) {
|
||||
|
@ -379,6 +420,21 @@ public class TaintVec {
|
|||
return src;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Only the lowest required bits are taken for the shift amount, i.e., the remainder when
|
||||
* divided by the length, often a power of 2. (Even if unbounded, a circular shift yields
|
||||
* the same result.) Values that fall off the edge are cycled to the opposite end.
|
||||
*
|
||||
* <pre>
|
||||
* +---+------+
|
||||
* | 0 | 1234 |
|
||||
* | 1 | 4123 |
|
||||
* | 2 | 3412 |
|
||||
* | 3 | 2341 |
|
||||
* | 4 | 1234 |
|
||||
* +---+------+
|
||||
* </pre>
|
||||
*/
|
||||
CIRCULAR {
|
||||
@Override
|
||||
int adjustRight(int right, int length) {
|
||||
|
@ -404,6 +460,7 @@ public class TaintVec {
|
|||
* Shift this vector some number of elements, in place
|
||||
*
|
||||
* @param right the number of elements to shift right, or negative for left
|
||||
* @param mode the behavior of the shift
|
||||
* @return this vector
|
||||
*/
|
||||
public TaintVec setShifted(int right, ShiftMode mode) {
|
||||
|
@ -451,7 +508,7 @@ public class TaintVec {
|
|||
TaintVec vec = new TaintVec(length);
|
||||
int shift = isBigEndian ? this.length - length : 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
vec.sets[i] = vec.sets[i + shift];
|
||||
vec.sets[i] = this.sets[i + shift];
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
@ -501,4 +558,19 @@ public class TaintVec {
|
|||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a subpiece of this vector
|
||||
*
|
||||
* @param offset the offset into this vector
|
||||
* @param length the number of sets to extract
|
||||
* @return the resulting vector
|
||||
*/
|
||||
public TaintVec sub(int offset, int length) {
|
||||
TaintVec vec = new TaintVec(length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
vec.sets[i] = this.sets[i + offset];
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
@ -31,31 +31,17 @@
|
|||
* domain, see the {@link ghidra.taint.model} package.
|
||||
*
|
||||
* <p>
|
||||
* Next, we implement the stand-alone emulator using
|
||||
* {@link ghidra.pcode.exec.debug.auxiliary.AuxDebuggerEmulatorPartsFactory}. Technically, that interface
|
||||
* requires more than necessary for a stand-alone emulator, but because a fully-integrated emulator
|
||||
* is the goal, you can start with it and leave its methods stubbed until you actually need them.
|
||||
* Next, we implement the emulator using {@link ghidra.pcode.emu.auxiliary.AuxEmulatorPartsFactory}.
|
||||
* The implementation of each method will move our attention to each part necessary to construct the
|
||||
* emulator. See the {@link ghidra.pcode.emu.taint.plain} package. The emulator itself
|
||||
* {@link ghidra.pcode.emu.taint.plain.TaintPcodeEmulator} is trivially derived from
|
||||
* emulator. See the {@link ghidra.pcode.emu.taint.state} package. The emulator itself
|
||||
* {@link ghidra.pcode.emu.taint.TaintPcodeEmulator} is trivially derived from
|
||||
* {@link ghidra.pcode.emu.auxiliary.AuxPcodeEmulator} and our factory.
|
||||
*
|
||||
* <p>
|
||||
* Next, we implement the trace-integrated emulator. For this, we just implement two more methods:
|
||||
* {@link ghidra.pcode.exec.debug.auxiliary.AuxDebuggerEmulatorPartsFactory#createTraceSharedState(ghidra.pcode.exec.trace.auxiliary.AuxTracePcodeEmulator, ghidra.pcode.exec.trace.BytesTracePcodeExecutorStatePiece)}
|
||||
* and
|
||||
* {@link ghidra.pcode.exec.debug.auxiliary.AuxDebuggerEmulatorPartsFactory#createTraceLocalState(ghidra.pcode.exec.trace.auxiliary.AuxTracePcodeEmulator, ghidra.pcode.emu.PcodeThread, ghidra.pcode.exec.trace.BytesTracePcodeExecutorStatePiece)}.
|
||||
* Then we derive {@link ghidra.pcode.emu.taint.trace.TaintTracePcodeEmulator} trivially from
|
||||
* {@link ghidra.pcode.exec.trace.auxiliary.AuxTracePcodeEmulator} and our factory. See the
|
||||
* {@link ghidra.pcode.emu.taint.trace} package.
|
||||
*
|
||||
* <p>
|
||||
* Next, in like fashion, we implement and derive the Debugger-integrated emulator. See the
|
||||
* {@link ghidra.pcode.emu.taint.full} package.
|
||||
*
|
||||
* <p>
|
||||
* Finally, we add some UI components to make the emulator's machine state visible to the user.
|
||||
* These are in the {@link ghidra.taint.gui.field} package.
|
||||
* Next, we provide trace integration by implementing
|
||||
* {@link ghidra.pcode.emu.taint.state.TaintPieceHandler}. Finally, we add some UI components to
|
||||
* make the emulator's machine state visible to the user. These are in the
|
||||
* {@link ghidra.taint.gui.field} package.
|
||||
*
|
||||
* <p>
|
||||
* There is a not-yet-integrated user-op library for tainting file reads. See
|
||||
|
|
|
@ -32,9 +32,11 @@ import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingService
|
|||
import ghidra.app.services.DebuggerEmulationService;
|
||||
import ghidra.app.services.DebuggerEmulationService.EmulationResult;
|
||||
import ghidra.app.services.DebuggerStaticMappingService;
|
||||
import ghidra.debug.api.emulation.DebuggerPcodeMachine;
|
||||
import ghidra.pcode.emu.PcodeMachine;
|
||||
import ghidra.pcode.emu.taint.TaintEmulatorFactory;
|
||||
import ghidra.pcode.emu.taint.TaintPcodeEmulator;
|
||||
import ghidra.pcode.emu.taint.state.TaintPieceHandler;
|
||||
import ghidra.pcode.emu.taint.trace.TaintTracePcodeEmulatorTest;
|
||||
import ghidra.pcode.emu.taint.trace.TaintTracePcodeExecutorStatePiece;
|
||||
import ghidra.program.model.util.StringPropertyMap;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.trace.database.ToyDBTraceBuilder.ToySchemaBuilder;
|
||||
|
@ -61,7 +63,7 @@ public class TaintDebuggerPcodeEmulatorTest extends AbstractGhidraHeadedDebugger
|
|||
assertEquals(1,
|
||||
emuService.getEmulatorFactories()
|
||||
.stream()
|
||||
.filter(f -> f instanceof TaintDebuggerPcodeEmulatorFactory)
|
||||
.filter(f -> f instanceof TaintEmulatorFactory)
|
||||
.count());
|
||||
}
|
||||
|
||||
|
@ -74,7 +76,7 @@ public class TaintDebuggerPcodeEmulatorTest extends AbstractGhidraHeadedDebugger
|
|||
|
||||
@Test
|
||||
public void testFactoryCreate() throws Exception {
|
||||
emuService.setEmulatorFactory(new TaintDebuggerPcodeEmulatorFactory());
|
||||
emuService.setEmulatorFactory(new TaintEmulatorFactory());
|
||||
|
||||
createAndOpenTrace();
|
||||
|
||||
|
@ -99,13 +101,13 @@ public class TaintDebuggerPcodeEmulatorTest extends AbstractGhidraHeadedDebugger
|
|||
}
|
||||
});
|
||||
|
||||
DebuggerPcodeMachine<?> emu = emuService.getCachedEmulator(tb.trace, result.schedule());
|
||||
assertTrue(emu instanceof TaintDebuggerPcodeEmulator);
|
||||
PcodeMachine<?> emu = emuService.getCachedEmulator(tb.trace, result.schedule());
|
||||
assertTrue(emu instanceof TaintPcodeEmulator);
|
||||
}
|
||||
|
||||
@Test
|
||||
// @Test // I've decided to remove this feature.
|
||||
public void testReadsProgramUsrProperties() throws Exception {
|
||||
emuService.setEmulatorFactory(new TaintDebuggerPcodeEmulatorFactory());
|
||||
emuService.setEmulatorFactory(new TaintEmulatorFactory());
|
||||
|
||||
createAndOpenTrace("x86:LE:64:default");
|
||||
createProgramFromTrace();
|
||||
|
@ -135,7 +137,7 @@ public class TaintDebuggerPcodeEmulatorTest extends AbstractGhidraHeadedDebugger
|
|||
.createInitializedBlock(".text", tb.addr(0x00400000), 0x1000, (byte) 0,
|
||||
TaskMonitor.DUMMY, false);
|
||||
StringPropertyMap progTaintMap = program.getUsrPropertyManager()
|
||||
.createStringPropertyMap(TaintTracePcodeExecutorStatePiece.NAME);
|
||||
.createStringPropertyMap(TaintPieceHandler.NAME);
|
||||
progTaintMap.add(tb.addr(0x00400800), "test_0");
|
||||
Assembler asm = Assemblers.getAssembler(program);
|
||||
|
||||
|
@ -147,7 +149,7 @@ public class TaintDebuggerPcodeEmulatorTest extends AbstractGhidraHeadedDebugger
|
|||
long scratch = emuService.emulate(tb.trace, time, TaskMonitor.DUMMY);
|
||||
|
||||
TracePropertyMap<String> traceTaintMap = tb.trace.getAddressPropertyManager()
|
||||
.getPropertyMap(TaintTracePcodeExecutorStatePiece.NAME, String.class);
|
||||
.getPropertyMap(TaintPieceHandler.NAME, String.class);
|
||||
TracePropertyMapSpace<String> taintRegSpace =
|
||||
traceTaintMap.getPropertyMapRegisterSpace(thread, 0, false);
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import ghidra.pcode.emu.linux.AbstractEmuLinuxSyscallUseropLibrary;
|
|||
import ghidra.pcode.emu.linux.EmuLinuxAmd64SyscallUseropLibraryTest;
|
||||
import ghidra.pcode.emu.linux.EmuLinuxAmd64SyscallUseropLibraryTest.Syscall;
|
||||
import ghidra.pcode.emu.sys.EmuProcessExitedException;
|
||||
import ghidra.pcode.emu.taint.TaintPcodeEmulator;
|
||||
import ghidra.pcode.emu.taint.lib.TaintEmuUnixFileSystem;
|
||||
import ghidra.pcode.emu.taint.lib.TaintFileReadsLinuxAmd64SyscallLibrary;
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||
|
|
|
@ -29,8 +29,11 @@ import org.junit.Test;
|
|||
import db.Transaction;
|
||||
import ghidra.app.plugin.assembler.*;
|
||||
import ghidra.pcode.emu.PcodeThread;
|
||||
import ghidra.pcode.emu.taint.TaintEmulatorFactory;
|
||||
import ghidra.pcode.emu.taint.TaintPcodeEmulator;
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||
import ghidra.pcode.exec.trace.AbstractTracePcodeEmulatorTest;
|
||||
import ghidra.pcode.exec.trace.TraceEmulationIntegration.Writer;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
|
@ -39,6 +42,7 @@ import ghidra.trace.database.ToyDBTraceBuilder;
|
|||
import ghidra.trace.database.target.DBTraceObjectManager;
|
||||
import ghidra.trace.model.*;
|
||||
import ghidra.trace.model.guest.TraceGuestPlatform;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.memory.TraceMemoryManager;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
import ghidra.trace.model.property.TracePropertyMap;
|
||||
|
@ -49,6 +53,17 @@ import ghidra.trace.model.thread.TraceThread;
|
|||
|
||||
public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest {
|
||||
|
||||
@Override
|
||||
protected Writer createWriter(TracePlatform platform, long snap) {
|
||||
Writer writer = super.createWriter(platform, snap);
|
||||
TaintEmulatorFactory.addHandlers(writer);
|
||||
return writer;
|
||||
}
|
||||
|
||||
TaintPcodeEmulator createEmulator(TracePlatform platform, Writer writer) {
|
||||
return new TaintPcodeEmulator(platform.getLanguage(), writer.callbacks());
|
||||
}
|
||||
|
||||
public static Map.Entry<TraceAddressSnapRange, String> makeTaintEntry(Trace trace,
|
||||
Lifespan span, AddressSpace space, long offset, String taint) {
|
||||
Address addr = space.getAddress(offset);
|
||||
|
@ -68,6 +83,8 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
*
|
||||
* <p>
|
||||
* We isolate exactly a read by executing sleigh.
|
||||
*
|
||||
* @throws Throwable because
|
||||
*/
|
||||
@Test
|
||||
public void testReadStateMemory() throws Throwable {
|
||||
|
@ -80,7 +97,8 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
taintMap.set(Lifespan.nowOn(0), tb.range(0x00400000, 0x00400003), "test_0");
|
||||
}
|
||||
|
||||
TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
TaintPcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
PcodeThread<Pair<byte[], TaintVec>> emuThread = emu.newThread(thread.getPath());
|
||||
emuThread.getExecutor().executeSleigh("RAX = *0x00400000:8;");
|
||||
|
||||
|
@ -110,7 +128,8 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
mapSpace.set(Lifespan.nowOn(0), regEBX, "test_0");
|
||||
}
|
||||
|
||||
TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
TaintPcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
PcodeThread<Pair<byte[], TaintVec>> emuThread = emu.newThread(thread.getPath());
|
||||
emuThread.getExecutor().executeSleigh("RAX = RBX;");
|
||||
|
||||
|
@ -130,7 +149,8 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
|
||||
initTrace(tb, "", List.of());
|
||||
|
||||
TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
TaintPcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
TaintVec taintVal = TaintVec.empties(8);
|
||||
TaintSet testTaint = TaintSet.of(new TaintMark("test_0", Set.of()));
|
||||
for (int i = 0; i < 4; i++) {
|
||||
|
@ -141,7 +161,7 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
Pair.of(tb.arr(0, 0, 0, 0, 0, 0, 0, 0), taintVal));
|
||||
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(tb.host, 1, 0);
|
||||
writer.writeDown(1);
|
||||
}
|
||||
TracePropertyMap<String> taintMap =
|
||||
tb.trace.getAddressPropertyManager().getPropertyMap("Taint", String.class);
|
||||
|
@ -156,9 +176,10 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
|
||||
TraceThread thread = initTrace(tb, "", List.of());
|
||||
|
||||
TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
TaintPcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
PcodeThread<Pair<byte[], TaintVec>> emuThread = emu.newThread(thread.getPath());
|
||||
TaintVec taintVal = TaintVec.empties(8);
|
||||
TaintVec taintVal = TaintVec.empties(4);
|
||||
TaintSet testTaint = TaintSet.of(new TaintMark("test_0", Set.of()));
|
||||
for (int i = 0; i < 4; i++) {
|
||||
taintVal.set(i, testTaint);
|
||||
|
@ -166,7 +187,7 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
emuThread.getState().setVar(tb.reg("EAX"), Pair.of(tb.arr(0, 0, 0, 0), taintVal));
|
||||
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(tb.host, 1, 0);
|
||||
writer.writeDown(1);
|
||||
}
|
||||
TracePropertyMap<String> taintMap =
|
||||
tb.trace.getAddressPropertyManager().getPropertyMap("Taint", String.class);
|
||||
|
@ -192,7 +213,8 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
"MOV qword ptr [0x00600000], RAX",
|
||||
"MOV qword ptr [0x00600000], RBX"));
|
||||
|
||||
TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
TaintPcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
PcodeThread<Pair<byte[], TaintVec>> emuThread = emu.newThread(thread.getPath());
|
||||
emuThread.getState()
|
||||
.setVar(tb.reg("RAX"), Pair.of(
|
||||
|
@ -201,11 +223,11 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
|
||||
emuThread.stepInstruction();
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(tb.host, 1, 0);
|
||||
writer.writeDown(1);
|
||||
}
|
||||
emuThread.stepInstruction();
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(tb.host, 2, 0);
|
||||
writer.writeDown(2);
|
||||
}
|
||||
|
||||
TracePropertyMap<String> taintMap =
|
||||
|
@ -229,19 +251,20 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
List.of(
|
||||
"XOR RAX, RAX"));
|
||||
|
||||
TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
TaintPcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
PcodeThread<Pair<byte[], TaintVec>> emuThread = emu.newThread(thread.getPath());
|
||||
emuThread.getState()
|
||||
.setVar(tb.reg("RAX"), Pair.of(
|
||||
tb.arr(1, 2, 3, 4, 5, 6, 7, 8),
|
||||
TaintVec.copies(TaintSet.parse("test_0"), 8)));
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(tb.host, 0, 0);
|
||||
writer.writeDown(0);
|
||||
}
|
||||
|
||||
emuThread.stepInstruction();
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(tb.host, 1, 0);
|
||||
writer.writeDown(1);
|
||||
}
|
||||
|
||||
TracePropertyMap<String> taintMap =
|
||||
|
@ -263,19 +286,20 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
List.of(
|
||||
"XOR EAX, EAX"));
|
||||
|
||||
TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.host, 0);
|
||||
Writer writer = createWriter(tb.host, 0);
|
||||
TaintPcodeEmulator emu = createEmulator(tb.host, writer);
|
||||
PcodeThread<Pair<byte[], TaintVec>> emuThread = emu.newThread(thread.getPath());
|
||||
emuThread.getState()
|
||||
.setVar(tb.reg("RAX"), Pair.of(
|
||||
tb.arr(1, 2, 3, 4, 5, 6, 7, 8),
|
||||
TaintVec.copies(TaintSet.parse("test_0"), 8)));
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(tb.host, 0, 0);
|
||||
writer.writeDown(0);
|
||||
}
|
||||
|
||||
emuThread.stepInstruction();
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(tb.host, 1, 0);
|
||||
writer.writeDown(1);
|
||||
}
|
||||
|
||||
TracePropertyMap<String> taintMap =
|
||||
|
@ -319,7 +343,8 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
mm.putBytes(0, tb.addr(0x00000000), ByteBuffer.wrap(buf.getBytes()));
|
||||
}
|
||||
|
||||
TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(x64, 0);
|
||||
Writer writer = createWriter(x64, 0);
|
||||
TaintPcodeEmulator emu = createEmulator(x64, writer);
|
||||
PcodeThread<Pair<byte[], TaintVec>> emuThread = emu.newThread(thread.getPath());
|
||||
emuThread.getState()
|
||||
.setVar(tb.reg(x64, "RAX"), Pair.of(
|
||||
|
@ -328,11 +353,11 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
|
|||
|
||||
emuThread.stepInstruction();
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(x64, 1, 0);
|
||||
writer.writeDown(1);
|
||||
}
|
||||
emuThread.stepInstruction();
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
emu.writeDown(x64, 2, 0);
|
||||
writer.writeDown(2);
|
||||
}
|
||||
|
||||
TracePropertyMap<String> taintMap =
|
||||
|
|
|
@ -16,13 +16,12 @@
|
|||
package ghidra.pcode.emu.symz3;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.microsoft.z3.BitVecNum;
|
||||
import com.microsoft.z3.Context;
|
||||
|
||||
import ghidra.pcode.exec.PcodeArithmetic;
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.symz3.model.SymValueZ3;
|
||||
|
@ -39,82 +38,11 @@ import ghidra.util.Msg;
|
|||
*/
|
||||
public abstract class AbstractSymZ3OffsetPcodeExecutorStatePiece<S>
|
||||
implements PcodeExecutorStatePiece<SymValueZ3, SymValueZ3> {
|
||||
/**
|
||||
* A map of address spaces to objects which store or cache state for that space
|
||||
*
|
||||
* @param <S> the type of object for each address space
|
||||
*/
|
||||
public abstract static class AbstractSpaceMap<S> {
|
||||
protected final Map<AddressSpace, S> spaces = new HashMap<>();
|
||||
|
||||
public abstract S getForSpace(AddressSpace space, boolean toWrite);
|
||||
|
||||
public Collection<S> values() {
|
||||
return spaces.values();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this when each S contains the complete state for the address space
|
||||
*
|
||||
* @param <S> the type of object for each address space
|
||||
*/
|
||||
public abstract static class SimpleSpaceMap<S> extends AbstractSpaceMap<S> {
|
||||
/**
|
||||
* Construct a new space internally associated with the given address space
|
||||
*
|
||||
* <p>
|
||||
* As the name implies, this often simply wraps {@code S}'s constructor
|
||||
*
|
||||
* @param space the address space
|
||||
* @return the new space
|
||||
*/
|
||||
protected abstract S newSpace(AddressSpace space);
|
||||
|
||||
@Override
|
||||
public S getForSpace(AddressSpace space, boolean toWrite) {
|
||||
return spaces.computeIfAbsent(space, s -> newSpace(s));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this when each S is possibly a cache to some other state (backing) object
|
||||
*
|
||||
* @param <B> the type of the object backing the cache for each address space
|
||||
* @param <S> the type of cache for each address space
|
||||
*/
|
||||
public abstract static class CacheingSpaceMap<B, S> extends AbstractSpaceMap<S> {
|
||||
/**
|
||||
* Get the object backing the cache for the given address space
|
||||
*
|
||||
* @param space the space
|
||||
* @return the backing object
|
||||
*/
|
||||
protected abstract B getBacking(AddressSpace space);
|
||||
|
||||
/**
|
||||
* Construct a new space internally associated with the given address space, having the
|
||||
* given backing
|
||||
*
|
||||
* <p>
|
||||
* As the name implies, this often simply wraps {@code S}'s constructor
|
||||
*
|
||||
* @param space the address space
|
||||
* @param backing the backing, if applicable. null for the unique space
|
||||
* @return the new space
|
||||
*/
|
||||
protected abstract S newSpace(AddressSpace space, B backing);
|
||||
|
||||
@Override
|
||||
public S getForSpace(AddressSpace space, boolean toWrite) {
|
||||
return spaces.computeIfAbsent(space,
|
||||
s -> newSpace(s, s.isUniqueSpace() ? null : getBacking(s)));
|
||||
}
|
||||
}
|
||||
|
||||
protected final Language language;
|
||||
protected final PcodeArithmetic<SymValueZ3> addressArithmetic;
|
||||
protected final PcodeArithmetic<SymValueZ3> arithmetic;
|
||||
protected final PcodeStateCallbacks cb;
|
||||
protected final AddressSpace uniqueSpace;
|
||||
|
||||
/**
|
||||
|
@ -123,12 +51,15 @@ public abstract class AbstractSymZ3OffsetPcodeExecutorStatePiece<S>
|
|||
* @param language the language (used for its memory model)
|
||||
* @param addressArithmetic the arithmetic used for addresses
|
||||
* @param arithmetic an arithmetic used to generate default values of {@code T}
|
||||
* @param cb callbacks to receive emulation events
|
||||
*/
|
||||
public AbstractSymZ3OffsetPcodeExecutorStatePiece(Language language,
|
||||
PcodeArithmetic<SymValueZ3> addressArithmetic, PcodeArithmetic<SymValueZ3> arithmetic) {
|
||||
PcodeArithmetic<SymValueZ3> addressArithmetic, PcodeArithmetic<SymValueZ3> arithmetic,
|
||||
PcodeStateCallbacks cb) {
|
||||
this.language = language;
|
||||
this.addressArithmetic = addressArithmetic;
|
||||
this.arithmetic = arithmetic;
|
||||
this.cb = cb;
|
||||
uniqueSpace = language.getAddressFactory().getUniqueSpace();
|
||||
}
|
||||
|
||||
|
@ -147,6 +78,11 @@ public abstract class AbstractSymZ3OffsetPcodeExecutorStatePiece<S>
|
|||
return arithmetic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<PcodeExecutorStatePiece<?, ?>> streamPieces() {
|
||||
return Stream.of(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a value in the unique space
|
||||
*
|
||||
|
@ -158,9 +94,9 @@ public abstract class AbstractSymZ3OffsetPcodeExecutorStatePiece<S>
|
|||
* @param size the number of bytes to write (the size of the value)
|
||||
* @param val the value to store
|
||||
*/
|
||||
protected void setUnique(SymValueZ3 offset, int size, SymValueZ3 val) {
|
||||
protected void setUnique(SymValueZ3 offset, int size, SymValueZ3 val, PcodeStateCallbacks cb) {
|
||||
S s = getForSpace(uniqueSpace, true);
|
||||
setInSpace(s, offset, size, val);
|
||||
setInSpace(s, offset, size, val, cb);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -173,9 +109,9 @@ public abstract class AbstractSymZ3OffsetPcodeExecutorStatePiece<S>
|
|||
* @param size the number of bytes to read (the size of the value)
|
||||
* @return the read value
|
||||
*/
|
||||
protected SymValueZ3 getUnique(SymValueZ3 offset, int size) {
|
||||
protected SymValueZ3 getUnique(SymValueZ3 offset, int size, PcodeStateCallbacks cb) {
|
||||
S s = getForSpace(uniqueSpace, false);
|
||||
return getFromSpace(s, offset, size);
|
||||
return getFromSpace(s, offset, size, cb);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -196,8 +132,10 @@ public abstract class AbstractSymZ3OffsetPcodeExecutorStatePiece<S>
|
|||
* @param offset the offset within the space
|
||||
* @param size the number of bytes to write (the size of the value)
|
||||
* @param val the value to store
|
||||
* @param cb callbacks to receive emulation events
|
||||
*/
|
||||
protected abstract void setInSpace(S space, SymValueZ3 offset, int size, SymValueZ3 val);
|
||||
protected abstract void setInSpace(S space, SymValueZ3 offset, int size, SymValueZ3 val,
|
||||
PcodeStateCallbacks cb);
|
||||
|
||||
/**
|
||||
* Get a value from the given space
|
||||
|
@ -205,9 +143,11 @@ public abstract class AbstractSymZ3OffsetPcodeExecutorStatePiece<S>
|
|||
* @param space the address space
|
||||
* @param offset the offset within the space
|
||||
* @param size the number of bytes to read (the size of the value)
|
||||
* @param cb callbacks to receive emulation events
|
||||
* @return the read value
|
||||
*/
|
||||
protected abstract SymValueZ3 getFromSpace(S space, SymValueZ3 offset, int size);
|
||||
protected abstract SymValueZ3 getFromSpace(S space, SymValueZ3 offset, int size,
|
||||
PcodeStateCallbacks cb);
|
||||
|
||||
/**
|
||||
* In case spaces are generated lazily, and we're reading from a space that doesn't yet exist,
|
||||
|
@ -219,16 +159,14 @@ public abstract class AbstractSymZ3OffsetPcodeExecutorStatePiece<S>
|
|||
* @param size the number of bytes to read (the size of the value)
|
||||
* @return the default value
|
||||
*/
|
||||
protected SymValueZ3 getFromNullSpace(int size) {
|
||||
protected SymValueZ3 getFromNullSpace(int size, PcodeStateCallbacks cb) {
|
||||
Msg.warn(this,
|
||||
"getFromNullSpace is returning 0 but that might not be what we want for symz3");
|
||||
return arithmetic.fromConst(0, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVar(AddressSpace space, SymValueZ3 offset, int size, boolean quantize,
|
||||
SymValueZ3 val) {
|
||||
|
||||
protected void setVarInternal(AddressSpace space, SymValueZ3 offset, int size, boolean quantize,
|
||||
SymValueZ3 val, PcodeStateCallbacks cb) {
|
||||
//Msg.info(this, "setVar for space: " + space + " offset: " + offset + " size: " + size + " val: " + val);
|
||||
assert val != null;
|
||||
assert offset != null;
|
||||
|
@ -246,7 +184,7 @@ public abstract class AbstractSymZ3OffsetPcodeExecutorStatePiece<S>
|
|||
throw new IllegalArgumentException("Cannot write to constant space");
|
||||
}
|
||||
if (space.isUniqueSpace()) {
|
||||
setUnique(offset, size, val);
|
||||
setUnique(offset, size, val, cb);
|
||||
return;
|
||||
}
|
||||
S s = getForSpace(space, true);
|
||||
|
@ -256,12 +194,22 @@ public abstract class AbstractSymZ3OffsetPcodeExecutorStatePiece<S>
|
|||
* convert to long, and quantize. You could also express the quantization symbolically, but
|
||||
* it rarely comes up.
|
||||
*/
|
||||
setInSpace(s, offset, size, val);
|
||||
setInSpace(s, offset, size, val, cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SymValueZ3 getVar(AddressSpace space, SymValueZ3 offset, int size, boolean quantize,
|
||||
Reason reason) {
|
||||
public void setVar(AddressSpace space, SymValueZ3 offset, int size, boolean quantize,
|
||||
SymValueZ3 val) {
|
||||
setVarInternal(space, offset, size, quantize, val, cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVarInternal(AddressSpace space, SymValueZ3 offset, int size, SymValueZ3 val) {
|
||||
setVarInternal(space, offset, size, false, val, PcodeStateCallbacks.NONE);
|
||||
}
|
||||
|
||||
protected SymValueZ3 getVarInternal(AddressSpace space, SymValueZ3 offset, int size,
|
||||
boolean quantize, Reason reason, PcodeStateCallbacks cb) {
|
||||
//checkRange(space, offset, size);
|
||||
//Msg.info(this, "getVar for space: " + space + " offset: " + offset + " size: " + size + " quantize: " + quantize);
|
||||
if (space.isConstantSpace()) {
|
||||
|
@ -281,14 +229,26 @@ public abstract class AbstractSymZ3OffsetPcodeExecutorStatePiece<S>
|
|||
}
|
||||
}
|
||||
if (space.isUniqueSpace()) {
|
||||
return getUnique(offset, size);
|
||||
return getUnique(offset, size, cb);
|
||||
}
|
||||
S s = getForSpace(space, false);
|
||||
//Msg.info(this, "Now we likely have a space to get from: " + s);
|
||||
if (s == null) {
|
||||
return getFromNullSpace(size);
|
||||
return getFromNullSpace(size, cb);
|
||||
}
|
||||
//offset = quantizeOffset(space, offset);
|
||||
return getFromSpace(s, offset, size);
|
||||
return getFromSpace(s, offset, size, cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SymValueZ3 getVar(AddressSpace space, SymValueZ3 offset, int size, boolean quantize,
|
||||
Reason reason) {
|
||||
return getVarInternal(space, offset, size, quantize, reason, cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SymValueZ3 getVarInternal(AddressSpace space, SymValueZ3 offset, int size,
|
||||
Reason reason) {
|
||||
return getVarInternal(space, offset, size, false, reason, PcodeStateCallbacks.NONE);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,11 +13,16 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.symz3.full;
|
||||
package ghidra.pcode.emu.symz3;
|
||||
|
||||
import ghidra.app.plugin.core.debug.service.emulation.AbstractDebuggerPcodeEmulatorFactory;
|
||||
import ghidra.debug.api.emulation.DebuggerPcodeMachine;
|
||||
import ghidra.debug.api.emulation.EmulatorFactory;
|
||||
import ghidra.debug.api.emulation.PcodeDebuggerAccess;
|
||||
import ghidra.pcode.emu.PcodeMachine;
|
||||
import ghidra.pcode.emu.symz3.state.SymZ3PcodeEmulator;
|
||||
import ghidra.pcode.emu.symz3.state.SymZ3PieceHandler;
|
||||
import ghidra.pcode.exec.trace.TraceEmulationIntegration;
|
||||
import ghidra.pcode.exec.trace.TraceEmulationIntegration.Writer;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTraceAccess;
|
||||
|
||||
/**
|
||||
* An emulator factory for making the {@link SymZ3DebuggerPcodeEmulator} discoverable to the UI
|
||||
|
@ -26,7 +31,17 @@ import ghidra.debug.api.emulation.PcodeDebuggerAccess;
|
|||
* This is the final class to create a full Debugger-integrated emulator. This class is what makes
|
||||
* it appear in the menu of possible emulators the user may configure.
|
||||
*/
|
||||
public class SymZ3DebuggerPcodeEmulatorFactory extends AbstractDebuggerPcodeEmulatorFactory {
|
||||
public class SymZ3EmulatorFactory implements EmulatorFactory {
|
||||
|
||||
public static Writer delayedWriteTrace(PcodeTraceAccess access) {
|
||||
Writer writer = TraceEmulationIntegration.bytesDelayedWrite(access);
|
||||
addHandlers(writer);
|
||||
return writer;
|
||||
}
|
||||
|
||||
public static void addHandlers(Writer writer) {
|
||||
writer.putHandler(new SymZ3PieceHandler());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
|
@ -34,7 +49,8 @@ public class SymZ3DebuggerPcodeEmulatorFactory extends AbstractDebuggerPcodeEmul
|
|||
}
|
||||
|
||||
@Override
|
||||
public DebuggerPcodeMachine<?> create(PcodeDebuggerAccess access) {
|
||||
return new SymZ3DebuggerPcodeEmulator(access);
|
||||
public PcodeMachine<?> create(PcodeDebuggerAccess access, Writer writer) {
|
||||
addHandlers(writer);
|
||||
return new SymZ3PcodeEmulator(access.getLanguage(), writer.callbacks());
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ import com.microsoft.z3.*;
|
|||
import ghidra.pcode.emu.symz3.lib.Z3InfixPrinter;
|
||||
import ghidra.pcode.emu.symz3.lib.Z3MemoryWitness;
|
||||
import ghidra.pcode.emu.symz3.lib.Z3MemoryWitness.WitnessType;
|
||||
import ghidra.pcode.exec.PcodeStateCallbacks;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.symz3.model.SymValueZ3;
|
||||
import ghidra.util.Msg;
|
||||
|
@ -67,6 +68,8 @@ import ghidra.util.Msg;
|
|||
public class SymZ3MemoryMap {
|
||||
// TODO ... encapsulate traversal of memvals so it can become private
|
||||
public Map<String, SymValueZ3> memvals;
|
||||
private NavigableMap<Long, SymValueZ3> byOffset;
|
||||
|
||||
private List<Z3MemoryWitness> witnesses;
|
||||
|
||||
private Language language;
|
||||
|
@ -80,9 +83,9 @@ public class SymZ3MemoryMap {
|
|||
}
|
||||
|
||||
public SymZ3MemoryMap(Language language) {
|
||||
memvals = new HashMap<String, SymValueZ3>();
|
||||
memvals = new HashMap<>();
|
||||
this.language = language;
|
||||
witnesses = new ArrayList<Z3MemoryWitness>();
|
||||
witnesses = new ArrayList<>();
|
||||
}
|
||||
|
||||
protected Entry<String, String> valuationForMemval(Context ctx, Z3InfixPrinter z3p,
|
||||
|
@ -110,7 +113,7 @@ public class SymZ3MemoryMap {
|
|||
if (!reported.add(addressExpr)) {
|
||||
return null;
|
||||
}
|
||||
SymValueZ3 vv = load(w.address(), w.bytesMoved(), false);
|
||||
SymValueZ3 vv = load(w.address(), w.bytesMoved(), false, PcodeStateCallbacks.NONE);
|
||||
BitVecExpr v = vv.getBitVecExpr(ctx);
|
||||
if (v == null) {
|
||||
return Map.entry("MEM " + z3p.infixWithBrackets(addressExpr), "null (?)");
|
||||
|
@ -159,7 +162,8 @@ public class SymZ3MemoryMap {
|
|||
continue;
|
||||
}
|
||||
reported.add(addressExpr);
|
||||
SymValueZ3 value = load(w.address(), w.bytesMoved(), false);
|
||||
SymValueZ3 value =
|
||||
load(w.address(), w.bytesMoved(), false, PcodeStateCallbacks.NONE);
|
||||
BitVecExpr vexpr = value.getBitVecExpr(ctx);
|
||||
if (vexpr == null) {
|
||||
result.append("MEM " + z3p.infixWithBrackets(addressExpr) + " is null (?)");
|
||||
|
@ -192,7 +196,8 @@ public class SymZ3MemoryMap {
|
|||
return Stream.concat(forMemVals, forWitnesses);
|
||||
}
|
||||
|
||||
public SymValueZ3 load(SymValueZ3 offset, int size, boolean addWitness) {
|
||||
public SymValueZ3 load(SymValueZ3 offset, int size, boolean addWitness,
|
||||
PcodeStateCallbacks cb) {
|
||||
try (Context ctx = new Context()) {
|
||||
if (addWitness) {
|
||||
witnesses.add(new Z3MemoryWitness(offset, size, WitnessType.LOAD));
|
||||
|
@ -273,6 +278,7 @@ public class SymZ3MemoryMap {
|
|||
// this is the primary advantage of the non-byte based model, storage is super easy
|
||||
Msg.debug(this, "set memory location " + address + " size " + size + " to " + val);
|
||||
memvals.put(offset.bitVecExprString, val);
|
||||
byOffset = null;
|
||||
}
|
||||
else {
|
||||
// for the byte-based model, we simply must store each byte separately.
|
||||
|
@ -299,6 +305,7 @@ public class SymZ3MemoryMap {
|
|||
}
|
||||
BitVecExpr valportion = ctx.mkExtract(high, low, bval);
|
||||
memvals.put(byteAddressAsString, new SymValueZ3(ctx, valportion));
|
||||
byOffset = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -314,4 +321,19 @@ public class SymZ3MemoryMap {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Entry<Long, SymValueZ3> getNextEntry(long offset) {
|
||||
if (byOffset == null) {
|
||||
byOffset = new TreeMap<>(Long::compareUnsigned);
|
||||
try (Context ctx = new Context()) {
|
||||
for (Entry<String, SymValueZ3> ent : memvals.entrySet()) {
|
||||
BitVecExpr bvOff = SymValueZ3.deserializeBitVecExpr(ctx, ent.getKey());
|
||||
if (bvOff.isNumeral()) {
|
||||
byOffset.put(((BitVecNum) bvOff).getLong(), ent.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return byOffset.ceilingEntry(offset);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ package ghidra.pcode.emu.symz3;
|
|||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import ghidra.pcode.emu.symz3.plain.SymZ3Space;
|
||||
import ghidra.pcode.exec.PcodeExecutorState;
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece;
|
||||
import ghidra.symz3.model.SymValueZ3;
|
||||
|
@ -27,5 +26,5 @@ public interface SymZ3PairedPcodeExecutorState
|
|||
|
||||
PcodeExecutorStatePiece<byte[], byte[]> getLeft();
|
||||
|
||||
AbstractSymZ3PcodeExecutorStatePiece<? extends SymZ3Space> getRight();
|
||||
SymZ3PcodeExecutorStatePiece getRight();
|
||||
}
|
||||
|
|
|
@ -17,19 +17,12 @@ package ghidra.pcode.emu.symz3;
|
|||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import ghidra.app.plugin.core.debug.service.emulation.RWTargetMemoryPcodeExecutorStatePiece;
|
||||
import ghidra.app.plugin.core.debug.service.emulation.RWTargetRegistersPcodeExecutorStatePiece;
|
||||
import ghidra.pcode.emu.*;
|
||||
import ghidra.pcode.emu.DefaultPcodeThread.PcodeThreadExecutor;
|
||||
import ghidra.pcode.emu.auxiliary.AuxEmulatorPartsFactory;
|
||||
import ghidra.pcode.emu.auxiliary.AuxPcodeEmulator;
|
||||
import ghidra.pcode.emu.symz3.plain.SymZ3PcodeExecutorState;
|
||||
import ghidra.pcode.emu.symz3.trace.SymZ3TracePcodeExecutorState;
|
||||
import ghidra.pcode.emu.symz3.state.SymZ3PcodeExecutorState;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.pcode.exec.debug.auxiliary.AuxDebuggerEmulatorPartsFactory;
|
||||
import ghidra.pcode.exec.debug.auxiliary.AuxDebuggerPcodeEmulator;
|
||||
import ghidra.pcode.exec.trace.BytesTracePcodeExecutorStatePiece;
|
||||
import ghidra.pcode.exec.trace.TracePcodeExecutorState;
|
||||
import ghidra.pcode.exec.trace.auxiliary.AuxTracePcodeEmulator;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.symz3.model.SymValueZ3;
|
||||
|
||||
|
@ -48,21 +41,16 @@ import ghidra.symz3.model.SymValueZ3;
|
|||
* <li>P-code Arithmetic: {@link SymZ3PcodeArithmetic}</li>
|
||||
* <li>Userop Library: {@link SymZ3PcodeUseropLibrary}</li>
|
||||
* <li>P-code Executor: {@link SymZ3PcodeThreadExecutor}</li>
|
||||
* <li>Machine State</li>
|
||||
* <ul>
|
||||
* <li>Stand alone: {@link SymZ3PcodeExecutorState}</li>
|
||||
* <li>Trace integrated: {@link SymZ3TracePcodeExecutorState}</li>
|
||||
* <li>Debugger integrated: Not applicable. Uses trace integration only.</li>
|
||||
* </ul>
|
||||
* <li>Machine State: {@link SymZ3PcodeExecutorState}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* If you're following from the {@link ghidra.symz3} package documentation, you'll want to return to
|
||||
* {@link ghidra.pcode.emu.symz3.plain} before you examine the trace-integrated state. Similarly,
|
||||
* {@link ghidra.pcode.emu.symz3.state} before you examine the trace-integrated state. Similarly,
|
||||
* you'll want to return to {@link ghidra.pcode.emu.symz3.trace} before you examine the
|
||||
* Debugger-integrated state.
|
||||
*/
|
||||
public enum SymZ3PartsFactory implements AuxDebuggerEmulatorPartsFactory<SymValueZ3> {
|
||||
public enum SymZ3PartsFactory implements AuxEmulatorPartsFactory<SymValueZ3> {
|
||||
/** This singleton factory instance */
|
||||
INSTANCE;
|
||||
|
||||
|
@ -140,44 +128,15 @@ public enum SymZ3PartsFactory implements AuxDebuggerEmulatorPartsFactory<SymValu
|
|||
|
||||
@Override
|
||||
public PcodeExecutorState<Pair<byte[], SymValueZ3>> createSharedState(
|
||||
AuxPcodeEmulator<SymValueZ3> emulator, BytesPcodeExecutorStatePiece concrete) {
|
||||
return new SymZ3PcodeExecutorState(emulator.getLanguage(), concrete);
|
||||
AuxPcodeEmulator<SymValueZ3> emulator, BytesPcodeExecutorStatePiece concrete,
|
||||
PcodeStateCallbacks cb) {
|
||||
return new SymZ3PcodeExecutorState(emulator.getLanguage(), concrete, cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PcodeExecutorState<Pair<byte[], SymValueZ3>> createLocalState(
|
||||
AuxPcodeEmulator<SymValueZ3> emulator, PcodeThread<Pair<byte[], SymValueZ3>> thread,
|
||||
BytesPcodeExecutorStatePiece concrete) {
|
||||
return new SymZ3PcodeExecutorState(emulator.getLanguage(), concrete);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TracePcodeExecutorState<Pair<byte[], SymValueZ3>> createTraceSharedState(
|
||||
AuxTracePcodeEmulator<SymValueZ3> emulator,
|
||||
BytesTracePcodeExecutorStatePiece concrete) {
|
||||
return new SymZ3TracePcodeExecutorState(concrete);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TracePcodeExecutorState<Pair<byte[], SymValueZ3>> createTraceLocalState(
|
||||
AuxTracePcodeEmulator<SymValueZ3> emulator,
|
||||
PcodeThread<Pair<byte[], SymValueZ3>> emuThread,
|
||||
BytesTracePcodeExecutorStatePiece concrete) {
|
||||
return new SymZ3TracePcodeExecutorState(concrete);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TracePcodeExecutorState<Pair<byte[], SymValueZ3>> createDebuggerSharedState(
|
||||
AuxDebuggerPcodeEmulator<SymValueZ3> emulator,
|
||||
RWTargetMemoryPcodeExecutorStatePiece concrete) {
|
||||
return new SymZ3TracePcodeExecutorState(concrete);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TracePcodeExecutorState<Pair<byte[], SymValueZ3>> createDebuggerLocalState(
|
||||
AuxDebuggerPcodeEmulator<SymValueZ3> emulator,
|
||||
PcodeThread<Pair<byte[], SymValueZ3>> emuThread,
|
||||
RWTargetRegistersPcodeExecutorStatePiece concrete) {
|
||||
return new SymZ3TracePcodeExecutorState(concrete);
|
||||
BytesPcodeExecutorStatePiece concrete, PcodeStateCallbacks cb) {
|
||||
return new SymZ3PcodeExecutorState(emulator.getLanguage(), concrete, cb);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,6 +77,11 @@ public enum SymZ3PcodeArithmetic implements PcodeArithmetic<SymValueZ3> {
|
|||
this.endian = endian;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<SymValueZ3> getDomain() {
|
||||
return SymValueZ3.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Endian getEndian() {
|
||||
return endian;
|
||||
|
|
|
@ -30,7 +30,6 @@ import ghidra.app.plugin.processors.sleigh.template.OpTpl;
|
|||
import ghidra.app.util.pcode.StringPcodeFormatter;
|
||||
import ghidra.pcode.emu.PcodeMachine;
|
||||
import ghidra.pcode.emu.symz3.lib.Z3InfixPrinter;
|
||||
import ghidra.pcode.emu.symz3.plain.SymZ3Space;
|
||||
import ghidra.symz3.model.SymValueZ3;
|
||||
|
||||
public interface SymZ3PcodeEmulatorTrait
|
||||
|
@ -48,7 +47,7 @@ public interface SymZ3PcodeEmulatorTrait
|
|||
@Override
|
||||
SymZ3PairedPcodeExecutorState getSharedState();
|
||||
|
||||
default AbstractSymZ3PcodeExecutorStatePiece<? extends SymZ3Space> getSharedSymbolicState() {
|
||||
default SymZ3PcodeExecutorStatePiece getSharedSymbolicState() {
|
||||
return getSharedState().getRight();
|
||||
}
|
||||
|
||||
|
|
|
@ -17,19 +17,19 @@ package ghidra.pcode.emu.symz3;
|
|||
|
||||
import java.io.PrintStream;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.microsoft.z3.Context;
|
||||
|
||||
import ghidra.pcode.emu.symz3.lib.Z3InfixPrinter;
|
||||
import ghidra.pcode.emu.symz3.plain.SymZ3Preconditions;
|
||||
import ghidra.pcode.emu.symz3.plain.SymZ3Space;
|
||||
import ghidra.pcode.exec.ConcretionError;
|
||||
import ghidra.pcode.exec.PcodeArithmetic;
|
||||
import ghidra.pcode.emu.symz3.state.*;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.listing.Instruction;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
|
@ -47,22 +47,12 @@ import ghidra.symz3.model.SymValueZ3;
|
|||
* more capable state piece, we have to ensure that type can be substituted. Thus, we have to create
|
||||
* these abstract classes from which the actual state pieces are derived, leaving {@code <S>}
|
||||
* bounded, but unspecified.
|
||||
*
|
||||
* @param <S> the type of spaces
|
||||
*/
|
||||
public abstract class AbstractSymZ3PcodeExecutorStatePiece<S extends SymZ3Space>
|
||||
extends AbstractSymZ3OffsetPcodeExecutorStatePiece<S>
|
||||
public class SymZ3PcodeExecutorStatePiece
|
||||
extends AbstractSymZ3OffsetPcodeExecutorStatePiece<SymZ3Space>
|
||||
implements InternalSymZ3RecordsPreconditions, InternalSymZ3RecordsExecution {
|
||||
|
||||
/**
|
||||
* The map from address space to storage space
|
||||
*
|
||||
* <p>
|
||||
* While the concept is introduced in the super class, we're not required to actually use one.
|
||||
* We just have to implement {@link #getForSpace(AddressSpace, boolean)}. Nevertheless, the
|
||||
* provided map is probably the best way, so we'll follow the pattern.
|
||||
*/
|
||||
protected final AbstractSpaceMap<S> spaceMap = newSpaceMap(this.language);
|
||||
protected final Map<AddressSpace, SymZ3Space> spaceMap = new HashMap<>();
|
||||
|
||||
protected final SymZ3Preconditions preconditions = new SymZ3Preconditions();
|
||||
// LATER: These two are a recurring concern, and should be separated out
|
||||
|
@ -75,23 +65,44 @@ public abstract class AbstractSymZ3PcodeExecutorStatePiece<S extends SymZ3Space>
|
|||
* @param language the emulator's language
|
||||
* @param addressArithmetic the arithmetic for the address type
|
||||
* @param arithmetic the arithmetic for the value type
|
||||
* @param cb callbacks to receive emulation events
|
||||
*/
|
||||
public AbstractSymZ3PcodeExecutorStatePiece(Language language,
|
||||
PcodeArithmetic<SymValueZ3> addressArithmetic, PcodeArithmetic<SymValueZ3> arithmetic) {
|
||||
super(language, addressArithmetic, arithmetic);
|
||||
public SymZ3PcodeExecutorStatePiece(Language language,
|
||||
PcodeArithmetic<SymValueZ3> addressArithmetic, PcodeArithmetic<SymValueZ3> arithmetic,
|
||||
PcodeStateCallbacks cb) {
|
||||
super(language, addressArithmetic, arithmetic, cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension point: Create the actual space map
|
||||
* Create the SymZ3 piece
|
||||
*
|
||||
* <p>
|
||||
* This will need to be implemented by each state piece, i.e., non-abstract derivating class.
|
||||
* The space map will provide instances of {@code <S>}, which will provide the actual (extended)
|
||||
* storage logic.
|
||||
*
|
||||
* @return the space map
|
||||
* @param language the language of the emulator
|
||||
* @param addressArithmetic the address arithmetic, likely taken from the concrete piece
|
||||
* @param cb callbacks to receive emulation events
|
||||
*/
|
||||
protected abstract AbstractSpaceMap<S> newSpaceMap(Language language);
|
||||
public SymZ3PcodeExecutorStatePiece(Language language,
|
||||
PcodeArithmetic<SymValueZ3> addressArithmetic, PcodeStateCallbacks cb) {
|
||||
this(language, addressArithmetic, SymZ3PcodeArithmetic.forLanguage(language), cb);
|
||||
}
|
||||
|
||||
protected SymZ3Space newSpace(AddressSpace space) {
|
||||
if (space.isConstantSpace()) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
else if (space.isRegisterSpace()) {
|
||||
return new SymZ3RegisterSpace(language, space,
|
||||
SymZ3PcodeExecutorStatePiece.this);
|
||||
}
|
||||
else if (space.isUniqueSpace()) {
|
||||
return new SymZ3UniqueSpace();
|
||||
}
|
||||
else if (space.isLoadedMemorySpace()) {
|
||||
return new SymZ3MemorySpace(language, space, SymZ3PcodeExecutorStatePiece.this);
|
||||
}
|
||||
else {
|
||||
throw new AssertionError("not yet supported space: " + space.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemBuffer getConcreteBuffer(Address address, Purpose purpose) {
|
||||
|
@ -105,8 +116,11 @@ public abstract class AbstractSymZ3PcodeExecutorStatePiece<S extends SymZ3Space>
|
|||
* Here, we just follow the pattern: delegate to the space map.
|
||||
*/
|
||||
@Override
|
||||
protected S getForSpace(AddressSpace space, boolean toWrite) {
|
||||
return spaceMap.getForSpace(space, toWrite);
|
||||
protected SymZ3Space getForSpace(AddressSpace space, boolean toWrite) {
|
||||
if (toWrite) {
|
||||
return spaceMap.computeIfAbsent(space, this::newSpace);
|
||||
}
|
||||
return spaceMap.get(space);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -117,8 +131,9 @@ public abstract class AbstractSymZ3PcodeExecutorStatePiece<S extends SymZ3Space>
|
|||
* the storage space.
|
||||
*/
|
||||
@Override
|
||||
protected void setInSpace(SymZ3Space space, SymValueZ3 offset, int size, SymValueZ3 val) {
|
||||
space.set(offset, size, val);
|
||||
protected void setInSpace(SymZ3Space space, SymValueZ3 offset, int size, SymValueZ3 val,
|
||||
PcodeStateCallbacks cb) {
|
||||
space.set(offset, size, val, cb);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -129,13 +144,28 @@ public abstract class AbstractSymZ3PcodeExecutorStatePiece<S extends SymZ3Space>
|
|||
* the storage space.
|
||||
*/
|
||||
@Override
|
||||
protected SymValueZ3 getFromSpace(SymZ3Space space, SymValueZ3 offset, int size) {
|
||||
return space.get(offset, size);
|
||||
protected SymValueZ3 getFromSpace(SymZ3Space space, SymValueZ3 offset, int size,
|
||||
PcodeStateCallbacks cb) {
|
||||
return space.get(offset, size, cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Register, SymValueZ3> getRegisterValues() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<Long, SymValueZ3> getNextEntryInternal(AddressSpace space, long offset) {
|
||||
SymZ3Space s = getForSpace(space, false);
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
return s.getNextEntry(offset);
|
||||
}
|
||||
|
||||
public String printableSummary() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (S space : spaceMap.values()) {
|
||||
for (SymZ3Space space : spaceMap.values()) {
|
||||
result.append(space.printableSummary());
|
||||
}
|
||||
result.append(this.preconditions.printableSummary());
|
||||
|
@ -182,7 +212,7 @@ public abstract class AbstractSymZ3PcodeExecutorStatePiece<S extends SymZ3Space>
|
|||
|
||||
@Override
|
||||
public void clear() {
|
||||
spaceMap.spaces.clear();
|
||||
spaceMap.clear();
|
||||
preconditions.clear();
|
||||
ops.clear();
|
||||
instructions.clear();
|
|
@ -30,7 +30,6 @@ import ghidra.pcode.emu.ThreadPcodeExecutorState;
|
|||
import ghidra.pcode.emu.auxiliary.AuxPcodeEmulator;
|
||||
import ghidra.pcode.emu.auxiliary.AuxPcodeThread;
|
||||
import ghidra.pcode.emu.symz3.lib.Z3InfixPrinter;
|
||||
import ghidra.pcode.emu.symz3.plain.SymZ3Space;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||
|
@ -76,7 +75,7 @@ public class SymZ3PcodeThread extends AuxPcodeThread<SymValueZ3>
|
|||
return getState().getSharedState().getLeft();
|
||||
}
|
||||
|
||||
public AbstractSymZ3PcodeExecutorStatePiece<? extends SymZ3Space> getSharedSymbolicState() {
|
||||
public SymZ3PcodeExecutorStatePiece getSharedSymbolicState() {
|
||||
return getState().getSharedState().getRight();
|
||||
}
|
||||
|
||||
|
@ -84,7 +83,7 @@ public class SymZ3PcodeThread extends AuxPcodeThread<SymValueZ3>
|
|||
return getState().getLocalState().getLeft();
|
||||
}
|
||||
|
||||
public AbstractSymZ3PcodeExecutorStatePiece<? extends SymZ3Space> getLocalSymbolicState() {
|
||||
public SymZ3PcodeExecutorStatePiece getLocalSymbolicState() {
|
||||
return getState().getLocalState().getRight();
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,8 @@ public class SymZ3RegisterMap {
|
|||
|
||||
// TODO: make this be private and provide appropriate methods
|
||||
// in the map, all registers are base registers.
|
||||
public Map<Register, SymValueZ3> regvals = new HashMap<Register, SymValueZ3>();
|
||||
public Map<Register, SymValueZ3> regvals = new HashMap<>();
|
||||
private NavigableMap<Long, SymValueZ3> byOffset;
|
||||
|
||||
//private List<String> createdSymbolics = new ArrayList<String>();
|
||||
private final Set<String> registerNamesRead = new HashSet<String>();
|
||||
|
@ -85,6 +86,7 @@ public class SymZ3RegisterMap {
|
|||
private void updateRegisterHelper(Context ctx, Register r, SymValueZ3 update) {
|
||||
if (r.isBaseRegister()) {
|
||||
regvals.put(r, update);
|
||||
byOffset = null;
|
||||
return;
|
||||
}
|
||||
// so, we want to update the base, but also need to keep portions of it.
|
||||
|
@ -114,6 +116,7 @@ public class SymZ3RegisterMap {
|
|||
result = ctx.mkConcat(result, right);
|
||||
}
|
||||
regvals.put(base, new SymValueZ3(ctx, result));
|
||||
byOffset = null;
|
||||
}
|
||||
|
||||
public SymValueZ3 getRegister(Register r) {
|
||||
|
@ -228,4 +231,14 @@ public class SymZ3RegisterMap {
|
|||
return valuationFor(ctx, z3p, r);
|
||||
});
|
||||
}
|
||||
|
||||
public Entry<Long, SymValueZ3> getNextEntry(long offset) {
|
||||
if (byOffset == null) {
|
||||
byOffset = new TreeMap<>();
|
||||
for (Entry<Register, SymValueZ3> ent : regvals.entrySet()) {
|
||||
byOffset.put(ent.getKey().getAddress().getOffset(), ent.getValue());
|
||||
}
|
||||
}
|
||||
return byOffset.ceilingEntry(offset);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,76 +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.emu.symz3.full;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import ghidra.debug.api.emulation.PcodeDebuggerAccess;
|
||||
import ghidra.pcode.emu.symz3.*;
|
||||
import ghidra.pcode.emu.symz3.plain.SymZ3PcodeEmulator;
|
||||
import ghidra.pcode.exec.debug.auxiliary.AuxDebuggerEmulatorPartsFactory;
|
||||
import ghidra.pcode.exec.debug.auxiliary.AuxDebuggerPcodeEmulator;
|
||||
import ghidra.symz3.model.SymValueZ3;
|
||||
|
||||
/**
|
||||
* A Debugger-integrated emulator with symbolic z3 summarization
|
||||
*/
|
||||
public class SymZ3DebuggerPcodeEmulator extends AuxDebuggerPcodeEmulator<SymValueZ3>
|
||||
implements SymZ3PcodeEmulatorTrait {
|
||||
/**
|
||||
* Create an emulator
|
||||
*
|
||||
* @param access the trace-and-debugger access shim
|
||||
*/
|
||||
public SymZ3DebuggerPcodeEmulator(PcodeDebuggerAccess access) {
|
||||
super(access);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* Here, we just return the singleton parts factory. This appears simple because all the
|
||||
* complexity is encapsulated in the factory. See {@link SymZ3PartsFactory} to see everything
|
||||
* the implementation actually entails. Notice that this is the same parts factory used by
|
||||
* {@link SymZ3PcodeEmulator}. The {@link AuxDebugggerPcodeEmulator} knows to use the more
|
||||
* capable state parts.
|
||||
*/
|
||||
@Override
|
||||
protected AuxDebuggerEmulatorPartsFactory<SymValueZ3> getPartsFactory() {
|
||||
return SymZ3PartsFactory.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SymZ3PcodeThread newThread() {
|
||||
return (SymZ3PcodeThread) super.newThread();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SymZ3PcodeThread newThread(String name) {
|
||||
return (SymZ3PcodeThread) super.newThread(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Collection<? extends SymZ3PcodeThread> getAllThreads() {
|
||||
return (Collection<? extends SymZ3PcodeThread>) super.getAllThreads();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SymZ3PairedPcodeExecutorState getSharedState() {
|
||||
return (SymZ3PairedPcodeExecutorState) super.getSharedState();
|
||||
}
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.symz3.plain;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import ghidra.pcode.emu.symz3.AbstractSymZ3PcodeExecutorStatePiece;
|
||||
import ghidra.pcode.emu.symz3.SymZ3PcodeArithmetic;
|
||||
import ghidra.pcode.exec.PcodeArithmetic;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.symz3.model.SymValueZ3;
|
||||
|
||||
/**
|
||||
* The state piece for holding symbolic values in the emulator's machine state
|
||||
*
|
||||
*/
|
||||
public class SymZ3PcodeExecutorStatePiece extends AbstractSymZ3PcodeExecutorStatePiece<SymZ3Space> {
|
||||
/**
|
||||
* Create the SymZ3 piece
|
||||
*
|
||||
* @param language the language of the emulator
|
||||
* @param addressArithmetic the address arithmetic, likely taken from the concrete piece
|
||||
*/
|
||||
public SymZ3PcodeExecutorStatePiece(Language language,
|
||||
PcodeArithmetic<SymValueZ3> addressArithmetic) {
|
||||
super(language, addressArithmetic, SymZ3PcodeArithmetic.forLanguage(language));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* Here we use the simplest scheme for creating a map of {@link SymZ3Space}s. This is
|
||||
* essentially a lazy map from address space to some object for managing symbolic values in that
|
||||
* address space. The space could be a memory space, register space, unique space, etc. This
|
||||
* piece will look up the space, creating it if necessary, and then delegate the get and set
|
||||
* methods.
|
||||
*/
|
||||
@Override
|
||||
protected AbstractSpaceMap<SymZ3Space> newSpaceMap(Language language) {
|
||||
return new SimpleSpaceMap<SymZ3Space>() {
|
||||
@Override
|
||||
protected SymZ3Space newSpace(AddressSpace space) {
|
||||
if (space.isConstantSpace()) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
else if (space.isRegisterSpace()) {
|
||||
return new SymZ3RegisterSpace(space, language);
|
||||
}
|
||||
else if (space.isUniqueSpace()) {
|
||||
return new SymZ3UniqueSpace();
|
||||
}
|
||||
else if (space.isLoadedMemorySpace()) {
|
||||
return new SymZ3MemorySpace(language);
|
||||
}
|
||||
else {
|
||||
throw new AssertionError("not yet supported space: " + space.toString());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String printableSummary() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (SymZ3Space space : spaceMap.values()) {
|
||||
result.append(space.printableSummary());
|
||||
}
|
||||
result.append(this.preconditions.printableSummary());
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Register, SymValueZ3> getRegisterValues() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
/**
|
||||
* In addition to clearing out all the state, you would probably also want to clear the
|
||||
* instruction and op lists.
|
||||
*/
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
|
@ -13,15 +13,18 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.symz3.plain;
|
||||
package ghidra.pcode.emu.symz3.state;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.microsoft.z3.Context;
|
||||
|
||||
import ghidra.pcode.emu.symz3.AbstractSymZ3OffsetPcodeExecutorStatePiece;
|
||||
import ghidra.pcode.emu.symz3.SymZ3MemoryMap;
|
||||
import ghidra.pcode.emu.symz3.lib.Z3InfixPrinter;
|
||||
import ghidra.pcode.exec.PcodeStateCallbacks;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.symz3.model.SymValueZ3;
|
||||
|
||||
|
@ -37,22 +40,35 @@ import ghidra.symz3.model.SymValueZ3;
|
|||
* to the SymZ3MemoryMap and there is just a bit of plumbing here.
|
||||
*/
|
||||
public class SymZ3MemorySpace extends SymZ3Space {
|
||||
private final AddressSpace space;
|
||||
private final AbstractSymZ3OffsetPcodeExecutorStatePiece<?> piece;
|
||||
private final SymZ3MemoryMap mmap;
|
||||
|
||||
private SymZ3MemoryMap mmap;
|
||||
|
||||
public SymZ3MemorySpace(Language language) {
|
||||
public SymZ3MemorySpace(Language language, AddressSpace space,
|
||||
AbstractSymZ3OffsetPcodeExecutorStatePiece<?> piece) {
|
||||
super();
|
||||
this.space = space;
|
||||
this.piece = piece;
|
||||
mmap = new SymZ3MemoryMap(language);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SymValueZ3 get(SymValueZ3 offset, int size) {
|
||||
return mmap.load(offset, size, true);
|
||||
public SymValueZ3 get(SymValueZ3 offset, int size, PcodeStateCallbacks cb) {
|
||||
if (!mmap.hasValueFor(offset, size)) {
|
||||
cb.readUninitialized(piece, space, offset, size);
|
||||
}
|
||||
return mmap.load(offset, size, true, cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(SymValueZ3 offset, int size, SymValueZ3 val) {
|
||||
public void set(SymValueZ3 offset, int size, SymValueZ3 val, PcodeStateCallbacks cb) {
|
||||
mmap.store(offset, size, val);
|
||||
cb.dataWritten(piece, space, offset, size, val);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<Long, SymValueZ3> getNextEntry(long offset) {
|
||||
return mmap.getNextEntry(offset);
|
||||
}
|
||||
|
||||
@Override
|
|
@ -13,10 +13,13 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.symz3.plain;
|
||||
package ghidra.pcode.emu.symz3.state;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import ghidra.pcode.emu.PcodeEmulationCallbacks;
|
||||
import ghidra.pcode.emu.auxiliary.AuxEmulatorPartsFactory;
|
||||
import ghidra.pcode.emu.auxiliary.AuxPcodeEmulator;
|
||||
import ghidra.pcode.emu.symz3.*;
|
||||
|
@ -24,17 +27,28 @@ import ghidra.program.model.lang.Language;
|
|||
import ghidra.symz3.model.SymValueZ3;
|
||||
|
||||
/**
|
||||
* A stand-alone emulator with symbolic Z3 summarization analysis
|
||||
* An emulator with symbolic Z3 summarization analysis
|
||||
*/
|
||||
public class SymZ3PcodeEmulator extends AuxPcodeEmulator<SymValueZ3>
|
||||
implements SymZ3PcodeEmulatorTrait {
|
||||
/**
|
||||
* Create an emulator
|
||||
*
|
||||
* @param language the language (processor model)
|
||||
* @param cb callbacks to receive emulation events
|
||||
*/
|
||||
public SymZ3PcodeEmulator(Language language,
|
||||
PcodeEmulationCallbacks<Pair<byte[], SymValueZ3>> cb) {
|
||||
super(language, cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an emulator
|
||||
*
|
||||
* @param language the language (processor model)
|
||||
*/
|
||||
public SymZ3PcodeEmulator(Language language) {
|
||||
super(language);
|
||||
this(language, PcodeEmulationCallbacks.none());
|
||||
}
|
||||
|
||||
/**
|
|
@ -13,11 +13,10 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.symz3.plain;
|
||||
package ghidra.pcode.emu.symz3.state;
|
||||
|
||||
import ghidra.pcode.emu.symz3.*;
|
||||
import ghidra.pcode.exec.BytesPcodeExecutorStatePiece;
|
||||
import ghidra.pcode.exec.IndependentPairedPcodeExecutorState;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.symz3.model.SymValueZ3;
|
||||
|
||||
|
@ -52,14 +51,16 @@ public class SymZ3PcodeExecutorState
|
|||
*
|
||||
* @param language the language for creating the symz3 piece
|
||||
* @param concrete the concrete piece
|
||||
* @param cb callbacks to receive emulation events
|
||||
*/
|
||||
public SymZ3PcodeExecutorState(Language language, BytesPcodeExecutorStatePiece concrete) {
|
||||
this(concrete,
|
||||
new SymZ3PcodeExecutorStatePiece(language, SymZ3PcodeArithmetic.forLanguage(language)));
|
||||
public SymZ3PcodeExecutorState(Language language, BytesPcodeExecutorStatePiece concrete,
|
||||
PcodeStateCallbacks cb) {
|
||||
this(concrete, new SymZ3PcodeExecutorStatePiece(language,
|
||||
SymZ3PcodeArithmetic.forLanguage(language), cb));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractSymZ3PcodeExecutorStatePiece<? extends SymZ3Space> getRight() {
|
||||
public SymZ3PcodeExecutorStatePiece getRight() {
|
||||
return (SymZ3PcodeExecutorStatePiece) super.getRight();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
/* ###
|
||||
* 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.emu.symz3.state;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import com.microsoft.z3.Context;
|
||||
|
||||
import ghidra.lifecycle.Unfinished;
|
||||
import ghidra.pcode.emu.PcodeThread;
|
||||
import ghidra.pcode.exec.ConcretionError;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece;
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||
import ghidra.pcode.exec.trace.TraceEmulationIntegration.AbstractPropertyBasedPieceHandler;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTracePropertyAccess;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.symz3.model.SymValueZ3;
|
||||
|
||||
public class SymZ3PieceHandler
|
||||
extends AbstractPropertyBasedPieceHandler<SymValueZ3, SymValueZ3, String> {
|
||||
public static final String NAME = "SymValueZ3";
|
||||
|
||||
private record SymZ3Varnode(AddressSpace space, String offset, int size) {
|
||||
public SymZ3Varnode(AddressSpace space, SymValueZ3 offset, int size) {
|
||||
this(space, offset.bitVecExprString, size);
|
||||
}
|
||||
|
||||
public SymValueZ3 offset(Context ctx) {
|
||||
return new SymValueZ3(ctx, SymValueZ3.deserializeBitVecExpr(ctx, offset));
|
||||
}
|
||||
}
|
||||
|
||||
private final Map<PcodeThread<?>, Set<SymZ3Varnode>> abstractWritten = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public Class<SymValueZ3> getAddressDomain() {
|
||||
return SymValueZ3.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<SymValueZ3> getValueDomain() {
|
||||
return SymValueZ3.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getPropertyName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<String> getPropertyType() {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abstractWritten(PcodeTraceDataAccess acc, AddressSet written, PcodeThread<?> thread,
|
||||
PcodeExecutorStatePiece<SymValueZ3, SymValueZ3> piece, AddressSpace space,
|
||||
SymValueZ3 offset, int length, SymValueZ3 value) {
|
||||
try {
|
||||
Address address = piece.getAddressArithmetic().toAddress(offset, space, Purpose.STORE);
|
||||
dataWritten(acc, written, thread, piece, address, length, value);
|
||||
}
|
||||
catch (ConcretionError e) {
|
||||
abstractWritten.computeIfAbsent(thread, t -> new HashSet<>())
|
||||
.add(new SymZ3Varnode(space, offset, length));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int abstractReadUninit(PcodeTraceDataAccess acc, PcodeThread<?> thread,
|
||||
PcodeExecutorStatePiece<SymValueZ3, SymValueZ3> piece, AddressSpace space,
|
||||
SymValueZ3 offset, int length) {
|
||||
String string = acc.getPropertyAccess(NAME, String.class).get(Address.NO_ADDRESS);
|
||||
if (string == null) {
|
||||
return 0;
|
||||
}
|
||||
return Unfinished.TODO("need to implement extraction from: " + string);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decodeFrom(PcodeExecutorStatePiece<SymValueZ3, SymValueZ3> piece,
|
||||
AddressSetView limit, AddressRange range, String propertyValue) {
|
||||
/**
|
||||
* NOTE: We're ignoring limit here, because we've not really implemented byte-wise property
|
||||
* access.
|
||||
*/
|
||||
SymValueZ3 result = SymValueZ3.parse(propertyValue);
|
||||
piece.setVarInternal(range.getAddressSpace(), range.getMinAddress().getOffset(),
|
||||
(int) range.getLength(), result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDown(PcodeTraceDataAccess into, PcodeThread<?> thread,
|
||||
PcodeExecutorStatePiece<SymValueZ3, SymValueZ3> piece, AddressSetView written) {
|
||||
super.writeDown(into, thread, piece, written);
|
||||
Set<SymZ3Varnode> symWritten = abstractWritten.get(thread);
|
||||
if (symWritten == null) {
|
||||
return;
|
||||
}
|
||||
StringBuffer buf = new StringBuffer();
|
||||
try (Context ctx = new Context()) {
|
||||
for (SymZ3Varnode vn : symWritten) {
|
||||
SymValueZ3 offset = vn.offset(ctx);
|
||||
SymValueZ3 value = piece.getVarInternal(vn.space, offset, vn.size, Reason.INSPECT);
|
||||
buf.append("::");
|
||||
buf.append(offset);
|
||||
buf.append("<==>");
|
||||
buf.append(value.serialize());
|
||||
}
|
||||
}
|
||||
/**
|
||||
* NOTE: This won't work for threads, but then again, how would one address a register
|
||||
* abstractly?
|
||||
*/
|
||||
String val = buf.isEmpty() ? null : buf.toString();
|
||||
into.getPropertyAccess(NAME, String.class).put(Address.NO_ADDRESS, val);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void encodeInto(PcodeTracePropertyAccess<String> property, AddressRange range,
|
||||
SymValueZ3 value) {
|
||||
property.put(range.getMinAddress(), value.serialize());
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.symz3.plain;
|
||||
package ghidra.pcode.emu.symz3.state;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
|
@ -13,15 +13,17 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.symz3.plain;
|
||||
package ghidra.pcode.emu.symz3.state;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.microsoft.z3.Context;
|
||||
|
||||
import ghidra.pcode.emu.symz3.AbstractSymZ3OffsetPcodeExecutorStatePiece;
|
||||
import ghidra.pcode.emu.symz3.SymZ3RegisterMap;
|
||||
import ghidra.pcode.emu.symz3.lib.Z3InfixPrinter;
|
||||
import ghidra.pcode.exec.PcodeStateCallbacks;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.lang.Register;
|
||||
|
@ -41,13 +43,16 @@ import ghidra.util.Msg;
|
|||
public class SymZ3RegisterSpace extends SymZ3Space {
|
||||
private final SymZ3RegisterMap rmap = new SymZ3RegisterMap();
|
||||
|
||||
private final AddressSpace space;
|
||||
private final Language language;
|
||||
private final AddressSpace space;
|
||||
private final AbstractSymZ3OffsetPcodeExecutorStatePiece<?> piece;
|
||||
|
||||
public SymZ3RegisterSpace(AddressSpace space, Language language) {
|
||||
public SymZ3RegisterSpace(Language language, AddressSpace space,
|
||||
AbstractSymZ3OffsetPcodeExecutorStatePiece<?> piece) {
|
||||
super();
|
||||
this.space = space;
|
||||
this.language = language;
|
||||
this.piece = piece;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -70,7 +75,7 @@ public class SymZ3RegisterSpace extends SymZ3Space {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void set(SymValueZ3 offset, int size, SymValueZ3 val) {
|
||||
public void set(SymValueZ3 offset, int size, SymValueZ3 val, PcodeStateCallbacks cb) {
|
||||
Register r = getRegister(offset, size);
|
||||
if (r == null) {
|
||||
Msg.warn(this, "set is ignoring set register with offset: " + offset + " and size: " +
|
||||
|
@ -78,17 +83,26 @@ public class SymZ3RegisterSpace extends SymZ3Space {
|
|||
return;
|
||||
}
|
||||
this.rmap.updateRegister(r, val);
|
||||
cb.dataWritten(piece, space, offset, size, val);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SymValueZ3 get(SymValueZ3 offset, int size) {
|
||||
public SymValueZ3 get(SymValueZ3 offset, int size, PcodeStateCallbacks cb) {
|
||||
Register r = getRegister(offset, size);
|
||||
if (r == null) {
|
||||
Msg.warn(this, "unable to get register with space: " + space.getSpaceID() +
|
||||
" offset_long: " + offset + " size: " + size);
|
||||
return null;
|
||||
}
|
||||
if (!rmap.hasValueForRegister(r)) {
|
||||
cb.readUninitialized(piece, space, offset, size);
|
||||
}
|
||||
SymValueZ3 result = this.rmap.getRegister(r);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<Long, SymValueZ3> getNextEntry(long offset) {
|
||||
return rmap.getNextEntry(offset);
|
||||
}
|
||||
}
|
|
@ -13,14 +13,16 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.pcode.emu.symz3.plain;
|
||||
package ghidra.pcode.emu.symz3.state;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.microsoft.z3.Context;
|
||||
|
||||
import ghidra.pcode.emu.symz3.lib.Z3InfixPrinter;
|
||||
import ghidra.pcode.exec.PcodeStateCallbacks;
|
||||
import ghidra.symz3.model.SymValueZ3;
|
||||
|
||||
/**
|
||||
|
@ -32,12 +34,14 @@ import ghidra.symz3.model.SymValueZ3;
|
|||
* user's current trace.
|
||||
*/
|
||||
public abstract class SymZ3Space {
|
||||
public abstract void set(SymValueZ3 offset, int size, SymValueZ3 val);
|
||||
public abstract void set(SymValueZ3 offset, int size, SymValueZ3 val, PcodeStateCallbacks cb);
|
||||
|
||||
public abstract SymValueZ3 get(SymValueZ3 offset, int size);
|
||||
public abstract SymValueZ3 get(SymValueZ3 offset, int size, PcodeStateCallbacks cb);
|
||||
|
||||
public abstract String printableSummary();
|
||||
|
||||
public abstract Stream<Map.Entry<String, String>> streamValuations(Context ctx,
|
||||
Z3InfixPrinter z3p);
|
||||
|
||||
public abstract Entry<Long, SymValueZ3> getNextEntry(long offset);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue