From 38798da71e0b34e54e15eaaaea49397ced2aab26 Mon Sep 17 00:00:00 2001
From: Dan <46821332+nsadeveloper789@users.noreply.github.com>
Date: Thu, 22 Jul 2021 13:36:21 -0400
Subject: [PATCH] GP-660: Added "Emulate Program" and "Add Emulated Thread"
actions for loading a program into a purely-emulated trace.
---
Ghidra/Debug/Debugger/certification.manifest | 1 +
.../src/main/help/help/TOC_Source.xml | 4 +
.../DebuggerEmulationServicePlugin.html | 44 +++
.../DebuggerStaticMappingPlugin.html | 6 +-
.../DebuggerThreadsPlugin.html | 19 +-
.../core/debug/gui/DebuggerResources.java | 46 +++
.../gui/register/DebuggerRegistersPlugin.java | 33 +-
.../register/DebuggerRegistersProvider.java | 26 +-
.../DebuggerEmulationServicePlugin.java | 222 ++++++++++-
.../EmulatorOutOfMemoryException.java | 22 ++
.../emulation/ProgramEmulationUtils.java | 354 ++++++++++++++++++
.../ReadsTargetMemoryPcodeExecutorState.java | 48 ++-
.../DebuggerModelServiceProxyPlugin.java | 44 ++-
.../DebuggerStaticMappingServicePlugin.java | 58 ++-
.../modules/DebuggerStaticMappingUtils.java | 141 +++++++
.../DebuggerStaticMappingService.java | 22 +-
.../DebuggerBreakpointsPluginScreenShots.java | 5 +-
.../stack/DebuggerStackPluginScreenShots.java | 3 +-
.../DebuggerBreakpointMarkerPluginTest.java | 3 +-
.../DebuggerBreakpointsProviderTest.java | 3 +-
.../listing/DebuggerListingProviderTest.java | 11 +-
.../gui/stack/DebuggerStackProviderTest.java | 3 +-
.../DebuggerLogicalBreakpointServiceTest.java | 7 +-
.../DebuggerEmulationServiceTest.java | 98 +++++
.../DebuggerStaticMappingServiceTest.java | 6 +-
.../java/ghidra/trace/database/DBTrace.java | 9 +-
26 files changed, 1069 insertions(+), 169 deletions(-)
create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerEmulationServicePlugin/DebuggerEmulationServicePlugin.html
create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/EmulatorOutOfMemoryException.java
create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/ProgramEmulationUtils.java
create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingUtils.java
create mode 100644 Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServiceTest.java
diff --git a/Ghidra/Debug/Debugger/certification.manifest b/Ghidra/Debug/Debugger/certification.manifest
index e0ef58ee77..d07f0be24b 100644
--- a/Ghidra/Debug/Debugger/certification.manifest
+++ b/Ghidra/Debug/Debugger/certification.manifest
@@ -41,6 +41,7 @@ src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-enable-al
src/main/help/help/topics/DebuggerBreakpointsPlugin/images/breakpoints-make-effective.png||GHIDRA||||END|
src/main/help/help/topics/DebuggerConsolePlugin/DebuggerConsolePlugin.html||GHIDRA||||END|
src/main/help/help/topics/DebuggerConsolePlugin/images/DebuggerConsolePlugin.png||GHIDRA||||END|
+src/main/help/help/topics/DebuggerEmulationServicePlugin/DebuggerEmulationServicePlugin.html||GHIDRA||||END|
src/main/help/help/topics/DebuggerInterpreterPlugin/DebuggerInterpreterPlugin.html||GHIDRA||||END|
src/main/help/help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html||GHIDRA||||END|
src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerGoToDialog.png||GHIDRA||||END|
diff --git a/Ghidra/Debug/Debugger/src/main/help/help/TOC_Source.xml b/Ghidra/Debug/Debugger/src/main/help/help/TOC_Source.xml
index d21c44c30c..9019d4a84a 100644
--- a/Ghidra/Debug/Debugger/src/main/help/help/TOC_Source.xml
+++ b/Ghidra/Debug/Debugger/src/main/help/help/TOC_Source.xml
@@ -94,6 +94,10 @@
sortgroup="g"
target="help/topics/DebuggerTraceManagerServicePlugin/DebuggerTraceManagerServicePlugin.html" />
+
This service plugin provides emulation to the Trace + Manager and provides actions for launching emulated traces from programs, i.e., without + requiring a connected debugger. Please note that "pure emulation" of a target program, while it + doesn't require a platform to execute the target natively, it typically does require + significant state initialization and dependency stubbing, except in limited circumstances.
+ +This action is available whenever a program is active. It will create a new trace suitable + for "pure emulation" of the current program starting at the current address. More precisely, it + will open a new blank trace, initializes it with the current program's memory map, allocates a + stack, and creates a thread whose program counter is initialized to the current address and + whose registers are initialized to the register context at the current address. Optionally, any + other initialization can be done by manually modifying the trace in the UI, or using a script. + The trace and/or p-code stepping actions can then be used to emulate.
+ +This action is available whenever a "pure emulation" trace is active. It spawns a new thread + in the current trace suitable for emulating starting at the current address. More precisely, it + allocates another stack and creates a new thread whose program counter is initialized to the + current address and whose registers are initialized to the register context at the current + address. Optionally, other registers can be initialized via the UI or a script. The new thread + is activated so that stepping actions will affect it by default.
+ + diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerStaticMappingPlugin/DebuggerStaticMappingPlugin.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerStaticMappingPlugin/DebuggerStaticMappingPlugin.html index 33dc843704..cb7711c878 100644 --- a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerStaticMappingPlugin/DebuggerStaticMappingPlugin.html +++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerStaticMappingPlugin/DebuggerStaticMappingPlugin.html @@ -28,9 +28,9 @@ automation, e.g., the Map Modules bot, or by higher-level user actions, e.g., the Map Modules - action. This under-the-hood static mapping window displays the mappings table, allowing users or - developers to diagnose image mapping issues and manually add mappings, regardless of reported - modules and/or sections. For most users, there is no reason to access this window. + action. This under-the-hood static mapping window displays the mappings table, allowing users + or developers to diagnose image mapping issues and manually add mappings, regardless of + reported modules and/or sections. For most users, there is no reason to access this window.Selecting a thread in the table will navigate to (or "activate" or "focus") - that thread. Windows which are sensitive to the current thread will update. Notably, the Registers window - will display the activated thread's register values. Selecting a thread in the table will navigate to (or "activate" or "focus") that thread. + Windows which are sensitive to the current thread will update. Notably, the Registers window will + display the activated thread's register values. Listing windows with configured location tracking will re-compute that location with the thread's context and navigate to it. The thread timeline plots all recorded threads. Threads which are alive will @@ -77,16 +77,17 @@ The state always reflects the present, or latest known, state.
The user can navigate through time within the current trace by using the caret in the plot
- column header. There are also actions for "stepping the trace" forward and backward. See the
- Time window for a way to
+ column header. There are also actions for "stepping the trace" forward and backward. See the Time window for a way to
display and navigate to specific events in the trace's timeline. Note that stepping away from
the present will prevent most windows from interacting with the live target. While some
components and scripts may interact with the target and record things "into the present," such
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java
index 1a3661221c..3939a016d7 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java
@@ -169,6 +169,8 @@ public interface DebuggerResources {
ImageIcon ICON_BLANK = ResourceManager.loadImage("images/blank.png");
ImageIcon ICON_PACKAGE = ResourceManager.loadImage("images/debugger32.png");
+ ImageIcon ICON_EMULATE = ICON_PROCESS; // TODO
+
HelpLocation HELP_PACKAGE = new HelpLocation("Debugger", "package");
String HELP_ANCHOR_PLUGIN = "plugin";
@@ -489,6 +491,50 @@ public interface DebuggerResources {
}
}
+ interface EmulateProgramAction {
+ String NAME = "Emulate Program in new Trace";
+ String DESCRIPTION = "Emulate the current program in a new trace starting at the cursor";
+ Icon ICON = ICON_EMULATE;
+ String GROUP = GROUP_GENERAL;
+ String HELP_ANCHOR = "emulate_program";
+
+ static ActionBuilder builder(Plugin owner) {
+ String ownerName = owner.getName();
+ return new ActionBuilder(NAME, ownerName)
+ .description(DESCRIPTION)
+ .toolBarIcon(ICON)
+ .toolBarGroup(GROUP)
+ .menuPath(DebuggerPluginPackage.NAME, NAME)
+ .menuIcon(ICON)
+ .menuGroup(GROUP)
+ .popupMenuPath(NAME)
+ .popupMenuIcon(ICON)
+ .popupMenuGroup(GROUP)
+ .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
+ }
+ }
+
+ interface EmulateAddThreadAction {
+ String NAME = "Add Emulated Thread to Trace";
+ String DESCRIPTION = "Add an emulated thread to the current trace starting here";
+ Icon ICON = ICON_THREAD;
+ String GROUP = GROUP_GENERAL;
+ String HELP_ANCHOR = "add_emulated_thread";
+
+ static ActionBuilder builder(Plugin owner) {
+ String ownerName = owner.getName();
+ return new ActionBuilder(NAME, ownerName)
+ .description(DESCRIPTION)
+ .menuPath(DebuggerPluginPackage.NAME, NAME)
+ .menuIcon(ICON)
+ .menuGroup(GROUP)
+ .popupMenuPath(NAME)
+ .popupMenuIcon(ICON)
+ .popupMenuGroup(GROUP)
+ .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
+ }
+ }
+
abstract class AbstractQuickLaunchAction extends DockingAction {
public static final String NAME = "Quick Launch";
public static final Icon ICON = ICON_DEBUGGER; // TODO: A different icon?
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersPlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersPlugin.java
index cf52e441f7..6a5166d39c 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersPlugin.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersPlugin.java
@@ -59,8 +59,10 @@ public class DebuggerRegistersPlugin extends AbstractDebuggerPlugin {
protected DebuggerRegistersProvider connectedProvider;
- private final Map
+ * Most of these are already integrated via the {@link DebuggerEmulationService}. Please see if that
+ * service satisfies your use case before employing these directly.
+ */
+public enum ProgramEmulationUtils {
+ ;
+
+ /**
+ * Conventional prefix for first snapshot to identify "pure emulation" traces.
+ */
+ public static final String EMULATION_STARTED_AT = "Emulation started at ";
+
+ /**
+ * Suggests a name for a new trace for emulation of the given program
+ *
+ * @param program the program to emulate
+ * @return the suggested name
+ */
+ public static String getTraceName(Program program) {
+ DomainFile df = program.getDomainFile();
+ if (df != null) {
+ return "Emulate " + df.getName();
+ }
+ return "Emulate " + program.getName();
+ }
+
+ /**
+ * Suggests the initial module name for loading a program into an emulated trace
+ *
+ * @param program the program comprising the module to "load"
+ * @return the suggested module name
+ */
+ public static String getModuleName(Program program) {
+ String executablePath = program.getExecutablePath();
+ if (executablePath != null) {
+ return executablePath;
+ }
+ DomainFile df = program.getDomainFile();
+ if (df != null) {
+ return df.getName();
+ }
+ return program.getName();
+ }
+
+ /**
+ * Convert permissions for a program memory block into flags for a trace memory region
+ *
+ * @param block the block whose permissions to convert
+ * @return the corresponding set of flags
+ */
+ public static Set
+ * This creates a region for each loaded, non-overlay block in the program. Permissions/flags
+ * are assigned accordingly. A single static mapping is generated to cover the entire range of
+ * created regions. Note that no bytes are copied in, as that could be prohibitive for large
+ * programs. Instead, the emulator should load them, based on the static mapping, as needed.
+ *
+ *
+ * A transaction must already be started on the destination trace.
+ *
+ * @param snapshot the destination snapshot, usually 0
+ * @param program the progam to load
+ */
+ public static void loadExecutable(TraceSnapshot snapshot, Program program) {
+ Trace trace = snapshot.getTrace();
+ Address min = null;
+ Address max = null;
+ try {
+ for (MemoryBlock block : program.getMemory().getBlocks()) {
+ if (!block.isLoaded()) {
+ continue;
+ }
+ if (block.isOverlay()) {
+ continue;
+ }
+ AddressRange range = new AddressRangeImpl(block.getStart(), block.getEnd());
+ min = min == null ? range.getMinAddress()
+ : ComparatorMath.cmin(min, range.getMinAddress());
+ max = max == null ? range.getMaxAddress()
+ : ComparatorMath.cmax(max, range.getMaxAddress());
+ String modName = getModuleName(program);
+
+ // TODO: Do I populate modules, since the mapping will already be done?
+ trace.getMemoryManager()
+ .createRegion(
+ "Modules[" + modName + "].Sections[" + block.getName() + "]",
+ snapshot.getKey(), range, getRegionFlags(block));
+ }
+ DebuggerStaticMappingUtils.addMapping(
+ new DefaultTraceLocation(trace, null, Range.atLeast(snapshot.getKey()), min),
+ new ProgramLocation(program, min),
+ max.subtract(min), false);
+ }
+ catch (TraceOverlappedRegionException | DuplicateNameException
+ | TraceConflictedMappingException e) {
+ throw new AssertionError(e);
+ }
+ // N.B. Bytes will be loaded lazily
+ }
+
+ /**
+ * Spawn a new thread in the given trace at the given creation snap
+ *
+ *
+ * This does not initialize the thread's state. It simply creates it.
+ *
+ * @param trace the trace to contain the new thread
+ * @param snap the creation shap of the new thread
+ * @return the new thread
+ */
+ public static TraceThread spawnThread(Trace trace, long snap) {
+ TraceThreadManager tm = trace.getThreadManager();
+ long next = tm.getAllThreads().size();
+ while (!tm.getThreadsByPath("Threads[" + next + "]").isEmpty()) {
+ next++;
+ }
+ try {
+ return tm.createThread("Threads[" + next + "]", "[" + next + "]", snap);
+ }
+ catch (DuplicateNameException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ /**
+ * Initialize a thread's registers using program context and an optional stack
+ *
+ * @param trace the trace containing the thread
+ * @param snap the destination snap for the register state
+ * @param thread the thread whose registers to initialize
+ * @param program the program whose context to use
+ * @param tracePc the program counter in the trace's memory map
+ * @param programPc the program counter in the program's memory map
+ * @param stack optionally, the region representing the thread's stack
+ */
+ public static void initializeRegisters(Trace trace, long snap, TraceThread thread,
+ Program program, Address tracePc, Address programPc, TraceMemoryRegion stack) {
+ TraceMemoryRegisterSpace space =
+ trace.getMemoryManager().getMemoryRegisterSpace(thread, true);
+ if (program != null) {
+ ProgramContext ctx = program.getProgramContext();
+ for (Register reg : Stream.of(ctx.getRegistersWithValues())
+ .map(Register::getBaseRegister)
+ .collect(Collectors.toSet())) {
+ RegisterValue rv = ctx.getRegisterValue(reg, programPc);
+ if (!rv.hasAnyValue()) {
+ continue;
+ }
+ // Set all the mask bits
+ space.setValue(snap, new RegisterValue(reg, BigInteger.ZERO).combineValues(rv));
+ }
+ }
+ space.setValue(snap, new RegisterValue(trace.getBaseLanguage().getProgramCounter(),
+ tracePc.getOffsetAsBigInteger()));
+ if (stack != null) {
+ CompilerSpec cSpec = trace.getBaseCompilerSpec();
+ Address sp = cSpec.stackGrowsNegative()
+ ? stack.getMaxAddress()
+ : stack.getMinAddress();
+ space.setValue(snap,
+ new RegisterValue(cSpec.getStackPointer(), sp.getOffsetAsBigInteger()));
+ }
+ }
+
+ /**
+ * Attempt to allocate a new stack region for the given thread
+ *
+ * @param trace the trace containing the stack and thread
+ * @param snap the creation snap for the new region
+ * @param thread the thread for which the stack is being allocated
+ * @param size the desired size of the region
+ * @return the new region representing the allocated stack
+ *
+ * @throws EmulatorOutOfMemoryException if the stack cannot be allocated
+ */
+ public static TraceMemoryRegion allocateStack(Trace trace, long snap, TraceThread thread,
+ long size) {
+ AddressSpace space = trace.getBaseCompilerSpec().getStackBaseSpace();
+ AddressSet except0 = new AddressSet(space.getAddress(0x1000), space.getMaxAddress());
+ TraceMemoryManager mm = trace.getMemoryManager();
+ AddressSetView left =
+ new DifferenceAddressSetView(except0, mm.getRegionsAddressSet(snap));
+ try {
+ for (AddressRange candidate : left) {
+ if (Long.compareUnsigned(candidate.getLength(), size) > 0) {
+ AddressRange alloc = new AddressRangeImpl(candidate.getMinAddress(), size);
+ return mm.createRegion(thread.getPath() + ".Stack", snap, alloc,
+ TraceMemoryFlag.READ, TraceMemoryFlag.WRITE);
+ }
+ }
+ }
+ catch (AddressOverflowException | TraceOverlappedRegionException
+ | DuplicateNameException e) {
+ throw new AssertionError(e);
+ }
+ throw new EmulatorOutOfMemoryException();
+ }
+
+ /**
+ * Create a new trace with a single thread, ready for emulation of the given program
+ *
+ * @param program the program to emulate
+ * @param pc the initial program counter for the new single thread
+ * @param consumer the consumer of the new trace
+ * @return the new trace
+ * @throws IOException if the trace cannot be created
+ */
+ public static Trace launchEmulationTrace(Program program, Address pc, Object consumer)
+ throws IOException {
+ Trace trace = null;
+ boolean success = false;
+ try {
+ trace = new DBTrace(getTraceName(program), program.getCompilerSpec(), consumer);
+ try (UndoableTransaction tid = UndoableTransaction.start(trace, "Emulate", false)) {
+ TraceSnapshot initial =
+ trace.getTimeManager().createSnapshot(EMULATION_STARTED_AT + pc);
+ long snap = initial.getKey();
+ loadExecutable(initial, program);
+ doLaunchEmulationThread(trace, snap, program, pc, pc);
+ tid.commit();
+ }
+ success = true;
+ return trace;
+ }
+ catch (LanguageNotFoundException e) {
+ throw new AssertionError(e);
+ }
+ finally {
+ if (!success && trace != null) {
+ trace.release(consumer);
+ }
+ }
+ }
+
+ /**
+ * Create a new emulated thread within an existing trace
+ *
+ * @param trace the trace to contain the new thread
+ * @param snap the creation snap for the new thread
+ * @param program the program whose context to use for initial register values
+ * @param tracePc the program counter in the trace's memory map
+ * @param programPc the program counter in the program's memory map
+ * @return the new thread
+ */
+ public static TraceThread doLaunchEmulationThread(Trace trace, long snap, Program program,
+ Address tracePc, Address programPc) {
+ TraceThread thread = spawnThread(trace, snap);
+ TraceMemoryRegion stack = allocateStack(trace, snap, thread, 0x4000);
+ initializeRegisters(trace, snap, thread, program, tracePc, programPc, stack);
+ return thread;
+ }
+
+ /**
+ * Same as {@link #doLaunchEmulationThread(Trace, long, Program, Address, Address)}, but within
+ * a transaction
+ */
+ public static TraceThread launchEmulationThread(Trace trace, long snap, Program program,
+ Address tracePc, Address programPc) {
+ try (UndoableTransaction tid =
+ UndoableTransaction.start(trace, "Emulate new Thread", false)) {
+ TraceThread thread = doLaunchEmulationThread(trace, snap, program, tracePc, programPc);
+ tid.commit();
+ return thread;
+ }
+ }
+
+ /**
+ * Check if the given trace is for "pure emulation"
+ *
+ * @param trace the trace to check
+ * @return true if created for emulation, false otherwise
+ */
+ public static boolean isEmulatedProgram(Trace trace) {
+ TraceSnapshot snapshot = trace.getTimeManager().getSnapshot(0, false);
+ if (snapshot == null) {
+ return false;
+ }
+ if (!snapshot.getDescription().startsWith(EMULATION_STARTED_AT)) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/ReadsTargetMemoryPcodeExecutorState.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/ReadsTargetMemoryPcodeExecutorState.java
index 808afb8bb8..21b139e0e7 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/ReadsTargetMemoryPcodeExecutorState.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/ReadsTargetMemoryPcodeExecutorState.java
@@ -44,30 +44,37 @@ public class ReadsTargetMemoryPcodeExecutorState
@Override
protected void fillUninitialized(AddressSet uninitialized) {
- // TODO: fillUnknownWithStaticImages?
- if (!isLive()) {
- return;
- }
- AddressSet unknown = computeUnknown(uninitialized);
- if (unknown.isEmpty()) {
- return;
- }
- fillUnknownWithRecorder(unknown);
+ AddressSet unknown;
unknown = computeUnknown(uninitialized);
if (unknown.isEmpty()) {
return;
}
+ if (fillUnknownWithRecorder(unknown)) {
+ unknown = computeUnknown(uninitialized);
+ if (unknown.isEmpty()) {
+ return;
+ }
+ }
+ if (fillUnknownWithStaticImages(unknown)) {
+ unknown = computeUnknown(uninitialized);
+ if (unknown.isEmpty()) {
+ return;
+ }
+ }
Msg.warn(this, "Emulator read from UNKNOWN state: " + unknown);
}
- protected void fillUnknownWithRecorder(AddressSet unknown) {
+ protected boolean fillUnknownWithRecorder(AddressSet unknown) {
+ if (!isLive()) {
+ return false;
+ }
waitTimeout(recorder.captureProcessMemory(unknown, TaskMonitor.DUMMY));
+ return true;
}
- private void fillUnknownWithStaticImages(AddressSet unknown) {
- if (!space.isMemorySpace()) {
- return;
- }
+ private boolean fillUnknownWithStaticImages(AddressSet unknown) {
+ boolean result = false;
+ // TODO: Expand to block? DON'T OVERWRITE KNOWN!
DebuggerStaticMappingService mappingService =
tool.getService(DebuggerStaticMappingService.class);
byte[] data = new byte[4096];
@@ -76,12 +83,15 @@ public class ReadsTargetMemoryPcodeExecutorState
.entrySet()) {
Program program = ent.getKey();
ShiftAndAddressSetView shifted = ent.getValue();
- Msg.warn(this,
- "Filling in unknown trace memory in emulator using mapped image: " +
- program + ": " + shifted.getAddressSetView());
long shift = shifted.getShift();
Memory memory = program.getMemory();
- for (AddressRange rng : shifted.getAddressSetView()) {
+ AddressSetView initialized = memory.getLoadedAndInitializedAddressSet();
+ AddressSetView toRead = shifted.getAddressSetView().intersect(initialized);
+ Msg.warn(this,
+ "Filling in unknown trace memory in emulator using mapped image: " +
+ program + ": " + toRead);
+
+ for (AddressRange rng : toRead) {
long lower = rng.getMinAddress().getOffset();
long fullLen = rng.getLength();
while (fullLen > 0) {
@@ -101,8 +111,10 @@ public class ReadsTargetMemoryPcodeExecutorState
lower += len;
fullLen -= len;
}
+ result = true;
}
}
+ return result;
}
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/DebuggerModelServiceProxyPlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/DebuggerModelServiceProxyPlugin.java
index 851c91e95d..29678510b0 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/DebuggerModelServiceProxyPlugin.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/DebuggerModelServiceProxyPlugin.java
@@ -63,22 +63,22 @@ import ghidra.util.datastruct.ListenerSet;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
-@PluginInfo( //
- shortDescription = "Debugger models manager service (proxy to front-end)", //
- description = "Manage debug sessions, connections, and trace recording", //
- category = PluginCategoryNames.DEBUGGER, //
- packageName = DebuggerPluginPackage.NAME, //
- status = PluginStatus.RELEASED, //
- eventsConsumed = { ProgramActivatedPluginEvent.class, //
- ProgramClosedPluginEvent.class, //
- }, //
- servicesRequired = { //
- DebuggerTraceManagerService.class, //
- }, //
- servicesProvided = { //
- DebuggerModelService.class, //
- } //
-)
+@PluginInfo(
+ shortDescription = "Debugger models manager service (proxy to front-end)",
+ description = "Manage debug sessions, connections, and trace recording",
+ category = PluginCategoryNames.DEBUGGER,
+ packageName = DebuggerPluginPackage.NAME,
+ status = PluginStatus.RELEASED,
+ eventsConsumed = {
+ ProgramActivatedPluginEvent.class,
+ ProgramClosedPluginEvent.class,
+ },
+ servicesRequired = {
+ DebuggerTraceManagerService.class,
+ },
+ servicesProvided = {
+ DebuggerModelService.class,
+ })
public class DebuggerModelServiceProxyPlugin extends Plugin
implements DebuggerModelServiceInternal {
@@ -401,11 +401,17 @@ public class DebuggerModelServiceProxyPlugin extends Plugin
DockingAction action = it.next();
it.remove();
tool.removeAction(action);
+ String[] path = action.getMenuBarData().getMenuPath();
+ tool.setMenuGroup(Arrays.copyOf(path, path.length - 1), null);
}
for (DebuggerProgramLaunchOffer offer : offers) {
- actionDebugProgramMenus.add(DebugProgramAction.menuBuilder(offer, this, delegate)
+ DockingAction action = DebugProgramAction.menuBuilder(offer, this, delegate)
.onAction(ctx -> debugProgramMenuActivated(offer))
- .buildAndInstall(tool));
+ .build();
+ actionDebugProgramMenus.add(action);
+ String[] path = action.getMenuBarData().getMenuPath();
+ tool.setMenuGroup(Arrays.copyOf(path, path.length - 1), DebugProgramAction.GROUP, "zz");
+ tool.addAction(action);
}
}
@@ -449,7 +455,6 @@ public class DebuggerModelServiceProxyPlugin extends Plugin
ProgramActivatedPluginEvent evt = (ProgramActivatedPluginEvent) event;
currentProgram = evt.getActiveProgram();
currentProgramPath = getProgramPath(currentProgram);
-
updateActionDebugProgram();
}
if (event instanceof ProgramClosedPluginEvent) {
@@ -457,7 +462,6 @@ public class DebuggerModelServiceProxyPlugin extends Plugin
if (currentProgram == evt.getProgram()) {
currentProgram = null;
currentProgramPath = null;
-
updateActionDebugProgram();
}
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingServicePlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingServicePlugin.java
index 3f695ff998..1a22e6c920 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingServicePlugin.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingServicePlugin.java
@@ -63,26 +63,25 @@ import ghidra.util.exception.CancelledException;
import ghidra.util.exception.VersionException;
import ghidra.util.task.TaskMonitor;
-@PluginInfo( //
- shortDescription = "Debugger static mapping manager", //
- description = "Track and manage static mappings (program-trace relocations)", //
- category = PluginCategoryNames.DEBUGGER, //
- packageName = DebuggerPluginPackage.NAME, //
- status = PluginStatus.RELEASED, //
+@PluginInfo(
+ shortDescription = "Debugger static mapping manager",
+ description = "Track and manage static mappings (program-trace relocations)",
+ category = PluginCategoryNames.DEBUGGER,
+ packageName = DebuggerPluginPackage.NAME,
+ status = PluginStatus.RELEASED,
eventsConsumed = {
- ProgramOpenedPluginEvent.class, //
- ProgramClosedPluginEvent.class, //
- TraceOpenedPluginEvent.class, //
- TraceClosedPluginEvent.class, //
- }, //
- servicesRequired = { //
- ProgramManager.class, //
- DebuggerTraceManagerService.class, //
- }, //
- servicesProvided = { //
- DebuggerStaticMappingService.class, //
- } //
-)
+ ProgramOpenedPluginEvent.class,
+ ProgramClosedPluginEvent.class,
+ TraceOpenedPluginEvent.class,
+ TraceClosedPluginEvent.class,
+ },
+ servicesRequired = {
+ ProgramManager.class,
+ DebuggerTraceManagerService.class,
+ },
+ servicesProvided = {
+ DebuggerStaticMappingService.class,
+ })
public class DebuggerStaticMappingServicePlugin extends Plugin
implements DebuggerStaticMappingService, DomainFolderChangeAdapter {
@@ -1013,8 +1012,8 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
for (ModuleMapEntry ent : entries) {
monitor.checkCanceled();
try {
- addModuleMapping(ent.getModule(), ent.getModuleRange().getLength(),
- ent.getProgram(), truncateExisting);
+ DebuggerStaticMappingUtils.addModuleMapping(ent.getModule(),
+ ent.getModuleRange().getLength(), ent.getProgram(), truncateExisting);
}
catch (Exception e) {
Msg.error(this, "Could not add mapping " + ent + ": " + e.getMessage());
@@ -1022,16 +1021,6 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
}
}
- @Override
- public void addSectionMapping(TraceSection from, Program toProgram, MemoryBlock to,
- boolean truncateExisting) throws TraceConflictedMappingException {
- TraceLocation fromLoc = new DefaultTraceLocation(from.getTrace(), null,
- from.getModule().getLifespan(), from.getStart());
- ProgramLocation toLoc = new ProgramLocation(toProgram, to.getStart());
- long length = Math.min(from.getRange().getLength(), to.getSize());
- addMapping(fromLoc, toLoc, length, truncateExisting);
- }
-
@Override
public void addSectionMappings(Collection
+ * Note if the trace is backed by a Ghidra database, the caller must already have started a
+ * transaction on the relevant domain object.
+ *
+ *
+ * @param from the source trace location, including lifespan
+ * @param to the destination program location
+ * @param length the length of the mapped region
+ * @param truncateExisting true to delete or truncate the lifespan of overlapping entries
+ * @throws TraceConflictedMappingException if a conflicting mapping overlaps the source and
+ * {@code truncateExisting} is false.
+ */
+ public static void addMapping(TraceLocation from, ProgramLocation to, long length,
+ boolean truncateExisting)
+ throws TraceConflictedMappingException {
+ Program tp = to.getProgram();
+ if (tp instanceof TraceProgramView) {
+ throw new IllegalArgumentException(
+ "Mapping destination cannot be a " + TraceProgramView.class.getSimpleName());
+ }
+ TraceStaticMappingManager manager = from.getTrace().getStaticMappingManager();
+ URL toURL = ProgramURLUtils.getUrlFromProgram(tp);
+ if (toURL == null) {
+ noProject(DebuggerStaticMappingService.class);
+ }
+ try {
+ Address start = from.getAddress();
+ Address end = start.addNoWrap(length - 1);
+ // Also check end in the destination
+ Address toAddress = to.getAddress();
+ toAddress.addNoWrap(length - 1); // Anticipate possible AddressOverflow
+ AddressRangeImpl range = new AddressRangeImpl(start, end);
+ if (truncateExisting) {
+ long truncEnd = DBTraceUtils.lowerEndpoint(from.getLifespan()) - 1;
+ for (TraceStaticMapping existing : List
+ .copyOf(manager.findAllOverlapping(range, from.getLifespan()))) {
+ existing.delete();
+ if (Long.compareUnsigned(existing.getStartSnap(), truncEnd) < 0) {
+ manager.add(existing.getTraceAddressRange(),
+ Range.closed(existing.getStartSnap(), truncEnd),
+ existing.getStaticProgramURL(), existing.getStaticAddress());
+ }
+ }
+ }
+ manager.add(range, from.getLifespan(), toURL,
+ toAddress.toString(true));
+ }
+ catch (AddressOverflowException e) {
+ throw new IllegalArgumentException("Length would cause address overflow", e);
+ }
+ }
+
+ /**
+ * Add a static mapping (relocation) from the given module to the given program
+ *
+ *
+ * This is simply a shortcut and does not mean to imply that all mappings must represent module
+ * relocations. The lifespan is that of the module's.
+ *
+ * @param from the source module
+ * @param length the "size" of the module -- {@code max-min+1} as loaded/mapped in memory
+ * @param toProgram the destination program
+ * @see #addMapping(TraceLocation, ProgramLocation, long, boolean)
+ */
+ public static void addModuleMapping(TraceModule from, long length, Program toProgram,
+ boolean truncateExisting) throws TraceConflictedMappingException {
+ TraceLocation fromLoc =
+ new DefaultTraceLocation(from.getTrace(), null, from.getLifespan(), from.getBase());
+ ProgramLocation toLoc = new ProgramLocation(toProgram, toProgram.getImageBase());
+ addMapping(fromLoc, toLoc, length, truncateExisting);
+ }
+
+ /**
+ * Add a static mapping (relocation) from the given section to the given program memory block
+ *
+ *
+ * This is simply a shortcut and does not mean to imply that all mappings must represent section
+ * relocations. In most cases the lengths of the from and to objects match exactly, but this may
+ * not be the case. Whatever the case, the minimum length is computed, and the start addresses
+ * are used as the location. The lifespan is that of the section's containing module.
+ *
+ * @param from the source section
+ * @param toProgram the destination program
+ * @param to the destination memory block
+ * @see #addMapping(TraceLocation, ProgramLocation, long, boolean)
+ */
+ public static void addSectionMapping(TraceSection from, Program toProgram, MemoryBlock to,
+ boolean truncateExisting) throws TraceConflictedMappingException {
+ TraceLocation fromLoc = new DefaultTraceLocation(from.getTrace(), null,
+ from.getModule().getLifespan(), from.getStart());
+ ProgramLocation toLoc = new ProgramLocation(toProgram, to.getStart());
+ long length = Math.min(from.getRange().getLength(), to.getSize());
+ addMapping(fromLoc, toLoc, length, truncateExisting);
+ }
+}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerStaticMappingService.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerStaticMappingService.java
index d0b0739975..52a1f1b6cc 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerStaticMappingService.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/services/DebuggerStaticMappingService.java
@@ -487,7 +487,7 @@ public interface DebuggerStaticMappingService {
}
/**
- * A {@code (shift,view)} pair for describing sets of mapped addresses
+ * <<<<<<< HEAD A {@code (shift,view)} pair for describing sets of mapped addresses
*/
public class ShiftAndAddressSetView {
private final long shift;
@@ -559,7 +559,8 @@ public interface DebuggerStaticMappingService {
boolean truncateExisting) throws TraceConflictedMappingException;
/**
- * Add several static mappings (relocations)
+ * ======= >>>>>>> d694542c5 (GP-660: Put program filler back in. Need to performance test.) Add
+ * several static mappings (relocations)
*
*
* This will group the entries by trace and add each's entries in a single transaction. If any
@@ -574,23 +575,6 @@ public interface DebuggerStaticMappingService {
void addModuleMappings(Collection
- * This is simply a shortcut and does not mean to imply that all mappings must represent section
- * relocations. In most cases the lengths of the from and to objects match exactly, but this may
- * not be the case. Whatever the case, the minimum length is computed, and the start addresses
- * are used as the location. The lifespan is that of the section's containing module.
- *
- * @param from the source section
- * @param toProgram the destination program
- * @param to the destination memory block
- * @see #addMapping(TraceLocation, ProgramLocation, long, boolean)
- */
- void addSectionMapping(TraceSection from, Program toProgram, MemoryBlock to,
- boolean truncateExisting) throws TraceConflictedMappingException;
-
/**
* Add several static mappings (relocations)
*
diff --git a/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsPluginScreenShots.java b/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsPluginScreenShots.java
index 18b1923a3b..ab7125c811 100644
--- a/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsPluginScreenShots.java
+++ b/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsPluginScreenShots.java
@@ -30,6 +30,7 @@ import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest.Test
import ghidra.app.plugin.core.debug.service.breakpoint.DebuggerLogicalBreakpointServicePlugin;
import ghidra.app.plugin.core.debug.service.model.DebuggerModelServiceProxyPlugin;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin;
+import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils;
import ghidra.app.plugin.core.debug.service.tracemgr.DebuggerTraceManagerServicePlugin;
import ghidra.app.plugin.core.progmgr.ProgramManagerPlugin;
import ghidra.app.services.*;
@@ -130,12 +131,12 @@ public class DebuggerBreakpointsPluginScreenShots extends GhidraScreenShotGenera
mb.testProcess3.addRegion("echo:.text", mb.rng(0x7fac0000, 0x7fac0fff), "rx");
try (UndoableTransaction tid = UndoableTransaction.start(trace1, "Add mapping", true)) {
- mappingService.addMapping(
+ DebuggerStaticMappingUtils.addMapping(
new DefaultTraceLocation(trace1, null, Range.atLeast(0L), addr(trace1, 0x00400000)),
new ProgramLocation(program, addr(program, 0x00400000)), 0x00210000, false);
}
try (UndoableTransaction tid = UndoableTransaction.start(trace3, "Add mapping", true)) {
- mappingService.addMapping(
+ DebuggerStaticMappingUtils.addMapping(
new DefaultTraceLocation(trace3, null, Range.atLeast(0L), addr(trace3, 0x7fac0000)),
new ProgramLocation(program, addr(program, 0x00400000)), 0x00010000, false);
}
diff --git a/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/stack/DebuggerStackPluginScreenShots.java b/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/stack/DebuggerStackPluginScreenShots.java
index 140c0308d2..c389c5889e 100644
--- a/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/stack/DebuggerStackPluginScreenShots.java
+++ b/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/stack/DebuggerStackPluginScreenShots.java
@@ -20,6 +20,7 @@ import org.junit.*;
import com.google.common.collect.Range;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin;
+import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils;
import ghidra.app.plugin.core.debug.service.tracemgr.DebuggerTraceManagerServicePlugin;
import ghidra.app.plugin.core.progmgr.ProgramManagerPlugin;
import ghidra.app.services.*;
@@ -114,7 +115,7 @@ public class DebuggerStackPluginScreenShots extends GhidraScreenShotGenerator {
root.createFile("trace", tb.trace, TaskMonitor.DUMMY);
root.createFile("echo", program, TaskMonitor.DUMMY);
try (UndoableTransaction tid = tb.startTransaction()) {
- mappingService.addMapping(
+ DebuggerStaticMappingUtils.addMapping(
new DefaultTraceLocation(tb.trace, null, Range.atLeast(snap), tb.addr(0x00400000)),
new ProgramLocation(program, addr(program, 0x00400000)), 0x10000, false);
}
diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPluginTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPluginTest.java
index 503ca5f715..f389a1051b 100644
--- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPluginTest.java
+++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPluginTest.java
@@ -41,6 +41,7 @@ import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin;
+import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils;
import ghidra.app.services.*;
import ghidra.app.services.LogicalBreakpoint.Enablement;
import ghidra.app.util.viewer.listingpanel.ListingPanel;
@@ -116,7 +117,7 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu
protected void addMapping(Trace trace) throws Exception {
try (UndoableTransaction tid = UndoableTransaction.start(trace, "Add mapping", true)) {
- mappingService.addMapping(
+ DebuggerStaticMappingUtils.addMapping(
new DefaultTraceLocation(trace, null, Range.atLeast(0L), addr(trace, 0x55550123)),
new ProgramLocation(program, addr(program, 0x00400123)), 0x1000, false);
}
diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProviderTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProviderTest.java
index afa7879316..084373dafd 100644
--- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProviderTest.java
+++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProviderTest.java
@@ -32,6 +32,7 @@ import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
import ghidra.app.plugin.core.debug.gui.breakpoint.DebuggerBreakpointsProvider.LogicalBreakpointTableModel;
import ghidra.app.plugin.core.debug.gui.console.DebuggerConsolePlugin;
+import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils;
import ghidra.app.services.*;
import ghidra.app.services.LogicalBreakpoint.Enablement;
import ghidra.async.AsyncTestUtils;
@@ -70,7 +71,7 @@ public class DebuggerBreakpointsProviderTest extends AbstractGhidraHeadedDebugge
protected void addMapping(Trace trace, Program prog) throws Exception {
try (UndoableTransaction tid = UndoableTransaction.start(trace, "Add mapping", true)) {
- mappingService.addMapping(
+ DebuggerStaticMappingUtils.addMapping(
new DefaultTraceLocation(trace, null, Range.atLeast(0L), addr(trace, 0x55550000)),
new ProgramLocation(prog, addr(prog, 0x00400000)), 0x1000, false);
}
diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProviderTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProviderTest.java
index e40336e992..ed502a991e 100644
--- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProviderTest.java
+++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProviderTest.java
@@ -42,6 +42,7 @@ import ghidra.app.plugin.core.debug.gui.console.DebuggerConsolePlugin;
import ghidra.app.plugin.core.debug.gui.console.DebuggerConsoleProvider.BoundAction;
import ghidra.app.plugin.core.debug.gui.console.DebuggerConsoleProvider.LogRow;
import ghidra.app.plugin.core.debug.gui.modules.DebuggerMissingModuleActionContext;
+import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils;
import ghidra.app.services.*;
import ghidra.app.util.viewer.listingpanel.ListingPanel;
import ghidra.async.SwingExecutorService;
@@ -461,7 +462,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
TraceLocation from =
new DefaultTraceLocation(tb.trace, null, Range.atLeast(0L), tb.addr(0x00400000));
ProgramLocation to = new ProgramLocation(program, ss.getAddress(0x00600000));
- mappingService.addMapping(from, to, 0x8000, false);
+ DebuggerStaticMappingUtils.addMapping(from, to, 0x8000, false);
}
waitForProgram(program);
waitForDomainObject(tb.trace);
@@ -512,7 +513,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
TraceLocation from =
new DefaultTraceLocation(tb.trace, null, Range.atLeast(0L), tb.addr(0x00400000));
ProgramLocation to = new ProgramLocation(program, ss.getAddress(0x00600000));
- mappingService.addMapping(from, to, 0x8000, false);
+ DebuggerStaticMappingUtils.addMapping(from, to, 0x8000, false);
thread = tb.getOrAddThread("Thread1", 0);
Register pc = tb.trace.getBaseLanguage().getProgramCounter();
@@ -552,7 +553,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
TraceLocation from =
new DefaultTraceLocation(tb.trace, null, Range.atLeast(0L), tb.addr(0x00400000));
ProgramLocation to = new ProgramLocation(program, ss.getAddress(0x00600000));
- mappingService.addMapping(from, to, 0x8000, false);
+ DebuggerStaticMappingUtils.addMapping(from, to, 0x8000, false);
}
waitForProgram(program);
waitForDomainObject(tb.trace);
@@ -643,7 +644,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
TraceLocation from =
new DefaultTraceLocation(tb.trace, null, Range.atLeast(0L), tb.addr(0x00400000));
ProgramLocation to = new ProgramLocation(program, ss.getAddress(0x00600000));
- mappingService.addMapping(from, to, 0x8000, false);
+ DebuggerStaticMappingUtils.addMapping(from, to, 0x8000, false);
thread = tb.getOrAddThread("Thread1", 0);
Register pc = tb.trace.getBaseLanguage().getProgramCounter();
@@ -906,7 +907,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
TraceLocation from =
new DefaultTraceLocation(tb.trace, null, Range.atLeast(0L), tb.addr(0x00400000));
ProgramLocation to = new ProgramLocation(program, ss.getAddress(0x00600000));
- mappingService.addMapping(from, to, 0x8000, false);
+ DebuggerStaticMappingUtils.addMapping(from, to, 0x8000, false);
}
waitForProgram(program);
waitForDomainObject(tb.trace);
diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/stack/DebuggerStackProviderTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/stack/DebuggerStackProviderTest.java
index 04e86a3671..a85db35c31 100644
--- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/stack/DebuggerStackProviderTest.java
+++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/stack/DebuggerStackProviderTest.java
@@ -28,6 +28,7 @@ import com.google.common.collect.Range;
import generic.Unique;
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin;
+import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils;
import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet;
@@ -496,7 +497,7 @@ public class DebuggerStackProviderTest extends AbstractGhidraHeadedDebuggerGUITe
TraceLocation dloc =
new DefaultTraceLocation(tb.trace, null, Range.atLeast(0L), tb.addr(0x00400000));
ProgramLocation sloc = new ProgramLocation(program, addr(program, 0x00600000));
- mappingService.addMapping(dloc, sloc, 0x1000, false);
+ DebuggerStaticMappingUtils.addMapping(dloc, sloc, 0x1000, false);
}
waitForDomainObject(tb.trace);
diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/breakpoint/DebuggerLogicalBreakpointServiceTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/breakpoint/DebuggerLogicalBreakpointServiceTest.java
index 10bbe5fc39..32c207a659 100644
--- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/breakpoint/DebuggerLogicalBreakpointServiceTest.java
+++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/breakpoint/DebuggerLogicalBreakpointServiceTest.java
@@ -26,6 +26,7 @@ import org.junit.*;
import generic.Unique;
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
+import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils;
import ghidra.app.services.*;
import ghidra.app.services.LogicalBreakpoint.Enablement;
import ghidra.async.AsyncReference;
@@ -273,8 +274,10 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe
waitFor(() -> r.getTraceMemoryRegion(region), "Recorder missed region: " + region);
try (UndoableTransaction tid =
UndoableTransaction.start(t, "Add .text mapping", true)) {
- mappingService.addMapping(new DefaultTraceLocation(t, null, textRegion.getLifespan(),
- textRegion.getMinAddress()), new ProgramLocation(p, addr(p, 0x00400000)), 0x1000,
+ DebuggerStaticMappingUtils.addMapping(
+ new DefaultTraceLocation(t, null, textRegion.getLifespan(),
+ textRegion.getMinAddress()),
+ new ProgramLocation(p, addr(p, 0x00400000)), 0x1000,
false);
}
}
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
new file mode 100644
index 0000000000..b8cb8d0530
--- /dev/null
+++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServiceTest.java
@@ -0,0 +1,98 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.app.plugin.core.debug.service.emulation;
+
+import static org.junit.Assert.*;
+
+import java.math.BigInteger;
+
+import org.junit.Test;
+
+import generic.Unique;
+import ghidra.app.plugin.assembler.Assembler;
+import ghidra.app.plugin.assembler.Assemblers;
+import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
+import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.lang.Register;
+import ghidra.program.model.mem.Memory;
+import ghidra.program.model.mem.MemoryBlock;
+import ghidra.trace.model.Trace;
+import ghidra.trace.model.memory.TraceMemoryRegisterSpace;
+import ghidra.trace.model.thread.TraceThread;
+import ghidra.trace.model.time.TraceSchedule;
+import ghidra.util.database.UndoableTransaction;
+import ghidra.util.task.TaskMonitor;
+
+public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerGUITest {
+ protected DebuggerEmulationServicePlugin emulationPlugin;
+
+ @Test
+ public void testPureEmulation() throws Exception {
+ emulationPlugin = addPlugin(tool, DebuggerEmulationServicePlugin.class);
+
+ // TODO: Action enablement doesn't work without CodeBrowser???
+ // Probably missing some contextChanged, but I have no provider!
+ addPlugin(tool, CodeBrowserPlugin.class);
+
+ createProgram();
+ intoProject(program);
+ Assembler asm = Assemblers.getAssembler(program);
+ Memory memory = program.getMemory();
+ Address addrText = addr(program, 0x00400000);
+ Register regPC = program.getRegister("pc");
+ Register regR0 = program.getRegister("r0");
+ Register regR1 = program.getRegister("r1");
+ try (UndoableTransaction tid = UndoableTransaction.start(program, "Initialize", true)) {
+ MemoryBlock blockText =
+ memory.createInitializedBlock(".text", addrText, 0x1000, (byte) 0,
+ TaskMonitor.DUMMY, false);
+ blockText.setExecute(true);
+ memory.createInitializedBlock(".data", addr(program, 0x00600000), 0x1000, (byte) 0,
+ TaskMonitor.DUMMY, false);
+ asm.assemble(addrText, "mov r0, r1");
+ program.getProgramContext()
+ .setValue(regR1, addrText, addrText, new BigInteger("1234", 16));
+ }
+
+ programManager.openProgram(program);
+ waitForSwing();
+
+ assertTrue(emulationPlugin.actionEmulateProgram.isEnabled());
+ performAction(emulationPlugin.actionEmulateProgram);
+
+ Trace trace = traceManager.getCurrentTrace();
+ assertNotNull(trace);
+
+ TraceThread thread = Unique.assertOne(trace.getThreadManager().getAllThreads());
+ TraceMemoryRegisterSpace regs =
+ trace.getMemoryManager().getMemoryRegisterSpace(thread, false);
+ assertEquals(new BigInteger("00400000", 16),
+ regs.getViewValue(0, regPC).getUnsignedValue());
+ 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);
+
+ assertEquals(new BigInteger("00400002", 16),
+ regs.getViewValue(scratch, regPC).getUnsignedValue());
+ assertEquals(new BigInteger("1234", 16),
+ regs.getViewValue(scratch, regR0).getUnsignedValue());
+ assertEquals(new BigInteger("1234", 16),
+ regs.getViewValue(scratch, regR1).getUnsignedValue());
+ }
+}
diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingServiceTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingServiceTest.java
index d2fe5d5554..f42ff513f8 100644
--- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingServiceTest.java
+++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingServiceTest.java
@@ -75,7 +75,7 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
dynSpace.getAddress(0x00100000));
ProgramLocation to = new ProgramLocation(program, stSpace.getAddress(0x00200000));
try (UndoableTransaction tid = tb.startTransaction()) {
- mappingService.addMapping(from, to, 0x1000, false);
+ DebuggerStaticMappingUtils.addMapping(from, to, 0x1000, false);
}
waitForDomainObject(tb.trace);
}
@@ -85,7 +85,7 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
dynSpace.getAddress(0x00100800));
ProgramLocation to = new ProgramLocation(program, stSpace.getAddress(0x00300000));
try (UndoableTransaction tid = tb.startTransaction()) {
- mappingService.addMapping(from, to, 0x1000, truncateExisting);
+ DebuggerStaticMappingUtils.addMapping(from, to, 0x1000, truncateExisting);
}
}
@@ -104,7 +104,7 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
dynSpace.getAddress(0x00102000));
ProgramLocation to = new ProgramLocation(program, stSpace.getAddress(0x00200000));
try (UndoableTransaction tid = tb.startTransaction()) {
- mappingService.addMapping(from, to, 0x800, false);
+ DebuggerStaticMappingUtils.addMapping(from, to, 0x800, false);
}
waitForDomainObject(tb.trace);
}
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTrace.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTrace.java
index 4912377b78..e7f4eeace0 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTrace.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTrace.java
@@ -134,10 +134,15 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
throws IOException, LanguageNotFoundException {
super(new DBHandle(), DBOpenMode.CREATE, TaskMonitor.DUMMY, name, DB_TIME_INTERVAL,
DB_BUFFER_SIZE, consumer);
+
this.storeFactory = new DBCachedObjectStoreFactory(this);
this.baseLanguage = baseCompilerSpec.getLanguage();
- this.baseCompilerSpec = baseCompilerSpec;
- this.baseAddressFactory = new ProgramAddressFactory(baseLanguage, baseCompilerSpec);
+ // Need to "downgrade" the compiler spec, so nothing program-specific seeps in
+ // TODO: Should there be a TraceCompilerSpec?
+ this.baseCompilerSpec =
+ baseLanguage.getCompilerSpecByID(baseCompilerSpec.getCompilerSpecID());
+ this.baseAddressFactory =
+ new ProgramAddressFactory(this.baseLanguage, this.baseCompilerSpec);
try (UndoableTransaction tid = UndoableTransaction.start(this, "Create", false)) {
initOptions(DBOpenMode.CREATE);