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

This commit is contained in:
Ryan Kurtz 2025-08-21 11:04:12 -04:00
commit 7b36c1649f
162 changed files with 5298 additions and 5887 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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),

View file

@ -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),

View file

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

View file

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