From eb168f0b1b6e5ac9dd6f3befd34c3a910e004e1b Mon Sep 17 00:00:00 2001 From: Dan <46821332+nsadeveloper789@users.noreply.github.com> Date: Fri, 28 Aug 2026 15:33:34 +0000 Subject: [PATCH] GP-7191: Fix FlatDebuggerAPI register reads when used with emulation. --- .../ghidra/debug/flatapi/FlatDebuggerAPI.java | 111 ++++++++---------- .../flatapi/AbstractFlatDebuggerAPITest.java | 12 +- .../flatapi/DeadFlatDebuggerAPITest.java | 9 +- .../debug/flatapi/FlatDebuggerRmiAPITest.java | 30 ++--- 4 files changed, 74 insertions(+), 88 deletions(-) diff --git a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/flatapi/FlatDebuggerAPI.java b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/flatapi/FlatDebuggerAPI.java index e4f1e4e8e8..bf8db1b8f6 100644 --- a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/flatapi/FlatDebuggerAPI.java +++ b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/flatapi/FlatDebuggerAPI.java @@ -55,6 +55,7 @@ import ghidra.trace.model.target.TraceObject; import ghidra.trace.model.target.TraceObjectValue; import ghidra.trace.model.target.path.KeyPath; import ghidra.trace.model.thread.TraceThread; +import ghidra.trace.model.time.TraceSnapshot; import ghidra.trace.model.time.schedule.TraceSchedule; import ghidra.util.MathUtilities; import ghidra.util.Swing; @@ -63,7 +64,6 @@ import ghidra.util.task.TaskMonitor; /** * This interface is a flattened version of the Debugger and Trace APIs. - * *
* To use this "mix-in" interface, extend {@link GhidraScript} as you normally would for your * script, but also add this interface to the {@code implements} clause of your script, e.g., @@ -73,7 +73,6 @@ public interface FlatDebuggerAPI { /** * The method used to wait on futures. - * *
* By default, this waits at most 1 minute. * @@ -91,7 +90,6 @@ public interface FlatDebuggerAPI { /** * Get the script state - * *
* This is required to get various debugger services. It should be implemented by virtue of * extending {@link GhidraScript}. @@ -102,7 +100,6 @@ public interface FlatDebuggerAPI { /** * Require a service from the tool - * *
* If the service is missing, an exception is thrown directing the user to run the script from * the Debugger tool. @@ -237,7 +234,6 @@ public interface FlatDebuggerAPI { /** * Get the current thread - * *
* While uncommon, it is possible for there to be a current trace, but no current thread. * @@ -278,7 +274,6 @@ public interface FlatDebuggerAPI { /** * Get the current trace program view - * *
* The view is an adapter for traces that allows them to be used as a {@link Program}. However, * it only works for a chosen snapshot. Typically, {@link TraceProgramView#getSnap()} for this @@ -310,7 +305,6 @@ public interface FlatDebuggerAPI { /** * Get the current frame, 0 being the innermost - * *
* If the target doesn't support frames, this will return 0 * @@ -323,7 +317,6 @@ public interface FlatDebuggerAPI { /** * Get the current snap, i.e., snapshot key - * *
* Snaps are the trace's notion of time. Positive keys should be monotonic with respect to time: * a higher value implies a later point in time. Negative keys do not; they are used as scratch @@ -339,7 +332,6 @@ public interface FlatDebuggerAPI { /** * Get the current emulation schedule - * *
* This constitutes the current snapshot and an optional schedule of emulation steps. If there * is a schedule, then the view's snap will be the destination scratch snap rather than the @@ -353,7 +345,6 @@ public interface FlatDebuggerAPI { /** * Make the given trace the active trace - * *
* If the trace is not already open in the tool, it will be opened automatically * @@ -373,7 +364,6 @@ public interface FlatDebuggerAPI { /** * Make the given thread the active thread - * *
* if the trace is not already open in the tool, it will be opened automatically * @@ -403,7 +393,6 @@ public interface FlatDebuggerAPI { /** * Make the given snapshot the active snapshot - * *
* Activating negative snapshot keys is not recommended. The trace manager uses negative keys * for emulation scratch space and will activate them indirectly as needed. @@ -425,7 +414,6 @@ public interface FlatDebuggerAPI { /** * Get the current trace program view and address - * *
* This constitutes a portion of the debugger coordinates plus the current dynamic address. The * program given by {@link ProgramLocation#getProgram()} can be safely cast to @@ -449,7 +437,6 @@ public interface FlatDebuggerAPI { /** * Go to the given dynamic location in the dynamic listing - * *
* To "go to" a point in time, use {@link #activateSnap(long)} or * {@link #emulate(Trace, TraceSchedule, TaskMonitor)}. @@ -494,7 +481,6 @@ public interface FlatDebuggerAPI { /** * Get the current program - * *
* This is implemented by virtue of extending {@link FlatProgramAPI}, which is inherited via * {@link GhidraScript}. @@ -521,7 +507,6 @@ public interface FlatDebuggerAPI { /** * Translate the given static location to the corresponding dynamic location - * *
* This uses the trace's static mappings (see {@link Trace#getStaticMappingManager()} and * {@link DebuggerStaticMappingService}) to translate a static location to the corresponding @@ -542,7 +527,6 @@ public interface FlatDebuggerAPI { /** * Translate the given static address to the corresponding dynamic address - * *
* This does the same as {@link #translateStaticToDynamic(ProgramLocation)}, but assumes the * address is for the current program. The returned address is for the current trace view. @@ -558,7 +542,6 @@ public interface FlatDebuggerAPI { /** * Translate the given dynamic location to the corresponding static location - * *
* This does the opposite of {@link #translateStaticToDynamic(ProgramLocation)}. The resulting * static location could be for any open program, not just the current one, since a target may @@ -574,7 +557,6 @@ public interface FlatDebuggerAPI { /** * Translate the given dynamic address to the corresponding static address - * *
* This does the same as {@link #translateDynamicToStatic(ProgramLocation)}, but assumes the * address is for the current trace view. The returned address is for the current program. If @@ -603,7 +585,6 @@ public interface FlatDebuggerAPI { /** * Load the given program into a trace suitable for emulation in the UI, starting at the given * address - * *
* Note that the program bytes are not actually loaded into the trace. Rather a static mapping * is generated, allowing the emulator to load bytes from the target program lazily. The trace @@ -716,7 +697,6 @@ public interface FlatDebuggerAPI { /** * Step the current trace count skipped instructions via emulation - * *
* Note there's no such thing as "skipping in reverse." If a negative count is given, this will * behave the same as {@link #stepEmuInstruction(long, TaskMonitor)}. @@ -739,7 +719,6 @@ public interface FlatDebuggerAPI { /** * Step the current trace count skipped p-code operations via emulation - * *
* Note there's no such thing as "skipping in reverse." If a negative count is given, this will * behave the same as {@link #stepEmuPcodeOp(int, TaskMonitor)}. @@ -779,7 +758,6 @@ public interface FlatDebuggerAPI { /** * Create an address range, avoiding address overflow by truncating - * *
* If the length would cause address overflow, it is adjusted such that the range's maximum * address is the space's maximum address. @@ -823,7 +801,8 @@ public interface FlatDebuggerAPI { default void refreshMemoryIfLive(Trace trace, long snap, Address start, int length, TaskMonitor monitor) throws CancelledException { Target target = getTargetService().getTarget(trace); - if (target == null || target.getSnap() != snap) { + + if (!isLive(target, snap)) { return; } target.readMemory(new AddressSet(safeRange(start, length)), monitor); @@ -900,7 +879,6 @@ public interface FlatDebuggerAPI { /** * Search trace memory for a given masked byte sequence - * *
* NOTE: This searches the trace only. It will not interrogate the live target. There are * two mechanisms for searching a live target's full memory: 1) Capture the full memory (or the @@ -908,7 +886,6 @@ public interface FlatDebuggerAPI { * {@link #refreshMemoryIfLive(Trace, long, Address, int, TaskMonitor)} -- then search the * trace. 2) If possible, invoke the target debugger's search functions -- using, e.g., * {@link #executeCapture(String)}. - * *
* This delegates to * {@link TraceMemoryOperations#findBytes(long, AddressRange, ByteBuffer, ByteBuffer, boolean, TaskMonitor)}. @@ -918,7 +895,6 @@ public interface FlatDebuggerAPI { * {@code 00} is matched as usual, as is any stale byte. Only those ranges which have * never been recorded are culled. While not required, memory is conventionally read * and recorded in pages, so culling tends to occur at page boundaries. - * *
* Be wary of leading or trailing wildcards, i.e., masked-out bytes. The full data array must
* fit within the given range after culling. For example, suppose the byte {@code 12} is
@@ -965,6 +941,35 @@ public interface FlatDebuggerAPI {
mask == null ? null : ByteBuffer.wrap(mask), forward, monitor);
}
+ /**
+ * Check if the given snap could incorporate any "live" data of the target
+ *
+ * @param target the target
+ * @param snap the snapshot key
+ * @return true if live data retrieval is appropriate from the given snapshot's view
+ */
+ default boolean isLive(Target target, long snap) {
+ if (target == null || !target.isValid()) {
+ return false;
+ }
+
+ Trace trace = target.getTrace();
+ TraceSnapshot snapshot = trace.getTimeManager().getSnapshot(snap, false);
+ if (Objects.equals(target.getTime(), snapshot.getSchedule())) {
+ return true;
+ }
+
+ // LATER: This may be expensive....
+ TraceTimeViewport viewport = trace.createTimeViewport();
+ viewport.setSnap(snap);
+ for (long s : viewport.getReversedSnaps()) {
+ if (target.getSnap() == s) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Copy registers from target to trace, if applicable and not already cached
*
@@ -976,10 +981,9 @@ public interface FlatDebuggerAPI {
*/
default void refreshRegistersIfLive(TracePlatform platform, TraceThread thread, int frame,
long snap, Collection
* The success or failure of this method depends on a few factors. First is the user-selected
* control mode for the trace. See {@link #setControlMode(ControlMode)}. In read-only mode, this
@@ -1319,7 +1324,6 @@ public interface FlatDebuggerAPI {
/**
* Patch memory of the given target, according to its current control mode
- *
*
* If you intend to apply several patches, consider using {@link #createStateEditor(Trace,long)}
* and {@link #writeMemory(StateEditor, Address, byte[])}
@@ -1336,7 +1340,6 @@ public interface FlatDebuggerAPI {
/**
* Patch memory of the current target, according to the current control mode
- *
*
* If you intend to apply several patches, consider using {@link #createStateEditor()} and
* {@link #writeMemory(StateEditor, Address, byte[])}
@@ -1351,7 +1354,6 @@ public interface FlatDebuggerAPI {
/**
* Patch a register using the given editor
- *
*
* The success or failure of this methods depends on a few factors. First is the user-selected
* control mode for the trace. See {@link #setControlMode(ControlMode)}. In read-only mode, this
@@ -1382,7 +1384,6 @@ public interface FlatDebuggerAPI {
/**
* Patch a register of the given context, according to its current control mode
- *
*
* If you intend to apply several patches, consider using
* {@link #createStateEditor(TraceThread,int,long)} and
@@ -1418,7 +1419,6 @@ public interface FlatDebuggerAPI {
/**
* Patch a register of the current thread, according to the current control mode
- *
*
* If you intend to apply several patches, consider using {@link #createStateEditor()} and
* {@link #writeRegister(StateEditor, RegisterValue)}.
@@ -1571,7 +1571,6 @@ public interface FlatDebuggerAPI {
/**
* Resume execution of the live target for the given trace thread
- *
*
* This is commonly called "continue" or "go," as well.
*
@@ -1584,7 +1583,6 @@ public interface FlatDebuggerAPI {
/**
* Resume execution of the live target for the given trace
- *
*
* This is commonly called "continue" or "go," as well.
*
@@ -1609,7 +1607,6 @@ public interface FlatDebuggerAPI {
/**
* Interrupt execution of the live target for the given trace thread
- *
*
* This is commonly called "pause" or "break," as well, but not "stop."
*
@@ -1622,7 +1619,6 @@ public interface FlatDebuggerAPI {
/**
* Interrupt execution of the live target for the given trace
- *
*
* This is commonly called "pause" or "break," as well, but not "stop."
*
@@ -1647,7 +1643,6 @@ public interface FlatDebuggerAPI {
/**
* Terminate execution of the live target for the given trace thread
- *
*
* This is commonly called "stop" as well.
*
@@ -1660,7 +1655,6 @@ public interface FlatDebuggerAPI {
/**
* Terminate execution of the live target for the given trace
- *
*
* This is commonly called "stop" as well.
*
@@ -1685,7 +1679,6 @@ public interface FlatDebuggerAPI {
/**
* Get the current state of the given trace
- *
*
* If the trace does not have a live target, it is considered
* {@link TraceExecutionState#TERMINATED} (even if the trace never technically had a
@@ -1711,7 +1704,6 @@ public interface FlatDebuggerAPI {
/**
* Get the current state of the given thread
- *
*
* If the thread does not have a corresponding live target thread, it is considered
* {@link TraceExecutionState#TERMINATED} (even if the thread never technically had a
@@ -1744,7 +1736,6 @@ public interface FlatDebuggerAPI {
/**
* Check if the current target is alive
- *
*
* NOTE: To be "current," the target must be recorded, and its trace must be the current
* trace.
@@ -1767,7 +1758,6 @@ public interface FlatDebuggerAPI {
/**
* Check if the current target thread is alive
- *
*
* NOTE: To be the "current" target thread, the target must be recorded, and its trace
* thread must be the current thread.
@@ -1780,7 +1770,6 @@ public interface FlatDebuggerAPI {
/**
* Wait for the trace's target to break
- *
*
* If the trace has no target, this method returns immediately, i.e., it assumes the target has
* terminated.
@@ -2028,7 +2017,6 @@ public interface FlatDebuggerAPI {
/**
* Get all the breakpoints
- *
*
* This returns all logical breakpoints among all open programs and traces (targets)
*
@@ -2104,7 +2092,6 @@ public interface FlatDebuggerAPI {
/**
* Perform some operations expected to cause changes, and then wait for those changes to settle
- *
*
* Use this via a try-with-resources block containing the operations causing changes.
*
@@ -2123,7 +2110,7 @@ public interface FlatDebuggerAPI {
*/
default Set
* NOTE: Many asynchronous events take place when creating a breakpoint, esp., among
* several live targets. Furthermore, some targets may adjust the breakpoint specification just
@@ -2153,7 +2139,7 @@ public interface FlatDebuggerAPI {
default Set
* This might also be called a "read watchpoint" or a "read access breakpoint."
*
@@ -2210,7 +2195,6 @@ public interface FlatDebuggerAPI {
/**
* Set a write breakpoint at the given location
- *
*
* This might also be called a "write watchpoint" or a "write access breakpoint."
*
@@ -2227,7 +2211,6 @@ public interface FlatDebuggerAPI {
/**
* Set an access breakpoint at the given location
- *
*
* This might also be called a "watchpoint."
*
@@ -2265,7 +2248,7 @@ public interface FlatDebuggerAPI {
default Set
* This method includes as many components as its author knows to flush. It flushes the trace's
* event queue. Then, it waits for various services' changes to settle, in dependency order.
@@ -2320,7 +2302,6 @@ public interface FlatDebuggerAPI {
* Note that some stages use timeouts. It's also possible the target had not generated all the
* expected events by the time this method began flushing its queue. Thus, callers should still
* check that some expected condition is met and possibly repeat the flush before proceeding.
- *
*
* There are additional dependents in the GUI; however, scripts should not depend on them, so we
* do not wait on them.
diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test/java/ghidra/debug/flatapi/AbstractFlatDebuggerAPITest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test/java/ghidra/debug/flatapi/AbstractFlatDebuggerAPITest.java
index ee97b43919..4456c23c01 100644
--- a/Ghidra/Test/DebuggerIntegrationTest/src/test/java/ghidra/debug/flatapi/AbstractFlatDebuggerAPITest.java
+++ b/Ghidra/Test/DebuggerIntegrationTest/src/test/java/ghidra/debug/flatapi/AbstractFlatDebuggerAPITest.java
@@ -95,7 +95,7 @@ public abstract class AbstractFlatDebuggerAPITest