diff --git a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/emulation/PcodeDebuggerAccess.java b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/emulation/PcodeDebuggerAccess.java
index 7936ffcdf7..fb865c4fc8 100644
--- a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/emulation/PcodeDebuggerAccess.java
+++ b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/emulation/PcodeDebuggerAccess.java
@@ -24,7 +24,7 @@ import ghidra.trace.model.thread.TraceThread;
*
*
* In addition to the trace "coordinates" encapsulated by {@link PcodeTraceAccess}, this
- * encapsulates the tool controlling a session and the session's trace recorder. This permits p-code
+ * encapsulates the tool controlling a session and the session's target. This permits p-code
* executor/emulator states to access target data and to access session data, e.g., data from mapped
* static images. It supports the same method chain pattern as {@link PcodeTraceAccess}, but
* starting with {@link DefaultPcodeDebuggerAccess}.
diff --git a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/tracemgr/DebuggerCoordinates.java b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/tracemgr/DebuggerCoordinates.java
index d5785d009d..76b2e8bef2 100644
--- a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/tracemgr/DebuggerCoordinates.java
+++ b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/tracemgr/DebuggerCoordinates.java
@@ -310,8 +310,14 @@ public class DebuggerCoordinates {
if (frameLevel == null) {
return objThread.getCanonicalPath();
}
- TraceStack stack =
- thread.getTrace().getStackManager().getStack(thread, time.getSnap(), false);
+ TraceStack stack;
+ try {
+ stack = thread.getTrace().getStackManager().getStack(thread, time.getSnap(), false);
+ }
+ catch (IllegalStateException e) {
+ // Schema does not specify a stack
+ return objThread.getCanonicalPath();
+ }
if (stack == null) {
return objThread.getCanonicalPath();
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/DisassemblyInject.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/DisassemblyInject.java
index b58f3e9ad3..c0b40f3904 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/DisassemblyInject.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/DisassemblyInject.java
@@ -15,11 +15,11 @@
*/
package ghidra.app.plugin.core.debug.disassemble;
-import ghidra.app.plugin.core.debug.disassemble.DisassemblyInjectInfo.CompilerInfo;
+import ghidra.app.plugin.core.debug.disassemble.DisassemblyInjectInfo.PlatformInfo;
+import ghidra.debug.api.platform.DebuggerPlatformMapper;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.AddressSetView;
-import ghidra.program.model.lang.Language;
-import ghidra.trace.model.Trace;
+import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.thread.TraceThread;
import ghidra.util.Msg;
import ghidra.util.classfinder.ExtensionPoint;
@@ -38,7 +38,7 @@ import ghidra.util.classfinder.ExtensionPoint;
* configure the disassembler (namely seeding its context), the one invoked last will have "the last
* word." As such, each inject should avoid unnecessarily erasing existing context.
*/
-@DisassemblyInjectInfo(compilers = {}) // Use as default
+@DisassemblyInjectInfo(platforms = {}) // Use as default
public interface DisassemblyInject extends ExtensionPoint {
/**
* If present, get the information annotation on this inject
@@ -56,16 +56,16 @@ public interface DisassemblyInject extends ExtensionPoint {
}
/**
- * Check if this inject applies to the given trace
+ * Check if this inject applies to the given trace platform
*
- * @param trace the trace to check
+ * @param platform the platform to check
* @return true if applicable, false otherwise
*/
- default boolean isApplicable(Trace trace) {
- for (CompilerInfo info : getInfo().compilers()) {
- if (info.langID().equals(trace.getBaseLanguage().getLanguageID().toString())) {
+ default boolean isApplicable(TracePlatform platform) {
+ for (PlatformInfo info : getInfo().platforms()) {
+ if (info.langID().equals(platform.getLanguage().getLanguageID().toString())) {
if (info.compilerID().isBlank() || info.compilerID()
- .equals(trace.getBaseCompilerSpec().getCompilerSpecID().toString())) {
+ .equals(platform.getCompilerSpec().getCompilerSpecID().toString())) {
return true;
}
}
@@ -86,36 +86,35 @@ public interface DisassemblyInject extends ExtensionPoint {
* A pre-auto disassembly hook
*
*
- * This hook is invoked by the {@link DisassembleAtPcDebuggerBot} before disassembly actually
+ * This hook is invoked by the {@link DebuggerPlatformMapper} before disassembly actually
* begins. The callback occurs within the command's background thread. In general, the inject
* should limit its operation to inspecting the trace database and configuring the command.
*
* @param tool the tool that will execute the command
* @param command the command to be configured, which is about to execute
- * @param trace the trace whose bytes to disassemble
- * @param language the language for the disassembler
+ * @param platform the trace platform for the disassembler
* @param snap the snap the snap at which to disassemble
* @param thread the thread whose PC is being disassembled
* @param startSet the starting address set, usually just the PC
* @param restricted the set of disassemblable addresses
*/
- default void pre(PluginTool tool, TraceDisassembleCommand command, Trace trace,
- Language language, long snap, TraceThread thread, AddressSetView startSet,
- AddressSetView restricted) {
+ default void pre(PluginTool tool, TraceDisassembleCommand command, TracePlatform platform,
+ long snap, TraceThread thread, AddressSetView startSet, AddressSetView restricted) {
}
/**
* A post-auto disassembly hook
*
*
- * This hook is invoked by the {@link DisassembleAtPcDebuggerBot} after disassembly completes.
- * The callback occurs within the command's background thread.
+ * This hook is invoked by the {@link DebuggerPlatformMapper} after disassembly completes. The
+ * callback occurs within the command's background thread.
*
* @param tool the tool that just executed the disassembly command
- * @param trace the trace whose bytes were disassembled
+ * @param platform the trace platform for the disassembler
* @param snap the snap the snap at which disassembly was performed
* @param disassembled the addresses that were actually disassembled
*/
- default void post(PluginTool tool, Trace trace, long snap, AddressSetView disassembled) {
+ default void post(PluginTool tool, TracePlatform platform, long snap,
+ AddressSetView disassembled) {
}
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/DisassemblyInjectInfo.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/DisassemblyInjectInfo.java
index 1724219ecf..1b4cc32fb3 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/DisassemblyInjectInfo.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/DisassemblyInjectInfo.java
@@ -17,6 +17,8 @@ package ghidra.app.plugin.core.debug.disassemble;
import java.lang.annotation.*;
+import ghidra.trace.model.guest.TracePlatform;
+
/**
* Information about the applicability of a disassembly inject
*/
@@ -24,9 +26,9 @@ import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
public @interface DisassemblyInjectInfo {
/**
- * A language-compiler-ID pair
+ * A language-compiler-ID pair identifying a trace platform
*/
- public @interface CompilerInfo {
+ public @interface PlatformInfo {
/**
* The language ID, e.g., "x86:64:LE:default"
*
@@ -46,12 +48,12 @@ public @interface DisassemblyInjectInfo {
}
/**
- * A list of language-compiler-ID pairs for which this inject applies
+ * A list of platforms for which this inject applies
*
- * @see DisassemblyInject#isApplicable(ghidra.trace.model.Trace)
- * @return the language-compiler-ID pairs
+ * @see DisassemblyInject#isApplicable(TracePlatform)
+ * @return the platforms
*/
- CompilerInfo[] compilers();
+ PlatformInfo[] platforms();
/**
* The "position" of this inject's invocation
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/LoadEmulatorAutoReadMemorySpec.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/LoadEmulatorAutoReadMemorySpec.java
index 891d373b47..e230e09747 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/LoadEmulatorAutoReadMemorySpec.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/LoadEmulatorAutoReadMemorySpec.java
@@ -38,6 +38,11 @@ import ghidra.trace.model.memory.TraceMemoryState;
public class LoadEmulatorAutoReadMemorySpec implements AutoReadMemorySpec {
public static final String CONFIG_NAME = "2_LOAD_EMULATOR";
+ @Override
+ public boolean equals(Object obj) {
+ return this.getClass() == obj.getClass();
+ }
+
@Override
public String getConfigName() {
return CONFIG_NAME;
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/NoneAutoReadMemorySpec.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/NoneAutoReadMemorySpec.java
index d603e40ae5..3a20cee074 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/NoneAutoReadMemorySpec.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/NoneAutoReadMemorySpec.java
@@ -27,6 +27,11 @@ import ghidra.program.model.address.AddressSetView;
public class NoneAutoReadMemorySpec implements AutoReadMemorySpec {
public static final String CONFIG_NAME = "0_READ_NONE";
+ @Override
+ public boolean equals(Object obj) {
+ return this.getClass() == obj.getClass();
+ }
+
@Override
public String getConfigName() {
return CONFIG_NAME;
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/VisibleAutoReadMemorySpec.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/VisibleAutoReadMemorySpec.java
index 3cde8958e3..0c5b29f110 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/VisibleAutoReadMemorySpec.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/VisibleAutoReadMemorySpec.java
@@ -32,6 +32,11 @@ import ghidra.util.task.TaskMonitor;
public class VisibleAutoReadMemorySpec implements AutoReadMemorySpec {
public static final String CONFIG_NAME = "1_READ_VISIBLE";
+ @Override
+ public boolean equals(Object obj) {
+ return this.getClass() == obj.getClass();
+ }
+
@Override
public String getConfigName() {
return CONFIG_NAME;
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/VisibleROOnceAutoReadMemorySpec.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/VisibleROOnceAutoReadMemorySpec.java
index 922fccd948..dd481f886c 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/VisibleROOnceAutoReadMemorySpec.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/VisibleROOnceAutoReadMemorySpec.java
@@ -33,6 +33,11 @@ import ghidra.util.task.TaskMonitor;
public class VisibleROOnceAutoReadMemorySpec implements AutoReadMemorySpec {
public static final String CONFIG_NAME = "1_READ_VIS_RO_ONCE";
+ @Override
+ public boolean equals(Object obj) {
+ return this.getClass() == obj.getClass();
+ }
+
@Override
public String getConfigName() {
return CONFIG_NAME;
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformMapper.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformMapper.java
index 0eb12f1736..b6874585ee 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformMapper.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/AbstractDebuggerPlatformMapper.java
@@ -16,7 +16,8 @@
package ghidra.app.plugin.core.debug.mapping;
import java.util.Collection;
-import java.util.Set;
+import java.util.Comparator;
+import java.util.stream.Collectors;
import ghidra.app.plugin.core.debug.disassemble.DisassemblyInject;
import ghidra.app.plugin.core.debug.disassemble.TraceDisassembleCommand;
@@ -25,11 +26,11 @@ import ghidra.debug.api.platform.DisassemblyResult;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.Endian;
-import ghidra.program.model.lang.Language;
import ghidra.trace.model.Trace;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.thread.TraceThread;
+import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.task.TaskMonitor;
public abstract class AbstractDebuggerPlatformMapper implements DebuggerPlatformMapper {
@@ -63,8 +64,12 @@ public abstract class AbstractDebuggerPlatformMapper implements DebuggerPlatform
return trace.getCodeManager().instructions().getAt(snap, start) != null;
}
- protected Collection getDisassemblyInjections(TraceObject object) {
- return Set.of();
+ protected Collection getDisassemblyInjections(TracePlatform platform) {
+ return ClassSearcher.getInstances(DisassemblyInject.class)
+ .stream()
+ .filter(i -> i.isApplicable(platform))
+ .sorted(Comparator.comparing(i -> i.getPriority()))
+ .collect(Collectors.toList());
}
@Override
@@ -75,12 +80,11 @@ public abstract class AbstractDebuggerPlatformMapper implements DebuggerPlatform
}
TracePlatform platform = trace.getPlatformManager().getPlatform(getCompilerSpec(object));
- Collection injects = getDisassemblyInjections(object);
+ Collection injects = getDisassemblyInjections(platform);
TraceDisassembleCommand dis = new TraceDisassembleCommand(platform, start, restricted);
- Language language = platform.getLanguage();
AddressSet startSet = new AddressSet(start);
for (DisassemblyInject i : injects) {
- i.pre(tool, dis, trace, language, snap, thread, startSet, restricted);
+ i.pre(tool, dis, platform, snap, thread, startSet, restricted);
}
boolean result = dis.applyToTyped(trace.getFixedProgramView(snap), monitor);
if (!result) {
@@ -88,7 +92,7 @@ public abstract class AbstractDebuggerPlatformMapper implements DebuggerPlatform
}
AddressSetView actualSet = dis.getDisassembledAddressSet();
for (DisassemblyInject i : injects) {
- i.post(tool, trace, snap, actualSet);
+ i.post(tool, platform, snap, actualSet);
}
return DisassemblyResult.success(actualSet != null && !actualSet.isEmpty());
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/legacy/LegacyDebuggerPlatformOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/HostDebuggerPlatformOpinion.java
similarity index 55%
rename from Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/legacy/LegacyDebuggerPlatformOpinion.java
rename to Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/HostDebuggerPlatformOpinion.java
index 88d187007c..3c5802b4d7 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/legacy/LegacyDebuggerPlatformOpinion.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/HostDebuggerPlatformOpinion.java
@@ -13,30 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package ghidra.app.plugin.core.debug.mapping.legacy;
+package ghidra.app.plugin.core.debug.mapping;
-import java.util.*;
-import java.util.stream.Collectors;
+import java.util.Set;
-import ghidra.app.plugin.core.debug.disassemble.DisassemblyInject;
-import ghidra.app.plugin.core.debug.mapping.*;
import ghidra.debug.api.platform.DebuggerPlatformMapper;
import ghidra.framework.plugintool.PluginTool;
-import ghidra.lifecycle.Transitional;
import ghidra.program.model.lang.CompilerSpec;
import ghidra.trace.model.Trace;
import ghidra.trace.model.target.TraceObject;
-import ghidra.util.classfinder.ClassSearcher;
/**
- * An opinion which retains the front-end functionality when using the mapped recorder, i.e., when
- * displaying non-object-based traces.
+ * An opinion which just uses the trace's "host" platform, i.e., because the target created the
+ * trace with the correct host language. Other mappers assume the trace language is DATA, and that
+ * the real language must be mapped as a guest platform.
*/
-@Transitional
-public class LegacyDebuggerPlatformOpinion implements DebuggerPlatformOpinion {
+public class HostDebuggerPlatformOpinion implements DebuggerPlatformOpinion {
- protected static class LegacyDebuggerPlatformMapper extends AbstractDebuggerPlatformMapper {
- public LegacyDebuggerPlatformMapper(PluginTool tool, Trace trace) {
+ protected static class HostDebuggerPlatformMapper extends AbstractDebuggerPlatformMapper {
+ public HostDebuggerPlatformMapper(PluginTool tool, Trace trace) {
super(tool, trace);
}
@@ -54,23 +49,13 @@ public class LegacyDebuggerPlatformOpinion implements DebuggerPlatformOpinion {
public boolean canInterpret(TraceObject newFocus, long snap) {
return true;
}
-
- @Override
- protected Collection getDisassemblyInjections(TraceObject object) {
- // Track an injects set using a listener instead?
- return ClassSearcher.getInstances(DisassemblyInject.class)
- .stream()
- .filter(i -> i.isApplicable(trace))
- .sorted(Comparator.comparing(i -> i.getPriority()))
- .collect(Collectors.toList());
- }
}
enum Offers implements DebuggerPlatformOffer {
- LEGACY {
+ HOST {
@Override
public String getDescription() {
- return "Legacy (Already mapped by recorder)";
+ return "Host/base (Language already chosen by target)";
}
@Override
@@ -85,12 +70,12 @@ public class LegacyDebuggerPlatformOpinion implements DebuggerPlatformOpinion {
@Override
public DebuggerPlatformMapper take(PluginTool tool, Trace trace) {
- return new LegacyDebuggerPlatformMapper(tool, trace);
+ return new HostDebuggerPlatformMapper(tool, trace);
}
@Override
public boolean isCreatorOf(DebuggerPlatformMapper mapper) {
- return mapper.getClass() == LegacyDebuggerPlatformMapper.class;
+ return mapper.getClass() == HostDebuggerPlatformMapper.class;
}
};
}
@@ -98,9 +83,6 @@ public class LegacyDebuggerPlatformOpinion implements DebuggerPlatformOpinion {
@Override
public Set getOffers(Trace trace, TraceObject focus, long snap,
boolean includeOverrides) {
- if (trace.getObjectManager().getRootObject() != null) {
- return Set.of();
- }
- return Set.of(Offers.LEGACY);
+ return Set.of(Offers.HOST);
}
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/arm/ArmDisassemblyInject.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/arm/ArmDisassemblyInject.java
index efed76639c..6321e9a2d8 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/arm/ArmDisassemblyInject.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/arm/ArmDisassemblyInject.java
@@ -18,38 +18,38 @@ package ghidra.app.plugin.core.debug.platform.arm;
import java.math.BigInteger;
import ghidra.app.plugin.core.debug.disassemble.*;
-import ghidra.app.plugin.core.debug.disassemble.DisassemblyInjectInfo.CompilerInfo;
+import ghidra.app.plugin.core.debug.disassemble.DisassemblyInjectInfo.PlatformInfo;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.lang.*;
-import ghidra.trace.model.Trace;
+import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.memory.TraceMemorySpace;
import ghidra.trace.model.memory.TraceMemoryState;
import ghidra.trace.model.thread.TraceThread;
import ghidra.util.Msg;
@DisassemblyInjectInfo(
- compilers = {
- @CompilerInfo(langID = "ARM:LE:32:v8"),
- @CompilerInfo(langID = "ARM:LE:32:v8T"),
- @CompilerInfo(langID = "ARM:LEBE:32:v8LEInstruction"),
- @CompilerInfo(langID = "ARM:BE:32:v8"),
- @CompilerInfo(langID = "ARM:BE:32:v8T"),
- @CompilerInfo(langID = "ARM:LE:32:v7"),
- @CompilerInfo(langID = "ARM:LEBE:32:v7LEInstruction"),
- @CompilerInfo(langID = "ARM:BE:32:v7"),
- @CompilerInfo(langID = "ARM:LE:32:Cortex"),
- @CompilerInfo(langID = "ARM:BE:32:Cortex"),
- @CompilerInfo(langID = "ARM:LE:32:v6"),
- @CompilerInfo(langID = "ARM:BE:32:v6"),
- @CompilerInfo(langID = "ARM:LE:32:v5t"),
- @CompilerInfo(langID = "ARM:BE:32:v5t"),
- @CompilerInfo(langID = "ARM:LE:32:v5"),
- @CompilerInfo(langID = "ARM:BE:32:v5"),
- @CompilerInfo(langID = "ARM:LE:32:v4t"),
- @CompilerInfo(langID = "ARM:BE:32:v4t"),
- @CompilerInfo(langID = "ARM:LE:32:v4"),
- @CompilerInfo(langID = "ARM:BE:32:v4"),
+ platforms = {
+ @PlatformInfo(langID = "ARM:LE:32:v8"),
+ @PlatformInfo(langID = "ARM:LE:32:v8T"),
+ @PlatformInfo(langID = "ARM:LEBE:32:v8LEInstruction"),
+ @PlatformInfo(langID = "ARM:BE:32:v8"),
+ @PlatformInfo(langID = "ARM:BE:32:v8T"),
+ @PlatformInfo(langID = "ARM:LE:32:v7"),
+ @PlatformInfo(langID = "ARM:LEBE:32:v7LEInstruction"),
+ @PlatformInfo(langID = "ARM:BE:32:v7"),
+ @PlatformInfo(langID = "ARM:LE:32:Cortex"),
+ @PlatformInfo(langID = "ARM:BE:32:Cortex"),
+ @PlatformInfo(langID = "ARM:LE:32:v6"),
+ @PlatformInfo(langID = "ARM:BE:32:v6"),
+ @PlatformInfo(langID = "ARM:LE:32:v5t"),
+ @PlatformInfo(langID = "ARM:BE:32:v5t"),
+ @PlatformInfo(langID = "ARM:LE:32:v5"),
+ @PlatformInfo(langID = "ARM:BE:32:v5"),
+ @PlatformInfo(langID = "ARM:LE:32:v4t"),
+ @PlatformInfo(langID = "ARM:BE:32:v4t"),
+ @PlatformInfo(langID = "ARM:LE:32:v4"),
+ @PlatformInfo(langID = "ARM:BE:32:v4"),
})
public class ArmDisassemblyInject implements DisassemblyInject {
protected static final long THUMB_BIT = 0x20;
@@ -59,15 +59,15 @@ public class ArmDisassemblyInject implements DisassemblyInject {
}
@Override
- public void pre(PluginTool tool, TraceDisassembleCommand command, Trace trace,
- Language language, long snap, TraceThread thread, AddressSetView startSet,
- AddressSetView restricted) {
+ public void pre(PluginTool tool, TraceDisassembleCommand command, TracePlatform platform,
+ long snap, TraceThread thread, AddressSetView startSet, AddressSetView restricted) {
/**
* TODO: There are probably several avenues to figure the TMode. The most important, I think
- * is the cpsr register, when it's available. For auto-pc, the trace recorder ought to have
- * recorded cpsr at the recorded tick.
+ * is the cpsr register, when it's available. For auto-pc, the target ought to have recorded
+ * cpsr at the current snapshot.
*/
+ Language language = platform.getLanguage();
Register cpsrReg = language.getRegister("cpsr");
Register tModeReg = language.getRegister("TMode");
@@ -77,24 +77,22 @@ public class ArmDisassemblyInject implements DisassemblyInject {
return;
}
- TraceMemorySpace regs = trace.getMemoryManager().getMemoryRegisterSpace(thread, false);
+ TraceMemorySpace regs =
+ platform.getTrace().getMemoryManager().getMemoryRegisterSpace(thread, false);
/**
* Some variants (particularly Cortex-M) are missing cpsr. This seems to indicate it only
* supports THUMB. There is an epsr (xpsr in gdb), but we don't have it in our models, and
* its TMode bit must be set, or it will fault.
+ *
+ * TODO: If registers are recorded as generic objects, then we can find epsr/xpsr whether or
+ * not its in the Sleigh model.
*/
- if (regs == null || regs.getState(snap, cpsrReg) != TraceMemoryState.KNOWN) {
+ if (regs == null || regs.getState(platform, snap, cpsrReg) != TraceMemoryState.KNOWN) {
command.setInitialContext(new RegisterValue(tModeReg, BigInteger.ONE));
return;
}
- /**
- * TODO: Once we have register mapping figured out for object-based traces, we need to have
- * this check the cpsr register there, instead. Better yet, regarding epsr and xpsr, we can
- * actually check them, even though they don't exist in the slaspec, because we have access
- * to the raw recorded register objects.
- */
- RegisterValue cpsrVal = regs.getValue(snap, cpsrReg);
+ RegisterValue cpsrVal = regs.getValue(platform, snap, cpsrReg);
if (isThumbMode(cpsrVal)) {
command.setInitialContext(new RegisterValue(tModeReg, BigInteger.ONE));
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/dbgeng/DbgengDebuggerPlatformOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/dbgeng/DbgengDebuggerPlatformOpinion.java
index b9c3d56f02..e56248a251 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/dbgeng/DbgengDebuggerPlatformOpinion.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/dbgeng/DbgengDebuggerPlatformOpinion.java
@@ -24,6 +24,7 @@ import ghidra.debug.api.platform.DebuggerPlatformMapper;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.lang.*;
import ghidra.trace.model.Trace;
+import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.target.TraceObject;
public class DbgengDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion {
@@ -39,7 +40,7 @@ public class DbgengDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpini
// TODO: Map registers: efl,rfl,rflags->eflags
@Override
- protected Collection getDisassemblyInjections(TraceObject object) {
+ protected Collection getDisassemblyInjections(TracePlatform platform) {
return INJECTS;
}
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/dbgeng/DbgengX64DisassemblyInject.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/dbgeng/DbgengX64DisassemblyInject.java
index 3c6b3960c6..6e9d4d774f 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/dbgeng/DbgengX64DisassemblyInject.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/dbgeng/DbgengX64DisassemblyInject.java
@@ -22,7 +22,7 @@ import java.util.Set;
import java.util.stream.Collectors;
import ghidra.app.plugin.core.debug.disassemble.*;
-import ghidra.app.plugin.core.debug.disassemble.DisassemblyInjectInfo.CompilerInfo;
+import ghidra.app.plugin.core.debug.disassemble.DisassemblyInjectInfo.PlatformInfo;
import ghidra.app.services.DebuggerTargetService;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.MemBufferByteProvider;
@@ -35,6 +35,7 @@ import ghidra.program.model.lang.*;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.util.ProgramContextImpl;
import ghidra.trace.model.Trace;
+import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.modules.TraceModule;
import ghidra.trace.model.thread.TraceThread;
import ghidra.util.Msg;
@@ -42,9 +43,9 @@ import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@DisassemblyInjectInfo(
- compilers = {
- @CompilerInfo(langID = "x86:LE:64:default", compilerID = "windows"),
- @CompilerInfo(langID = "x86:LE:64:default", compilerID = "clangwindows"),
+ platforms = {
+ @PlatformInfo(langID = "x86:LE:64:default", compilerID = "windows"),
+ @PlatformInfo(langID = "x86:LE:64:default", compilerID = "clangwindows"),
})
public class DbgengX64DisassemblyInject implements DisassemblyInject {
@@ -53,13 +54,13 @@ public class DbgengX64DisassemblyInject implements DisassemblyInject {
}
@Override
- public void pre(PluginTool tool, TraceDisassembleCommand command, Trace trace,
- Language language, long snap, TraceThread thread, AddressSetView startSet,
- AddressSetView restricted) {
+ public void pre(PluginTool tool, TraceDisassembleCommand command, TracePlatform platform,
+ long snap, TraceThread thread, AddressSetView startSet, AddressSetView restricted) {
AddressRange first = startSet.getFirstRange();
if (first == null) {
return;
}
+ Trace trace = platform.getTrace();
DebuggerTargetService targetService = tool.getService(DebuggerTargetService.class);
Target target = targetService == null ? null : targetService.getTarget(trace);
Collection extends TraceModule> modules =
@@ -75,6 +76,8 @@ public class DbgengX64DisassemblyInject implements DisassemblyInject {
return;
}
Mode mode = modes.iterator().next();
+
+ Language language = platform.getLanguage();
Register longModeReg = language.getRegister("longMode");
Register addrsizeReg = language.getRegister("addrsize");
Register opsizeReg = language.getRegister("opsize");
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/ProgramEmulationUtils.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/ProgramEmulationUtils.java
index 9f9c9f4e01..0fad550f77 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/ProgramEmulationUtils.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/ProgramEmulationUtils.java
@@ -21,12 +21,15 @@ import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import org.jdom.JDOMException;
+
import db.Transaction;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils.Extrema;
import ghidra.app.services.DebuggerEmulationService;
import ghidra.dbg.target.*;
-import ghidra.dbg.target.schema.TargetObjectSchema;
+import ghidra.dbg.target.schema.*;
+import ghidra.dbg.target.schema.TargetObjectSchema.SchemaName;
import ghidra.dbg.util.*;
import ghidra.framework.model.DomainFile;
import ghidra.program.model.address.*;
@@ -57,6 +60,70 @@ public class ProgramEmulationUtils {
private ProgramEmulationUtils() {
}
+ public static final String EMU_CTX_XML = """
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ """;
+ public static final SchemaContext EMU_CTX;
+ public static final TargetObjectSchema EMU_SESSION_SCHEMA;
+ static {
+ try {
+ EMU_CTX = XmlSchemaContext.deserialize(EMU_CTX_XML);
+ }
+ catch (JDOMException e) {
+ throw new AssertionError(e);
+ }
+ EMU_SESSION_SCHEMA = EMU_CTX.getSchema(new SchemaName("EmuSession"));
+ }
+
public static final String BLOCK_NAME_STACK = "STACK";
/**
@@ -193,11 +260,8 @@ public class ProgramEmulationUtils {
// N.B. Bytes will be loaded lazily
}
- public static PathPattern computePattern(Trace trace, Class extends TargetObject> iface) {
- TargetObjectSchema root = trace.getObjectManager().getRootSchema();
- if (root == null) {
- return new PathPattern(PathUtils.parse("Memory[]"));
- }
+ public static PathPattern computePattern(TargetObjectSchema root, Trace trace,
+ Class extends TargetObject> iface) {
PathMatcher matcher = root.searchFor(iface, true);
PathPattern pattern = matcher.getSingletonPattern();
if (pattern == null || pattern.countWildcards() != 1) {
@@ -208,11 +272,19 @@ public class ProgramEmulationUtils {
}
public static PathPattern computePatternRegion(Trace trace) {
- return computePattern(trace, TargetMemoryRegion.class);
+ TargetObjectSchema root = trace.getObjectManager().getRootSchema();
+ if (root == null) {
+ return new PathPattern(PathUtils.parse("Memory[]"));
+ }
+ return computePattern(root, trace, TargetMemoryRegion.class);
}
public static PathPattern computePatternThread(Trace trace) {
- return computePattern(trace, TargetThread.class);
+ TargetObjectSchema root = trace.getObjectManager().getRootSchema();
+ if (root == null) {
+ return new PathPattern(PathUtils.parse("Threads[]"));
+ }
+ return computePattern(root, trace, TargetThread.class);
}
/**
@@ -429,6 +501,7 @@ public class ProgramEmulationUtils {
try {
trace = new DBTrace(getTraceName(program), program.getCompilerSpec(), consumer);
try (Transaction tx = trace.openTransaction("Emulate")) {
+ trace.getObjectManager().createRootObject(EMU_SESSION_SCHEMA);
TraceSnapshot initial =
trace.getTimeManager().createSnapshot(EMULATION_STARTED_AT + pc);
long snap = initial.getKey();
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/StackUnwinder.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/StackUnwinder.java
index 72b164ca50..b5065a5f3a 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/StackUnwinder.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/StackUnwinder.java
@@ -178,13 +178,19 @@ public class StackUnwinder {
Address pcVal = null;
TraceThread thread = coordinates.getThread();
long viewSnap = coordinates.getViewSnap();
- TraceStack stack = trace.getStackManager().getLatestStack(thread, viewSnap);
- if (stack != null) {
- TraceStackFrame frame = stack.getFrame(level, false);
- if (frame != null) {
- pcVal = frame.getProgramCounter(viewSnap);
+ try {
+ TraceStack stack = trace.getStackManager().getLatestStack(thread, viewSnap);
+ if (stack != null) {
+ TraceStackFrame frame = stack.getFrame(level, false);
+ if (frame != null) {
+ pcVal = frame.getProgramCounter(viewSnap);
+ }
}
}
+ catch (IllegalStateException e) {
+ // Schema does not specify a stack
+ // leave pcVal = null, so we'll get it from registers
+ }
TraceMemorySpace regs = Objects.requireNonNull(
trace.getMemoryManager().getMemoryRegisterSpace(thread, level, false),
"Frame must have a register bank");
diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/disassemble/DebuggerDisassemblyTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/disassemble/DebuggerDisassemblyTest.java
index 54dcabbd05..84c4f3be3b 100644
--- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/disassemble/DebuggerDisassemblyTest.java
+++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/disassemble/DebuggerDisassemblyTest.java
@@ -17,6 +17,7 @@ package ghidra.app.plugin.core.debug.disassemble;
import static org.junit.Assert.*;
+import java.io.ByteArrayInputStream;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.Objects;
@@ -24,7 +25,8 @@ import java.util.Set;
import java.util.function.Predicate;
import java.util.function.Supplier;
-import org.junit.*;
+import org.junit.Before;
+import org.junit.Test;
import db.Transaction;
import docking.action.DockingActionIf;
@@ -32,11 +34,13 @@ import generic.Unique;
import ghidra.app.context.ListingActionContext;
import ghidra.app.plugin.core.assembler.AssemblerPluginTestHelper;
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerTest;
+import ghidra.app.plugin.core.debug.gui.action.LoadEmulatorAutoReadMemorySpec;
import ghidra.app.plugin.core.debug.gui.listing.*;
import ghidra.app.plugin.core.debug.service.control.DebuggerControlServicePlugin;
+import ghidra.app.plugin.core.debug.service.emulation.DebuggerEmulationServicePlugin;
+import ghidra.app.plugin.core.debug.service.emulation.ProgramEmulationUtils;
import ghidra.app.plugin.core.debug.service.platform.DebuggerPlatformServicePlugin;
-import ghidra.app.services.DebuggerControlService;
-import ghidra.app.services.DebuggerPlatformService;
+import ghidra.app.services.*;
import ghidra.dbg.target.TargetEnvironment;
import ghidra.dbg.target.schema.SchemaContext;
import ghidra.dbg.target.schema.TargetObjectSchema.SchemaName;
@@ -57,6 +61,7 @@ import ghidra.trace.database.target.DBTraceObject;
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.TraceMemoryFlag;
import ghidra.trace.model.memory.TraceObjectMemoryRegion;
import ghidra.trace.model.program.TraceProgramView;
@@ -65,6 +70,7 @@ import ghidra.trace.model.target.TraceObject.ConflictResolution;
import ghidra.trace.model.target.TraceObjectKeyPath;
import ghidra.trace.model.thread.TraceObjectThread;
import ghidra.trace.model.thread.TraceThread;
+import ghidra.trace.model.time.schedule.TraceSchedule;
import ghidra.util.task.TaskMonitor;
public class DebuggerDisassemblyTest extends AbstractGhidraHeadedDebuggerTest {
@@ -149,6 +155,10 @@ public class DebuggerDisassemblyTest extends AbstractGhidraHeadedDebuggerTest {
listingProvider.setAutoDisassemble(true);
}
+ protected void enableLoadEmulator() throws Throwable {
+ runSwing(() -> listingProvider.setAutoReadMemorySpec(new LoadEmulatorAutoReadMemorySpec()));
+ }
+
protected DebuggerListingActionContext createActionContext(Address start, int len) {
TraceProgramView view = tb.trace.getProgramView();
ProgramSelection sel = new ProgramSelection(start, start.addWrap(len - 1));
@@ -220,7 +230,7 @@ public class DebuggerDisassemblyTest extends AbstractGhidraHeadedDebuggerTest {
return thread;
}
- protected void setLegacyProgramCounter(long offset, TraceThread thread, long snap) {
+ protected void setLegacyProgramCounterInStack(long offset, TraceThread thread, long snap) {
try (Transaction tx = tb.startTransaction()) {
DBTraceStackManager manager = tb.trace.getStackManager();
TraceStack stack = manager.getStack(thread, snap, true);
@@ -229,6 +239,15 @@ public class DebuggerDisassemblyTest extends AbstractGhidraHeadedDebuggerTest {
}
}
+ protected void setLegacyProgramCounterInRegs(long offset, TraceThread thread, long snap) {
+ try (Transaction tx = tb.startTransaction()) {
+ DBTraceMemoryManager memory = tb.trace.getMemoryManager();
+ DBTraceMemorySpace regs = memory.getMemoryRegisterSpace(thread, true);
+ Register pc = tb.language.getProgramCounter();
+ regs.setValue(0, new RegisterValue(pc, BigInteger.valueOf(offset)));
+ }
+ }
+
protected void createLegacyTrace(String langID, long offset,
Supplier byteSupplier) throws Throwable {
createAndOpenTrace(langID);
@@ -259,7 +278,7 @@ public class DebuggerDisassemblyTest extends AbstractGhidraHeadedDebuggerTest {
}
@Test
- public void testAutoDisasembleReDisasembleOffcut() throws Throwable {
+ public void testAutoDisasembleReDisasembleX8664Offcut() throws Throwable {
enableAutoDisassembly();
createLegacyTrace("x86:LE:64:default", 0x00400000, () -> tb.buf(0xeb, 0xff, 0xc0));
@@ -268,7 +287,7 @@ public class DebuggerDisassemblyTest extends AbstractGhidraHeadedDebuggerTest {
thread = tb.getOrAddThread("Thread 1", 0);
}
- setLegacyProgramCounter(0x00400000, thread, 0);
+ setLegacyProgramCounterInStack(0x00400000, thread, 0);
waitForPass(() -> {
DBTraceInstructionsMemoryView instructions = tb.trace.getCodeManager().instructions();
@@ -281,7 +300,7 @@ public class DebuggerDisassemblyTest extends AbstractGhidraHeadedDebuggerTest {
});
// The jump will advance one byte. Just simulate that by updating the stack and/or regs
- setLegacyProgramCounter(0x00400001, thread, 1);
+ setLegacyProgramCounterInStack(0x00400001, thread, 1);
traceManager.activateSnap(1);
waitForPass(() -> {
@@ -292,6 +311,93 @@ public class DebuggerDisassemblyTest extends AbstractGhidraHeadedDebuggerTest {
});
}
+ @Test
+ public void testAutoDisassembleReDisassembleX8664OffcutByEmulation() throws Throwable {
+ DebuggerEmulationService emuService = addPlugin(tool, DebuggerEmulationServicePlugin.class);
+ enableAutoDisassembly();
+ createLegacyTrace("x86:LE:64:default", 0x00400000, () -> tb.buf(0xeb, 0xff, 0xc0));
+
+ TraceThread thread;
+ try (Transaction tx = tb.startTransaction()) {
+ thread = tb.getOrAddThread("Thread 1", 0);
+ }
+
+ setLegacyProgramCounterInRegs(0x00400000, thread, 0);
+
+ waitForPass(() -> {
+ DBTraceInstructionsMemoryView instructions = tb.trace.getCodeManager().instructions();
+ assertMnemonic("JMP", instructions.getAt(0, tb.addr(0x00400000)));
+ /**
+ * Depending on preference for branch or fall-through, the disassembler may or may not
+ * proceed to the following instructions. I don't really care, since the test is the the
+ * JMP gets deleted after the update to PC.
+ */
+ });
+
+ TraceSchedule schedule = TraceSchedule.snap(0).steppedForward(thread, 1);
+ // Pre-load the cache, so I don't have to wait for background async emulation
+ long viewSnap = emuService.emulate(tb.trace, schedule, monitor);
+ traceManager.activateTime(schedule);
+ waitForSwing();
+ assertEquals(viewSnap, traceManager.getCurrentView().getSnap());
+
+ waitForPass(() -> {
+ DBTraceInstructionsMemoryView instructions = tb.trace.getCodeManager().instructions();
+ assertNull(instructions.getAt(viewSnap, tb.addr(0x00400000)));
+ assertMnemonic("INC", instructions.getAt(viewSnap, tb.addr(0x00400001)));
+ assertNull(instructions.getAt(viewSnap, tb.addr(0x00400003)));
+ });
+ }
+
+ @Test
+ public void testAutoDisassembleReDisassembleX8664OffcutByProgEmu() throws Throwable {
+ DebuggerEmulationService emuService = addPlugin(tool, DebuggerEmulationServicePlugin.class);
+
+ createProgram(getSLEIGH_X86_64_LANGUAGE());
+ Address start;
+ try (Transaction tx = program.openTransaction("Load")) {
+ start = program.getAddressFactory().getDefaultAddressSpace().getAddress(0x00400000);
+ program.getMemory()
+ .createInitializedBlock(".text", start,
+ new ByteArrayInputStream(arr("ebffc0")), 3, monitor, false);
+ }
+ intoProject(program);
+
+ useTrace(ProgramEmulationUtils.launchEmulationTrace(program, start, this));
+ tb.trace.release(this);
+ TraceThread thread = Unique.assertOne(tb.trace.getThreadManager().getAllThreads());
+
+ traceManager.openTrace(tb.trace);
+ traceManager.activateThread(thread);
+
+ enableLoadEmulator();
+ enableAutoDisassembly();
+
+ waitForPass(() -> {
+ DBTraceInstructionsMemoryView instructions = tb.trace.getCodeManager().instructions();
+ assertMnemonic("JMP", instructions.getAt(0, tb.addr(0x00400000)));
+ /**
+ * Depending on preference for branch or fall-through, the disassembler may or may not
+ * proceed to the following instructions. I don't really care, since the test is the the
+ * JMP gets deleted after the update to PC.
+ */
+ });
+
+ TraceSchedule schedule = TraceSchedule.snap(0).steppedForward(thread, 1);
+ // Pre-load the cache, so I don't have to wait for background async emulation
+ long viewSnap = emuService.emulate(tb.trace, schedule, monitor);
+ traceManager.activateTime(schedule);
+ waitForSwing();
+ assertEquals(viewSnap, traceManager.getCurrentView().getSnap());
+
+ waitForPass(() -> {
+ DBTraceInstructionsMemoryView instructions = tb.trace.getCodeManager().instructions();
+ assertNull(instructions.getAt(viewSnap, tb.addr(0x00400000)));
+ assertMnemonic("INC", instructions.getAt(viewSnap, tb.addr(0x00400001)));
+ assertNull(instructions.getAt(viewSnap, tb.addr(0x00400003)));
+ });
+ }
+
@Test
public void testAutoDisassembleGuestX8664WithPcInRegs() throws Throwable {
enableAutoDisassembly();
@@ -374,15 +480,28 @@ public class DebuggerDisassemblyTest extends AbstractGhidraHeadedDebuggerTest {
public void testCurrentDisassembleActionGuestArm() throws Throwable {
TraceObjectThread thread =
createPolyglotTrace("armv8le", 0x00400000, () -> tb.buf(0x1e, 0xff, 0x2f, 0xe1));
-
- // Set up registers so injects will select ARM
- // TODO
-
- Address start = tb.addr(0x00400000);
+ traceManager.activateThread(thread);
+ waitForSwing();
// Ensure the mapper is added to the trace
assertNotNull(platformService.getMapper(tb.trace, thread.getObject(), 0));
+ TracePlatform arm = Unique.assertOne(tb.trace.getPlatformManager().getGuestPlatforms());
+ // If cpsr is UNKNOWN, inject will assume, e.g., Cortex-M, and set THUMB mode.
+ try (Transaction tx = tb.startTransaction()) {
+ tb.trace.getObjectManager()
+ .createObject(
+ TraceObjectKeyPath.parse("Targets[0].Threads[0].Stack[0].Registers"))
+ .insert(Lifespan.nowOn(0), ConflictResolution.DENY);
+ DBTraceMemorySpace regs = Objects.requireNonNull(
+ tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, true));
+ Register cpsr = arm.getLanguage().getRegister("cpsr");
+ regs.setValue(arm, 0, new RegisterValue(cpsr, BigInteger.ZERO));
+ }
+ waitForDomainObject(tb.trace);
+
+ Address start = tb.addr(0x00400000);
+
ListingActionContext actionContext = createActionContext(start, 4);
performAction(disassemblerPlugin.actionDisassemble, actionContext, true);
waitForTasks();
@@ -394,19 +513,30 @@ public class DebuggerDisassemblyTest extends AbstractGhidraHeadedDebuggerTest {
}
@Test
- @Ignore("TODO")
public void testCurrentDisassembleActionGuestThumb() throws Throwable {
TraceObjectThread thread =
createPolyglotTrace("armv8le", 0x00400000, () -> tb.buf(0x70, 0x47));
-
- // Set up registers so injects will select THUMB
- // TODO
-
- Address start = tb.addr(0x00400000);
+ traceManager.activateThread(thread);
+ waitForSwing();
// Ensure the mapper is added to the trace
assertNotNull(platformService.getMapper(tb.trace, thread.getObject(), 0));
+ TracePlatform arm = Unique.assertOne(tb.trace.getPlatformManager().getGuestPlatforms());
+ try (Transaction tx = tb.startTransaction()) {
+ tb.trace.getObjectManager()
+ .createObject(
+ TraceObjectKeyPath.parse("Targets[0].Threads[0].Stack[0].Registers"))
+ .insert(Lifespan.nowOn(0), ConflictResolution.DENY);
+ DBTraceMemorySpace regs = Objects.requireNonNull(
+ tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, true));
+ Register cpsr = arm.getLanguage().getRegister("cpsr");
+ regs.setValue(arm, 0, new RegisterValue(cpsr, BigInteger.ONE.shiftLeft(5)));
+ }
+ waitForDomainObject(tb.trace);
+
+ Address start = tb.addr(0x00400000);
+
ListingActionContext actionContext = createActionContext(start, 4);
performAction(disassemblerPlugin.actionDisassemble, actionContext, true);
waitForTasks();
diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/mapping/TestDebuggerPlatformOpinion.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/mapping/TestDebuggerPlatformOpinion.java
index 2909b43bd3..b7ebeba866 100644
--- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/mapping/TestDebuggerPlatformOpinion.java
+++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/mapping/TestDebuggerPlatformOpinion.java
@@ -50,7 +50,7 @@ public class TestDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion
@Override
public int getConfidence() {
- return 1;
+ return HostDebuggerPlatformOpinion.Offers.HOST.getConfidence() + 1;
}
@Override
diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServiceTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServiceTest.java
index 8a48851d4c..a8ab65ddc9 100644
--- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServiceTest.java
+++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServiceTest.java
@@ -33,12 +33,14 @@ import ghidra.app.plugin.assembler.*;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerTest;
import ghidra.app.plugin.core.debug.mapping.DebuggerPlatformOpinion;
+import ghidra.app.plugin.core.debug.service.emulation.data.DefaultPcodeDebuggerAccess;
import ghidra.app.plugin.core.debug.service.platform.DebuggerPlatformServicePlugin;
import ghidra.app.services.DebuggerEmulationService.EmulationResult;
import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.app.services.DebuggerTraceManagerService.ActivationCause;
import ghidra.debug.api.platform.DebuggerPlatformMapper;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
+import ghidra.pcode.emu.PcodeThread;
import ghidra.pcode.exec.DecodePcodeExecutionException;
import ghidra.pcode.exec.InterruptPcodeExecutionException;
import ghidra.pcode.utils.Utils;
@@ -57,6 +59,7 @@ import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.memory.TraceMemoryManager;
import ghidra.trace.model.memory.TraceMemorySpace;
import ghidra.trace.model.thread.TraceThread;
+import ghidra.trace.model.time.TraceSnapshot;
import ghidra.trace.model.time.schedule.Scheduler;
import ghidra.trace.model.time.schedule.TraceSchedule;
import ghidra.util.task.TaskMonitor;
@@ -114,8 +117,8 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe
assertEquals(new BigInteger("0000", 16), regs.getViewValue(0, regR0).getUnsignedValue());
assertEquals(new BigInteger("1234", 16), regs.getViewValue(0, regR1).getUnsignedValue());
- long scratch =
- emulationPlugin.emulate(trace, TraceSchedule.parse("0:t0-1"), TaskMonitor.DUMMY);
+ long scratch = emulationPlugin.emulate(trace,
+ TraceSchedule.snap(0).steppedForward(thread, 1), TaskMonitor.DUMMY);
assertEquals(new BigInteger("00400002", 16),
regs.getViewValue(scratch, regPC).getUnsignedValue());
@@ -171,8 +174,8 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe
assertEquals(new BigInteger("00000400", 16),
regs.getViewValue(0, regR1).getUnsignedValue());
- long scratch =
- emulationPlugin.emulate(trace, TraceSchedule.parse("0:t0-1"), TaskMonitor.DUMMY);
+ long scratch = emulationPlugin.emulate(trace,
+ TraceSchedule.snap(0).steppedForward(thread, 1), TaskMonitor.DUMMY);
assertEquals(new BigInteger("00000402", 16),
regs.getViewValue(scratch, regPC).getUnsignedValue());
@@ -220,6 +223,7 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe
Trace trace = traceManager.getCurrentTrace();
assertNotNull(trace);
+ TraceThread thread = Unique.assertOne(trace.getThreadManager().getAllThreads());
TraceMemoryManager mem = trace.getMemoryManager();
assertEquals(new BigInteger("000100", 16),
@@ -229,8 +233,8 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe
assertEquals(new BigInteger("0800", 16),
mem.getViewValue(0, regW1).getUnsignedValue());
- long scratch =
- emulationPlugin.emulate(trace, TraceSchedule.parse("0:t0-1"), TaskMonitor.DUMMY);
+ long scratch = emulationPlugin.emulate(trace,
+ TraceSchedule.snap(0).steppedForward(thread, 1), TaskMonitor.DUMMY);
assertEquals(new BigInteger("000102", 16),
mem.getViewValue(scratch, regPC).getUnsignedValue());
@@ -377,7 +381,7 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe
EmulationResult result = emulationPlugin.run(trace.getPlatformManager().getHostPlatform(),
TraceSchedule.snap(0), TaskMonitor.DUMMY, Scheduler.oneThread(thread));
- assertEquals(TraceSchedule.parse("0:t0-1"), result.schedule());
+ assertEquals(TraceSchedule.snap(0).steppedForward(thread, 1), result.schedule());
assertTrue(result.error() instanceof DecodePcodeExecutionException);
long scratch = result.snapshot();
@@ -432,7 +436,7 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe
EmulationResult result = emulationPlugin.run(trace.getPlatformManager().getHostPlatform(),
TraceSchedule.snap(0), TaskMonitor.DUMMY, Scheduler.oneThread(thread));
- assertEquals(TraceSchedule.parse("0:t0-1"), result.schedule());
+ assertEquals(TraceSchedule.snap(0).steppedForward(thread, 1), result.schedule());
assertTrue(result.error() instanceof InterruptPcodeExecutionException);
long scratch = result.snapshot();
@@ -496,13 +500,13 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe
// This is already testing if the one set at the entry is ignored
EmulationResult result1 = emulationPlugin.run(trace.getPlatformManager().getHostPlatform(),
TraceSchedule.snap(0), monitor, Scheduler.oneThread(thread));
- assertEquals(TraceSchedule.parse("0:t0-1"), result1.schedule());
+ assertEquals(TraceSchedule.snap(0).steppedForward(thread, 1), result1.schedule());
assertTrue(result1.error() instanceof InterruptPcodeExecutionException);
// This will test if the one just hit gets ignored
EmulationResult result2 = emulationPlugin.run(trace.getPlatformManager().getHostPlatform(),
result1.schedule(), monitor, Scheduler.oneThread(thread));
- assertEquals(TraceSchedule.parse("0:t0-2"), result2.schedule());
+ assertEquals(TraceSchedule.snap(0).steppedForward(thread, 2), result2.schedule());
assertTrue(result1.error() instanceof InterruptPcodeExecutionException);
}
@@ -558,7 +562,8 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe
EmulationResult result = emulationPlugin.run(trace.getPlatformManager().getHostPlatform(),
TraceSchedule.snap(0), TaskMonitor.DUMMY, Scheduler.oneThread(thread));
- assertEquals(TraceSchedule.parse("0:t0-1.t0-2"), result.schedule());
+ assertEquals(TraceSchedule.snap(0).steppedForward(thread, 1).steppedPcodeForward(thread, 2),
+ result.schedule());
assertTrue(result.error() instanceof InterruptPcodeExecutionException);
long scratch = result.snapshot();
@@ -619,7 +624,7 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe
EmulationResult result = emulationPlugin.run(trace.getPlatformManager().getHostPlatform(),
TraceSchedule.snap(0), TaskMonitor.DUMMY, Scheduler.oneThread(thread));
- assertEquals(TraceSchedule.parse("0:t0-1"), result.schedule());
+ assertEquals(TraceSchedule.snap(0).steppedForward(thread, 1), result.schedule());
assertTrue(result.error() instanceof InterruptPcodeExecutionException);
long scratch = result.snapshot();
@@ -675,11 +680,11 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe
TraceMemorySpace regs = trace.getMemoryManager().getMemoryRegisterSpace(thread, false);
// Step as written to fill the cache
- waitOn(traceManager.activateAndNotify(current.time(TraceSchedule.parse("0:t0-1")),
- ActivationCause.USER));
+ waitOn(traceManager.activateAndNotify(
+ current.time(TraceSchedule.snap(0).steppedForward(thread, 1)), ActivationCause.USER));
waitForSwing();
- waitOn(traceManager.activateAndNotify(current.time(TraceSchedule.parse("0:t0-2")),
- ActivationCause.USER));
+ waitOn(traceManager.activateAndNotify(
+ current.time(TraceSchedule.snap(0).steppedForward(thread, 2)), ActivationCause.USER));
waitForSwing();
long scratch = traceManager.getCurrentView().getSnap();
@@ -699,11 +704,11 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe
}
// Check the cache is still valid
- waitOn(traceManager.activateAndNotify(current.time(TraceSchedule.parse("0:t0-1")),
- ActivationCause.USER));
+ waitOn(traceManager.activateAndNotify(
+ current.time(TraceSchedule.snap(0).steppedForward(thread, 1)), ActivationCause.USER));
waitForSwing();
- waitOn(traceManager.activateAndNotify(current.time(TraceSchedule.parse("0:t0-2")),
- ActivationCause.USER));
+ waitOn(traceManager.activateAndNotify(
+ current.time(TraceSchedule.snap(0).steppedForward(thread, 2)), ActivationCause.USER));
waitForSwing();
assertEquals(scratch, traceManager.getCurrentView().getSnap());
assertEquals(new BigInteger("1234", 16),
@@ -746,4 +751,37 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe
TraceMemorySpace regs = trace.getMemoryManager().getMemoryRegisterSpace(thread, false);
assertEquals(new BigInteger("2234", 16), regs.getViewValue(0, regSP).getUnsignedValue());
}
+
+ @Test
+ public void testNewThreadAfterLoadTrace() throws Exception {
+ createAndOpenTrace();
+ createProgramFromTrace();
+ intoProject(program);
+
+ long restartEmuSnap = 3;
+ try (Transaction tx = tb.startTransaction()) {
+ tb.trace.getObjectManager().createRootObject(ProgramEmulationUtils.EMU_SESSION_SCHEMA);
+ tb.trace.getTimeManager().getSnapshot(restartEmuSnap, true);
+ }
+ traceManager.activateTrace(tb.trace);
+ traceManager.activateSnap(restartEmuSnap);
+
+ try (Transaction tx = tb.startTransaction()) {
+ TracePlatform host = tb.trace.getPlatformManager().getHostPlatform();
+ DefaultPcodeDebuggerAccess access =
+ new DefaultPcodeDebuggerAccess(tool, null, host, restartEmuSnap);
+ BytesDebuggerPcodeEmulator emulator = new BytesDebuggerPcodeEmulator(access);
+
+ TraceSnapshot snapshot =
+ tb.trace.getTimeManager().createSnapshot("created new emulator thread");
+ long newSnap = snapshot.getKey();
+ emulator.writeDown(host, newSnap, newSnap);
+
+ TraceThread newTraceThread = ProgramEmulationUtils.doLaunchEmulationThread(tb.trace,
+ newSnap, program, tb.addr(0x00400000), addr(program, 0x00400000));
+ newTraceThread.setName("MyThread");
+
+ PcodeThread newEmuThread = emulator.newThread(newTraceThread.getPath());
+ }
+ }
}
diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/debug/flatapi/FlatDebuggerAPITest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/debug/flatapi/FlatDebuggerAPITest.java
index 937f0f89d1..4898e6ed2e 100644
--- a/Ghidra/Debug/Debugger/src/test/java/ghidra/debug/flatapi/FlatDebuggerAPITest.java
+++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/debug/flatapi/FlatDebuggerAPITest.java
@@ -497,9 +497,11 @@ public class FlatDebuggerAPITest extends AbstractGhidraHeadedDebuggerTest {
Address entry = createEmulatableProgram();
flat.emulateLaunch(entry);
- flat.emulate(TraceSchedule.parse("0:t0-1"), monitor);
+ TraceSchedule schedule =
+ traceManager.getCurrent().getTime().steppedForward(traceManager.getCurrentThread(), 1);
+ flat.emulate(schedule, monitor);
- assertEquals(TraceSchedule.parse("0:t0-1"), traceManager.getCurrent().getTime());
+ assertEquals(schedule, traceManager.getCurrent().getTime());
}
@Test
@@ -507,12 +509,14 @@ public class FlatDebuggerAPITest extends AbstractGhidraHeadedDebuggerTest {
Address entry = createEmulatableProgram();
flat.emulateLaunch(entry);
+ TraceSchedule schedule =
+ traceManager.getCurrent().getTime().steppedForward(traceManager.getCurrentThread(), 1);
flat.stepEmuInstruction(1, monitor);
- assertEquals(TraceSchedule.parse("0:t0-1"), traceManager.getCurrent().getTime());
+ assertEquals(schedule, traceManager.getCurrent().getTime());
flat.stepEmuInstruction(-1, monitor);
- assertEquals(TraceSchedule.parse("0"), traceManager.getCurrent().getTime());
+ assertEquals(TraceSchedule.ZERO, traceManager.getCurrent().getTime());
}
@Test
@@ -520,12 +524,15 @@ public class FlatDebuggerAPITest extends AbstractGhidraHeadedDebuggerTest {
Address entry = createEmulatableProgram();
flat.emulateLaunch(entry);
+ TraceSchedule schedule = traceManager.getCurrent()
+ .getTime()
+ .steppedPcodeForward(traceManager.getCurrentThread(), 1);
flat.stepEmuPcodeOp(1, monitor);
- assertEquals(TraceSchedule.parse("0:.t0-1"), traceManager.getCurrent().getTime());
+ assertEquals(schedule, traceManager.getCurrent().getTime());
flat.stepEmuPcodeOp(-1, monitor);
- assertEquals(TraceSchedule.parse("0"), traceManager.getCurrent().getTime());
+ assertEquals(TraceSchedule.ZERO, traceManager.getCurrent().getTime());
}
@Test
@@ -533,12 +540,14 @@ public class FlatDebuggerAPITest extends AbstractGhidraHeadedDebuggerTest {
Address entry = createEmulatableProgram();
flat.emulateLaunch(entry);
+ TraceSchedule schedule =
+ traceManager.getCurrent().getTime().skippedForward(traceManager.getCurrentThread(), 1);
flat.skipEmuInstruction(1, monitor);
- assertEquals(TraceSchedule.parse("0:t0-s1"), traceManager.getCurrent().getTime());
+ assertEquals(schedule, traceManager.getCurrent().getTime());
flat.skipEmuInstruction(-1, monitor);
- assertEquals(TraceSchedule.parse("0"), traceManager.getCurrent().getTime());
+ assertEquals(TraceSchedule.ZERO, traceManager.getCurrent().getTime());
}
@Test
@@ -546,12 +555,15 @@ public class FlatDebuggerAPITest extends AbstractGhidraHeadedDebuggerTest {
Address entry = createEmulatableProgram();
flat.emulateLaunch(entry);
+ TraceSchedule schedule = traceManager.getCurrent()
+ .getTime()
+ .skippedPcodeForward(traceManager.getCurrentThread(), 1);
flat.skipEmuPcodeOp(1, monitor);
- assertEquals(TraceSchedule.parse("0:.t0-s1"), traceManager.getCurrent().getTime());
+ assertEquals(schedule, traceManager.getCurrent().getTime());
flat.skipEmuPcodeOp(-1, monitor);
- assertEquals(TraceSchedule.parse("0"), traceManager.getCurrent().getTime());
+ assertEquals(TraceSchedule.ZERO, traceManager.getCurrent().getTime());
}
@Test
@@ -559,12 +571,16 @@ public class FlatDebuggerAPITest extends AbstractGhidraHeadedDebuggerTest {
Address entry = createEmulatableProgram();
flat.emulateLaunch(entry);
+ TraceSchedule schedule = traceManager.getCurrent()
+ .getTime()
+ .patched(traceManager.getCurrentThread(),
+ traceManager.getCurrentPlatform().getLanguage(), "r0=0x321");
flat.patchEmu("r0=0x321", monitor);
- assertEquals(TraceSchedule.parse("0:t0-{r0=0x321}"), traceManager.getCurrent().getTime());
+ assertEquals(schedule, traceManager.getCurrent().getTime());
flat.stepEmuInstruction(-1, monitor);
- assertEquals(TraceSchedule.parse("0"), traceManager.getCurrent().getTime());
+ assertEquals(TraceSchedule.ZERO, traceManager.getCurrent().getTime());
}
@Test
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/target/DBTraceObjectManager.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/target/DBTraceObjectManager.java
index 5c54750deb..b1067cd7ba 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/target/DBTraceObjectManager.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/target/DBTraceObjectManager.java
@@ -573,7 +573,12 @@ public class DBTraceObjectManager implements TraceObjectManager, DBTraceManager
public I getObjectByPath(long snap, String path,
Class iface) {
try (LockHold hold = trace.lockRead()) {
- return getObjectsByPath(Lifespan.at(snap), TraceObjectKeyPath.parse(path)).findAny()
+ TraceObjectKeyPath parsed = TraceObjectKeyPath.parse(path);
+ DBTraceObject object = getObjectByCanonicalPath(parsed);
+ if (object != null) {
+ return object.queryInterface(iface);
+ }
+ return getObjectsByPath(Lifespan.at(snap), parsed).findAny()
.map(o -> o.queryInterface(iface))
.orElse(null);
}
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/ToyDBTraceBuilder.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/ToyDBTraceBuilder.java
index 72a429dc5a..7ac6e113cd 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/ToyDBTraceBuilder.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/ToyDBTraceBuilder.java
@@ -803,8 +803,8 @@ public class ToyDBTraceBuilder implements AutoCloseable {
* @param path the path pattern
* @return the object or null
*/
- public TraceObject objAny(String pat) {
- return objAny(pat, Lifespan.at(0));
+ public TraceObject objAny(String path) {
+ return objAny(path, Lifespan.at(0));
}
public TraceObject objAny(String path, Lifespan span) {