diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/BytesTracePcodeExecutorStatePiece.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/BytesTracePcodeExecutorStatePiece.java
index 1d1710bd72..c4e08694c5 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/BytesTracePcodeExecutorStatePiece.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/BytesTracePcodeExecutorStatePiece.java
@@ -76,6 +76,10 @@ public class BytesTracePcodeExecutorStatePiece
}
}
+ protected AddressSetView intersectViewKnown(AddressSetView set) {
+ return backing.intersectViewKnown(set, true);
+ }
+
@Override
protected ULongSpanSet readUninitializedFromBacking(ULongSpanSet uninitialized) {
if (uninitialized.isEmpty()) {
@@ -84,7 +88,7 @@ public class BytesTracePcodeExecutorStatePiece
// TODO: Warn or bail when reading UNKNOWN bytes
// NOTE: Read without regard to gaps
// NOTE: Cannot write those gaps, though!!!
- AddressSetView knownButUninit = backing.intersectViewKnown(addrSet(uninitialized));
+ AddressSetView knownButUninit = intersectViewKnown(addrSet(uninitialized));
if (knownButUninit.isEmpty()) {
return uninitialized;
}
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/RequireHasKnownTraceCachedWriteBytesPcodeExecutorStatePiece.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/RequireHasKnownTraceCachedWriteBytesPcodeExecutorStatePiece.java
index 990ebbb2f2..46e7ee3474 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/RequireHasKnownTraceCachedWriteBytesPcodeExecutorStatePiece.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/RequireHasKnownTraceCachedWriteBytesPcodeExecutorStatePiece.java
@@ -52,8 +52,8 @@ public class RequireHasKnownTraceCachedWriteBytesPcodeExecutorStatePiece
}
@Override
- protected AddressSetView getKnown(PcodeTraceDataAccess backing) {
- return backing.getKnownBefore();
+ protected AddressSetView getKnown(PcodeTraceDataAccess backing, AddressSetView set) {
+ return backing.intersectViewKnown(set, true);
}
@Override
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/RequireIsKnownTraceCachedWriteBytesPcodeExecutorStatePiece.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/RequireIsKnownTraceCachedWriteBytesPcodeExecutorStatePiece.java
index e487911a18..e33df8cc8b 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/RequireIsKnownTraceCachedWriteBytesPcodeExecutorStatePiece.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/RequireIsKnownTraceCachedWriteBytesPcodeExecutorStatePiece.java
@@ -50,8 +50,8 @@ public class RequireIsKnownTraceCachedWriteBytesPcodeExecutorStatePiece
spaceMap.fork());
}
- protected AddressSetView getKnown(PcodeTraceDataAccess backing) {
- return backing.getKnownNow();
+ protected AddressSetView getKnown(PcodeTraceDataAccess backing, AddressSetView set) {
+ return backing.intersectViewKnown(set, false);
}
protected AccessPcodeExecutionException excFor(AddressSetView unknown) {
@@ -68,7 +68,7 @@ public class RequireIsKnownTraceCachedWriteBytesPcodeExecutorStatePiece
throw excFor(uninitialized);
}
// TODO: Could find first instead?
- AddressSetView unknown = uninitialized.subtract(getKnown(backing));
+ AddressSetView unknown = uninitialized.subtract(getKnown(backing, uninitialized));
if (unknown.isEmpty()) {
return size;
}
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/data/AbstractPcodeTraceDataAccess.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/data/AbstractPcodeTraceDataAccess.java
index ff0c8a9452..532ea340e5 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/data/AbstractPcodeTraceDataAccess.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/data/AbstractPcodeTraceDataAccess.java
@@ -23,6 +23,7 @@ import ghidra.trace.model.Lifespan;
import ghidra.trace.model.TraceTimeViewport;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.memory.*;
+import ghidra.trace.util.TraceRegisterUtils;
/**
* An abstract data-access shim, for either memory or registers
@@ -130,27 +131,8 @@ public abstract class AbstractPcodeTraceDataAccess implements InternalPcodeTrace
return hostSet.isEmpty() ? TraceMemoryState.KNOWN : TraceMemoryState.UNKNOWN;
}
- protected AddressSetView doGetKnown(Lifespan span) {
- TraceMemoryOperations ops = getMemoryOps(false);
- if (ops == null) {
- return new AddressSet();
- }
- return platform.mapHostToGuest(ops.getAddressesWithState(span,
- s -> s == TraceMemoryState.KNOWN));
- }
-
@Override
- public AddressSetView getKnownNow() {
- return doGetKnown(Lifespan.at(snap));
- }
-
- @Override
- public AddressSetView getKnownBefore() {
- return doGetKnown(Lifespan.since(snap));
- }
-
- @Override
- public AddressSetView intersectViewKnown(AddressSetView guestView) {
+ public AddressSetView intersectViewKnown(AddressSetView guestView, boolean useFullSpans) {
TraceMemoryOperations ops = getMemoryOps(false);
if (ops == null) {
return new AddressSet();
@@ -158,11 +140,20 @@ public abstract class AbstractPcodeTraceDataAccess implements InternalPcodeTrace
AddressSetView hostView = toOverlay(platform.mapGuestToHost(guestView));
AddressSet hostKnown = new AddressSet();
- for (long sn : viewport.getOrderedSnaps()) {
- hostKnown.add(ops.getAddressesWithState(sn, hostView,
- st -> st != null && st != TraceMemoryState.UNKNOWN));
+ if (useFullSpans) {
+ for (Lifespan span : viewport.getOrderedSpans()) {
+ hostKnown.add(ops.getAddressesWithState(span, hostView,
+ st -> st != null && st != TraceMemoryState.UNKNOWN));
+ }
}
- AddressSetView hostResult = hostView.intersect(hostKnown);
+ else {
+ for (long snap : viewport.getOrderedSnaps()) {
+ hostKnown.add(ops.getAddressesWithState(snap, hostView,
+ st -> st != null && st != TraceMemoryState.UNKNOWN));
+ }
+ }
+ AddressSetView hostResult =
+ TraceRegisterUtils.getPhysicalSet(hostView.intersect(hostKnown));
return platform.mapHostToGuest(hostResult);
}
@@ -176,7 +167,7 @@ public abstract class AbstractPcodeTraceDataAccess implements InternalPcodeTrace
AddressSetView hostView = toOverlay(platform.mapGuestToHost(guestView));
AddressSetView hostKnown = ops.getAddressesWithState(snap, hostView,
s -> s != null && s != TraceMemoryState.UNKNOWN);
- AddressSetView hostResult = hostView.subtract(hostKnown);
+ AddressSetView hostResult = TraceRegisterUtils.getPhysicalSet(hostView.subtract(hostKnown));
return platform.mapHostToGuest(hostResult);
}
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/data/DefaultPcodeTraceThreadAccess.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/data/DefaultPcodeTraceThreadAccess.java
index 2456b8027f..d6eaf8bb51 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/data/DefaultPcodeTraceThreadAccess.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/data/DefaultPcodeTraceThreadAccess.java
@@ -71,18 +71,9 @@ public class DefaultPcodeTraceThreadAccess
}
@Override
- public AddressSetView getKnownNow() {
- return memory.getKnownNow().union(registers.getKnownNow());
- }
-
- @Override
- public AddressSetView getKnownBefore() {
- return memory.getKnownBefore().union(registers.getKnownBefore());
- }
-
- @Override
- public AddressSetView intersectViewKnown(AddressSetView view) {
- return memory.intersectViewKnown(view).union(registers.intersectViewKnown(view));
+ public AddressSetView intersectViewKnown(AddressSetView view, boolean useFullSpans) {
+ return memory.intersectViewKnown(view, useFullSpans)
+ .union(registers.intersectViewKnown(view, useFullSpans));
}
@Override
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/data/PcodeTraceDataAccess.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/data/PcodeTraceDataAccess.java
index 71f68a4d49..3d4e6d66f4 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/data/PcodeTraceDataAccess.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/pcode/exec/trace/data/PcodeTraceDataAccess.java
@@ -63,43 +63,15 @@ public interface PcodeTraceDataAccess {
*/
TraceMemoryState getViewportState(AddressRange range);
- /**
- * Get the address set of {@link TraceMemoryState#KNOWN} memory in the source snapshot
- *
- *
- * Note, this does not consider the snapshot's viewport.
- *
- * @implNote This can be an expensive operation when the platform is a guest, since what would
- * ordinarily be a lazy address set must be computed and translated to the guest
- * address spaces.
- *
- * @return the address set
- */
- AddressSetView getKnownNow();
-
- /**
- * Get the address set of {@link TraceMemoryState#KNOWN} memory among all snapshots from 0 to
- * the source snapshot
- *
- *
- * Note, this does not consider the snapshot's viewport.
- *
- * @implNote This can be an expensive operation when the platform is a guest, since what would
- * ordinarily be a lazy address set must be computed and translated to the guest
- * address spaces.
- *
- * @return the address set
- */
- AddressSetView getKnownBefore();
-
/**
* Compute the intersection of the given address set and the set of
* {@link TraceMemoryState#KNOWN} or (@link {@link TraceMemoryState#ERROR} memory
*
* @param view the address set
+ * @param useFullSpans how to treat the viewport; true for ever known, false for known now.
* @return the intersection
*/
- AddressSetView intersectViewKnown(AddressSetView view);
+ AddressSetView intersectViewKnown(AddressSetView view, boolean useFullSpans);
/**
* Compute the intersection of the given address set and the set of
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemoryManager.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemoryManager.java
index 94e7483997..59599946b3 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemoryManager.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemoryManager.java
@@ -290,7 +290,7 @@ public class DBTraceMemoryManager extends AbstractDBTraceSpaceBasedManager predicate) {
return delegateAddressSet(getActiveMemorySpaces(),
m -> m.getAddressesWithState(snap, set, predicate));
@@ -311,6 +311,11 @@ public class DBTraceMemoryManager extends AbstractDBTraceSpaceBasedManager> doGetStates(Lifespan span,
+ AddressRange range) {
+ return delegateRead(range.getAddressSpace(), m -> m.doGetStates(span, range));
+ }
+
@Override
public Collection> getStates(long snap,
AddressRange range) {
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemorySpace.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemorySpace.java
index ab13bedd1c..72a269060e 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemorySpace.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemorySpace.java
@@ -434,7 +434,7 @@ public class DBTraceMemorySpace
}
@Override
- public AddressSetView getAddressesWithState(long snap, AddressSetView set,
+ public AddressSetView getAddressesWithState(Lifespan span, AddressSetView set,
Predicate predicate) {
try (LockHold hold = LockHold.lock(lock.readLock())) {
AddressSet remains = new AddressSet(set);
@@ -442,7 +442,7 @@ public class DBTraceMemorySpace
while (!remains.isEmpty()) {
AddressRange range = remains.getFirstRange();
remains.delete(range);
- for (Entry entry : doGetStates(snap,
+ for (Entry entry : doGetStates(span,
range)) {
AddressRange foundRange = entry.getKey().getRange();
remains.delete(foundRange);
@@ -455,21 +455,20 @@ public class DBTraceMemorySpace
}
}
- protected Collection> doGetStates(long snap,
+ protected Collection> doGetStates(Lifespan span,
AddressRange range) {
// TODO: A better way to handle memory-mapped registers?
if (getAddressSpace().isRegisterSpace() && !range.getAddressSpace().isRegisterSpace()) {
- return trace.getMemoryManager().getStates(snap, range);
+ return trace.getMemoryManager().doGetStates(span, range);
}
- return stateMapSpace.reduce(TraceAddressSnapRangeQuery.intersecting(range.getMinAddress(),
- range.getMaxAddress(), snap, snap)).entries();
+ return stateMapSpace.reduce(TraceAddressSnapRangeQuery.intersecting(range, span)).entries();
}
@Override
public Collection> getStates(long snap,
AddressRange range) {
assertInSpace(range);
- return doGetStates(snap, range);
+ return doGetStates(Lifespan.at(snap), range);
}
@Override
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/memory/TraceMemoryOperations.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/memory/TraceMemoryOperations.java
index 78d3b1d8cc..8e1adcb571 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/memory/TraceMemoryOperations.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/memory/TraceMemoryOperations.java
@@ -268,6 +268,20 @@ public interface TraceMemoryOperations {
Entry getViewMostRecentStateEntry(long snap,
Address address);
+ /**
+ * Get at least the subset of addresses having state satisfying the given predicate
+ *
+ * @param snap the time
+ * @param set the set to examine
+ * @param predicate a predicate on state to search for
+ * @return the address set
+ * @see #getAddressesWithState(Lifespan, AddressSetView, Predicate)
+ */
+ default AddressSetView getAddressesWithState(long snap, AddressSetView set,
+ Predicate predicate) {
+ return getAddressesWithState(Lifespan.at(snap), set, predicate);
+ }
+
/**
* Get at least the subset of addresses having state satisfying the given predicate
*
@@ -288,7 +302,7 @@ public interface TraceMemoryOperations {
* @param predicate a predicate on state to search for
* @return the address set
*/
- AddressSetView getAddressesWithState(long snap, AddressSetView set,
+ AddressSetView getAddressesWithState(Lifespan span, AddressSetView set,
Predicate predicate);
/**
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/TraceRegisterUtils.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/TraceRegisterUtils.java
index 5ac2a3bb25..5c19a936a6 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/TraceRegisterUtils.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/TraceRegisterUtils.java
@@ -60,6 +60,34 @@ public enum TraceRegisterUtils {
return result;
}
+ public static AddressRange getPhysicalRange(AddressRange range) {
+ AddressSpace space = range.getAddressSpace();
+ AddressSpace physical = space.getPhysicalSpace();
+ if (space == physical) {
+ return range;
+ }
+ return new AddressRangeImpl(
+ physical.getAddress(range.getMinAddress().getOffset()),
+ physical.getAddress(range.getMaxAddress().getOffset()));
+ }
+
+ /**
+ * Convert a set in an overlay space to the corresponding set in its physical space
+ *
+ * @param set a set contained entirely in one space
+ * @return the physical set
+ */
+ public static AddressSetView getPhysicalSet(AddressSetView set) {
+ if (set.isEmpty() || !set.getMinAddress().getAddressSpace().isOverlaySpace()) {
+ return set;
+ }
+ AddressSet result = new AddressSet();
+ for (AddressRange rng : set) {
+ result.add(getPhysicalRange(rng));
+ }
+ return result;
+ }
+
public static byte[] padOrTruncate(byte[] arr, int length) {
if (arr.length == length) {
return arr;
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/BytesTracePcodeEmulatorTest.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/BytesTracePcodeEmulatorTest.java
index 349ca08731..3ff4363ed4 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/BytesTracePcodeEmulatorTest.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/pcode/exec/trace/BytesTracePcodeEmulatorTest.java
@@ -942,6 +942,23 @@ public class BytesTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
}
}
+ @Test(expected = DecodePcodeExecutionException.class)
+ public void testUninitialized() throws Throwable {
+ try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "Toy:BE:64:default")) {
+ assertEquals(Register.NO_CONTEXT, tb.language.getContextBaseRegister());
+
+ TraceThread thread = initTrace(tb, """
+ pc = 0x00400000;
+ sp = 0x00110000;
+ """,
+ List.of()); // An empty, uninitialized program
+
+ BytesTracePcodeEmulator emu = new BytesTracePcodeEmulator(tb.host, 0);
+ PcodeThread emuThread = emu.newThread(thread.getPath());
+ emuThread.stepInstruction();
+ }
+ }
+
@Test
public void testMov_w_mW1_W0() throws Throwable {
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "dsPIC33F:LE:24:default")) {
diff --git a/Ghidra/Debug/TaintAnalysis/src/test/java/ghidra/pcode/emu/taint/full/TaintDebuggerPcodeEmulatorTest.java b/Ghidra/Debug/TaintAnalysis/src/test/java/ghidra/pcode/emu/taint/full/TaintDebuggerPcodeEmulatorTest.java
index 2138061a17..a9c781946a 100644
--- a/Ghidra/Debug/TaintAnalysis/src/test/java/ghidra/pcode/emu/taint/full/TaintDebuggerPcodeEmulatorTest.java
+++ b/Ghidra/Debug/TaintAnalysis/src/test/java/ghidra/pcode/emu/taint/full/TaintDebuggerPcodeEmulatorTest.java
@@ -15,7 +15,8 @@
*/
package ghidra.pcode.emu.taint.full;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import java.util.Set;
@@ -30,18 +31,18 @@ import ghidra.app.plugin.core.debug.service.emulation.DebuggerEmulationServicePl
import ghidra.app.plugin.core.debug.service.emulation.DebuggerPcodeMachine;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin;
import ghidra.app.services.DebuggerEmulationService;
+import ghidra.app.services.DebuggerEmulationService.EmulationResult;
import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.pcode.emu.taint.trace.TaintTracePcodeEmulatorTest;
import ghidra.pcode.emu.taint.trace.TaintTracePcodeExecutorStatePiece;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.util.StringPropertyMap;
import ghidra.program.util.ProgramLocation;
-import ghidra.trace.model.DefaultTraceLocation;
-import ghidra.trace.model.Lifespan;
+import ghidra.trace.model.*;
import ghidra.trace.model.property.TracePropertyMap;
import ghidra.trace.model.property.TracePropertyMapSpace;
import ghidra.trace.model.thread.TraceThread;
-import ghidra.trace.model.time.schedule.TraceSchedule;
+import ghidra.trace.model.time.schedule.*;
import ghidra.util.task.TaskMonitor;
public class TaintDebuggerPcodeEmulatorTest extends AbstractGhidraHeadedDebuggerGUITest {
@@ -75,11 +76,19 @@ public class TaintDebuggerPcodeEmulatorTest extends AbstractGhidraHeadedDebugger
traceManager.activateTrace(tb.trace);
- TraceSchedule time = TraceSchedule.parse("0:t0-1");
- emuService.emulate(tb.trace, time, TaskMonitor.DUMMY);
- traceManager.activateTime(time);
+ EmulationResult result =
+ emuService.run(tb.host, TraceSchedule.snap(0), monitor, new Scheduler() {
+ int calls = 0;
- DebuggerPcodeMachine> emu = emuService.getCachedEmulator(tb.trace, time);
+ @Override
+ public TickStep nextSlice(Trace trace) {
+ // Expect decode of uninitialized memory immediately
+ assertEquals(0, calls++);
+ return new TickStep(0, 1);
+ }
+ });
+
+ DebuggerPcodeMachine> emu = emuService.getCachedEmulator(tb.trace, result.schedule());
assertTrue(emu instanceof TaintDebuggerPcodeEmulator);
}
diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/BytesPcodeExecutorStateSpace.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/BytesPcodeExecutorStateSpace.java
index e34e3d826a..757de237a5 100644
--- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/BytesPcodeExecutorStateSpace.java
+++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/BytesPcodeExecutorStateSpace.java
@@ -32,6 +32,7 @@ import ghidra.util.Msg;
* @param if this space is a cache, the type of object backing this space
*/
public class BytesPcodeExecutorStateSpace {
+ protected final static byte[] EMPTY = new byte[] {};
protected final SemisparseByteArray bytes;
protected final Language language; // for logging diagnostics
protected final AddressSpace space;
@@ -197,8 +198,13 @@ public class BytesPcodeExecutorStateSpace {
warnUninit(uninitialized);
}
else if (reason == Reason.EXECUTE_DECODE) {
- throw new DecodePcodeExecutionException("Cannot decode uninitialized memory",
- space.getAddress(offset));
+ /**
+ * The callers may be reading ahead, so it's not appropriate to throw an exception here.
+ * Instead, communicate there's no more. If the buffer's empty on their end, they'll
+ * handle the error as appropriate. If it's in the emulator, the instruction decoder
+ * should eventually throw the decode exception.
+ */
+ return EMPTY;
}
return readBytes(offset, size, reason);
}
diff --git a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/DecodePcodeExecutionException.java b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/DecodePcodeExecutionException.java
index 2be36d8aa6..0d996c5b6f 100644
--- a/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/DecodePcodeExecutionException.java
+++ b/Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/DecodePcodeExecutionException.java
@@ -21,7 +21,7 @@ public class DecodePcodeExecutionException extends PcodeExecutionException {
private final Address pc;
public DecodePcodeExecutionException(String message, Address pc) {
- super(message + ", PC=" + pc);
+ super(message.contains("PC=") ? message : "%s (PC=%s)".formatted(message, pc));
this.pc = pc;
}