GP-1214: Adding copy-into-progarm actions (plugin). Moving export action.

This commit is contained in:
Dan 2021-11-29 11:34:28 -05:00
parent 71c476bb6a
commit 1b5384c00c
38 changed files with 3855 additions and 505 deletions

View file

@ -41,6 +41,8 @@ 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/DebuggerBreakpointsPlugin/images/breakpoints-make-effective.png||GHIDRA||||END|
src/main/help/help/topics/DebuggerConsolePlugin/DebuggerConsolePlugin.html||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/DebuggerConsolePlugin/images/DebuggerConsolePlugin.png||GHIDRA||||END|
src/main/help/help/topics/DebuggerCopyActionsPlugin/DebuggerCopyActionsPlugin.html||GHIDRA||||END|
src/main/help/help/topics/DebuggerCopyActionsPlugin/images/DebuggerCopyIntoProgramDialog.png||GHIDRA||||END|
src/main/help/help/topics/DebuggerEmulationServicePlugin/DebuggerEmulationServicePlugin.html||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/DebuggerInterpreterPlugin/DebuggerInterpreterPlugin.html||GHIDRA||||END|
src/main/help/help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html||GHIDRA||||END| src/main/help/help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html||GHIDRA||||END|

View file

@ -56,6 +56,7 @@
<tocdef id="DebuggerGettingStarted" text="Getting Started" <tocdef id="DebuggerGettingStarted" text="Getting Started"
sortgroup="a" sortgroup="a"
target="help/topics/Debugger/GettingStarted.html" > target="help/topics/Debugger/GettingStarted.html" >
<tocdef id="Launching" text="Launching a Target" <tocdef id="Launching" text="Launching a Target"
sortgroup="a" sortgroup="a"
target="help/topics/Debugger/GettingStarted.html#launching" /> target="help/topics/Debugger/GettingStarted.html#launching" />
@ -78,6 +79,10 @@
sortgroup="c1" sortgroup="c1"
target="help/topics/DebuggerConsolePlugin/DebuggerConsolePlugin.html" /> target="help/topics/DebuggerConsolePlugin/DebuggerConsolePlugin.html" />
<tocdef id="DebuggerCopyActionsPlugin" text="Copy Actions"
sortgroup="c2"
target="help/topics/DebuggerCopyActionsPlugin/DebuggerCopyActionsPlugin.html" />
<tocdef id="DebuggerObjectsPlugin" text="Commands and Objects" <tocdef id="DebuggerObjectsPlugin" text="Commands and Objects"
sortgroup="d" sortgroup="d"
target="help/topics/DebuggerObjectsPlugin/DebuggerObjectsPlugin.html" /> target="help/topics/DebuggerObjectsPlugin/DebuggerObjectsPlugin.html" />
@ -98,14 +103,14 @@
sortgroup="g1" sortgroup="g1"
target="help/topics/DebuggerEmulationServicePlugin/DebuggerEmulationServicePlugin.html" /> target="help/topics/DebuggerEmulationServicePlugin/DebuggerEmulationServicePlugin.html" />
<tocdef id="DebuggerRegistersPlugin" text="Registers"
sortgroup="h"
target="help/topics/DebuggerRegistersPlugin/DebuggerRegistersPlugin.html" />
<tocdef id="DebuggerMemoryBytesPlugin" text="Memory" <tocdef id="DebuggerMemoryBytesPlugin" text="Memory"
sortgroup="h1" sortgroup="h1"
target="help/topics/DebuggerMemoryBytesPlugin/DebuggerMemoryBytesPlugin.html" /> target="help/topics/DebuggerMemoryBytesPlugin/DebuggerMemoryBytesPlugin.html" />
<tocdef id="DebuggerRegistersPlugin" text="Registers"
sortgroup="h"
target="help/topics/DebuggerRegistersPlugin/DebuggerRegistersPlugin.html" />
<tocdef id="DebuggerListingPlugin" text="Dynamic Listing" <tocdef id="DebuggerListingPlugin" text="Dynamic Listing"
sortgroup="i" sortgroup="i"
target="help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html" /> target="help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html" />

View file

@ -0,0 +1,178 @@
<!DOCTYPE doctype PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN">
<HTML>
<HEAD>
<META name="generator" content=
"HTML Tidy for Java (vers. 2009-12-01), see jtidy.sourceforge.net">
<TITLE>Debugger: Copy Actions</TITLE>
<META http-equiv="Content-Type" content="text/html; charset=windows-1252">
<LINK rel="stylesheet" type="text/css" href="../../shared/Frontpage.css">
</HEAD>
<BODY lang="EN-US">
<H1><A name="plugin"></A>Debugger: Copy Actions</H1>
<P>In the course of debugging, the user may want to capture certain state and annotations from
the dynamic context into the static. This might include the contents of the stack, the heap, or
example data stored in uninitialized memory. The copy actions allow for the easy movement of
data and annotations from traces into programs. The actions are all accessed via the <SPAN
class="menu">Debugger</SPAN> menu.</P>
<H2>Actions</H2>
<H3><A name="copy_into_current"></A>Copy Into Current Program</H3>
<P>This action requires a selection of memory in a dynamic view. It copies selected contents
from the current trace (at the current time) into the current program. The <A href=
"#dialog">Copy Dialog</A> is presented with the current program set as the destination.</P>
<H3><A name="copy_into_new"></A>Copy Into New Program</H3>
<P>This action requires a selection of memory in a dynamic view. It copies selected contents
from the current trace (at the current time) into a new program. The <A href="#dialog">Copy
Dialog</A> is presented with <B>&lt;New Program&gt;</B> set as the destination.</P>
<H3><A name="export_view"></A>Export Trace View</H3>
<P>This action is available whenever a trace is open. The <A href=
"help/topics/ExporterPlugin/exporter.htm#Exporter_Dialog">Export Dialog</A> is presented for
the current trace at the current time. This provides a mechanism for capturing a particular
point in time from a trace to a file. The exported image can be analyzed in Ghidra or another
tool.</P>
<H2><A name="dialog"></A>Copy Dialog</H2>
<P>The <B>Copy Into...</B> actions both present the same dialog: (The <B>Export Trace View</B>
action uses a different dialog.)</P>
<TABLE width="100%">
<TBODY>
<TR>
<TD align="center" width="100%"><IMG alt="" src=
"images/DebuggerCopyIntoProgramDialog.png"></TD>
</TR>
</TBODY>
</TABLE>
<P>The dialog consists of several options, followed by a table that displays the proposed
ranges to copy. For selected ranges not contained in the destination program's memory, new
blocks are proposed. The source selection is always broken apart by regions defined in the
trace's memory manager.</P>
<H3>Options</H3>
<UL>
<LI>The <B>Destination</B> drop down allows the choice of an alternative destination. All
open programs, <B>&lt;New Program&gt;</B>, and <B>&lt;Temporary Program&gt;</B> are available
for selection. Modifying this option will reset the proposal. Choosing <B>&lt;New
Program&gt;</B> will prompt for a new destination program upon a successful copy. Choosing
<B>&lt;Temporary Program&gt;</B> will create a temporary, read-only program. The temporary
program can still be saved later.</LI>
<LI>The <B>Read live</B> checkbox includes the <A href=
"help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html#read_memory">Read Memory</A>
action in the copy process. This is only available when the trace is live and at the present.
This assures that bytes copied into the destination program are actually the bytes from the
live target, not just a stale cache or default zeros. <B>Note:</B> The copy operation will
proceed even if the read-live-memory step fails, or only partially succeeds. Include <A href=
"#state">State</A> if you need to know which bytes are up to date in the destination
program.</LI>
<LI>The <B>Relocate</B> checkbox enables the use of <A href=
"help/topics/DebuggerStaticMappingPlugin/DebuggerStaticMappingPlugin.html">Static
Mappings</A> when determining the destination addresses. This is only available for existing
programs, and will only operate on portions of the source trace that are mapped to the
destination program. Modifying this option will reset the proposal.</LI>
<LI>The <B><A name="use_overlays"></A>Use overlays</B> checkbox causes the dialog to propose
overlay blocks for destination ranges that already exist in the program's memory. When
unchecked, ranges are broken apart so that portions already in the destination memory map
will not modify the map. Portions not already in the memory map will generate new blocks.
When checked, destination ranges are not broken apart. If any portion already exists in the
destination memory map, the entire range will generate an overlay block. Modifying this
option will reset the proposal.</LI>
<LI>The <B>Include</B> checkboxes determine which contents are transferred from the current
trace view into the destination. The <B>Select All</B> and <B>Select None</B> buttons do as
they say. <B>Note:</B> Even if no items are selected, the destination blocks will be created,
if the dialog is confirmed.</LI>
<UL>
<LI><B>Bookmarks</B> copies bookmarks contained in the selection.</LI>
<LI><B>Breakpoints</B> copies breakpoints contained in the selection. <B>Note:</B> Since
programs do not support breakpoints, bookmarks are used instead. They are the same type
and category as those used by the <A href=
"help/topics/DebuggerBreakpointsPlugin/DebuggerBreakpointsPlugin.html">Breakpoints</A>
window.</LI>
<LI><B>Bytes</B> copies the actual memory contents of the selection. <B>Note:</B> When
copying into an uninitialized block, the entire block becomes initialized, i.e., all
<CODE>??</CODE>s become <CODE>00</CODE>s; however, only the selected ranges are actually
copied in.</LI>
<LI><B>Comments</B> copies all comments contained in the selection.</LI>
<LI><B>Data</B> copies all (non-dynamic) data annotations contained in the selection. It
is only available when the source and destination agree on data organization.</LI>
<LI><B>Dynamic Data</B> copies all data annotations for dynamic data types. This item
requires <B>Bytes</B> to also be copied, since the properties (particularly length) of
each unit may depend on the memory contents. It is only available when the source and
destination agree on data organization.</LI>
<LI><B>Instructions</B> copies all disassembled instructions in the selection. This item
requires <B>Bytes</B> to also be copied, since instructions depend on the memory
contents. It is only available when the source and destination have identical
languages.</LI>
<LI><B>Labels</B> copies all labels contained in the selection.</LI>
<LI><B>References</B> copies all memory references where both the "from" and "to"
addresses are contained in the selection.</LI>
<LI><B><A name="state"></A>State</B> copies the memory states (stale, error, known) of
the selection. <B>Note:</B> Since programs do not support memory state, the program's
color map is used instead.</LI>
</UL>
</UL>
<H3>Table Columns</H3>
<P>The table displays the proposal and allows for some adjustments. It has the following
columns:</P>
<UL>
<LI>Remove - a button to remove the selected range from the proposal. If applicable, the
destination block will no longer be created.</LI>
<LI>Region - the name of the source memory region of the trace.</LI>
<LI>Modules - the names of modules that touch the source range.</LI>
<LI>Sections - the names of sections that touch the source range.</LI>
<LI>SrcMin - the minimum address in the source range.</LI>
<LI>SrcMax - the maximum address in the source range.</LI>
<LI>Block - the name of the destination memory block of the program. If the block already
exists, the name is displayed with an asterisk. The block will not be created, but contents
will still be copied into it. If the block does not already exist, the name can be changed by
editing this cell.</LI>
<LI>Overlay - indicates whether or not a created block will be an overlay block. This cannot
be modified. See <A href="#use_overlays">Use overlays</A> above.</LI>
<LI>DstMin - the minimum address in the destination range.</LI>
<LI>DstMax - the maximum address in the destination range.</LI>
</UL>
<P>The <B>Copy</B> button confirms the dialog and copies <EM>all proposed ranges</EM> in the
table. If successful, the dialog is closed. The <B>Cancel</B> button dismisses the dialog
without performing any operation. The <B>Reset</B> button resets the proposal, in case entries
were accidentally removed or modified.</P>
</BODY>
</HTML>

View file

@ -49,7 +49,7 @@
surprising. For example, disassembling some instructions and then stepping back in time will surprising. For example, disassembling some instructions and then stepping back in time will
cause that disassembly to disappear.</P> cause that disassembly to disappear.</P>
<P>Because not all memory is captured, some background coloring is used to indicate the state <P>Because not all memory is recorded, some background coloring is used to indicate the state
of attempted memory reads. Regardless of state, the most-recent contents, as recorded in the of attempted memory reads. Regardless of state, the most-recent contents, as recorded in the
trace, are displayed in the listing, defaulting to 00. "Stale" memory, that is ranges of memory trace, are displayed in the listing, defaulting to 00. "Stale" memory, that is ranges of memory
which have not been read at the current time, are displayed with a darker background. Where which have not been read at the current time, are displayed with a darker background. Where
@ -142,33 +142,32 @@
and/or needs a version upgrade. It will attempt to open the program, allowing Ghidra to prompt and/or needs a version upgrade. It will attempt to open the program, allowing Ghidra to prompt
you about the situation.</P> you about the situation.</P>
<H3><A name="capture_memory"></A>Capture Memory</H3> <H3><A name="read_memory"></A>Read Memory</H3>
<P>This action is available when the current trace is "at the present" with a live target, and <P>This action is available when the current trace is "at the present" with a live target, and
there is a selection of addresses in the dynamic listing. It will instruct the recorder to there is a selection of addresses in the dynamic listing. It will instruct the recorder to read
capture the contents of memory for the selected range(s). Typically, the viewable addresses are and record the contents of memory for the selected range(s). Typically, the viewable addresses
automatically captured &mdash; see the Auto-Read action.</P> are automatically read, anyway &mdash; see the Auto-Read action.</P>
<H3><A name="auto_memory"></A>Auto-Read Memory</H3> <H3><A name="auto_memory"></A>Auto-Read Memory</H3>
<P>This action is always available on all dynamic listings. It configures whether or not the <P>This action is always available on all dynamic listings. It configures whether or not the
memory range(s) displayed in the listing are automatically captured. Like the Capture Memory memory range(s) displayed in the listing are automatically read and recorded. Like the Read
action, capture can only occur when the current trace is "at the present" with a live target. Memory action, it is only permitted when the current trace is "at the present" with a live
It occurs when the user scrolls the listing, or when the listing is otherwise navigated to a target. It occurs when the user scrolls the listing, or when the listing is otherwise navigated
new location. Note that other components may capture memory, regardless of this listing's to a new location. Note that other components may read memory, regardless of this listing's
configuration. For example, the recorder typically captures the page of memory pointed to by configuration. For example, the recorder typically reads the page of memory pointed to by the
the program counter. In other words, this action <EM>cannot</EM> "disable all memory captures." program counter. In other words, this action <EM>cannot</EM> "disable all memory reads." The
The options are pluggable, but currently consist of:</P> options are pluggable, but currently consist of:</P>
<UL> <UL>
<LI>Do Not Read Memory - disables automatic memory capture <EM>for this listing <LI>Do Not Read Memory - disables automatic memory reads <EM>for this listing only</EM>.</LI>
only</EM>.</LI>
<LI>Read Visible Memory - automatically reads stale ranges that enter this listing's <LI>Read Visible Memory - automatically reads stale ranges that enter this listing's
view.</LI> view.</LI>
<LI>Read Visible Memory, RO Once - (default) behaves like Read Visible Memory, except it will <LI>Read Visible Memory, RO Once - (default) behaves like Read Visible Memory, except it will
neglect to capture read-only ranges that have been captured previously.</LI> neglect read-only ranges that have been read previously.</LI>
</UL> </UL>
<H2><A name="colors"></A>Tool Options: Colors</H2> <H2><A name="colors"></A>Tool Options: Colors</H2>

View file

@ -36,7 +36,7 @@
limitation is that you cannot use snapshots to display different points in time for the same limitation is that you cannot use snapshots to display different points in time for the same
trace.</P> trace.</P>
<P>Because not all memory is captured, some background coloring is used to indicate the state <P>Because not all memory is recorded, some background coloring is used to indicate the state
of attempted memory reads. Regardless of state, the most-recent contents, as recorded in the of attempted memory reads. Regardless of state, the most-recent contents, as recorded in the
trace, are displayed in the window, defaulting to 00. "Stale" memory, that is ranges of memory trace, are displayed in the window, defaulting to 00. "Stale" memory, that is ranges of memory
which have not been read at the current time, are displayed with a darker background. Where which have not been read at the current time, are displayed with a darker background. Where
@ -103,33 +103,32 @@
</TBODY> </TBODY>
</TABLE> </TABLE>
<H3><A name="capture_memory"></A>Capture Memory</H3> <H3><A name="read_memory"></A>Read Memory</H3>
<P>This action is available when the current trace is "at the present" with a live target, and <P>This action is available when the current trace is "at the present" with a live target, and
there is a selection of addresses in the memory window. It will instruct the recorder to there is a selection of addresses in the memory window. It will instruct the recorder to read
capture the contents of memory for the selected range(s). Typically, the viewable addresses are and record the contents of memory for the selected range(s). Typically, the viewable addresses
automatically captured &mdash; see the Auto-Read action.</P> are automatically read &mdash; see the Auto-Read action.</P>
<H3><A name="auto_memory"></A>Auto-Read Memory</H3> <H3><A name="auto_memory"></A>Auto-Read Memory</H3>
<P>This action is always available on all memory windows. It configures whether or not the <P>This action is always available on all memory windows. It configures whether or not the
memory range(s) displayed in the window are automatically captured. Like the Capture Memory memory range(s) displayed in the window are automatically read and recorded. Like the Read
action, capture can only occur when the current trace is "at the present" with a live target. Memory action, it is only permitted when the current trace is "at the present" with a live
It occurs when the user scrolls the window, or when the window is otherwise navigated to a new target. It occurs when the user scrolls the window, or when the window is otherwise navigated
location. Note that other components may capture memory, regardless of this windows's to a new location. Note that other components may read memory, regardless of this windows's
configuration. For example, the recorder typically captures the page of memory pointed to by configuration. For example, the recorder typically reads the page of memory pointed to by the
the program counter. In other words, this action <EM>cannot</EM> "disable all memory captures." program counter. In other words, this action <EM>cannot</EM> "disable all memory reads." The
The options are pluggable, but currently consist of:</P> options are pluggable, but currently consist of:</P>
<UL> <UL>
<LI>Do Not Read Memory - disables automatic memory capture <EM>for this window <LI>Do Not Read Memory - disables automatic memory reads <EM>for this window only</EM>.</LI>
only</EM>.</LI>
<LI>Read Visible Memory - automatically reads stale ranges that enter this window's <LI>Read Visible Memory - automatically reads stale ranges that enter this window's
view.</LI> view.</LI>
<LI>Read Visible Memory, RO Once - (default) behaves like Read Visible Memory, except it will <LI>Read Visible Memory, RO Once - (default) behaves like Read Visible Memory, except it will
neglect to capture read-only ranges that have been captured previously.</LI> neglect read-only ranges that have been read previously.</LI>
</UL> </UL>
<H3><A name="Byte_Viewer_Options"></A>Byte Viewer Options</H3> <H3><A name="Byte_Viewer_Options"></A>Byte Viewer Options</H3>

View file

@ -144,8 +144,8 @@ public interface DebuggerResources {
ImageIcon ICON_AUTOREAD = ResourceManager.loadImage("images/autoread.png"); ImageIcon ICON_AUTOREAD = ResourceManager.loadImage("images/autoread.png");
// TODO: Draw a real icon. // TODO: Draw a real icon.
ImageIcon ICON_CAPTURE_MEMORY = ICON_REGIONS; ImageIcon ICON_READ_MEMORY = ICON_REGIONS;
//ResourceManager.loadImage("images/capture-memory.png"); //ResourceManager.loadImage("images/read-memory.png");
// TODO: Draw an icon // TODO: Draw an icon
ImageIcon ICON_MAP_IDENTICALLY = ResourceManager.loadImage("images/doubleArrow.png"); ImageIcon ICON_MAP_IDENTICALLY = ResourceManager.loadImage("images/doubleArrow.png");
@ -777,14 +777,15 @@ public interface DebuggerResources {
} }
} }
abstract class AbstractCaptureSelectedMemoryAction extends DockingAction { abstract class AbstractReadSelectedMemoryAction extends DockingAction {
public static final String NAME = "Capture Selected Memory"; public static final String NAME = "Read Selected Memory";
public static final Icon ICON = ICON_CAPTURE_MEMORY; public static final Icon ICON = ICON_READ_MEMORY;
public static final String HELP_ANCHOR = "capture_memory"; public static final String HELP_ANCHOR = "read_memory";
public AbstractCaptureSelectedMemoryAction(Plugin owner) { public AbstractReadSelectedMemoryAction(Plugin owner) {
super(NAME, owner.getName()); super(NAME, owner.getName());
setDescription("Capture memory for the selected addresses into the trace database"); setDescription(
"(Re-)read and record memory for the selected addresses into the trace database");
setHelpLocation(new HelpLocation(owner.getName(), HELP_ANCHOR)); setHelpLocation(new HelpLocation(owner.getName(), HELP_ANCHOR));
} }
} }
@ -885,7 +886,7 @@ public interface DebuggerResources {
interface AutoReadMemoryAction { interface AutoReadMemoryAction {
String NAME = "Auto-Read Target Memory"; String NAME = "Auto-Read Target Memory";
String DESCRIPTION = "Automatically capture visible memory from the live target"; String DESCRIPTION = "Automatically read and record visible memory from the live target";
String HELP_ANCHOR = "auto_memory"; String HELP_ANCHOR = "auto_memory";
String NAME_VIS_RO_ONCE = "Read Visible Memory, RO Once"; String NAME_VIS_RO_ONCE = "Read Visible Memory, RO Once";
@ -1081,7 +1082,43 @@ public interface DebuggerResources {
return new ActionBuilder(NAME, ownerName) return new ActionBuilder(NAME, ownerName)
.description(DESCRIPTION) .description(DESCRIPTION)
.menuGroup(GROUP) .menuGroup(GROUP)
.menuPath(NAME) .menuPath(DebuggerPluginPackage.NAME, NAME)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
interface CopyIntoProgramAction {
String NAME_PAT = "Copy Into %s Program";
String DESC_PAT = "Copy the current selection into %s program";
String GROUP = GROUP_MAINTENANCE;
}
interface CopyIntoCurrentProgramAction extends CopyIntoProgramAction {
String NAME = String.format(NAME_PAT, "Current");
String DESCRIPTION = String.format(DESC_PAT, "the current");
String HELP_ANCHOR = "copy_into_current";
static ActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ActionBuilder(NAME, ownerName)
.description(DESCRIPTION)
.menuGroup(GROUP)
.menuPath(DebuggerPluginPackage.NAME, NAME)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
interface CopyIntoNewProgramAction extends CopyIntoProgramAction {
String NAME = String.format(NAME_PAT, "New");
String DESCRIPTION = String.format(DESC_PAT, "a new");
String HELP_ANCHOR = "copy_into_new";
static ActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ActionBuilder(NAME, ownerName)
.description(DESCRIPTION)
.menuGroup(GROUP)
.menuPath(DebuggerPluginPackage.NAME, NAME)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR)); .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
} }
} }

View file

@ -27,7 +27,7 @@ import docking.menu.MultiStateDockingAction;
import docking.widgets.EventTrigger; import docking.widgets.EventTrigger;
import ghidra.app.plugin.core.debug.DebuggerCoordinates; import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.gui.DebuggerResources; import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractCaptureSelectedMemoryAction; import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractReadSelectedMemoryAction;
import ghidra.app.plugin.core.debug.gui.action.AutoReadMemorySpec.AutoReadMemorySpecConfigFieldCodec; import ghidra.app.plugin.core.debug.gui.action.AutoReadMemorySpec.AutoReadMemorySpecConfigFieldCodec;
import ghidra.app.plugin.core.debug.utils.BackgroundUtils; import ghidra.app.plugin.core.debug.utils.BackgroundUtils;
import ghidra.app.services.TraceRecorder; import ghidra.app.services.TraceRecorder;
@ -49,10 +49,10 @@ public abstract class DebuggerReadsMemoryTrait {
protected static final AutoConfigState.ClassHandler<DebuggerReadsMemoryTrait> CONFIG_STATE_HANDLER = protected static final AutoConfigState.ClassHandler<DebuggerReadsMemoryTrait> CONFIG_STATE_HANDLER =
AutoConfigState.wireHandler(DebuggerReadsMemoryTrait.class, MethodHandles.lookup()); AutoConfigState.wireHandler(DebuggerReadsMemoryTrait.class, MethodHandles.lookup());
protected class CaptureSelectedMemoryAction extends AbstractCaptureSelectedMemoryAction { protected class ReadSelectedMemoryAction extends AbstractReadSelectedMemoryAction {
public static final String GROUP = DebuggerResources.GROUP_GENERAL; public static final String GROUP = DebuggerResources.GROUP_GENERAL;
public CaptureSelectedMemoryAction() { public ReadSelectedMemoryAction() {
super(plugin); super(plugin);
setToolBarData(new ToolBarData(ICON, GROUP)); setToolBarData(new ToolBarData(ICON, GROUP));
setEnabled(false); setEnabled(false);
@ -89,14 +89,14 @@ public abstract class DebuggerReadsMemoryTrait {
} }
} }
protected class ForCaptureTraceListener extends TraceDomainObjectListener { protected class ForReadsTraceListener extends TraceDomainObjectListener {
public ForCaptureTraceListener() { public ForReadsTraceListener() {
listenFor(TraceSnapshotChangeType.ADDED, this::snapshotAdded); listenFor(TraceSnapshotChangeType.ADDED, this::snapshotAdded);
listenFor(TraceMemoryStateChangeType.CHANGED, this::memStateChanged); listenFor(TraceMemoryStateChangeType.CHANGED, this::memStateChanged);
} }
private void snapshotAdded(TraceSnapshot snapshot) { private void snapshotAdded(TraceSnapshot snapshot) {
actionCaptureSelected.updateEnabled(null); actionReadSelected.updateEnabled(null);
} }
private void memStateChanged(TraceAddressSnapRange range, TraceMemoryState oldIsNull, private void memStateChanged(TraceAddressSnapRange range, TraceMemoryState oldIsNull,
@ -120,7 +120,7 @@ public abstract class DebuggerReadsMemoryTrait {
@Override @Override
public void processMemoryAccessibilityChanged(TraceRecorder recorder) { public void processMemoryAccessibilityChanged(TraceRecorder recorder) {
Swing.runIfSwingOrRunLater(() -> { Swing.runIfSwingOrRunLater(() -> {
actionCaptureSelected.updateEnabled(null); actionReadSelected.updateEnabled(null);
}); });
} }
} }
@ -137,7 +137,7 @@ public abstract class DebuggerReadsMemoryTrait {
} }
protected MultiStateDockingAction<AutoReadMemorySpec> actionAutoRead; protected MultiStateDockingAction<AutoReadMemorySpec> actionAutoRead;
protected CaptureSelectedMemoryAction actionCaptureSelected; protected ReadSelectedMemoryAction actionReadSelected;
private final AutoReadMemorySpec defaultAutoSpec = private final AutoReadMemorySpec defaultAutoSpec =
AutoReadMemorySpec.fromConfigName(VisibleROOnceAutoReadMemorySpec.CONFIG_NAME); AutoReadMemorySpec.fromConfigName(VisibleROOnceAutoReadMemorySpec.CONFIG_NAME);
@ -149,8 +149,8 @@ public abstract class DebuggerReadsMemoryTrait {
protected final Plugin plugin; protected final Plugin plugin;
protected final ComponentProvider provider; protected final ComponentProvider provider;
protected final ForCaptureTraceListener traceListener = protected final ForReadsTraceListener traceListener =
new ForCaptureTraceListener(); new ForReadsTraceListener();
protected final ForAccessRecorderListener recorderListener = new ForAccessRecorderListener(); protected final ForAccessRecorderListener recorderListener = new ForAccessRecorderListener();
protected final ForVisibilityListener displayListener = new ForVisibilityListener(); protected final ForVisibilityListener displayListener = new ForVisibilityListener();
@ -257,10 +257,10 @@ public abstract class DebuggerReadsMemoryTrait {
} }
} }
public DockingAction installCaptureSelectedAction() { public DockingAction installReadSelectedAction() {
actionCaptureSelected = new CaptureSelectedMemoryAction(); actionReadSelected = new ReadSelectedMemoryAction();
provider.addLocalAction(actionCaptureSelected); provider.addLocalAction(actionReadSelected);
return actionCaptureSelected; return actionReadSelected;
} }
public AddressSetDisplayListener getDisplayListener() { public AddressSetDisplayListener getDisplayListener() {

View file

@ -0,0 +1,153 @@
/* ###
* 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.gui.copying;
import docking.ActionContext;
import docking.action.DockingAction;
import ghidra.app.context.ProgramLocationActionContext;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.core.debug.AbstractDebuggerPlugin;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
import ghidra.app.plugin.core.exporter.ExporterDialog;
import ghidra.app.services.*;
import ghidra.framework.plugintool.PluginInfo;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.program.util.ProgramSelection;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.program.TraceVariableSnapProgramView;
@PluginInfo(
shortDescription = "Copy and export trace data",
description = "Provides tool actions for moving data from traces to various destinations.",
category = PluginCategoryNames.DEBUGGER,
packageName = DebuggerPluginPackage.NAME,
status = PluginStatus.RELEASED,
eventsConsumed = {},
eventsProduced = {},
servicesRequired = {
DebuggerStaticMappingService.class,
ProgramManager.class,
},
servicesProvided = {})
public class DebuggerCopyActionsPlugin extends AbstractDebuggerPlugin {
protected static ProgramSelection getSelectionFromContext(ActionContext context) {
if (!(context instanceof ProgramLocationActionContext)) {
return null;
}
ProgramLocationActionContext ctx = (ProgramLocationActionContext) context;
return ctx.hasSelection() ? ctx.getSelection() : null;
}
protected DebuggerCopyIntoProgramDialog copyDialog = new DebuggerCopyIntoProgramDialog();
protected DockingAction actionExportView;
protected DockingAction actionCopyIntoCurrentProgram;
protected DockingAction actionCopyIntoNewProgram;
@AutoServiceConsumed
private ProgramManager programManager;
@AutoServiceConsumed
private DebuggerStaticMappingService mappingService;
@AutoServiceConsumed
private DebuggerModelService modelService;
public DebuggerCopyActionsPlugin(PluginTool tool) {
super(tool);
createActions();
}
protected void createActions() {
actionExportView = ExportTraceViewAction.builder(this)
.enabled(false)
.withContext(ProgramLocationActionContext.class)
.enabledWhen(this::checkTrace)
.onAction(this::activatedExportView)
.buildAndInstall(tool);
// Using programManager here depends on it calling tool.updateContext()
actionCopyIntoCurrentProgram = CopyIntoCurrentProgramAction.builder(this)
.enabled(false)
.withContext(ProgramLocationActionContext.class)
.enabledWhen(
ctx -> checkTraceSelection(ctx) && programManager.getCurrentProgram() != null)
.onAction(this::activatedCopyIntoCurrentProgram)
.buildAndInstall(tool);
actionCopyIntoNewProgram = CopyIntoNewProgramAction.builder(this)
.enabled(false)
.withContext(ProgramLocationActionContext.class)
.enabledWhen(this::checkTraceSelection)
.onAction(this::activatedCopyIntoNewProgram)
.buildAndInstall(tool);
}
protected boolean checkTrace(ProgramLocationActionContext context) {
return context.getProgram() instanceof TraceProgramView;
}
protected boolean checkTraceSelection(ProgramLocationActionContext context) {
return checkTrace(context) && context.hasSelection();
}
protected void activatedExportView(ProgramLocationActionContext context) {
if (!checkTrace(context)) {
return;
}
TraceProgramView view = (TraceProgramView) context.getProgram();
// Avoid odd race conditions by fixing the snap
TraceProgramView fixed = view instanceof TraceVariableSnapProgramView
? view.getTrace().getFixedProgramView(view.getSnap())
: view;
ExporterDialog dialog =
new ExporterDialog(tool, fixed.getDomainFile(), fixed,
getSelectionFromContext(context));
tool.showDialog(dialog);
}
protected void activatedCopyIntoCurrentProgram(ProgramLocationActionContext context) {
if (!checkTraceSelection(context)) {
return;
}
copyDialog.setSource((TraceProgramView) context.getProgram(), context.getSelection());
copyDialog.setProgramManager(programManager);
copyDialog.setStaticMappingService(mappingService);
copyDialog.setModelService(modelService);
copyDialog.setDestination(programManager.getCurrentProgram());
copyDialog.reset();
copyDialog.setStatusText("");
tool.showDialog(copyDialog);
}
protected void activatedCopyIntoNewProgram(ProgramLocationActionContext context) {
if (!checkTraceSelection(context)) {
return;
}
copyDialog.setSource((TraceProgramView) context.getProgram(), context.getSelection());
copyDialog.setProgramManager(programManager);
copyDialog.setStaticMappingService(mappingService);
copyDialog.setModelService(modelService);
copyDialog.setDestination(copyDialog.NEW_PROGRAM);
copyDialog.reset();
copyDialog.setStatusText("");
tool.showDialog(copyDialog);
}
}

View file

@ -0,0 +1,844 @@
/* ###
* 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.gui.copying;
import java.awt.*;
import java.io.IOException;
import java.util.*;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.*;
import java.util.stream.Collectors;
import javax.swing.*;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import com.google.common.collect.Range;
import docking.DialogComponentProvider;
import docking.widgets.table.*;
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.gui.copying.DebuggerCopyPlan.Copier;
import ghidra.app.services.*;
import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
import ghidra.program.database.ProgramDB;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.trace.model.memory.TraceMemoryManager;
import ghidra.trace.model.memory.TraceMemoryRegion;
import ghidra.trace.model.modules.*;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.util.*;
import ghidra.util.database.UndoableTransaction;
import ghidra.util.exception.CancelledException;
import ghidra.util.table.GhidraTableFilterPanel;
import ghidra.util.task.*;
public class DebuggerCopyIntoProgramDialog extends DialogComponentProvider {
static final int GAP = 5;
static final int BUTTON_SIZE = 32;
protected static class RangeEntry {
private final String regionName;
private final String moduleNames;
private final String sectionNames;
private final AddressRange srcRange;
private String blockName;
private final boolean create;
private final boolean overlay;
private final AddressRange dstRange;
protected RangeEntry(String regionName, String moduleNames, String sectionNames,
AddressRange srcRange, String blockName, boolean create, boolean overlay,
AddressRange dstRange) {
this.regionName = regionName;
this.moduleNames = moduleNames;
this.sectionNames = sectionNames;
this.srcRange = srcRange;
this.blockName = blockName;
this.create = create;
this.overlay = overlay;
this.dstRange = dstRange;
}
public String getRegionName() {
return regionName;
}
public String getModuleNames() {
return moduleNames;
}
public String getSectionNames() {
return sectionNames;
}
public AddressRange getSrcRange() {
return srcRange;
}
public Address getSrcMinAddress() {
return srcRange.getMinAddress();
}
public Address getSrcMaxAddress() {
return srcRange.getMaxAddress();
}
public AddressRange getDstRange() {
return dstRange;
}
public String getBlockName() {
return create ? blockName : (blockName + " *");
}
public void setBlockName(String blockName) {
if (!create) {
throw new IllegalStateException("Cannot modify name of existing block");
}
this.blockName = blockName;
}
public boolean isCreate() {
return create;
}
public boolean isOverlay() {
return overlay;
}
public Address getDstMinAddress() {
return dstRange.getMinAddress();
}
public Address getDstMaxAddress() {
return dstRange.getMaxAddress();
}
}
protected enum RangeTableColumns
implements EnumeratedTableColumn<RangeTableColumns, RangeEntry> {
REMOVE("Remove", String.class, e -> "Remove Range", (e, v) -> nop(), null),
REGION("Region", String.class, RangeEntry::getRegionName),
MODULES("Modules", String.class, RangeEntry::getModuleNames),
SECTIONS("Sections", String.class, RangeEntry::getSectionNames),
SRC_MIN("SrcMin", Address.class, RangeEntry::getSrcMinAddress),
SRC_MAX("SrcMax", Address.class, RangeEntry::getSrcMaxAddress),
BLOCK("Block", String.class, RangeEntry::getBlockName, RangeEntry::setBlockName, //
RangeEntry::isCreate),
OVERLAY("Overlay", Boolean.class, RangeEntry::isOverlay),
DST_MIN("DstMin", Address.class, RangeEntry::getDstMinAddress),
DST_MAX("DstMax", Address.class, RangeEntry::getDstMaxAddress);
private static void nop() {
}
private final String header;
private final Class<?> cls;
private final Function<RangeEntry, ?> getter;
private final BiConsumer<RangeEntry, Object> setter;
private final Predicate<RangeEntry> editable;
@SuppressWarnings("unchecked")
<T> RangeTableColumns(String header, Class<T> cls, Function<RangeEntry, T> getter,
BiConsumer<RangeEntry, T> setter, Predicate<RangeEntry> editable) {
this.header = header;
this.cls = cls;
this.getter = getter;
this.setter = (BiConsumer<RangeEntry, Object>) setter;
this.editable = editable;
}
<T> RangeTableColumns(String header, Class<T> cls, Function<RangeEntry, T> getter) {
this(header, cls, getter, null, null);
}
@Override
public String getHeader() {
return header;
}
@Override
public Class<?> getValueClass() {
return cls;
}
@Override
public Object getValueOf(RangeEntry row) {
return getter.apply(row);
}
@Override
public boolean isEditable(RangeEntry row) {
return setter != null && (editable == null || editable.test(row));
}
@Override
public void setValueOf(RangeEntry row, Object value) {
setter.accept(row, value);
}
}
protected static class RangeTableModel
extends DefaultEnumeratedColumnTableModel<RangeTableColumns, RangeEntry> {
public RangeTableModel() {
super("Ranges", RangeTableColumns.class);
}
@Override
public List<RangeTableColumns> defaultSortOrder() {
return List.of(RangeTableColumns.SRC_MIN);
}
}
protected interface CopyDestination {
default Program getExistingProgram() {
return null;
}
default boolean isExisting() {
return getExistingProgram() != null;
}
Program getOrCreateProgram(TraceProgramView source, Object consumer) throws IOException;
default void saveIfApplicable(Program program) {
}
}
protected static final CopyDestination TEMP_PROGRAM = new CopyDestination() {
@Override
public String toString() {
return "<Temporary Program>";
}
@Override
public Program getOrCreateProgram(TraceProgramView source, Object consumer)
throws IOException {
return new ProgramDB(source.getName(), source.getLanguage(), source.getCompilerSpec(),
consumer);
}
};
protected final CopyDestination NEW_PROGRAM = new CopyDestination() {
@Override
public String toString() {
return "<New Program>";
}
@Override
public Program getOrCreateProgram(TraceProgramView source, Object consumer)
throws IOException {
return new ProgramDB(source.getName(), source.getLanguage(), source.getCompilerSpec(),
consumer);
}
@Override
public void saveIfApplicable(Program program) {
programManager.saveProgramAs(program);
}
};
protected static class OpenProgramDestination implements CopyDestination {
private final Program program;
public OpenProgramDestination(Program program) {
this.program = program;
}
@Override
public String toString() {
return program.getName();
}
@Override
public Program getExistingProgram() {
return program;
}
@Override
public Program getOrCreateProgram(TraceProgramView source, Object consumer) {
return program;
}
}
protected DebuggerModelService modelService;
protected ProgramManager programManager;
protected DebuggerStaticMappingService staticMappingService;
protected TraceProgramView source;
protected AddressSetView set;
protected CompletableFuture<Void> lastTask;
protected CompletableFuture<?> captureTask;
protected final DefaultComboBoxModel<CopyDestination> comboDestinationModel =
new DefaultComboBoxModel<>();
protected JComboBox<CopyDestination> comboDestination;
protected final Map<Program, CopyDestination> programDestinations = new HashMap<>();
// TODO: Save these options to tool state?
protected JCheckBox cbCapture;
protected JCheckBox cbRelocate;
protected JCheckBox cbUseOverlays;
protected DebuggerCopyPlan plan = new DebuggerCopyPlan();
protected final RangeTableModel tableModel = new RangeTableModel();
protected GTable table;
protected GhidraTableFilterPanel<RangeEntry> filterPanel;
protected JButton resetButton;
public DebuggerCopyIntoProgramDialog() {
super("Copy Into Program", true, true, true, true);
populateComponents();
}
protected void populateComponents() {
plan.selectAll();
JPanel panel = new JPanel(new BorderLayout());
{
JPanel opts = new JPanel();
opts.setLayout(new BoxLayout(opts, BoxLayout.Y_AXIS));
{
Box progBox = Box.createHorizontalBox();
progBox.setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
progBox.add(new JLabel("Destination:"));
comboDestination = new JComboBox<>(comboDestinationModel);
comboDestination.setBorder(BorderFactory.createEmptyBorder(0, GAP, 0, 0));
comboDestination.addActionListener(e -> {
if (!isVisible()) {
return;
}
syncCbRelocateEnabled(getDestination());
reset();
});
progBox.add(comboDestination);
opts.add(progBox);
}
{
// Avoid Swing's automatic indentation
JPanel inner = new JPanel(new BorderLayout());
inner.setBorder(BorderFactory.createEmptyBorder(0, GAP, GAP, GAP));
cbCapture =
new JCheckBox("<html>Read live target's memory");
cbCapture.addActionListener(e -> {
if (!isVisible()) {
return;
}
reset();
});
inner.add(cbCapture);
opts.add(inner);
}
{
// Avoid Swing's automatic indentation
JPanel inner = new JPanel(new BorderLayout());
inner.setBorder(BorderFactory.createEmptyBorder(0, GAP, GAP, GAP));
cbRelocate =
new JCheckBox("<html>Relocate via Mappings. <b>WARNING:</b> No fixups");
cbRelocate.addActionListener(e -> {
if (!isVisible()) {
return;
}
reset();
});
inner.add(cbRelocate);
opts.add(inner);
}
{
// No swing indentation
JPanel inner = new JPanel(new BorderLayout());
inner.setBorder(BorderFactory.createEmptyBorder(0, GAP, GAP, GAP));
cbUseOverlays = new JCheckBox("<html>Use overlays where blocks already exist");
cbUseOverlays.addActionListener(e -> {
if (!isVisible()) {
return;
}
reset();
});
inner.add(cbUseOverlays);
opts.add(inner);
}
{
JPanel panelInclude = new JPanel(new GridLayout(0, 2, GAP, GAP));
panelInclude.setBorder(BorderFactory.createTitledBorder("Include:"));
JButton buttonSelectNone = new JButton("Select None");
buttonSelectNone.addActionListener(e -> plan.selectNone());
panelInclude.add(buttonSelectNone);
JButton buttonSelectAll = new JButton("Select All");
buttonSelectAll.addActionListener(e -> plan.selectAll());
panelInclude.add(buttonSelectAll);
for (Copier copier : plan.getAllCopiers()) {
panelInclude.add(plan.getCheckBox(copier));
}
opts.add(panelInclude);
}
panel.add(opts, BorderLayout.NORTH);
}
{
JPanel tablePanel = new JPanel(new BorderLayout());
table = new GTable(tableModel);
table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
tablePanel.add(new JScrollPane(table));
filterPanel = new GhidraTableFilterPanel<>(table, tableModel);
tablePanel.add(filterPanel, BorderLayout.SOUTH);
panel.add(tablePanel, BorderLayout.CENTER);
}
panel.setMinimumSize(new Dimension(600, 600));
addWorkPanel(panel);
addOKButton();
okButton.setText("Copy");
addCancelButton();
addResetButton();
TableColumnModel columnModel = table.getColumnModel();
TableColumn removeCol = columnModel.getColumn(RangeTableColumns.REMOVE.ordinal());
CellEditorUtils.installButton(table, filterPanel, removeCol, DebuggerResources.ICON_DELETE,
BUTTON_SIZE, this::removeEntry);
}
protected void addResetButton() {
resetButton = new JButton("Reset");
resetButton.setMnemonic('R');
resetButton.setName("Reset");
resetButton.addActionListener(e -> resetCallback());
addButton(resetButton);
}
@Override
protected void cancelCallback() {
synchronized (this) {
if (captureTask != null) {
captureTask.cancel(false);
}
}
super.cancelCallback();
}
@Override
protected void okCallback() {
super.okCallback();
lastTask = new CompletableFuture<>();
Task task = new Task("Copy Into Program", true, true, false) {
@Override
public void run(TaskMonitor monitor) throws CancelledException {
try {
executePlan(monitor);
Swing.runLater(() -> {
setStatusText("");
close();
});
}
catch (Exception e) {
Msg.error(this, "Error copying into program", e);
setStatusText("Error: " + e.getMessage());
}
}
};
task.addTaskListener(new TaskListener() {
@Override
public void taskCancelled(Task task) {
lastTask.cancel(false);
}
@Override
public void taskCompleted(Task task) {
lastTask.complete(null);
}
});
executeProgressTask(task, 500);
}
protected void resetCallback() {
reset();
}
protected void removeEntry(RangeEntry entry) {
tableModel.delete(entry);
}
protected TraceRecorder getRecorderIfReadsPresent() {
if (modelService == null) {
return null;
}
TraceRecorder recorder = modelService.getRecorder(source.getTrace());
if (recorder == null) {
return null;
}
if (!DebuggerCoordinates.view(source).withRecorder(recorder).isAliveAndReadsPresent()) {
return null;
}
return recorder;
}
protected void checkCbCaptureEnabled() {
boolean en = getRecorderIfReadsPresent() != null;
cbCapture.setEnabled(en);
cbCapture.setSelected(en);
}
public void setModelService(DebuggerModelService modelService) {
this.modelService = modelService;
checkCbCaptureEnabled();
}
public void setSource(TraceProgramView source, AddressSetView set) {
this.source = source;
this.set = set;
checkCbCaptureEnabled();
}
public void setProgramManager(ProgramManager programManager) {
this.programManager = programManager;
setSelectablePrograms(programManager.getAllOpenPrograms());
}
protected void setSelectablePrograms(Program[] programs) {
setSelectablePrograms(Arrays.asList(programs));
}
protected void setSelectablePrograms(Collection<Program> programs) {
programDestinations.clear();
comboDestinationModel.removeAllElements();
comboDestinationModel.addElement(NEW_PROGRAM);
comboDestinationModel.addElement(TEMP_PROGRAM);
for (Program program : new LinkedHashSet<>(programs)) {
OpenProgramDestination destination = new OpenProgramDestination(program);
programDestinations.put(program, destination);
comboDestinationModel.addElement(destination);
}
}
public void setDestination(Program program) {
setDestination(programDestinations.get(program));
}
protected void syncCbRelocateEnabled(CopyDestination dest) {
cbRelocate.setEnabled(dest.getExistingProgram() != null);
}
public void setDestination(CopyDestination dest) {
Objects.requireNonNull(dest);
syncCbRelocateEnabled(dest);
comboDestinationModel.setSelectedItem(dest);
}
public CopyDestination getDestination() {
return (CopyDestination) comboDestinationModel.getSelectedItem();
}
public void setCapture(boolean capture) {
if (capture && getRecorderIfReadsPresent() == null) {
throw new IllegalStateException(
"Cannot enable capture unless live and reading the present");
}
this.cbCapture.setSelected(capture);
}
public boolean isCapture() {
return (cbCapture.isSelected() && getRecorderIfReadsPresent() != null);
}
public void setRelocate(boolean relocate) {
if (relocate && !getDestination().isExisting()) {
throw new IllegalStateException("Cannot relocate when creating a new program");
}
this.cbRelocate.setSelected(relocate);
}
public boolean isRelocate() {
return cbRelocate.isSelected() && staticMappingService != null &&
getDestination().isExisting();
}
public void setUseOverlays(boolean useOverlays) {
if (useOverlays && !getDestination().isExisting()) {
// Technically, you can, but why would you?
throw new IllegalStateException("Cannot use overlays when creating a new program");
}
this.cbUseOverlays.setSelected(useOverlays);
}
public boolean isUseOverlays() {
return cbUseOverlays.isSelected() && getDestination().isExisting();
}
public void setStaticMappingService(DebuggerStaticMappingService staticMappingService) {
this.staticMappingService = staticMappingService;
cbRelocate.setEnabled(staticMappingService != null);
}
/**
* Re-populate the table based on destination and relocation settings
*/
public void reset() {
Program dest = getDestination().getExistingProgram();
plan.syncCopiersEnabled(source, dest);
if (isRelocate()) {
resetWithRelocation(isUseOverlays(), dest);
}
else {
resetWithoutRelocation(isUseOverlays(), dest);
}
}
protected String createName(String desired, Set<String> taken) {
if (taken.add(desired)) {
return desired;
}
String candidate = desired;
for (int i = 2;; i++) {
candidate = desired + "_" + i;
if (taken.add(candidate)) {
return candidate;
}
}
}
protected String computeRegionString(AddressRange rng) {
TraceMemoryManager mm = source.getTrace().getMemoryManager();
Collection<? extends TraceMemoryRegion> regions =
mm.getRegionsIntersecting(Range.singleton(source.getSnap()), rng);
return regions.isEmpty() ? "UNKNOWN" : regions.iterator().next().getName();
}
protected String computeModulesString(AddressRange rng) {
TraceModuleManager mm = source.getTrace().getModuleManager();
Collection<? extends TraceModule> modules =
mm.getModulesIntersecting(Range.singleton(source.getSnap()), rng);
return modules.stream().map(m -> m.getName()).collect(Collectors.joining(","));
}
protected String computeSectionsString(AddressRange rng) {
TraceModuleManager mm = source.getTrace().getModuleManager();
Collection<? extends TraceSection> sections =
mm.getSectionsIntersecting(Range.singleton(source.getSnap()), rng);
return sections.stream().map(s -> s.getName()).collect(Collectors.joining(","));
}
protected void createEntry(Collection<RangeEntry> result, AddressRange srcRange,
AddressRange dstRange, boolean overlay, Set<String> taken, MemoryBlock dstBlock) {
String srcName = computeRegionString(srcRange);
String dstName = dstBlock != null ? dstBlock.getName() : createName(srcName, taken);
String srcModules = computeModulesString(srcRange);
String srcSections = computeSectionsString(srcRange);
result.add(new RangeEntry(srcName, srcModules, srcSections, srcRange, dstName,
dstBlock == null, overlay, dstRange));
}
protected void createEntries(Collection<RangeEntry> result, boolean useOverlays,
MappedAddressRange mappedRng, AddressRange srcRange, AddressRange dstRange,
Set<String> taken, Program dest) {
if (dest == null) {
createEntry(result, srcRange, dstRange, false, taken, null);
return;
}
Memory memory = dest.getMemory();
AddressSetView hits =
memory.intersectRange(dstRange.getMinAddress(), dstRange.getMaxAddress());
if (!hits.isEmpty() && useOverlays) {
createEntry(result, srcRange, dstRange, true, taken, null);
return;
}
AddressSetView misses = new AddressSet(dstRange).subtract(hits);
for (AddressRange miss : misses) {
createEntry(result, mappedRng.mapDestinationToSource(miss), miss, false, taken, null);
}
for (AddressRange hit : hits) {
Address next = hit.getMinAddress();
while (next != null && hit.contains(next)) {
MemoryBlock block = memory.getBlock(next);
AddressRange dr = hit.intersectRange(block.getStart(), block.getEnd());
createEntry(result, mappedRng.mapDestinationToSource(dr), dr, false, taken, block);
next = block.getEnd().next();
}
}
}
protected void collectBlockNames(Collection<String> result, Program program) {
if (program == null) {
return;
}
for (MemoryBlock b : program.getMemory().getBlocks()) {
result.add(b.getName());
}
}
protected List<AddressRange> breakRangeByRegions(AddressRange srcRange) {
AddressSet remains = new AddressSet(srcRange);
List<AddressRange> result = new ArrayList<>();
for (TraceMemoryRegion region : source.getTrace()
.getMemoryManager()
.getRegionsIntersecting(Range.singleton(source.getSnap()), srcRange)) {
AddressRange range = region.getRange().intersect(srcRange);
result.add(range);
remains.delete(range);
}
remains.iterator().forEachRemaining(result::add);
return result;
}
protected void resetWithRelocation(boolean useOverlays, Program dest) {
Objects.requireNonNull(dest);
tableModel.clear();
List<RangeEntry> result = new ArrayList<>();
Set<String> taken = new HashSet<>();
collectBlockNames(taken, dest);
Collection<MappedAddressRange> mappedSet = staticMappingService
.getOpenMappedViews(source.getTrace(), set, source.getSnap())
.get(dest);
if (mappedSet == null) {
return;
}
for (MappedAddressRange mappedRng : mappedSet) {
for (AddressRange src : breakRangeByRegions(mappedRng.getSourceAddressRange())) {
AddressRange dst = mappedRng.mapSourceToDestination(src);
createEntries(result, useOverlays, mappedRng, src, dst, taken, dest);
}
}
tableModel.addAll(result);
}
protected MappedAddressRange identityMapped(AddressRange srng, Program dest) {
if (dest == null) { // New program
return new MappedAddressRange(srng, srng);
}
AddressSpace srcSpace = srng.getAddressSpace();
AddressSpace dstSpace = dest.getAddressFactory().getAddressSpace(srcSpace.getName());
if (dstSpace == null) {
return null;
}
long minOff = MathUtilities.unsignedMax(srng.getMinAddress().getOffset(),
dstSpace.getMinAddress().getOffset());
long maxOff = MathUtilities.unsignedMin(srng.getMaxAddress().getOffset(),
dstSpace.getMaxAddress().getOffset());
if (Long.compareUnsigned(minOff, maxOff) > 0) {
return null;
}
return new MappedAddressRange(
new AddressRangeImpl(srcSpace.getAddress(minOff), srcSpace.getAddress(maxOff)),
new AddressRangeImpl(dstSpace.getAddress(minOff), dstSpace.getAddress(maxOff)));
}
protected void resetWithoutRelocation(boolean useOverlays, Program dest) {
tableModel.clear();
List<RangeEntry> result = new ArrayList<>();
Set<String> taken = new HashSet<>();
collectBlockNames(taken, dest);
for (AddressRange rng : set) {
for (AddressRange src : breakRangeByRegions(rng)) {
MappedAddressRange id = identityMapped(src, dest);
if (id == null) {
continue;
}
createEntries(result, useOverlays, id, id.getSourceAddressRange(),
id.getDestinationAddressRange(), taken, dest);
}
}
tableModel.addAll(result);
}
protected MemoryBlock executeEntryBlock(RangeEntry entry, Program dest, TaskMonitor monitor)
throws Exception {
if (entry.isCreate()) {
return dest.getMemory()
.createInitializedBlock(entry.getBlockName(), entry.getDstMinAddress(),
entry.getDstRange().getLength(), (byte) 0, monitor, entry.isOverlay());
}
MemoryBlock block = dest.getMemory().getBlock(entry.getDstMinAddress());
if (plan.isRequiresInitializedMemory(source, dest) && !block.isInitialized()) {
return dest.getMemory().convertToInitialized(block, (byte) 0);
}
return block;
}
protected void executeEntry(RangeEntry entry, Program dest, TraceRecorder recorder,
TaskMonitor monitor)
throws Exception {
MemoryBlock block = executeEntryBlock(entry, dest, monitor);
Address dstMin = entry.getDstRange().getMinAddress();
if (block.isOverlay()) {
dstMin = block.getStart().getAddressSpace().getAddress(dstMin.getOffset());
}
if (recorder != null) {
executeCapture(entry.getSrcRange(), recorder, monitor);
}
plan.execute(source, entry.getSrcRange(), dest, dstMin, monitor);
}
protected TraceRecorder getRecorderIfEnabledAndReadsPresent() {
if (!cbCapture.isSelected()) {
return null;
}
return getRecorderIfReadsPresent();
}
protected void executeCapture(AddressRange range, TraceRecorder recorder, TaskMonitor monitor)
throws Exception {
synchronized (this) {
monitor.checkCanceled();
this.captureTask = recorder.captureProcessMemory(new AddressSet(range), monitor, false);
}
try {
captureTask.get(); // Not a fan, but whatever.
}
finally {
captureTask = null;
}
}
protected void executePlan(TaskMonitor monitor) throws Exception {
Program dest = getDestination().getOrCreateProgram(source, this);
boolean doRelease = !Arrays.asList(programManager.getAllOpenPrograms()).contains(dest);
TraceRecorder recorder = getRecorderIfEnabledAndReadsPresent();
try (UndoableTransaction tid = UndoableTransaction.start(dest, "Copy From Trace", true)) {
monitor.initialize(tableModel.getRowCount());
for (RangeEntry entry : tableModel.getModelData()) {
monitor.setMessage("Copying into " + entry.getDstRange());
executeEntry(entry, dest, recorder, monitor);
monitor.incrementProgress(1);
}
programManager.openProgram(dest);
}
finally {
if (doRelease) {
dest.release(this);
}
}
getDestination().saveIfApplicable(dest);
}
}

View file

@ -0,0 +1,449 @@
/* ###
* 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.gui.copying;
import java.util.*;
import javax.swing.JCheckBox;
import com.google.common.collect.Range;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.service.breakpoint.LogicalBreakpointInternal.ProgramBreakpoint;
import ghidra.app.util.viewer.listingpanel.PropertyBasedBackgroundColorModel;
import ghidra.program.database.IntRangeMap;
import ghidra.program.model.address.*;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*;
import ghidra.trace.model.breakpoint.TraceBreakpoint;
import ghidra.trace.model.memory.TraceMemoryManager;
import ghidra.trace.model.memory.TraceMemoryState;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor;
public class DebuggerCopyPlan {
public interface Copier {
String getName();
boolean isAvailable(TraceProgramView from, Program into);
Collection<Copier> getRequires();
Collection<Copier> getRequiredBy();
boolean isRequiresInitializedMemory();
void copy(TraceProgramView from, AddressRange fromRange, Program into, Address intoAddress,
TaskMonitor monitor) throws Exception;
}
public enum AllCopiers implements Copier {
BYTES("Bytes", List.of()) {
@Override
public boolean isRequiresInitializedMemory() {
return true;
}
@Override
public void copy(TraceProgramView from, AddressRange fromRange, Program into,
Address intoAddress, TaskMonitor monitor) throws Exception {
// This is perhaps too heavy handed....
into.getListing()
.clearCodeUnits(intoAddress, intoAddress.add(fromRange.getLength() - 1),
false);
byte[] buf = new byte[4096];
AddressRangeChunker chunker = new AddressRangeChunker(fromRange, buf.length);
for (AddressRange chunk : chunker) {
monitor.checkCanceled();
Address addr = chunk.getMinAddress();
int len = (int) chunk.getLength();
from.getMemory().getBytes(addr, buf, 0, len);
long off = addr.subtract(fromRange.getMinAddress());
Address dest = intoAddress.add(off);
into.getMemory().setBytes(dest, buf, 0, len);
}
}
},
STATE("State (as colors)", List.of()) {
@Override
public void copy(TraceProgramView from, AddressRange fromRange, Program into,
Address intoAddress, TaskMonitor monitor) throws Exception {
IntRangeMap map =
into.getIntRangeMap(PropertyBasedBackgroundColorModel.COLOR_PROPERTY_NAME);
if (map == null) {
map = into.createIntRangeMap(
PropertyBasedBackgroundColorModel.COLOR_PROPERTY_NAME);
}
AddressSet rngAsSet = new AddressSet(fromRange);
TraceMemoryManager mm = from.getTrace().getMemoryManager();
AddressSetView knownSet = mm.getAddressesWithState(from.getSnap(), rngAsSet,
s -> s == TraceMemoryState.KNOWN);
AddressSetView errorSet = mm.getAddressesWithState(from.getSnap(), rngAsSet,
s -> s == TraceMemoryState.ERROR);
AddressSetView staleSet = rngAsSet.subtract(knownSet).subtract(errorSet);
setShifted(map, fromRange.getMinAddress(), intoAddress, errorSet,
DebuggerResources.DEFAULT_COLOR_BACKGROUND_ERROR.getRGB());
setShifted(map, fromRange.getMinAddress(), intoAddress, staleSet,
DebuggerResources.DEFAULT_COLOR_BACKGROUND_STALE.getRGB());
}
public void setShifted(IntRangeMap map, Address src, Address dst, AddressSetView set,
int value) {
for (AddressRange rng : set) {
long offMin = rng.getMinAddress().subtract(src);
long offMax = rng.getMaxAddress().subtract(src);
Address dMin = dst.add(offMin);
Address dMax = dst.add(offMax);
map.setValue(dMin, dMax, value);
}
}
},
INSTRUCTIONS("Instructions", List.of(BYTES)) {
@Override
protected boolean checkAvailable(TraceProgramView from, Program into) {
return into == null || from.getLanguage() == into.getLanguage();
}
@Override
public void copy(TraceProgramView from, AddressRange fromRange, Program into,
Address intoAddress, TaskMonitor monitor) throws Exception {
Listing intoListing = into.getListing();
for (Instruction ins : from.getListing()
.getInstructions(new AddressSet(fromRange), true)) {
monitor.checkCanceled();
if (!ins.getPrototype().getLanguage().equals(into.getLanguage())) {
// Filter out "guest" instructions
continue;
}
long off = ins.getMinAddress().subtract(fromRange.getMinAddress());
Address dest = intoAddress.add(off);
intoListing.createInstruction(dest, ins.getPrototype(), ins, ins);
}
}
},
DATA("Data", List.of()) {
@Override
protected boolean checkAvailable(TraceProgramView from, Program into) {
return into == null || sameDataOrganization(from, into);
}
@Override
public void copy(TraceProgramView from, AddressRange fromRange, Program into,
Address intoAddress, TaskMonitor monitor)
throws Exception {
Listing intoListing = into.getListing();
for (Data data : from.getListing()
.getDefinedData(new AddressSet(fromRange), true)) {
monitor.checkCanceled();
long off = data.getMinAddress().subtract(fromRange.getMinAddress());
Address dest = intoAddress.add(off);
DataType dt = data.getDataType();
if (!(dt instanceof DynamicDataType)) {
intoListing.createData(dest, dt, data.getLength());
}
}
}
},
DYNAMIC_DATA("Dynamic Data", List.of(BYTES)) {
@Override
protected boolean checkAvailable(TraceProgramView from, Program into) {
return into == null || sameDataOrganization(from, into);
}
@Override
public void copy(TraceProgramView from, AddressRange fromRange, Program into,
Address intoAddress, TaskMonitor monitor) throws Exception {
Listing intoListing = into.getListing();
for (Data data : from.getListing()
.getDefinedData(new AddressSet(fromRange), true)) {
monitor.checkCanceled();
long off = data.getMinAddress().subtract(fromRange.getMinAddress());
Address dest = intoAddress.add(off);
DataType dt = data.getDataType();
if (dt instanceof DynamicDataType) {
intoListing.createData(dest, dt, data.getLength());
}
}
}
},
LABELS("Labels", List.of()) {
@Override
public void copy(TraceProgramView from, AddressRange fromRange, Program into,
Address intoAddress, TaskMonitor monitor) throws Exception {
SymbolTable intoTable = into.getSymbolTable();
for (Symbol label : from.getSymbolTable()
.getSymbols(new AddressSet(fromRange), SymbolType.LABEL, true)) {
monitor.checkCanceled();
if (label.getSource() == SourceType.DEFAULT) {
continue;
}
long off = label.getAddress().subtract(fromRange.getMinAddress());
Address dest = intoAddress.add(off);
Namespace destNs =
findOrCopyNamespace(label.getParentNamespace(), intoTable, into);
try {
intoTable.createLabel(dest, label.getName(), destNs, label.getSource());
}
catch (InvalidInputException e) {
throw new AssertionError(e);
}
}
}
private Namespace findOrCopyNamespace(Namespace ns, SymbolTable intoTable,
Program into) throws Exception {
if (ns.isGlobal()) {
return into.getGlobalNamespace();
}
Namespace destParent =
findOrCopyNamespace(ns.getParentNamespace(), intoTable, into);
return intoTable.getOrCreateNameSpace(destParent, ns.getName(),
ns.getSymbol().getSource());
}
},
BREAKPOINTS("Breakpoints (as bookmarks)", List.of()) {
@Override
public void copy(TraceProgramView from, AddressRange fromRange, Program into,
Address intoAddress, TaskMonitor monitor) throws Exception {
for (TraceBreakpoint bpt : from.getTrace()
.getBreakpointManager()
.getBreakpointsIntersecting(Range.singleton(from.getSnap()), fromRange)) {
monitor.checkCanceled();
long off = bpt.getMinAddress().subtract(fromRange.getMinAddress());
Address dest = intoAddress.add(off);
ProgramBreakpoint pb =
new ProgramBreakpoint(into, dest, bpt.getLength(), bpt.getKinds());
if (bpt.isEnabled()) {
pb.enable();
}
else {
pb.disable();
}
}
}
},
BOOKMARKS("Bookmarks", List.of()) {
@Override
public void copy(TraceProgramView from, AddressRange fromRange, Program into,
Address intoAddress, TaskMonitor monitor) throws Exception {
BookmarkManager intoBookmarks = into.getBookmarkManager();
Iterator<Bookmark> bit =
from.getBookmarkManager().getBookmarksIterator(fromRange.getMinAddress(), true);
while (bit.hasNext()) {
monitor.checkCanceled();
Bookmark bm = bit.next();
if (bm.getAddress().compareTo(fromRange.getMaxAddress()) > 0) {
break;
}
BookmarkType type = bm.getType();
long off = bm.getAddress().subtract(fromRange.getMinAddress());
Address dest = intoAddress.add(off);
BookmarkType destType = intoBookmarks.getBookmarkType(type.getTypeString());
if (destType == null) {
destType = intoBookmarks.defineType(type.getTypeString(), type.getIcon(),
type.getMarkerColor(), type.getMarkerPriority());
}
intoBookmarks.setBookmark(dest, destType.getTypeString(), bm.getCategory(),
bm.getComment());
}
}
},
REFERENCES("References (memory only)", List.of()) {
@Override
public void copy(TraceProgramView from, AddressRange fromRange, Program into,
Address intoAddress, TaskMonitor monitor) throws Exception {
ReferenceManager intoRefs = into.getReferenceManager();
for (Reference ref : from.getReferenceManager()
.getReferenceIterator(fromRange.getMinAddress())) {
monitor.checkCanceled();
if (ref.getFromAddress().compareTo(fromRange.getMaxAddress()) > 0) {
break;
}
if (ref.getSource() == SourceType.DEFAULT) {
continue;
}
// TODO: Other kinds of references?
if (!ref.isMemoryReference()) {
continue;
}
// Requiring both ends to be in copied range
if (!fromRange.contains(ref.getToAddress())) {
continue;
}
// NB. "from" is overloaded here
long offFrom = ref.getFromAddress().subtract(fromRange.getMinAddress());
long offTo = ref.getToAddress().subtract(fromRange.getMinAddress());
Address destFrom = intoAddress.add(offFrom);
Address destTo = intoAddress.add(offTo);
intoRefs.addMemoryReference(destFrom, destTo, ref.getReferenceType(),
ref.getSource(), ref.getOperandIndex());
}
}
},
COMMENTS("Comments", List.of()) {
@Override
public void copy(TraceProgramView from, AddressRange fromRange, Program into,
Address intoAddress, TaskMonitor monitor) throws Exception {
Listing fromListing = from.getListing();
Listing intoListing = into.getListing();
for (Address addr : fromListing.getCommentAddressIterator(new AddressSet(fromRange),
true)) {
monitor.checkCanceled();
long off = addr.subtract(fromRange.getMinAddress());
Address dest = intoAddress.add(off);
// Ugly, but there's not MAX/MIN_COMMENT_TYPE
for (int i = CodeUnit.EOL_COMMENT; i <= CodeUnit.REPEATABLE_COMMENT; i++) {
String comment = fromListing.getComment(i, addr);
if (comment == null) {
continue;
}
intoListing.setComment(dest, i, comment);
}
}
}
};
protected boolean sameDataOrganization(Program p1, Program p2) {
DataOrganization dataOrg1 = p1.getDataTypeManager().getDataOrganization();
DataOrganization dataOrg2 = p2.getDataTypeManager().getDataOrganization();
return dataOrg1.equals(dataOrg2);
}
public static final List<Copier> VALUES;
static {
List<AllCopiers> asList = Arrays.asList(values());
Collections.sort(asList, Comparator.comparing(AllCopiers::getName));
VALUES = Collections.unmodifiableList(asList);
}
final String name;
final Collection<Copier> requires;
final Collection<Copier> requiredBy = new HashSet<>();
private AllCopiers(String name, Collection<AllCopiers> requires) {
this.name = name;
this.requires = Collections.unmodifiableCollection(requires);
for (AllCopiers req : requires) {
req.requiredBy.add(this);
}
}
protected boolean checkAvailable(TraceProgramView from, Program into) {
return true;
}
@Override
public boolean isAvailable(TraceProgramView from, Program into) {
return checkAvailable(from, into) &&
getRequires().stream().allMatch(c -> c.isAvailable(from, into));
}
@Override
public String getName() {
return name;
}
@Override
public Collection<Copier> getRequires() {
return requires;
}
@Override
public Collection<Copier> getRequiredBy() {
return requiredBy;
}
@Override
public boolean isRequiresInitializedMemory() {
return false;
}
}
protected final Map<Copier, JCheckBox> checkBoxes = new LinkedHashMap<>();
public DebuggerCopyPlan() {
for (Copier copier : getAllCopiers()) {
JCheckBox cb = new JCheckBox(copier.getName());
Collection<Copier> requires = copier.getRequires();
Collection<Copier> requiredBy = copier.getRequiredBy();
if (!requires.isEmpty() || !requiredBy.isEmpty()) {
cb.addActionListener(e -> {
if (cb.isSelected()) {
for (Copier req : requires) {
checkBoxes.get(req).setSelected(true);
}
}
else {
for (Copier dep : requiredBy) {
checkBoxes.get(dep).setSelected(false);
}
}
});
}
checkBoxes.put(copier, cb);
}
}
public Collection<Copier> getAllCopiers() {
return AllCopiers.VALUES;
}
public JCheckBox getCheckBox(Copier copier) {
return checkBoxes.get(copier);
}
public void selectAll() {
for (JCheckBox cb : checkBoxes.values()) {
cb.setSelected(true);
}
}
public void selectNone() {
for (JCheckBox cb : checkBoxes.values()) {
cb.setSelected(false);
}
}
public void execute(TraceProgramView from, AddressRange fromRange, Program into,
Address intoAddress, TaskMonitor monitor) throws Exception {
for (Copier copier : getAllCopiers()) {
if (!copier.isAvailable(from, into)) {
continue;
}
if (!checkBoxes.get(copier).isSelected()) {
continue;
}
copier.copy(from, fromRange, into, intoAddress, monitor);
}
}
public void syncCopiersEnabled(TraceProgramView from, Program dest) {
for (Map.Entry<Copier, JCheckBox> ent : checkBoxes.entrySet()) {
ent.getValue().setEnabled(ent.getKey().isAvailable(from, dest));
}
}
public boolean isRequiresInitializedMemory(TraceProgramView from, Program dest) {
return checkBoxes.entrySet().stream().anyMatch(ent -> {
Copier copier = ent.getKey();
return copier.isRequiresInitializedMemory() &&
copier.isAvailable(from, dest) && ent.getValue().isSelected();
});
}
}

View file

@ -52,7 +52,6 @@ import ghidra.app.plugin.core.debug.gui.action.*;
import ghidra.app.plugin.core.debug.gui.modules.DebuggerMissingModuleActionContext; import ghidra.app.plugin.core.debug.gui.modules.DebuggerMissingModuleActionContext;
import ghidra.app.plugin.core.debug.utils.ProgramLocationUtils; import ghidra.app.plugin.core.debug.utils.ProgramLocationUtils;
import ghidra.app.plugin.core.debug.utils.ProgramURLUtils; import ghidra.app.plugin.core.debug.utils.ProgramURLUtils;
import ghidra.app.plugin.core.exporter.ExporterDialog;
import ghidra.app.services.*; import ghidra.app.services.*;
import ghidra.app.util.viewer.format.FormatManager; import ghidra.app.util.viewer.format.FormatManager;
import ghidra.app.util.viewer.listingpanel.ListingPanel; import ghidra.app.util.viewer.listingpanel.ListingPanel;
@ -72,7 +71,6 @@ import ghidra.program.util.ProgramSelection;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.modules.*; import ghidra.trace.model.modules.*;
import ghidra.trace.model.program.TraceProgramView; import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.program.TraceVariableSnapProgramView;
import ghidra.util.HTMLUtilities; import ghidra.util.HTMLUtilities;
import ghidra.util.Swing; import ghidra.util.Swing;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
@ -239,8 +237,7 @@ public class DebuggerListingProvider extends CodeViewerProvider {
protected SyncToStaticListingAction actionSyncToStaticListing; protected SyncToStaticListingAction actionSyncToStaticListing;
protected FollowsCurrentThreadAction actionFollowsCurrentThread; protected FollowsCurrentThreadAction actionFollowsCurrentThread;
protected MultiStateDockingAction<AutoReadMemorySpec> actionAutoReadMemory; protected MultiStateDockingAction<AutoReadMemorySpec> actionAutoReadMemory;
protected DockingAction actionCaptureSelectedMemory; protected DockingAction actionReadSelectedMemory;
protected DockingAction actionExportView;
protected DockingAction actionOpenProgram; protected DockingAction actionOpenProgram;
protected MultiStateDockingAction<LocationTrackingSpec> actionTrackLocation; protected MultiStateDockingAction<LocationTrackingSpec> actionTrackLocation;
@ -608,12 +605,7 @@ public class DebuggerListingProvider extends CodeViewerProvider {
actionGoTo = goToTrait.installAction(); actionGoTo = goToTrait.installAction();
actionTrackLocation = trackingTrait.installAction(); actionTrackLocation = trackingTrait.installAction();
actionAutoReadMemory = readsMemTrait.installAutoReadAction(); actionAutoReadMemory = readsMemTrait.installAutoReadAction();
actionCaptureSelectedMemory = readsMemTrait.installCaptureSelectedAction(); actionReadSelectedMemory = readsMemTrait.installReadSelectedAction();
actionExportView = ExportTraceViewAction.builder(plugin)
.enabledWhen(ctx -> current.getView() != null)
.onAction(this::activatedExportView)
.buildAndInstallLocal(this);
actionOpenProgram = OpenProgramAction.builder(plugin) actionOpenProgram = OpenProgramAction.builder(plugin)
.withContext(DebuggerOpenProgramActionContext.class) .withContext(DebuggerOpenProgramActionContext.class)
@ -623,20 +615,6 @@ public class DebuggerListingProvider extends CodeViewerProvider {
contextChanged(); contextChanged();
} }
private void activatedExportView(ActionContext context) {
if (current.getView() == null) {
return;
}
// Avoid odd race conditions by fixing the snap
TraceProgramView fixed = current.getView() instanceof TraceVariableSnapProgramView
? current.getTrace().getFixedProgramView(current.getSnap())
: current.getView();
ExporterDialog dialog =
new ExporterDialog(tool, fixed.getDomainFile(), fixed, getSelection());
tool.showDialog(dialog);
}
private void activatedOpenProgram(DebuggerOpenProgramActionContext context) { private void activatedOpenProgram(DebuggerOpenProgramActionContext context) {
programManager.openProgram(context.getDomainFile(), DomainFile.DEFAULT_VERSION, programManager.openProgram(context.getDomainFile(), DomainFile.DEFAULT_VERSION,
ProgramManager.OPEN_CURRENT); ProgramManager.OPEN_CURRENT);

View file

@ -148,7 +148,7 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
protected DockingAction actionGoTo; protected DockingAction actionGoTo;
protected FollowsCurrentThreadAction actionFollowsCurrentThread; protected FollowsCurrentThreadAction actionFollowsCurrentThread;
protected MultiStateDockingAction<AutoReadMemorySpec> actionAutoReadMemory; protected MultiStateDockingAction<AutoReadMemorySpec> actionAutoReadMemory;
protected DockingAction actionCaptureSelectedMemory; protected DockingAction actionReadSelectedMemory;
protected MultiStateDockingAction<LocationTrackingSpec> actionTrackLocation; protected MultiStateDockingAction<LocationTrackingSpec> actionTrackLocation;
protected ForMemoryBytesGoToTrait goToTrait; protected ForMemoryBytesGoToTrait goToTrait;
@ -257,7 +257,7 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
actionGoTo = goToTrait.installAction(); actionGoTo = goToTrait.installAction();
actionTrackLocation = trackingTrait.installAction(); actionTrackLocation = trackingTrait.installAction();
actionAutoReadMemory = readsMemTrait.installAutoReadAction(); actionAutoReadMemory = readsMemTrait.installAutoReadAction();
actionCaptureSelectedMemory = readsMemTrait.installCaptureSelectedAction(); actionReadSelectedMemory = readsMemTrait.installReadSelectedAction();
} }
@Override @Override

View file

@ -37,7 +37,7 @@ import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
import utilities.util.IDHashed; import utilities.util.IDHashed;
interface LogicalBreakpointInternal extends LogicalBreakpoint { public interface LogicalBreakpointInternal extends LogicalBreakpoint {
public static class ProgramBreakpoint { public static class ProgramBreakpoint {
public static Set<TraceBreakpointKind> kindsFromBookmark(Bookmark mark) { public static Set<TraceBreakpointKind> kindsFromBookmark(Bookmark mark) {
String[] parts = mark.getCategory().split(";"); String[] parts = mark.getCategory().split(";");

View file

@ -15,10 +15,11 @@
*/ */
package ghidra.app.plugin.core.debug.service.emulation; package ghidra.app.plugin.core.debug.service.emulation;
import java.util.Collection;
import java.util.Map.Entry; import java.util.Map.Entry;
import ghidra.app.services.DebuggerStaticMappingService; import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.app.services.DebuggerStaticMappingService.ShiftAndAddressSetView; import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
import ghidra.app.services.TraceRecorder; import ghidra.app.services.TraceRecorder;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
@ -78,22 +79,24 @@ public class ReadsTargetMemoryPcodeExecutorState
DebuggerStaticMappingService mappingService = DebuggerStaticMappingService mappingService =
tool.getService(DebuggerStaticMappingService.class); tool.getService(DebuggerStaticMappingService.class);
byte[] data = new byte[4096]; byte[] data = new byte[4096];
for (Entry<Program, ShiftAndAddressSetView> ent : mappingService for (Entry<Program, Collection<MappedAddressRange>> ent : mappingService
.getOpenMappedViews(trace, unknown, snap) .getOpenMappedViews(trace, unknown, snap)
.entrySet()) { .entrySet()) {
Program program = ent.getKey(); Program program = ent.getKey();
ShiftAndAddressSetView shifted = ent.getValue();
long shift = shifted.getShift();
Memory memory = program.getMemory(); Memory memory = program.getMemory();
AddressSetView initialized = memory.getLoadedAndInitializedAddressSet(); AddressSetView initialized = memory.getLoadedAndInitializedAddressSet();
AddressSetView toRead = shifted.getAddressSetView().intersect(initialized);
Collection<MappedAddressRange> mappedSet = ent.getValue();
for (MappedAddressRange mappedRng : mappedSet) {
AddressRange srng = mappedRng.getSourceAddressRange();
long shift = mappedRng.getShift();
for (AddressRange subsrng : initialized.intersectRange(srng.getMinAddress(),
srng.getMaxAddress())) {
Msg.warn(this, Msg.warn(this,
"Filling in unknown trace memory in emulator using mapped image: " + "Filling in unknown trace memory in emulator using mapped image: " +
program + ": " + toRead); program + ": " + subsrng);
long lower = subsrng.getMinAddress().getOffset();
for (AddressRange rng : toRead) { long fullLen = subsrng.getLength();
long lower = rng.getMinAddress().getOffset();
long fullLen = rng.getLength();
while (fullLen > 0) { while (fullLen > 0) {
int len = MathUtilities.unsignedMin(data.length, fullLen); int len = MathUtilities.unsignedMin(data.length, fullLen);
try { try {
@ -101,7 +104,8 @@ public class ReadsTargetMemoryPcodeExecutorState
memory.getBytes(space.getAddress(lower), data, 0, len); memory.getBytes(space.getAddress(lower), data, 0, len);
if (read < len) { if (read < len) {
Msg.warn(this, Msg.warn(this,
" Partial read of " + rng + ". Got " + read + " bytes"); " Partial read of " + subsrng + ". Got " + read +
" bytes");
} }
cache.putData(lower - shift, data, 0, read); cache.putData(lower - shift, data, 0, read);
} }
@ -114,6 +118,7 @@ public class ReadsTargetMemoryPcodeExecutorState
result = true; result = true;
} }
} }
}
return result; return result;
} }
} }

View file

@ -283,7 +283,6 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
private Program program; private Program program;
private AddressRange staticRange; private AddressRange staticRange;
private Long shift; // from static image to trace
public MappingEntry(TraceStaticMapping mapping) { public MappingEntry(TraceStaticMapping mapping) {
this.mapping = mapping; this.mapping = mapping;
@ -309,8 +308,6 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
Address minAddr = opened.getAddressFactory().getAddress(mapping.getStaticAddress()); Address minAddr = opened.getAddressFactory().getAddress(mapping.getStaticAddress());
Address maxAddr = addOrMax(minAddr, mapping.getLength() - 1); Address maxAddr = addOrMax(minAddr, mapping.getLength() - 1);
this.staticRange = new AddressRangeImpl(minAddr, maxAddr); this.staticRange = new AddressRangeImpl(minAddr, maxAddr);
this.shift = mapping.getMinTraceAddress().getOffset() -
staticRange.getMinAddress().getOffset();
return true; return true;
} }
return false; return false;
@ -320,7 +317,6 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
if (this.program == closed) { if (this.program == closed) {
this.program = null; this.program = null;
this.staticRange = null; this.staticRange = null;
this.shift = null;
return true; return true;
} }
return false; return false;
@ -565,7 +561,7 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
} }
protected void collectOpenMappedPrograms(AddressRange rng, Range<Long> span, protected void collectOpenMappedPrograms(AddressRange rng, Range<Long> span,
Map<Program, ShiftAndAddressSetView> result) { Map<Program, Collection<MappedAddressRange>> result) {
TraceAddressSnapRange tatr = new ImmutableTraceAddressSnapRange(rng, span); TraceAddressSnapRange tatr = new ImmutableTraceAddressSnapRange(rng, span);
for (Entry<TraceAddressSnapRange, MappingEntry> out : outbound.entrySet()) { for (Entry<TraceAddressSnapRange, MappingEntry> out : outbound.entrySet()) {
MappingEntry me = out.getValue(); MappingEntry me = out.getValue();
@ -575,16 +571,16 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
if (!out.getKey().intersects(tatr)) { if (!out.getKey().intersects(tatr)) {
continue; continue;
} }
AddressRange srcRng = out.getKey().getRange().intersect(rng);
ShiftAndAddressSetView set = result.computeIfAbsent(me.program, AddressRange dstRng = me.mapTraceRangeToProgram(rng);
p -> new ShiftAndAddressSetView(-me.shift, new AddressSet())); result.computeIfAbsent(me.program, p -> new TreeSet<>())
((AddressSet) set.getAddressSetView()).add(me.mapTraceRangeToProgram(rng)); .add(new MappedAddressRange(srcRng, dstRng));
} }
} }
public Map<Program, ShiftAndAddressSetView> getOpenMappedViews(AddressSetView set, public Map<Program, Collection<MappedAddressRange>> getOpenMappedViews(AddressSetView set,
Range<Long> span) { Range<Long> span) {
Map<Program, ShiftAndAddressSetView> result = new HashMap<>(); Map<Program, Collection<MappedAddressRange>> result = new HashMap<>();
for (AddressRange rng : set) { for (AddressRange rng : set) {
collectOpenMappedPrograms(rng, span, result); collectOpenMappedPrograms(rng, span, result);
} }
@ -715,7 +711,7 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
} }
protected void collectOpenMappedViews(AddressRange rng, protected void collectOpenMappedViews(AddressRange rng,
Map<TraceSnap, ShiftAndAddressSetView> result) { Map<TraceSnap, Collection<MappedAddressRange>> result) {
for (Entry<MappingEntry, Address> inPreceeding : inbound.headMapByValue( for (Entry<MappingEntry, Address> inPreceeding : inbound.headMapByValue(
rng.getMaxAddress(), true).entrySet()) { rng.getMaxAddress(), true).entrySet()) {
Address start = inPreceeding.getValue(); Address start = inPreceeding.getValue();
@ -726,14 +722,17 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
if (!me.isInProgramRange(rng)) { if (!me.isInProgramRange(rng)) {
continue; continue;
} }
ShiftAndAddressSetView set = result.computeIfAbsent(me.getTraceSnap(),
p -> new ShiftAndAddressSetView(me.shift, new AddressSet())); AddressRange srcRange = me.staticRange.intersect(rng);
((AddressSet) set.getAddressSetView()).add(me.mapProgramRangeToTrace(rng)); AddressRange dstRange = me.mapProgramRangeToTrace(rng);
result.computeIfAbsent(me.getTraceSnap(), p -> new TreeSet<>())
.add(new MappedAddressRange(srcRange, dstRange));
} }
} }
public Map<TraceSnap, ShiftAndAddressSetView> getOpenMappedViews(AddressSetView set) { public Map<TraceSnap, Collection<MappedAddressRange>> getOpenMappedViews(
Map<TraceSnap, ShiftAndAddressSetView> result = new HashMap<>(); AddressSetView set) {
Map<TraceSnap, Collection<MappedAddressRange>> result = new HashMap<>();
for (AddressRange rng : set) { for (AddressRange rng : set) {
collectOpenMappedViews(rng, result); collectOpenMappedViews(rng, result);
} }
@ -1219,9 +1218,8 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
} }
@Override @Override
public Map<Program, ShiftAndAddressSetView> getOpenMappedViews(Trace trace, public Map<Program, Collection<MappedAddressRange>> getOpenMappedViews(Trace trace,
AddressSetView set, AddressSetView set, long snap) {
long snap) {
InfoPerTrace info = requireTrackedInfo(trace); InfoPerTrace info = requireTrackedInfo(trace);
if (info == null) { if (info == null) {
return null; return null;
@ -1230,7 +1228,7 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
} }
@Override @Override
public Map<TraceSnap, ShiftAndAddressSetView> getOpenMappedViews(Program program, public Map<TraceSnap, Collection<MappedAddressRange>> getOpenMappedViews(Program program,
AddressSetView set) { AddressSetView set) {
InfoPerProgram info = requireTrackedInfo(program); InfoPerProgram info = requireTrackedInfo(program);
if (info == null) { if (info == null) {

View file

@ -487,27 +487,38 @@ public interface DebuggerStaticMappingService {
} }
/** /**
* <<<<<<< HEAD A {@code (shift,view)} pair for describing sets of mapped addresses * A pair for describing sets of mapped addresses
*
* <p>
* Note, the natural order is by the <em>destination</em> address.
*/ */
public class ShiftAndAddressSetView { public class MappedAddressRange implements Comparable<MappedAddressRange> {
private final long shift;
private final AddressSetView view;
public ShiftAndAddressSetView(long shift, AddressSetView view) { private final AddressRange srcRange;
this.shift = shift; private final AddressRange dstRange;
this.view = view; private final int hashCode;
private final long shift;
public MappedAddressRange(AddressRange srcRange, AddressRange dstRange) {
this.srcRange = srcRange;
this.dstRange = dstRange;
this.hashCode = Objects.hash(dstRange, srcRange);
this.shift = dstRange.getMinAddress().getOffset() -
srcRange.getMinAddress().getOffset();
}
@Override
public String toString() {
return "<MappedRange " + srcRange + "::" + dstRange + ">";
} }
/** /**
* Get the shift from the source address set to this address set * Get the shift from the source address range to this address range
* *
* <p> * <p>
* The meaning depends on what returned this view. If this view is the "static" set, then * The meaning depends on what returned this view. If this view is the "static" range, then
* this shift describes what was added to the offset of the "dynamic" address to get a * this shift describes what was added to the offset of the "dynamic" address to get a
* particular address in this set. Note that since not all addresses from the requested * particular address in the "static" range.
* source set may have been mapped, you cannot simply compare min addresses to obtain this
* shift. To "map back" to the source address from a destination address in this set,
* <em>subtract</em> this shift.
* *
* @return the shift * @return the shift
*/ */
@ -516,12 +527,107 @@ public interface DebuggerStaticMappingService {
} }
/** /**
* Get the destination address set view as mapped from the source address set * Map an address in the source range to the corresponding address in the destination range
* *
* @return the address set * @param saddr the source address (not validated)
* @return the destination address
*/ */
public AddressSetView getAddressSetView() { public Address mapSourceToDestination(Address saddr) {
return view; return dstRange.getAddressSpace().getAddress(saddr.getOffset() + shift);
}
/**
* Map an address in the destination range to the corresponding address in the source range
*
* @param daddr the destination address (not validated)
* @return the source address
*/
public Address mapDestinationToSource(Address daddr) {
return srcRange.getAddressSpace().getAddress(daddr.getOffset() - shift);
}
/**
* Map a sub-range of the source to the corresponding sub-range of the destination
*
* @param srng the source sub-range
* @return the destination sub-range
*/
public AddressRange mapSourceToDestination(AddressRange srng) {
try {
return new AddressRangeImpl(mapSourceToDestination(srng.getMinAddress()),
srng.getLength());
}
catch (AddressOverflowException e) {
throw new IllegalArgumentException(e);
}
}
/**
* Map a sub-range of the destination to the corresponding sub-range of the source
*
* @param drng the destination sub-range
* @return the source sub-range
*/
public AddressRange mapDestinationToSource(AddressRange drng) {
try {
return new AddressRangeImpl(mapDestinationToSource(drng.getMinAddress()),
drng.getLength());
}
catch (AddressOverflowException e) {
throw new IllegalArgumentException(e);
}
}
/**
* Get the source address range
*
* @return the address range
*/
public AddressRange getSourceAddressRange() {
return srcRange;
}
/**
* Get the destination address range
*
* @return the address range
*/
public AddressRange getDestinationAddressRange() {
return dstRange;
}
@Override
public int compareTo(MappedAddressRange that) {
int c;
c = this.dstRange.compareTo(that.dstRange);
if (c != 0) {
return c;
}
c = this.srcRange.compareTo(that.srcRange);
if (c != 0) {
return c;
}
return 0;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof MappedAddressRange)) {
return false;
}
MappedAddressRange that = (MappedAddressRange) obj;
if (!this.dstRange.equals(that.dstRange)) {
return false;
}
if (!this.srcRange.equals(that.srcRange)) {
return false;
}
return true;
}
@Override
public int hashCode() {
return hashCode;
} }
} }
@ -570,8 +676,7 @@ public interface DebuggerStaticMappingService {
boolean truncateExisting) throws TraceConflictedMappingException; boolean truncateExisting) throws TraceConflictedMappingException;
/** /**
* ======= >>>>>>> d694542c5 (GP-660: Put program filler back in. Need to performance test.) Add * Add several static mappings (relocations)
* several static mappings (relocations)
* *
* <p> * <p>
* This will group the entries by trace and add each's entries in a single transaction. If any * This will group the entries by trace and add each's entries in a single transaction. If any
@ -669,9 +774,9 @@ public interface DebuggerStaticMappingService {
* @param trace the source trace * @param trace the source trace
* @param set the source address set * @param set the source address set
* @param snap the source snap * @param snap the source snap
* @return a map of destination programs to corresponding computed destination address sets * @return a map of destination programs to corresponding computed destination address ranges
*/ */
Map<Program, ShiftAndAddressSetView> getOpenMappedViews(Trace trace, Map<Program, Collection<MappedAddressRange>> getOpenMappedViews(Trace trace,
AddressSetView set, long snap); AddressSetView set, long snap);
/** /**
@ -679,9 +784,9 @@ public interface DebuggerStaticMappingService {
* *
* @param program the destination program, from which we are mapping back * @param program the destination program, from which we are mapping back
* @param set the destination address set, from which we are mapping back * @param set the destination address set, from which we are mapping back
* @return a map of source traces to corresponding computed source address sets * @return a map of source traces to corresponding computed source address ranges
*/ */
Map<TraceSnap, ShiftAndAddressSetView> getOpenMappedViews(Program program, Map<TraceSnap, Collection<MappedAddressRange>> getOpenMappedViews(Program program,
AddressSetView set); AddressSetView set);
/** /**

View file

@ -0,0 +1,157 @@
/* ###
* 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.gui.copying;
import java.util.Set;
import org.junit.*;
import com.google.common.collect.Range;
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest.TestDebuggerTargetTraceMapper;
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin;
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingProvider;
import ghidra.app.plugin.core.debug.service.model.DebuggerModelServicePlugin;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin;
import ghidra.app.plugin.core.debug.service.tracemgr.DebuggerTraceManagerServicePlugin;
import ghidra.app.plugin.core.progmgr.ProgramManagerPlugin;
import ghidra.app.services.*;
import ghidra.dbg.model.TestDebuggerModelBuilder;
import ghidra.framework.model.DomainFolder;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.mem.Memory;
import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
import ghidra.trace.database.ToyDBTraceBuilder;
import ghidra.trace.database.memory.DBTraceMemoryManager;
import ghidra.trace.database.module.DBTraceModuleManager;
import ghidra.trace.model.DefaultTraceLocation;
import ghidra.trace.model.memory.TraceMemoryFlag;
import ghidra.trace.model.modules.TraceModule;
import ghidra.util.database.UndoableTransaction;
import ghidra.util.task.TaskMonitor;
import help.screenshot.GhidraScreenShotGenerator;
public class DebuggerCopyActionsPluginScreenShots extends GhidraScreenShotGenerator {
ProgramManager programManager;
DebuggerTraceManagerService traceManager;
DebuggerModelService modelService;
DebuggerStaticMappingServicePlugin mappingService;
DebuggerListingPlugin listingPlugin;
DebuggerListingProvider listingProvider;
DebuggerCopyActionsPlugin copyPlugin;
TestDebuggerModelBuilder mb;
ToyDBTraceBuilder tb;
@Before
public void setUpMine() throws Throwable {
programManager = addPlugin(tool, ProgramManagerPlugin.class);
traceManager = addPlugin(tool, DebuggerTraceManagerServicePlugin.class);
modelService = addPlugin(tool, DebuggerModelServicePlugin.class);
mappingService = addPlugin(tool, DebuggerStaticMappingServicePlugin.class);
listingPlugin = addPlugin(tool, DebuggerListingPlugin.class);
copyPlugin = addPlugin(tool, DebuggerCopyActionsPlugin.class);
listingProvider = waitForComponentProvider(DebuggerListingProvider.class);
mb = new TestDebuggerModelBuilder();
mb.createTestModel();
mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1));
tb = new ToyDBTraceBuilder(recorder.getTrace());
}
@After
public void tearDownMine() {
tb.close();
if (program != null) {
program.release(this);
}
}
@Test
public void testCaptureDebuggerCopyIntoProgramDialog() throws Throwable {
long snap;
try (UndoableTransaction tid = tb.startTransaction()) {
snap = tb.trace.getTimeManager().createSnapshot("First").getKey();
DBTraceMemoryManager mem = tb.trace.getMemoryManager();
mem.createRegion(".text", snap, tb.range(0x55550000, 0x5555ffff),
Set.of(TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE));
mem.createRegion(".data", snap, tb.range(0x55560000, 0x5556ffff),
Set.of(TraceMemoryFlag.READ, TraceMemoryFlag.WRITE));
mem.createRegion("[stack]", snap, tb.range(0x00100000, 0x001fffff),
Set.of(TraceMemoryFlag.READ, TraceMemoryFlag.WRITE));
DBTraceModuleManager mods = tb.trace.getModuleManager();
TraceModule modEcho = mods.addLoadedModule("Modules[/bin/echo]", "/bin/echo",
tb.range(0x55550000, 0x5556ffff), snap);
modEcho.addSection("Modules[/bin/echo].Sections[.text]", ".text",
tb.range(0x55550000, 0x5555ffff));
modEcho.addSection("Modules[/bin/echo].Sections[.data]", ".data",
tb.range(0x55560000, 0x5556ffff));
}
program = createDefaultProgram("echo", "Toy:BE:64:default", this);
AddressSpace stSpace = program.getAddressFactory().getDefaultAddressSpace();
try (UndoableTransaction tid = UndoableTransaction.start(program, "Add memory", true)) {
program.setImageBase(tb.addr(stSpace, 0x00400000), true);
Memory memory = program.getMemory();
memory.createInitializedBlock(".text", tb.addr(stSpace, 0x00400000), 0x10000, (byte) 0,
TaskMonitor.DUMMY, false);
memory.createInitializedBlock(".data", tb.addr(stSpace, 0x00600000), 0x10000, (byte) 0,
TaskMonitor.DUMMY, false);
}
DomainFolder root = tool.getProject().getProjectData().getRootFolder();
root.createFile(tb.trace.getName(), tb.trace, TaskMonitor.DUMMY);
root.createFile(program.getName(), program, TaskMonitor.DUMMY);
try (UndoableTransaction tid = tb.startTransaction()) {
mappingService.addMapping(
new DefaultTraceLocation(tb.trace, null, Range.atLeast(snap), tb.addr(0x55550000)),
new ProgramLocation(program, tb.addr(stSpace, 0x00400000)), 0x10000, true);
mappingService.addMapping(
new DefaultTraceLocation(tb.trace, null, Range.atLeast(snap), tb.addr(0x55560000)),
new ProgramLocation(program, tb.addr(stSpace, 0x00600000)), 0x10000, true);
}
traceManager.openTrace(tb.trace);
traceManager.activateTrace(tb.trace);
programManager.openProgram(program);
listingProvider.requestFocus();
waitForSwing();
listingProvider.setSelection(
new ProgramSelection(tb.trace.getMemoryManager().getRegionsAddressSet(snap)));
waitForCondition(() -> copyPlugin.actionCopyIntoCurrentProgram.isEnabled());
performAction(copyPlugin.actionCopyIntoCurrentProgram, false);
DebuggerCopyIntoProgramDialog dialog =
waitForDialogComponent(DebuggerCopyIntoProgramDialog.class);
dialog.setRelocate(true);
dialog.reset();
waitForSwing();
captureDialog(DebuggerCopyIntoProgramDialog.class, 700, 600);
}
}

View file

@ -495,8 +495,12 @@ public abstract class AbstractGhidraHeadedDebuggerGUITest
tool.getProject().getProjectData().getRootFolder().createFile(obj.getName(), obj, monitor); tool.getProject().getProjectData().getRootFolder().createFile(obj.getName(), obj, monitor);
} }
protected void createSnaplessTrace(String langID) throws IOException {
tb = new ToyDBTraceBuilder("dynamic-" + name.getMethodName(), langID);
}
protected void createSnaplessTrace() throws IOException { protected void createSnaplessTrace() throws IOException {
tb = new ToyDBTraceBuilder("dynamic-" + name.getMethodName(), LANGID_TOYBE64); createSnaplessTrace(LANGID_TOYBE64);
} }
protected void addSnapshot(String desc) throws IOException { protected void addSnapshot(String desc) throws IOException {
@ -505,16 +509,28 @@ public abstract class AbstractGhidraHeadedDebuggerGUITest
} }
} }
protected void createTrace() throws IOException { protected void createTrace(String langID) throws IOException {
createSnaplessTrace(); createSnaplessTrace(langID);
addSnapshot("First snap"); addSnapshot("First snap");
} }
protected void createAndOpenTrace() throws IOException { protected void createTrace() throws IOException {
createTrace(); createTrace(LANGID_TOYBE64);
}
protected void useTrace(Trace trace) {
tb = new ToyDBTraceBuilder(trace);
}
protected void createAndOpenTrace(String langID) throws IOException {
createTrace(langID);
traceManager.openTrace(tb.trace); traceManager.openTrace(tb.trace);
} }
protected void createAndOpenTrace() throws IOException {
createAndOpenTrace(LANGID_TOYBE64);
}
protected String getProgramName() { protected String getProgramName() {
return "static-" + getClass().getCanonicalName() + "." + name.getMethodName(); return "static-" + getClass().getCanonicalName() + "." + name.getMethodName();
} }

View file

@ -0,0 +1,505 @@
/* ###
* 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.gui.copying;
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
import com.google.common.collect.Range;
import generic.Unique;
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
import ghidra.app.plugin.core.debug.gui.action.AutoReadMemorySpec;
import ghidra.app.plugin.core.debug.gui.action.NoneAutoReadMemorySpec;
import ghidra.app.plugin.core.debug.gui.copying.DebuggerCopyIntoProgramDialog.RangeEntry;
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin;
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingProvider;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin;
import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.app.services.TraceRecorder;
import ghidra.dbg.DebuggerModelListener;
import ghidra.dbg.target.TargetObject;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
import ghidra.test.ToyProgramBuilder;
import ghidra.trace.database.memory.DBTraceMemoryManager;
import ghidra.trace.model.DefaultTraceLocation;
import ghidra.trace.model.TraceLocation;
import ghidra.trace.model.memory.TraceMemoryFlag;
import ghidra.util.database.UndoableTransaction;
public class DebuggerCopyActionsPluginTest extends AbstractGhidraHeadedDebuggerGUITest {
DebuggerCopyActionsPlugin copyActionsPlugin;
DebuggerListingPlugin listingPlugin;
DebuggerStaticMappingService mappingService;
DebuggerListingProvider listingProvider;
@Before
public void setupCopyActionsPluginTest() throws Exception {
mappingService = addPlugin(tool, DebuggerStaticMappingServicePlugin.class);
copyActionsPlugin = addPlugin(tool, DebuggerCopyActionsPlugin.class);
listingPlugin = addPlugin(tool, DebuggerListingPlugin.class);
listingProvider = waitForComponentProvider(DebuggerListingProvider.class);
}
@Test
public void testActionCopyIntoCurrentProgramWithoutRelocationCreateBlocks() throws Exception {
assertFalse(copyActionsPlugin.actionCopyIntoCurrentProgram.isEnabled());
createProgram();
AddressSpace stSpace = program.getAddressFactory().getDefaultAddressSpace();
programManager.openProgram(program);
assertFalse(copyActionsPlugin.actionCopyIntoCurrentProgram.isEnabled());
createAndOpenTrace();
try (UndoableTransaction tid = tb.startTransaction()) {
tb.trace.getMemoryManager()
.createRegion(".text", 0, tb.range(0x00400000, 0x0040ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
}
traceManager.activateTrace(tb.trace);
assertFalse(copyActionsPlugin.actionCopyIntoCurrentProgram.isEnabled());
listingProvider.requestFocus();
listingProvider
.setSelection(new ProgramSelection(tb.addr(0x00400000), tb.addr(0x0040ffff)));
waitForPass(() -> assertTrue(copyActionsPlugin.actionCopyIntoCurrentProgram.isEnabled()));
performAction(copyActionsPlugin.actionCopyIntoCurrentProgram, false);
DebuggerCopyIntoProgramDialog dialog =
waitForDialogComponent(DebuggerCopyIntoProgramDialog.class);
dialog.setRelocate(false);
dialog.reset();
RangeEntry entry = Unique.assertOne(dialog.tableModel.getModelData());
assertEquals(tb.range(stSpace, 0x00400000, 0x0040ffff), entry.getSrcRange());
assertEquals(tb.range(stSpace, 0x00400000, 0x0040ffff), entry.getDstRange());
assertEquals(".text", entry.getRegionName());
assertEquals(".text", entry.getBlockName());
assertTrue(entry.isCreate());
dialog.okCallback();
dialog.lastTask.get(1000, TimeUnit.MILLISECONDS);
waitForSwing();
MemoryBlock text = Unique.assertOne(Arrays.asList(program.getMemory().getBlocks()));
assertEquals(".text", text.getName());
}
@Test
public void testActionCopyIntoCurrentProgramWithoutRelocationCrossLanguage() throws Exception {
assertFalse(copyActionsPlugin.actionCopyIntoCurrentProgram.isEnabled());
createProgram(getSLEIGH_X86_LANGUAGE());
createAndOpenTrace(ToyProgramBuilder._X64);
assertFalse(copyActionsPlugin.actionCopyIntoCurrentProgram.isEnabled());
AddressSpace stSpace = program.getAddressFactory().getDefaultAddressSpace();
try (UndoableTransaction tid = UndoableTransaction.start(program, "Add blocks", true)) {
program.getMemory()
.createInitializedBlock(".text", tb.addr(stSpace, 0x00400000), 0x8000,
(byte) 0, monitor, false);
program.getMemory()
.createInitializedBlock(".text2", tb.addr(stSpace, 0x00408000), 0x8000,
(byte) 0, monitor, false);
}
try (UndoableTransaction tid = tb.startTransaction()) {
DBTraceMemoryManager mm = tb.trace.getMemoryManager();
mm.createRegion(".text", 0, tb.range(0x00400000, 0x0040ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
mm.putBytes(0, tb.addr(0x00401234), tb.buf(1, 2, 3, 4));
// This region should be excluded, since it cannot be mapped identically into 32-bits
mm.createRegion("lib:.text", 0, tb.range(0x7fff00400000L, 0x7fff0040ffffL),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
// This region should be partially excluded, because 32-bits
// This is not likely to ever happen in practice, but be prepared
mm.createRegion(".straddle", 0, tb.range(0xfffff000L, 0x100000fffL),
TraceMemoryFlag.READ, TraceMemoryFlag.WRITE);
}
programManager.openProgram(program);
traceManager.activateTrace(tb.trace);
assertFalse(copyActionsPlugin.actionCopyIntoCurrentProgram.isEnabled());
listingProvider.requestFocus();
listingProvider.setSelection(new ProgramSelection(tb.set(
tb.range(0x00400000, 0x0040ffff),
tb.range(0x7fff00400000L, 0x7fff0040ffffL),
tb.range(0xfffff000L, 0x100000fffL))));
waitForPass(() -> assertTrue(copyActionsPlugin.actionCopyIntoCurrentProgram.isEnabled()));
performAction(copyActionsPlugin.actionCopyIntoCurrentProgram, false);
DebuggerCopyIntoProgramDialog dialog =
waitForDialogComponent(DebuggerCopyIntoProgramDialog.class);
dialog.setRelocate(false);
dialog.reset();
List<RangeEntry> entries = List.copyOf(dialog.tableModel.getModelData());
assertEquals(3, entries.size());
RangeEntry entry;
entry = entries.get(0);
assertEquals(tb.range(0x00400000, 0x00407fff), entry.getSrcRange());
assertEquals(tb.range(stSpace, 0x00400000, 0x00407fff), entry.getDstRange());
assertEquals(".text", entry.getRegionName());
assertEquals(".text *", entry.getBlockName());
assertFalse(entry.isCreate());
entry = entries.get(1);
assertEquals(tb.range(0x00408000, 0x0040ffff), entry.getSrcRange());
assertEquals(tb.range(stSpace, 0x00408000, 0x0040ffff), entry.getDstRange());
assertEquals(".text", entry.getRegionName());
assertEquals(".text2 *", entry.getBlockName());
assertFalse(entry.isCreate());
entry = entries.get(2);
assertEquals(tb.range(0xfffff000L, 0xffffffffL), entry.getSrcRange());
assertEquals(tb.range(stSpace, 0xfffff000L, 0xffffffffL), entry.getDstRange());
assertEquals(".straddle", entry.getRegionName());
assertEquals(".straddle", entry.getBlockName());
assertTrue(entry.isCreate());
dialog.okCallback();
dialog.lastTask.get(1000, TimeUnit.MILLISECONDS);
waitForSwing();
byte[] dest = new byte[4];
program.getMemory().getBytes(tb.addr(stSpace, 0x00401234), dest);
assertArrayEquals(tb.arr(1, 2, 3, 4), dest);
}
@Test
public void testActionCopyIntoCurrentProgramWithRelocationExistingBlocks() throws Exception {
assertFalse(copyActionsPlugin.actionCopyIntoCurrentProgram.isEnabled());
createAndOpenTrace();
createProgramFromTrace();
intoProject(program);
intoProject(tb.trace);
try (UndoableTransaction tid = tb.startTransaction()) {
tb.trace.getMemoryManager()
.createRegion(".text", 0, tb.range(0x55550000, 0x5555ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
}
traceManager.activateTrace(tb.trace);
assertFalse(copyActionsPlugin.actionCopyIntoCurrentProgram.isEnabled());
programManager.openProgram(program);
assertFalse(copyActionsPlugin.actionCopyIntoCurrentProgram.isEnabled());
AddressSpace stSpace = program.getAddressFactory().getDefaultAddressSpace();
MemoryBlock block;
try (UndoableTransaction tid = UndoableTransaction.start(program, "Create block", true)) {
block = program.getMemory()
.createUninitializedBlock(".text", tb.addr(stSpace, 0x00400000), 0x10000,
false);
}
TraceLocation tloc =
new DefaultTraceLocation(tb.trace, null, Range.atLeast(0L), tb.addr(0x55550000));
ProgramLocation ploc = new ProgramLocation(program, tb.addr(stSpace, 0x00400000));
try (UndoableTransaction tid = tb.startTransaction()) {
mappingService.addMapping(tloc, ploc, 0x10000, true);
}
waitForValue(() -> mappingService
.getOpenMappedViews(tb.trace, tb.set(tb.range(0x55550000, 0x5555ffff)), 0)
.get(program));
listingProvider.requestFocus();
listingProvider
.setSelection(new ProgramSelection(tb.addr(0x55550000), tb.addr(0x5555ffff)));
waitForPass(() -> assertTrue(copyActionsPlugin.actionCopyIntoCurrentProgram.isEnabled()));
performAction(copyActionsPlugin.actionCopyIntoCurrentProgram, false);
DebuggerCopyIntoProgramDialog dialog =
waitForDialogComponent(DebuggerCopyIntoProgramDialog.class);
dialog.setRelocate(true);
dialog.reset();
RangeEntry entry = Unique.assertOne(dialog.tableModel.getModelData());
assertEquals(tb.range(stSpace, 0x55550000, 0x5555ffff), entry.getSrcRange());
assertEquals(tb.range(stSpace, 0x00400000, 0x0040ffff), entry.getDstRange());
assertEquals(".text", entry.getRegionName());
assertEquals(".text *", entry.getBlockName());
assertFalse(entry.isCreate());
dialog.okCallback();
dialog.lastTask.get(1000, TimeUnit.MILLISECONDS);
waitForSwing();
MemoryBlock text = Unique.assertOne(Arrays.asList(program.getMemory().getBlocks()));
assertEquals(block, text);
}
@Test
public void testActionCopyIntoCurrentProgramWithRelocationOverlayBlocks() throws Exception {
assertFalse(copyActionsPlugin.actionCopyIntoCurrentProgram.isEnabled());
createAndOpenTrace();
createProgramFromTrace();
intoProject(program);
intoProject(tb.trace);
try (UndoableTransaction tid = tb.startTransaction()) {
tb.trace.getMemoryManager()
.createRegion(".text", 0, tb.range(0x55550000, 0x5555ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
}
traceManager.activateTrace(tb.trace);
assertFalse(copyActionsPlugin.actionCopyIntoCurrentProgram.isEnabled());
programManager.openProgram(program);
assertFalse(copyActionsPlugin.actionCopyIntoCurrentProgram.isEnabled());
AddressSpace stSpace = program.getAddressFactory().getDefaultAddressSpace();
MemoryBlock block;
try (UndoableTransaction tid = UndoableTransaction.start(program, "Create block", true)) {
block = program.getMemory()
.createUninitializedBlock(".text", tb.addr(stSpace, 0x00400000), 0x10000,
false);
}
TraceLocation tloc =
new DefaultTraceLocation(tb.trace, null, Range.atLeast(0L), tb.addr(0x55550000));
ProgramLocation ploc = new ProgramLocation(program, tb.addr(stSpace, 0x00400000));
try (UndoableTransaction tid = tb.startTransaction()) {
mappingService.addMapping(tloc, ploc, 0x10000, true);
}
waitForValue(() -> mappingService
.getOpenMappedViews(tb.trace, tb.set(tb.range(0x55550000, 0x5555ffff)), 0)
.get(program));
listingProvider.requestFocus();
listingProvider
.setSelection(new ProgramSelection(tb.addr(0x55550000), tb.addr(0x5555ffff)));
waitForPass(() -> assertTrue(copyActionsPlugin.actionCopyIntoCurrentProgram.isEnabled()));
performAction(copyActionsPlugin.actionCopyIntoCurrentProgram, false);
DebuggerCopyIntoProgramDialog dialog =
waitForDialogComponent(DebuggerCopyIntoProgramDialog.class);
dialog.setRelocate(true);
dialog.setUseOverlays(true);
dialog.reset();
RangeEntry entry = Unique.assertOne(dialog.tableModel.getModelData());
assertEquals(tb.range(stSpace, 0x55550000, 0x5555ffff), entry.getSrcRange());
assertEquals(tb.range(stSpace, 0x00400000, 0x0040ffff), entry.getDstRange());
assertEquals(".text", entry.getRegionName());
assertEquals(".text_2", entry.getBlockName());
assertTrue(entry.isCreate());
dialog.okCallback();
dialog.lastTask.get(1000, TimeUnit.MILLISECONDS);
waitForSwing();
MemoryBlock text2 =
Unique.assertOne(Arrays.asList(program.getMemory().getBlock(".text_2")));
assertNotEquals(block, text2);
assertTrue(text2.isOverlay());
}
@Test
public void testActionCopyIntoNewProgram() throws Exception {
assertFalse(copyActionsPlugin.actionCopyIntoNewProgram.isEnabled());
createAndOpenTrace();
try (UndoableTransaction tid = tb.startTransaction()) {
tb.trace.getMemoryManager()
.createRegion(".text", 0, tb.range(0x55550000, 0x5555ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
}
traceManager.activateTrace(tb.trace);
assertFalse(copyActionsPlugin.actionCopyIntoNewProgram.isEnabled());
listingProvider.requestFocus();
listingProvider
.setSelection(new ProgramSelection(tb.addr(0x55550000), tb.addr(0x5555ffff)));
waitForPass(() -> assertTrue(copyActionsPlugin.actionCopyIntoNewProgram.isEnabled()));
performAction(copyActionsPlugin.actionCopyIntoNewProgram, false);
DebuggerCopyIntoProgramDialog dialog =
waitForDialogComponent(DebuggerCopyIntoProgramDialog.class);
dialog.setDestination(DebuggerCopyIntoProgramDialog.TEMP_PROGRAM);
RangeEntry entry = Unique.assertOne(dialog.tableModel.getModelData());
assertEquals(tb.range(0x55550000, 0x5555ffff), entry.getSrcRange());
assertEquals(tb.range(0x55550000, 0x5555ffff), entry.getDstRange());
assertEquals(".text", entry.getRegionName());
assertEquals(".text", entry.getBlockName());
assertTrue(entry.isCreate());
entry.setBlockName(".my_text");
dialog.okCallback();
dialog.lastTask.get(1000, TimeUnit.MILLISECONDS);
waitForSwing();
// Declare my own, or the @After will try to release it erroneously
Program program = waitForValue(() -> programManager.getCurrentProgram());
AddressSpace stSpace = program.getAddressFactory().getDefaultAddressSpace();
MemoryBlock text = Unique.assertOne(Arrays.asList(program.getMemory().getBlocks()));
assertEquals(tb.addr(stSpace, 0x55550000), text.getStart());
assertEquals(".my_text", text.getName());
}
@Test
public void testActionCopyIntoNewProgramAdjacentRegions() throws Exception {
assertFalse(copyActionsPlugin.actionCopyIntoNewProgram.isEnabled());
createAndOpenTrace();
try (UndoableTransaction tid = tb.startTransaction()) {
tb.trace.getMemoryManager()
.createRegion(".text", 0, tb.range(0x55550000, 0x5555ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
tb.trace.getMemoryManager()
.createRegion(".data", 0, tb.range(0x55560000, 0x5556ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.WRITE);
}
traceManager.activateTrace(tb.trace);
assertFalse(copyActionsPlugin.actionCopyIntoNewProgram.isEnabled());
listingProvider.requestFocus();
listingProvider
.setSelection(new ProgramSelection(tb.addr(0x55550000), tb.addr(0x5556ffff)));
waitForPass(() -> assertTrue(copyActionsPlugin.actionCopyIntoNewProgram.isEnabled()));
performAction(copyActionsPlugin.actionCopyIntoNewProgram, false);
DebuggerCopyIntoProgramDialog dialog =
waitForDialogComponent(DebuggerCopyIntoProgramDialog.class);
assertFalse(dialog.cbCapture.isEnabled());
assertFalse(dialog.cbCapture.isSelected());
dialog.setDestination(DebuggerCopyIntoProgramDialog.TEMP_PROGRAM);
assertEquals(2, dialog.tableModel.getRowCount());
RangeEntry entry;
entry = dialog.tableModel.getRowObject(0);
assertEquals(tb.range(0x55550000, 0x5555ffff), entry.getSrcRange());
assertEquals(tb.range(0x55550000, 0x5555ffff), entry.getDstRange());
assertEquals(".text", entry.getRegionName());
assertEquals(".text", entry.getBlockName());
assertTrue(entry.isCreate());
entry = dialog.tableModel.getRowObject(1);
assertEquals(tb.range(0x55560000, 0x5556ffff), entry.getSrcRange());
assertEquals(tb.range(0x55560000, 0x5556ffff), entry.getDstRange());
assertEquals(".data", entry.getRegionName());
assertEquals(".data", entry.getBlockName());
assertTrue(entry.isCreate());
dialog.okCallback();
dialog.lastTask.get(1000, TimeUnit.MILLISECONDS);
waitForSwing();
// Declare my own, or the @After will try to release it erroneously
Program program = waitForValue(() -> programManager.getCurrentProgram());
assertEquals(2, program.getMemory().getBlocks().length);
}
@Test
public void testActionCopyIntoNewProgramCaptureLive() throws Exception {
assertFalse(copyActionsPlugin.actionCopyIntoNewProgram.isEnabled());
createTestModel();
var listener = new DebuggerModelListener() {
int count = 0;
@Override
public void memoryUpdated(TargetObject memory, Address address, byte[] data) {
count++;
}
};
mb.testModel.addModelListener(listener);
mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1));
useTrace(recorder.getTrace());
mb.testProcess1.memory.addRegion(".text", mb.rng(0x55550000, 0x5555ffff), "rx");
mb.testProcess1.memory.setMemory(mb.addr(0x55550000), mb.arr(1, 2, 3, 4, 5, 6, 7, 8));
waitForPass(() -> {
assertEquals(1, tb.trace.getMemoryManager().getAllRegions().size());
});
listingProvider.setAutoReadMemorySpec(
AutoReadMemorySpec.fromConfigName(NoneAutoReadMemorySpec.CONFIG_NAME));
traceManager.openTrace(tb.trace);
traceManager.activateTrace(tb.trace);
assertFalse(copyActionsPlugin.actionCopyIntoNewProgram.isEnabled());
listingProvider.requestFocus();
listingProvider
.setSelection(new ProgramSelection(tb.addr(0x55550000), tb.addr(0x5555ffff)));
waitForPass(() -> assertTrue(copyActionsPlugin.actionCopyIntoNewProgram.isEnabled()));
performAction(copyActionsPlugin.actionCopyIntoNewProgram, false);
DebuggerCopyIntoProgramDialog dialog =
waitForDialogComponent(DebuggerCopyIntoProgramDialog.class);
assertTrue(dialog.cbCapture.isEnabled());
assertTrue(dialog.cbCapture.isSelected());
dialog.setDestination(DebuggerCopyIntoProgramDialog.TEMP_PROGRAM);
RangeEntry entry = Unique.assertOne(dialog.tableModel.getModelData());
assertEquals(tb.range(0x55550000, 0x5555ffff), entry.getSrcRange());
assertEquals(tb.range(0x55550000, 0x5555ffff), entry.getDstRange());
assertEquals("[.text]", entry.getRegionName());
assertEquals("[.text]", entry.getBlockName());
assertTrue(entry.isCreate());
entry.setBlockName(".my_text");
assertEquals(0, listener.count);
dialog.okCallback();
dialog.lastTask.get(10000, TimeUnit.MILLISECONDS);
waitForSwing();
assertEquals(16, listener.count);
// Declare my own, or the @After will try to release it erroneously
Program program = waitForValue(() -> programManager.getCurrentProgram());
AddressSpace stSpace = program.getAddressFactory().getDefaultAddressSpace();
MemoryBlock text = Unique.assertOne(Arrays.asList(program.getMemory().getBlocks()));
assertEquals(tb.addr(stSpace, 0x55550000), text.getStart());
assertEquals(".my_text", text.getName());
byte[] arr = new byte[8];
text.getBytes(tb.addr(stSpace, 0x55550000), arr);
assertArrayEquals(tb.arr(1, 2, 3, 4, 5, 6, 7, 8), arr);
}
}

View file

@ -0,0 +1,743 @@
/* ###
* 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.gui.copying;
import static org.junit.Assert.*;
import java.awt.Color;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.*;
import javax.swing.JCheckBox;
import org.junit.Test;
import com.google.common.collect.Range;
import ghidra.app.plugin.assembler.Assembler;
import ghidra.app.plugin.assembler.Assemblers;
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.gui.copying.DebuggerCopyPlan.AllCopiers;
import ghidra.app.util.viewer.listingpanel.PropertyBasedBackgroundColorModel;
import ghidra.program.database.IntRangeMap;
import ghidra.program.disassemble.Disassembler;
import ghidra.program.disassemble.DisassemblerMessageListener;
import ghidra.program.model.address.*;
import ghidra.program.model.data.*;
import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*;
import ghidra.trace.database.breakpoint.DBTraceBreakpointManager;
import ghidra.trace.database.memory.DBTraceMemoryManager;
import ghidra.trace.database.program.DBTraceVariableSnapProgramView;
import ghidra.trace.database.symbol.*;
import ghidra.trace.model.breakpoint.TraceBreakpointKind;
import ghidra.trace.model.memory.TraceMemoryFlag;
import ghidra.trace.model.memory.TraceMemoryState;
import ghidra.util.database.UndoableTransaction;
import ghidra.util.task.TaskMonitor;
public class DebuggerCopyPlanTests extends AbstractGhidraHeadedDebuggerGUITest {
public static class TestDynamicDataType extends CountedDynamicDataType {
public static final TestDynamicDataType dataType = new TestDynamicDataType();
public TestDynamicDataType() {
super("test_dyn", "A test dynamic type", ShortDataType.dataType, ByteDataType.dataType,
0, 2, 0xffff);
}
}
@Test
public void testBytes() throws Exception {
createTrace();
createProgram(getSLEIGH_X86_64_LANGUAGE());
AddressSpace stSpace = program.getAddressFactory().getDefaultAddressSpace();
DBTraceVariableSnapProgramView view = tb.trace.getProgramView();
assertTrue(AllCopiers.BYTES.isAvailable(view, program));
Random r = new Random();
byte src[] = new byte[0x10000];
r.nextBytes(src);
try (UndoableTransaction tid = tb.startTransaction()) {
DBTraceMemoryManager memory = tb.trace.getMemoryManager();
memory.createRegion(".text", 0, tb.range(0x55550000, 0x5555ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
memory.putBytes(0, tb.addr(0x55550000), ByteBuffer.wrap(src));
}
Address paddr = tb.addr(stSpace, 0x00400000);
assertTrue(AllCopiers.BYTES.isRequiresInitializedMemory());
try (UndoableTransaction tid = UndoableTransaction.start(program, "Copy", true)) {
program.getMemory()
.createInitializedBlock(".text", paddr, 0x10000, (byte) 0, TaskMonitor.DUMMY,
false);
AllCopiers.BYTES.copy(view, tb.range(0x55550000, 0x5555ffff), program, paddr,
TaskMonitor.DUMMY);
}
byte dst[] = new byte[0x10000];
program.getMemory().getBytes(paddr, dst);
assertArrayEquals(src, dst);
}
@Test
public void testState() throws Exception {
createTrace();
createProgram(getSLEIGH_X86_64_LANGUAGE());
AddressSpace stSpace = program.getAddressFactory().getDefaultAddressSpace();
DBTraceVariableSnapProgramView view = tb.trace.getProgramView();
assertTrue(AllCopiers.STATE.isAvailable(view, program));
try (UndoableTransaction tid = tb.startTransaction()) {
DBTraceMemoryManager memory = tb.trace.getMemoryManager();
memory.createRegion(".text", 0, tb.range(0x55550000, 0x5555ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
memory.putBytes(0, tb.addr(0x55550000), ByteBuffer.allocate(4096));
memory.setState(0, tb.addr(0x55551000), TraceMemoryState.ERROR);
}
Address paddr = tb.addr(stSpace, 0x00400000);
assertFalse(AllCopiers.STATE.isRequiresInitializedMemory());
try (UndoableTransaction tid = UndoableTransaction.start(program, "Copy", true)) {
program.getMemory()
.createInitializedBlock(".text", paddr, 0x10000, (byte) 0, TaskMonitor.DUMMY,
false);
AllCopiers.STATE.copy(view, tb.range(0x55550000, 0x5555ffff), program, paddr,
TaskMonitor.DUMMY);
}
IntRangeMap map =
program.getIntRangeMap(PropertyBasedBackgroundColorModel.COLOR_PROPERTY_NAME);
AddressSet staleSet =
map.getAddressSet(DebuggerResources.DEFAULT_COLOR_BACKGROUND_STALE.getRGB());
assertEquals(tb.set(tb.range(stSpace, 0x00401001, 0x0040ffff)), staleSet);
AddressSet errorSet =
map.getAddressSet(DebuggerResources.DEFAULT_COLOR_BACKGROUND_ERROR.getRGB());
assertEquals(tb.set(tb.range(stSpace, 0x00401000, 0x00401000)), errorSet);
}
@Test
public void testInstructionsMismatched() throws Exception {
createTrace();
createProgram(getSLEIGH_X86_64_LANGUAGE());
assertFalse(AllCopiers.INSTRUCTIONS.isAvailable(tb.trace.getProgramView(), program));
}
@Test
public void testInstructionsDepBytes() throws Exception {
DebuggerCopyPlan plan = new DebuggerCopyPlan();
JCheckBox cbInstructions = plan.getCheckBox(AllCopiers.INSTRUCTIONS);
JCheckBox cbBytes = plan.getCheckBox(AllCopiers.BYTES);
assertFalse(cbInstructions.isSelected());
assertFalse(cbBytes.isSelected());
cbInstructions.doClick();
assertTrue(cbInstructions.isSelected());
assertTrue(cbBytes.isSelected());
cbInstructions.doClick();
assertFalse(cbInstructions.isSelected());
assertTrue(cbBytes.isSelected());
cbInstructions.doClick();
assertTrue(cbInstructions.isSelected());
assertTrue(cbBytes.isSelected());
cbBytes.doClick();
assertFalse(cbInstructions.isSelected());
assertFalse(cbBytes.isSelected());
cbBytes.doClick();
assertFalse(cbInstructions.isSelected());
assertTrue(cbBytes.isSelected());
}
@Test
public void testInstructions() throws Exception {
createTrace();
createProgram();
AddressSpace stSpace = program.getAddressFactory().getDefaultAddressSpace();
DBTraceVariableSnapProgramView view = tb.trace.getProgramView();
assertTrue(AllCopiers.BYTES.isAvailable(view, program));
assertTrue(AllCopiers.INSTRUCTIONS.isAvailable(view, program));
AddressRange trng = tb.range(0x55550000, 0x5555ffff);
Assembler asm = Assemblers.getAssembler(view);
try (UndoableTransaction tid = tb.startTransaction()) {
DBTraceMemoryManager memory = tb.trace.getMemoryManager();
memory.createRegion(".text", 0, trng, TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
InstructionIterator iit = asm.assemble(tb.addr(0x55550000),
"imm r0, #1234",
"imm r1, #2045",
"add r0, r1");
assertTrue(iit.hasNext());
}
try (UndoableTransaction tid = UndoableTransaction.start(program, "Copy", true)) {
Address paddr = tb.addr(stSpace, 0x00400000);
program.getMemory()
.createInitializedBlock(".text", paddr, 0x10000, (byte) 0, TaskMonitor.DUMMY,
false);
AllCopiers.BYTES.copy(view, trng, program, paddr, TaskMonitor.DUMMY);
AllCopiers.INSTRUCTIONS.copy(view, trng, program, paddr, TaskMonitor.DUMMY);
}
List<Instruction> instructions = new ArrayList<>();
program.getListing().getInstructions(true).forEachRemaining(instructions::add);
assertEquals(3, instructions.size());
Instruction ins;
ins = instructions.get(0);
assertEquals(tb.addr(stSpace, 0x00400000), ins.getAddress());
assertEquals("imm r0,#0x4d2", ins.toString());
ins = instructions.get(1);
assertEquals(tb.addr(stSpace, 0x00400002), ins.getAddress());
assertEquals("imm r1,#0x7fd", ins.toString());
ins = instructions.get(2);
assertEquals(tb.addr(stSpace, 0x00400004), ins.getAddress());
assertEquals("add r0,r1", ins.toString());
}
@Test
public void testInstructionsWithDefaultContext() throws Exception {
createTrace("x86:LE:64:default");
createProgram(getSLEIGH_X86_64_LANGUAGE());
AddressSpace stSpace = program.getAddressFactory().getDefaultAddressSpace();
DBTraceVariableSnapProgramView view = tb.trace.getProgramView();
assertTrue(AllCopiers.BYTES.isAvailable(view, program));
assertTrue(AllCopiers.INSTRUCTIONS.isAvailable(view, program));
AddressRange trng = tb.range(0x55550000, 0x5555ffff);
Assembler asm = Assemblers.getAssembler(view);
try (UndoableTransaction tid = tb.startTransaction()) {
DBTraceMemoryManager memory = tb.trace.getMemoryManager();
memory.createRegion(".text", 0, trng, TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
InstructionIterator iit = asm.assemble(tb.addr(0x55550000),
"MOV RAX, 1234",
"MOV RCX, 2345",
"ADD RAX, RCX");
assertTrue(iit.hasNext());
}
try (UndoableTransaction tid = UndoableTransaction.start(program, "Copy", true)) {
Address paddr = tb.addr(stSpace, 0x00400000);
program.getMemory()
.createInitializedBlock(".text", paddr, 0x10000, (byte) 0, TaskMonitor.DUMMY,
false);
AllCopiers.BYTES.copy(view, trng, program, paddr, TaskMonitor.DUMMY);
AllCopiers.INSTRUCTIONS.copy(view, trng, program, paddr, TaskMonitor.DUMMY);
}
List<Instruction> instructions = new ArrayList<>();
program.getListing().getInstructions(true).forEachRemaining(instructions::add);
assertEquals(3, instructions.size());
Instruction ins;
ins = instructions.get(0);
assertEquals(tb.addr(stSpace, 0x00400000), ins.getAddress());
assertEquals("MOV RAX,0x4d2", ins.toString());
ins = instructions.get(1);
assertEquals(tb.addr(stSpace, 0x00400007), ins.getAddress());
assertEquals("MOV RCX,0x929", ins.toString());
ins = instructions.get(2);
assertEquals(tb.addr(stSpace, 0x0040000e), ins.getAddress());
assertEquals("ADD RAX,RCX", ins.toString());
}
@Test
public void testInstructionsWithContext() throws Exception {
createTrace("x86:LE:64:default");
createProgram(getSLEIGH_X86_64_LANGUAGE());
AddressSpace stSpace = program.getAddressFactory().getDefaultAddressSpace();
DBTraceVariableSnapProgramView view = tb.trace.getProgramView();
assertTrue(AllCopiers.BYTES.isAvailable(view, program));
assertTrue(AllCopiers.INSTRUCTIONS.isAvailable(view, program));
AddressRange trng = tb.range(0x55550000, 0x5555ffff);
// Assembler asm = Assemblers.getAssembler(view);
Register contextReg = tb.language.getContextBaseRegister();
Register longMode = tb.language.getRegister("longMode");
RegisterValue rv = tb.trace.getRegisterContextManager()
.getValueWithDefault(tb.language, contextReg, 0, tb.addr(0x55550000));
rv = rv.assign(longMode, BigInteger.ZERO);
Instruction checkCtx;
try (UndoableTransaction tid = tb.startTransaction()) {
DBTraceMemoryManager memory = tb.trace.getMemoryManager();
memory.createRegion(".text", 0, trng, TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
tb.trace.getRegisterContextManager().setValue(tb.language, rv, Range.atLeast(0L), trng);
// TODO: Once GP-1426 is resolved, use the assembler
/*
InstructionIterator iit = asm.assemble(tb.addr(0x55550000),
"MOV EAX, 1234",
"MOV ECX, 2345",
"ADD EAX, ECX");
checkCtx = iit.next();
*/
memory.putBytes(0, tb.addr(0x55550000), tb.buf(
0xb8, 0xd2, 0x04, 0x00, 0x00, // MOV EAX,1234
0xb9, 0x29, 0x09, 0x00, 0x00, // MOV ECX,2345
0x01, 0xc8 // ADD EAX,ECX
));
Disassembler
.getDisassembler(view, TaskMonitor.DUMMY, DisassemblerMessageListener.IGNORE)
.disassemble(tb.addr(0x55550000), tb.set(tb.range(0x55550000, 0x5555000b)));
checkCtx = tb.trace.getCodeManager().instructions().getAt(0, tb.addr(0x55550000));
}
// Sanity pre-check
RegisterValue insCtx = checkCtx.getRegisterValue(contextReg);
assertFalse(insCtx.equals(tb.trace.getRegisterContextManager()
.getDefaultValue(tb.language, contextReg, checkCtx.getAddress())));
try (UndoableTransaction tid = UndoableTransaction.start(program, "Copy", true)) {
Address paddr = tb.addr(stSpace, 0x00400000);
program.getMemory()
.createInitializedBlock(".text", paddr, 0x10000, (byte) 0, TaskMonitor.DUMMY,
false);
AllCopiers.BYTES.copy(view, trng, program, paddr, TaskMonitor.DUMMY);
AllCopiers.INSTRUCTIONS.copy(view, trng, program, paddr, TaskMonitor.DUMMY);
}
List<Instruction> instructions = new ArrayList<>();
program.getListing().getInstructions(true).forEachRemaining(instructions::add);
assertEquals(3, instructions.size());
Instruction ins;
ins = instructions.get(0);
assertEquals(tb.addr(stSpace, 0x00400000), ins.getAddress());
assertEquals("MOV EAX,0x4d2", ins.toString());
ins = instructions.get(1);
assertEquals(tb.addr(stSpace, 0x00400005), ins.getAddress());
assertEquals("MOV ECX,0x929", ins.toString());
ins = instructions.get(2);
assertEquals(tb.addr(stSpace, 0x0040000a), ins.getAddress());
assertEquals("ADD EAX,ECX", ins.toString());
}
@Test
public void testDataMismatched() throws Exception {
createTrace();
createProgram(getSLEIGH_X86_64_LANGUAGE());
assertFalse(AllCopiers.DATA.isAvailable(tb.trace.getProgramView(), program));
}
@Test
public void testData() throws Exception {
createTrace();
createProgram();
AddressSpace stSpace = program.getAddressFactory().getDefaultAddressSpace();
DBTraceVariableSnapProgramView view = tb.trace.getProgramView();
assertTrue(AllCopiers.DATA.isAvailable(view, program));
AddressRange trng = tb.range(0x55560000, 0x5556ffff);
try (UndoableTransaction tid = tb.startTransaction()) {
DBTraceMemoryManager memory = tb.trace.getMemoryManager();
memory.createRegion(".data", 0, trng, TraceMemoryFlag.READ, TraceMemoryFlag.WRITE);
tb.addData(0, tb.addr(0x55560000), ByteDataType.dataType, tb.buf(0x12));
tb.addData(0, tb.addr(0x55560001), ShortDataType.dataType, tb.buf(0x12, 0x34));
tb.addData(0, tb.addr(0x55560003), IntegerDataType.dataType,
tb.buf(0x12, 0x34, 0x56, 0x78));
tb.addData(0, tb.addr(0x55560007), LongLongDataType.dataType,
tb.buf(0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0));
tb.addData(0, tb.addr(0x5556000f), TestDynamicDataType.dataType,
tb.buf(0x00, 0x03, 0x00, 0x01, 0x02));
}
try (UndoableTransaction tid = UndoableTransaction.start(program, "Copy", true)) {
Address paddr = tb.addr(stSpace, 0x00600000);
program.getMemory()
.createInitializedBlock(".data", paddr, 0x10000, (byte) 0, TaskMonitor.DUMMY,
false);
AllCopiers.DATA.copy(view, trng, program, paddr, TaskMonitor.DUMMY);
}
List<Data> data = new ArrayList<>();
program.getListing().getDefinedData(true).forEachRemaining(data::add);
// NB. Bytes were not copied. Dynamic omitted.
assertEquals(4, data.size());
Data dat;
dat = data.get(0);
assertEquals(tb.addr(stSpace, 0x00600000), dat.getAddress());
assertEquals("db 0h", dat.toString());
dat = data.get(1);
assertEquals(tb.addr(stSpace, 0x00600001), dat.getAddress());
assertEquals("short 0h", dat.toString());
dat = data.get(2);
assertEquals(tb.addr(stSpace, 0x00600003), dat.getAddress());
assertEquals("int 0h", dat.toString());
dat = data.get(3);
assertEquals(tb.addr(stSpace, 0x00600007), dat.getAddress());
assertEquals("longlong 0h", dat.toString());
}
@Test
public void testDynamicDataMismatched() throws Exception {
createTrace();
createProgram(getSLEIGH_X86_64_LANGUAGE());
assertFalse(AllCopiers.DYNAMIC_DATA.isAvailable(tb.trace.getProgramView(), program));
}
@Test
public void testDynamicData() throws Exception {
createTrace();
createProgram();
AddressSpace stSpace = program.getAddressFactory().getDefaultAddressSpace();
DBTraceVariableSnapProgramView view = tb.trace.getProgramView();
assertTrue(AllCopiers.DYNAMIC_DATA.isAvailable(view, program));
AddressRange trng = tb.range(0x55560000, 0x5556ffff);
try (UndoableTransaction tid = tb.startTransaction()) {
DBTraceMemoryManager memory = tb.trace.getMemoryManager();
memory.createRegion(".data", 0, trng, TraceMemoryFlag.READ, TraceMemoryFlag.WRITE);
tb.addData(0, tb.addr(0x55560000), ByteDataType.dataType, tb.buf(0x12));
tb.addData(0, tb.addr(0x55560001), ShortDataType.dataType, tb.buf(0x12, 0x34));
tb.addData(0, tb.addr(0x55560003), IntegerDataType.dataType,
tb.buf(0x12, 0x34, 0x56, 0x78));
tb.addData(0, tb.addr(0x55560007), LongLongDataType.dataType,
tb.buf(0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0));
tb.addData(0, tb.addr(0x5556000f), TestDynamicDataType.dataType,
tb.buf(0x00, 0x03, 0x00, 0x01, 0x02));
}
try (UndoableTransaction tid = UndoableTransaction.start(program, "Copy", true)) {
Address paddr = tb.addr(stSpace, 0x00600000);
program.getMemory()
.createInitializedBlock(".data", paddr, 0x10000, (byte) 0, TaskMonitor.DUMMY,
false);
AllCopiers.BYTES.copy(view, trng, program, paddr, TaskMonitor.DUMMY);
AllCopiers.DATA.copy(view, trng, program, paddr, TaskMonitor.DUMMY);
AllCopiers.DYNAMIC_DATA.copy(view, trng, program, paddr, TaskMonitor.DUMMY);
}
List<Data> data = new ArrayList<>();
program.getListing().getDefinedData(true).forEachRemaining(data::add);
// NB. Bytes were not copied. Dynamic omitted.
assertEquals(5, data.size());
Data dat;
Data cmp;
dat = data.get(0);
assertEquals(tb.addr(stSpace, 0x00600000), dat.getAddress());
assertEquals("db 12h", dat.toString());
dat = data.get(1);
assertEquals(tb.addr(stSpace, 0x00600001), dat.getAddress());
assertEquals("short 1234h", dat.toString());
dat = data.get(2);
assertEquals(tb.addr(stSpace, 0x00600003), dat.getAddress());
assertEquals("int 12345678h", dat.toString());
dat = data.get(3);
assertEquals(tb.addr(stSpace, 0x00600007), dat.getAddress());
assertEquals("longlong 123456789ABCDEF0h", dat.toString());
dat = data.get(4);
assertEquals(tb.addr(stSpace, 0x0060000f), dat.getAddress());
assertEquals("test_dyn ", dat.toString());
assertEquals(4, dat.getNumComponents()); // count + 3 elements
cmp = dat.getComponent(0);
assertEquals("short 3h", cmp.toString());
cmp = dat.getComponent(1);
assertEquals("db 0h", cmp.toString());
cmp = dat.getComponent(2);
assertEquals("db 1h", cmp.toString());
cmp = dat.getComponent(3);
assertEquals("db 2h", cmp.toString());
}
@Test
public void testLabels() throws Exception {
createTrace();
createProgram(getSLEIGH_X86_64_LANGUAGE());
AddressSpace stSpace = program.getAddressFactory().getDefaultAddressSpace();
DBTraceVariableSnapProgramView view = tb.trace.getProgramView();
assertTrue(AllCopiers.LABELS.isAvailable(view, program));
try (UndoableTransaction tid = tb.startTransaction()) {
DBTraceMemoryManager memory = tb.trace.getMemoryManager();
memory.createRegion(".text", 0, tb.range(0x55550000, 0x5555ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
DBTraceNamespaceSymbol global = tb.trace.getSymbolManager().getGlobalNamespace();
DBTraceLabelSymbolView labels = tb.trace.getSymbolManager().labels();
labels.create(0, null, tb.addr(0x55550000), "test_label1", global, SourceType.IMPORTED);
labels.create(0, null, tb.addr(0x55550005), "test_label2", global,
SourceType.USER_DEFINED);
DBTraceNamespaceSymbolView namespaces = tb.trace.getSymbolManager().namespaces();
DBTraceNamespaceSymbol testNs = namespaces.add("test_ns", global, SourceType.ANALYSIS);
DBTraceNamespaceSymbol testNsChild =
namespaces.add("test_ns_child", testNs, SourceType.USER_DEFINED);
labels.create(0, null, tb.addr(0x55550800), "test_label3", testNsChild,
SourceType.ANALYSIS);
}
Address paddr = tb.addr(stSpace, 0x00400000);
try (UndoableTransaction tid = UndoableTransaction.start(program, "Copy", true)) {
program.getMemory()
.createInitializedBlock(".text", paddr, 0x10000, (byte) 0, TaskMonitor.DUMMY,
false);
AllCopiers.LABELS.copy(view, tb.range(0x55550000, 0x5555ffff), program, paddr,
TaskMonitor.DUMMY);
}
List<Symbol> symbols = new ArrayList<>();
program.getSymbolTable().getSymbolIterator(true).forEachRemaining(symbols::add);
assertEquals(3, symbols.size());
Symbol sym;
Namespace ns;
sym = symbols.get(0);
assertEquals("test_label1", sym.getName());
assertEquals(tb.addr(stSpace, 0x00400000), sym.getAddress());
assertEquals(SourceType.IMPORTED, sym.getSource());
assertTrue(sym.isGlobal());
sym = symbols.get(1);
assertEquals("test_label2", sym.getName());
assertEquals(tb.addr(stSpace, 0x00400005), sym.getAddress());
assertEquals(SourceType.USER_DEFINED, sym.getSource());
assertTrue(sym.isGlobal());
sym = symbols.get(2);
assertEquals("test_label3", sym.getName());
assertEquals(tb.addr(stSpace, 0x00400800), sym.getAddress());
assertEquals(SourceType.ANALYSIS, sym.getSource());
assertFalse(sym.isGlobal());
ns = sym.getParentNamespace();
assertEquals("test_ns_child", ns.getName());
assertEquals(SourceType.USER_DEFINED, ns.getSymbol().getSource());
assertFalse(ns.isGlobal());
ns = ns.getParentNamespace();
assertEquals("test_ns", ns.getName());
assertEquals(SourceType.ANALYSIS, ns.getSymbol().getSource());
assertFalse(ns.isGlobal());
ns = ns.getParentNamespace();
assertTrue(ns.isGlobal());
}
@Test
public void testBreakpoints() throws Exception {
createTrace();
createProgram(getSLEIGH_X86_64_LANGUAGE());
AddressSpace stSpace = program.getAddressFactory().getDefaultAddressSpace();
DBTraceVariableSnapProgramView view = tb.trace.getProgramView();
assertTrue(AllCopiers.BREAKPOINTS.isAvailable(view, program));
try (UndoableTransaction tid = tb.startTransaction()) {
DBTraceMemoryManager memory = tb.trace.getMemoryManager();
memory.createRegion(".text", 0, tb.range(0x55550000, 0x5555ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
DBTraceBreakpointManager breakpoints = tb.trace.getBreakpointManager();
breakpoints.placeBreakpoint("[1]", 0, tb.addr(0x55550123), List.of(),
Set.of(TraceBreakpointKind.SW_EXECUTE), true, "Test-1");
breakpoints.placeBreakpoint("[2]", 0, tb.addr(0x55550321), List.of(),
Set.of(TraceBreakpointKind.SW_EXECUTE), false, "Test-2");
}
Address paddr = tb.addr(stSpace, 0x55550000);
try (UndoableTransaction tid = UndoableTransaction.start(program, "Init", true)) {
program.getMemory()
.createInitializedBlock(".text", paddr, 0x10000,
(byte) 0, TaskMonitor.DUMMY, false);
// Set up a collision. This is normal with relocations
program.getBookmarkManager()
.setBookmark(tb.addr(stSpace, 0x55550123), "BreakpointDisabled", "SW_EXECUTE;1",
"");
AllCopiers.BREAKPOINTS.copy(view, tb.range(0x55550000, 0x5555ffff), program, paddr,
TaskMonitor.DUMMY);
}
List<Bookmark> bookmarks = new ArrayList<>();
program.getBookmarkManager().getBookmarksIterator().forEachRemaining(bookmarks::add);
assertEquals(2, bookmarks.size());
Collections.sort(bookmarks, Comparator.comparing(Bookmark::getAddress));
Bookmark bm;
bm = bookmarks.get(0);
assertEquals(tb.addr(stSpace, 0x55550123), bm.getAddress());
assertEquals("BreakpointEnabled", bm.getTypeString());
assertEquals("SW_EXECUTE;1", bm.getCategory());
bm = bookmarks.get(1);
assertEquals(tb.addr(stSpace, 0x55550321), bm.getAddress());
assertEquals("BreakpointDisabled", bm.getTypeString());
assertEquals("SW_EXECUTE;1", bm.getCategory());
}
@Test
public void testBookmarks() throws Exception {
createTrace();
createProgram(getSLEIGH_X86_64_LANGUAGE());
AddressSpace stSpace = program.getAddressFactory().getDefaultAddressSpace();
DBTraceVariableSnapProgramView view = tb.trace.getProgramView();
assertTrue(AllCopiers.BOOKMARKS.isAvailable(view, program));
try (UndoableTransaction tid = tb.startTransaction()) {
DBTraceMemoryManager memory = tb.trace.getMemoryManager();
memory.createRegion(".text", 0, tb.range(0x55550000, 0x5555ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
BookmarkManager bookmarks = view.getBookmarkManager();
bookmarks.defineType("TestType", DebuggerResources.ICON_DEBUGGER, Color.BLUE, 1);
bookmarks.setBookmark(tb.addr(0x55550123), "TestType", "TestCategory", "Test Comment");
}
Address paddr = tb.addr(stSpace, 0x55550000);
try (UndoableTransaction tid = UndoableTransaction.start(program, "Init", true)) {
program.getMemory()
.createInitializedBlock(".text", paddr, 0x10000,
(byte) 0, TaskMonitor.DUMMY, false);
AllCopiers.BOOKMARKS.copy(view, tb.range(0x55550000, 0x5555ffff), program, paddr,
TaskMonitor.DUMMY);
}
List<Bookmark> bookmarks = new ArrayList<>();
program.getBookmarkManager().getBookmarksIterator().forEachRemaining(bookmarks::add);
assertEquals(1, bookmarks.size());
Bookmark bm;
bm = bookmarks.get(0);
assertEquals(tb.addr(stSpace, 0x55550123), bm.getAddress());
BookmarkType type = program.getBookmarkManager().getBookmarkType("TestType");
assertNotNull(type);
assertEquals(type.getTypeString(), bm.getTypeString());
assertEquals("TestCategory", bm.getCategory());
assertEquals("Test Comment", bm.getComment());
assertEquals(DebuggerResources.ICON_DEBUGGER, type.getIcon());
assertEquals(Color.BLUE, type.getMarkerColor());
assertEquals(1, type.getMarkerPriority());
}
@Test
public void testReferences() throws Exception {
createTrace();
createProgram(getSLEIGH_X86_64_LANGUAGE());
AddressSpace stSpace = program.getAddressFactory().getDefaultAddressSpace();
DBTraceVariableSnapProgramView view = tb.trace.getProgramView();
assertTrue(AllCopiers.REFERENCES.isAvailable(view, program));
try (UndoableTransaction tid = tb.startTransaction()) {
DBTraceMemoryManager memory = tb.trace.getMemoryManager();
memory.createRegion(".text", 0, tb.range(0x55550000, 0x5555ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
memory.createRegion(".data", 0, tb.range(0x55560000, 0x5556ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.WRITE);
ReferenceManager references = view.getReferenceManager();
references.addMemoryReference(tb.addr(0x55550123),
tb.addr(0x55550321), RefType.COMPUTED_CALL, SourceType.USER_DEFINED, -1);
references.addMemoryReference(tb.addr(0x55550123),
tb.addr(0x55560321), RefType.READ, SourceType.USER_DEFINED, -1);
references.addMemoryReference(tb.addr(0x55560123),
tb.addr(0x55550321), RefType.PARAM, SourceType.USER_DEFINED, -1);
references.addMemoryReference(tb.addr(0x55560123),
tb.addr(0x55560321), RefType.DATA, SourceType.USER_DEFINED, -1);
}
Address paddr = tb.addr(stSpace, 0x55550000);
try (UndoableTransaction tid = UndoableTransaction.start(program, "Init", true)) {
program.getMemory()
.createInitializedBlock(".text", paddr, 0x10000,
(byte) 0, TaskMonitor.DUMMY, false);
AllCopiers.REFERENCES.copy(view, tb.range(0x55550000, 0x5555ffff), program, paddr,
TaskMonitor.DUMMY);
}
List<Reference> references = new ArrayList<>();
program.getReferenceManager().getReferenceIterator(paddr).forEachRemaining(references::add);
assertEquals(1, references.size());
Reference ref;
ref = references.get(0);
assertEquals(tb.addr(stSpace, 0x55550123), ref.getFromAddress());
assertEquals(tb.addr(stSpace, 0x55550321), ref.getToAddress());
assertEquals(RefType.COMPUTED_CALL, ref.getReferenceType());
assertEquals(SourceType.USER_DEFINED, ref.getSource());
assertEquals(-1, ref.getOperandIndex());
}
@Test
public void testComments() throws Exception {
createTrace();
createProgram(getSLEIGH_X86_64_LANGUAGE());
AddressSpace stSpace = program.getAddressFactory().getDefaultAddressSpace();
DBTraceVariableSnapProgramView view = tb.trace.getProgramView();
assertTrue(AllCopiers.COMMENTS.isAvailable(view, program));
try (UndoableTransaction tid = tb.startTransaction()) {
DBTraceMemoryManager memory = tb.trace.getMemoryManager();
memory.createRegion(".text", 0, tb.range(0x55550000, 0x5555ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
Listing listing = view.getListing();
listing.setComment(tb.addr(0x55550123), CodeUnit.EOL_COMMENT, "Test EOL Comment");
listing.setComment(tb.addr(0x55550321), CodeUnit.PLATE_COMMENT, "Test Plate Comment");
}
Address paddr = tb.addr(stSpace, 0x55550000);
try (UndoableTransaction tid = UndoableTransaction.start(program, "Init", true)) {
program.getMemory()
.createInitializedBlock(".text", paddr, 0x10000,
(byte) 0, TaskMonitor.DUMMY, false);
AllCopiers.COMMENTS.copy(view, tb.range(0x55550000, 0x5555ffff), program, paddr,
TaskMonitor.DUMMY);
}
Set<Address> addresses = new HashSet<>();
Listing listing = program.getListing();
listing.getCommentAddressIterator(program.getMemory(), true)
.forEachRemaining(addresses::add);
assertEquals(Set.of(tb.addr(stSpace, 0x55550123), tb.addr(stSpace, 0x55550321)), addresses);
assertEquals("Test EOL Comment",
listing.getComment(CodeUnit.EOL_COMMENT, tb.addr(stSpace, 0x55550123)));
assertEquals("Test Plate Comment",
listing.getComment(CodeUnit.PLATE_COMMENT, tb.addr(stSpace, 0x55550321)));
}
}

View file

@ -983,11 +983,11 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
@Test @Test
@Ignore("TODO") // Needs attention, but low priority @Ignore("TODO") // Needs attention, but low priority
public void testActionCaptureSelectedMemory() throws Exception { public void testActionReadSelectedMemory() throws Exception {
byte[] data = incBlock(); byte[] data = incBlock();
byte[] zero = new byte[data.length]; byte[] zero = new byte[data.length];
ByteBuffer buf = ByteBuffer.allocate(data.length); ByteBuffer buf = ByteBuffer.allocate(data.length);
assertFalse(listingProvider.actionCaptureSelectedMemory.isEnabled()); assertFalse(listingProvider.actionReadSelectedMemory.isEnabled());
listingProvider.setAutoReadMemorySpec(readNone); listingProvider.setAutoReadMemorySpec(readNone);
// To verify enabled requires live target // To verify enabled requires live target
@ -1002,12 +1002,12 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
traceManager.activateTrace(tb.trace); traceManager.activateTrace(tb.trace);
waitForSwing(); waitForSwing();
// Still // Still
assertFalse(listingProvider.actionCaptureSelectedMemory.isEnabled()); assertFalse(listingProvider.actionReadSelectedMemory.isEnabled());
listingProvider.setSelection(sel); listingProvider.setSelection(sel);
waitForSwing(); waitForSwing();
// Still // Still
assertFalse(listingProvider.actionCaptureSelectedMemory.isEnabled()); assertFalse(listingProvider.actionReadSelectedMemory.isEnabled());
// Now, simulate the sequence that typically enables the action // Now, simulate the sequence that typically enables the action
createTestModel(); createTestModel();
@ -1024,12 +1024,12 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
// NOTE: recordTargetContainerAndOpenTrace has already activated the trace // NOTE: recordTargetContainerAndOpenTrace has already activated the trace
// Action is still disabled, because it requires a selection // Action is still disabled, because it requires a selection
assertFalse(listingProvider.actionCaptureSelectedMemory.isEnabled()); assertFalse(listingProvider.actionReadSelectedMemory.isEnabled());
listingProvider.setSelection(sel); listingProvider.setSelection(sel);
waitForSwing(); waitForSwing();
// Now, it should be enabled // Now, it should be enabled
assertTrue(listingProvider.actionCaptureSelectedMemory.isEnabled()); assertTrue(listingProvider.actionReadSelectedMemory.isEnabled());
// First check nothing captured yet // First check nothing captured yet
buf.clear(); buf.clear();
@ -1038,7 +1038,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
assertArrayEquals(zero, buf.array()); assertArrayEquals(zero, buf.array());
// Verify that the action performs the expected task // Verify that the action performs the expected task
performAction(listingProvider.actionCaptureSelectedMemory); performAction(listingProvider.actionReadSelectedMemory);
waitForBusyTool(tool); waitForBusyTool(tool);
waitForDomainObject(trace); waitForDomainObject(trace);
@ -1053,28 +1053,28 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
// Verify that setting the memory inaccessible disables the action // Verify that setting the memory inaccessible disables the action
mb.testProcess1.memory.setAccessible(false); mb.testProcess1.memory.setAccessible(false);
waitForPass(() -> assertFalse(listingProvider.actionCaptureSelectedMemory.isEnabled())); waitForPass(() -> assertFalse(listingProvider.actionReadSelectedMemory.isEnabled()));
// Verify that setting it accessible re-enables it (assuming we still have selection) // Verify that setting it accessible re-enables it (assuming we still have selection)
mb.testProcess1.memory.setAccessible(true); mb.testProcess1.memory.setAccessible(true);
waitForPass(() -> assertTrue(listingProvider.actionCaptureSelectedMemory.isEnabled())); waitForPass(() -> assertTrue(listingProvider.actionReadSelectedMemory.isEnabled()));
// Verify that moving into the past disables the action // Verify that moving into the past disables the action
TraceSnapshot forced = recorder.forceSnapshot(); TraceSnapshot forced = recorder.forceSnapshot();
waitForSwing(); // UI Wants to sync with new snap. Wait.... waitForSwing(); // UI Wants to sync with new snap. Wait....
traceManager.activateSnap(forced.getKey() - 1); traceManager.activateSnap(forced.getKey() - 1);
waitForSwing(); waitForSwing();
assertFalse(listingProvider.actionCaptureSelectedMemory.isEnabled()); assertFalse(listingProvider.actionReadSelectedMemory.isEnabled());
// Verify that advancing to the present enables the action (assuming a selection) // Verify that advancing to the present enables the action (assuming a selection)
traceManager.activateSnap(forced.getKey()); traceManager.activateSnap(forced.getKey());
waitForSwing(); waitForSwing();
assertTrue(listingProvider.actionCaptureSelectedMemory.isEnabled()); assertTrue(listingProvider.actionReadSelectedMemory.isEnabled());
// Verify that stopping the recording disables the action // Verify that stopping the recording disables the action
recorder.stopRecording(); recorder.stopRecording();
waitForSwing(); waitForSwing();
assertFalse(listingProvider.actionCaptureSelectedMemory.isEnabled()); assertFalse(listingProvider.actionReadSelectedMemory.isEnabled());
// TODO: When resume recording is implemented, verify action is enabled with selection // TODO: When resume recording is implemented, verify action is enabled with selection
} }

View file

@ -741,11 +741,11 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
@Test @Test
@Ignore("TODO") // Needs attention, but low priority @Ignore("TODO") // Needs attention, but low priority
// Accessibility listener does not seem to work // Accessibility listener does not seem to work
public void testActionCaptureSelectedMemory() throws Exception { public void testActionReadSelectedMemory() throws Exception {
byte[] data = incBlock(); byte[] data = incBlock();
byte[] zero = new byte[data.length]; byte[] zero = new byte[data.length];
ByteBuffer buf = ByteBuffer.allocate(data.length); ByteBuffer buf = ByteBuffer.allocate(data.length);
assertFalse(memBytesProvider.actionCaptureSelectedMemory.isEnabled()); assertFalse(memBytesProvider.actionReadSelectedMemory.isEnabled());
memBytesProvider.setAutoReadMemorySpec(readNone); memBytesProvider.setAutoReadMemorySpec(readNone);
// To verify enabled requires live target // To verify enabled requires live target
@ -760,12 +760,12 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
traceManager.activateTrace(tb.trace); traceManager.activateTrace(tb.trace);
waitForSwing(); waitForSwing();
// Still // Still
assertFalse(memBytesProvider.actionCaptureSelectedMemory.isEnabled()); assertFalse(memBytesProvider.actionReadSelectedMemory.isEnabled());
memBytesProvider.setSelection(sel); memBytesProvider.setSelection(sel);
waitForSwing(); waitForSwing();
// Still // Still
assertFalse(memBytesProvider.actionCaptureSelectedMemory.isEnabled()); assertFalse(memBytesProvider.actionReadSelectedMemory.isEnabled());
// Now, simulate the sequence that typically enables the action // Now, simulate the sequence that typically enables the action
createTestModel(); createTestModel();
@ -782,21 +782,21 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
// NOTE: recordTargetContainerAndOpenTrace has already activated the trace // NOTE: recordTargetContainerAndOpenTrace has already activated the trace
// Action is still disabled, because it requires a selection // Action is still disabled, because it requires a selection
assertFalse(memBytesProvider.actionCaptureSelectedMemory.isEnabled()); assertFalse(memBytesProvider.actionReadSelectedMemory.isEnabled());
memBytesProvider.setSelection(sel); memBytesProvider.setSelection(sel);
waitForSwing(); waitForSwing();
// Now, it should be enabled // Now, it should be enabled
assertTrue(memBytesProvider.actionCaptureSelectedMemory.isEnabled()); assertTrue(memBytesProvider.actionReadSelectedMemory.isEnabled());
// First check nothing captured yet // First check nothing recorded yet
buf.clear(); buf.clear();
assertEquals(data.length, assertEquals(data.length,
trace.getMemoryManager().getBytes(recorder.getSnap(), addr(trace, 0x55550000), buf)); trace.getMemoryManager().getBytes(recorder.getSnap(), addr(trace, 0x55550000), buf));
assertArrayEquals(zero, buf.array()); assertArrayEquals(zero, buf.array());
// Verify that the action performs the expected task // Verify that the action performs the expected task
performAction(memBytesProvider.actionCaptureSelectedMemory); performAction(memBytesProvider.actionReadSelectedMemory);
waitForBusyTool(tool); waitForBusyTool(tool);
waitForDomainObject(trace); waitForDomainObject(trace);
@ -811,28 +811,28 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
// Verify that setting the memory inaccessible disables the action // Verify that setting the memory inaccessible disables the action
mb.testProcess1.memory.setAccessible(false); mb.testProcess1.memory.setAccessible(false);
waitForPass(() -> assertFalse(memBytesProvider.actionCaptureSelectedMemory.isEnabled())); waitForPass(() -> assertFalse(memBytesProvider.actionReadSelectedMemory.isEnabled()));
// Verify that setting it accessible re-enables it (assuming we still have selection) // Verify that setting it accessible re-enables it (assuming we still have selection)
mb.testProcess1.memory.setAccessible(true); mb.testProcess1.memory.setAccessible(true);
waitForPass(() -> assertTrue(memBytesProvider.actionCaptureSelectedMemory.isEnabled())); waitForPass(() -> assertTrue(memBytesProvider.actionReadSelectedMemory.isEnabled()));
// Verify that moving into the past disables the action // Verify that moving into the past disables the action
TraceSnapshot forced = recorder.forceSnapshot(); TraceSnapshot forced = recorder.forceSnapshot();
waitForSwing(); // UI Wants to sync with new snap. Wait.... waitForSwing(); // UI Wants to sync with new snap. Wait....
traceManager.activateSnap(forced.getKey() - 1); traceManager.activateSnap(forced.getKey() - 1);
waitForSwing(); waitForSwing();
assertFalse(memBytesProvider.actionCaptureSelectedMemory.isEnabled()); assertFalse(memBytesProvider.actionReadSelectedMemory.isEnabled());
// Verify that advancing to the present enables the action (assuming a selection) // Verify that advancing to the present enables the action (assuming a selection)
traceManager.activateSnap(forced.getKey()); traceManager.activateSnap(forced.getKey());
waitForSwing(); waitForSwing();
assertTrue(memBytesProvider.actionCaptureSelectedMemory.isEnabled()); assertTrue(memBytesProvider.actionReadSelectedMemory.isEnabled());
// Verify that stopping the recording disables the action // Verify that stopping the recording disables the action
recorder.stopRecording(); recorder.stopRecording();
waitForSwing(); waitForSwing();
assertFalse(memBytesProvider.actionCaptureSelectedMemory.isEnabled()); assertFalse(memBytesProvider.actionReadSelectedMemory.isEnabled());
// TODO: When resume recording is implemented, verify action is enabled with selection // TODO: When resume recording is implemented, verify action is enabled with selection
} }

View file

@ -27,7 +27,7 @@ import com.google.common.collect.Range;
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest; import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
import ghidra.app.services.DebuggerStaticMappingService; import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.app.services.DebuggerStaticMappingService.ShiftAndAddressSetView; import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
import ghidra.framework.model.DomainFile; import ghidra.framework.model.DomainFile;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
@ -339,7 +339,7 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
public void testAddMappingThenTranslateTraceViewToStaticEmpty() throws Exception { public void testAddMappingThenTranslateTraceViewToStaticEmpty() throws Exception {
addMapping(); addMapping();
Map<Program, ShiftAndAddressSetView> views = Map<Program, Collection<MappedAddressRange>> views =
mappingService.getOpenMappedViews(tb.trace, new AddressSet(), 0); mappingService.getOpenMappedViews(tb.trace, new AddressSet(), 0);
assertTrue(views.isEmpty()); assertTrue(views.isEmpty());
} }
@ -360,18 +360,19 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
// After // After
set.add(dynSpace.getAddress(0xbadbadbadL), dynSpace.getAddress(0xbadbadbadL + 0xff)); set.add(dynSpace.getAddress(0xbadbadbadL), dynSpace.getAddress(0xbadbadbadL + 0xff));
Map<Program, ShiftAndAddressSetView> views = Map<Program, Collection<MappedAddressRange>> views =
mappingService.getOpenMappedViews(tb.trace, set, 0); mappingService.getOpenMappedViews(tb.trace, set, 0);
assertEquals(1, views.size()); assertEquals(1, views.size());
ShiftAndAddressSetView shifted = views.get(program); Collection<MappedAddressRange> mappedSet = views.get(program);
assertEquals(0x100000, shifted.getShift());
AddressSetView inStatic = shifted.getAddressSetView(); assertEquals(Set.of(
assertEquals(3, inStatic.getNumAddressRanges()); new MappedAddressRange(tb.range(0x00100000, 0x001000ff),
AddressSet expected = new AddressSet(); tb.range(stSpace, 0x00200000, 0x002000ff)),
expected.add(stSpace.getAddress(0x00200000), stSpace.getAddress(0x002000ff)); new MappedAddressRange(tb.range(0x00100c0d, 0x00100ccc),
expected.add(stSpace.getAddress(0x00200c0d), stSpace.getAddress(0x00200ccc)); tb.range(stSpace, 0x00200c0d, 0x00200ccc)),
expected.add(stSpace.getAddress(0x00201000 - 0x100), stSpace.getAddress(0x00200fff)); new MappedAddressRange(tb.range(0x00100f00, 0x00100fff),
assertEquals(expected, inStatic); tb.range(stSpace, 0x00200f00, 0x00200fff))),
mappedSet);
} }
@Test @Test
@ -380,7 +381,7 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
copyTrace(); copyTrace();
add2ndMapping(); add2ndMapping();
Map<TraceSnap, ShiftAndAddressSetView> views = Map<TraceSnap, Collection<MappedAddressRange>> views =
mappingService.getOpenMappedViews(program, new AddressSet()); mappingService.getOpenMappedViews(program, new AddressSet());
assertTrue(views.isEmpty()); assertTrue(views.isEmpty());
} }
@ -403,30 +404,34 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
// After // After
set.add(stSpace.getAddress(0xbadbadbadL), stSpace.getAddress(0xbadbadbadL + 0xff)); set.add(stSpace.getAddress(0xbadbadbadL), stSpace.getAddress(0xbadbadbadL + 0xff));
Map<TraceSnap, ShiftAndAddressSetView> views = Map<TraceSnap, Collection<MappedAddressRange>> views =
mappingService.getOpenMappedViews(program, set); mappingService.getOpenMappedViews(program, set);
Msg.info(this, views); Msg.info(this, views);
assertEquals(2, views.size()); assertEquals(2, views.size());
ShiftAndAddressSetView shifted1 = views.get(new DefaultTraceSnap(tb.trace, 0)); Collection<MappedAddressRange> mappedSet1 = views.get(new DefaultTraceSnap(tb.trace, 0));
assertEquals(-0x100000, shifted1.getShift()); Collection<MappedAddressRange> mappedSet2 = views.get(new DefaultTraceSnap(copy, 0));
AddressSetView in1st = shifted1.getAddressSetView();
assertEquals(5, in1st.getNumAddressRanges());
AddressSetView in2nd = views.get(new DefaultTraceSnap(copy, 0)).getAddressSetView();
assertEquals(3, in2nd.getNumAddressRanges());
AddressSet expectedIn1st = new AddressSet(); assertEquals(Set.of(
AddressSet expectedIn2nd = new AddressSet(); new MappedAddressRange(tb.range(stSpace, 0x00200000, 0x002000ff),
expectedIn1st.add(dynSpace.getAddress(0x00100000), dynSpace.getAddress(0x001000ff)); tb.range(0x00100000, 0x001000ff)),
expectedIn1st.add(dynSpace.getAddress(0x00100800 - 0x10), new MappedAddressRange(tb.range(stSpace, 0x002007f0, 0x0020080f),
dynSpace.getAddress(0x00100800 + 0xf)); tb.range(0x001007f0, 0x0010080f)),
expectedIn1st.add(dynSpace.getAddress(0x00101000 - 0x100), dynSpace.getAddress(0x00100fff)); new MappedAddressRange(tb.range(stSpace, 0x00200f00, 0x00200fff),
expectedIn2nd.add(expectedIn1st); tb.range(0x00100f00, 0x00100fff)),
new MappedAddressRange(tb.range(stSpace, 0x00200000, 0x002000ff),
tb.range(0x00102000, 0x001020ff)),
new MappedAddressRange(tb.range(stSpace, 0x002007f0, 0x002007ff),
tb.range(0x001027f0, 0x001027ff))),
mappedSet1);
expectedIn1st.add(dynSpace.getAddress(0x00102000), dynSpace.getAddress(0x001020ff)); assertEquals(Set.of(
expectedIn1st.add(dynSpace.getAddress(0x00102800 - 0x10), dynSpace.getAddress(0x001027ff)); new MappedAddressRange(tb.range(stSpace, 0x00200000, 0x002000ff),
tb.range(0x00100000, 0x001000ff)),
assertEquals(expectedIn1st, in1st); new MappedAddressRange(tb.range(stSpace, 0x002007f0, 0x0020080f),
assertEquals(expectedIn2nd, in2nd); tb.range(0x001007f0, 0x0010080f)),
new MappedAddressRange(tb.range(stSpace, 0x00200f00, 0x00200fff),
tb.range(0x00100f00, 0x00100fff))),
mappedSet2);
} }
@Test @Test

View file

@ -220,9 +220,7 @@ public class DBTraceDataTypeManager extends DataTypeManagerDB
@Override @Override
public DataOrganization getDataOrganization() { public DataOrganization getDataOrganization() {
if (dataOrganization == null) { if (dataOrganization == null) {
// TODO: Do I need to have a base compiler spec? dataOrganization = trace.getBaseCompilerSpec().getDataOrganization();
dataOrganization =
trace.getBaseLanguage().getDefaultCompilerSpec().getDataOrganization();
} }
return dataOrganization; return dataOrganization;
} }

View file

@ -30,6 +30,7 @@ import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.mem.MemoryAccessException; import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
import ghidra.trace.database.DBTraceUtils; import ghidra.trace.database.DBTraceUtils;
import ghidra.trace.database.context.DBTraceRegisterContextManager;
import ghidra.trace.database.context.DBTraceRegisterContextSpace; import ghidra.trace.database.context.DBTraceRegisterContextSpace;
import ghidra.trace.database.language.DBTraceGuestLanguage; import ghidra.trace.database.language.DBTraceGuestLanguage;
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
@ -621,13 +622,12 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
@Override @Override
public BigInteger getValue(Register register, boolean signed) { public BigInteger getValue(Register register, boolean signed) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) { try (LockHold hold = LockHold.lock(space.lock.readLock())) {
DBTraceRegisterContextSpace ctxSpace = DBTraceRegisterContextManager manager = space.trace.getRegisterContextManager();
space.trace.getRegisterContextManager().get(space, false); RegisterValue rv =
if (ctxSpace == null) { manager.getValueWithDefault(getLanguage(), register, getStartSnap(), getAddress());
if (rv == null) {
return null; return null;
} }
RegisterValue rv =
ctxSpace.getValue(getLanguage(), register, getStartSnap(), getAddress());
return signed ? rv.getSignedValue() : rv.getUnsignedValue(); return signed ? rv.getSignedValue() : rv.getUnsignedValue();
} }
} }
@ -635,12 +635,9 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
@Override @Override
public RegisterValue getRegisterValue(Register register) { public RegisterValue getRegisterValue(Register register) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) { try (LockHold hold = LockHold.lock(space.lock.readLock())) {
DBTraceRegisterContextSpace ctxSpace = DBTraceRegisterContextManager manager = space.trace.getRegisterContextManager();
space.trace.getRegisterContextManager().get(space, false); return manager.getValueWithDefault(getLanguage(), register, getStartSnap(),
if (ctxSpace == null) { getAddress());
return null;
}
return ctxSpace.getValue(getLanguage(), register, getStartSnap(), getAddress());
} }
} }

View file

@ -38,6 +38,7 @@ import ghidra.trace.model.listing.TraceInstructionsView;
import ghidra.trace.util.OverlappingObjectIterator; import ghidra.trace.util.OverlappingObjectIterator;
import ghidra.trace.util.TraceChangeRecord; import ghidra.trace.util.TraceChangeRecord;
import ghidra.util.LockHold; import ghidra.util.LockHold;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -66,34 +67,9 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
this.conflictCodeUnit = conflictCodeUnit; this.conflictCodeUnit = conflictCodeUnit;
} }
protected void doSetContexts(Range<Long> lifespan, Address min, Address max,
ProcessorContextView context) {
Language language = space.baseLanguage;
Register contextReg = language.getContextBaseRegister();
if (contextReg == null) {
return;
}
RegisterValue newValue = context.getRegisterValue(contextReg);
DBTraceRegisterContextManager ctxMgr = space.trace.getRegisterContextManager();
if (Objects.equals(ctxMgr.getDefaultValue(language, contextReg, min), newValue)) {
DBTraceRegisterContextSpace ctxSpace = ctxMgr.get(space, false);
if (ctxSpace == null) {
return;
}
ctxSpace.setValue(language, null, lifespan, new AddressRangeImpl(min, max));
return;
}
DBTraceRegisterContextSpace ctxSpace = ctxMgr.get(space, true);
// TODO: Do not save non-flowing context beyond???
ctxSpace.setValue(language, newValue, lifespan, new AddressRangeImpl(min, max));
}
protected Instruction doCreateInstruction(Range<Long> lifespan, Address address, protected Instruction doCreateInstruction(Range<Long> lifespan, Address address,
InstructionPrototype prototype, Instruction protoInstr) { InstructionPrototype prototype, Instruction protoInstr) {
try { try {
doSetContexts(lifespan, address, address.addNoWrap(prototype.getLength() - 1),
protoInstr);
Instruction created = doCreate(lifespan, address, prototype, protoInstr); Instruction created = doCreate(lifespan, address, prototype, protoInstr);
// copy override settings to replacement instruction // copy override settings to replacement instruction
if (protoInstr.isFallThroughOverridden()) { if (protoInstr.isFallThroughOverridden()) {
@ -183,6 +159,27 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
super(space, space.instructionMapSpace); super(space, space.instructionMapSpace);
} }
protected void doSetContexts(TraceAddressSnapRange tasr, Language language,
ProcessorContextView context) {
Register contextReg = language.getContextBaseRegister();
if (contextReg == null || contextReg == Register.NO_CONTEXT) {
return;
}
RegisterValue newValue = context.getRegisterValue(contextReg);
DBTraceRegisterContextManager ctxMgr = space.trace.getRegisterContextManager();
if (Objects.equals(ctxMgr.getDefaultValue(language, contextReg, tasr.getX1()), newValue)) {
DBTraceRegisterContextSpace ctxSpace = ctxMgr.get(space, false);
if (ctxSpace == null) {
return;
}
ctxSpace.setValue(language, null, tasr.getLifespan(), tasr.getRange());
return;
}
DBTraceRegisterContextSpace ctxSpace = ctxMgr.get(space, true);
// TODO: Do not save non-flowing context beyond???
ctxSpace.setValue(language, newValue, tasr.getLifespan(), tasr.getRange());
}
protected DBTraceInstruction doCreate(Range<Long> lifespan, Address address, protected DBTraceInstruction doCreate(Range<Long> lifespan, Address address,
InstructionPrototype prototype, ProcessorContextView context) InstructionPrototype prototype, ProcessorContextView context)
throws CodeUnitInsertionException, AddressOverflowException { throws CodeUnitInsertionException, AddressOverflowException {
@ -214,6 +211,8 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
throw new CodeUnitInsertionException("Code units cannot overlap"); throw new CodeUnitInsertionException("Code units cannot overlap");
} }
doSetContexts(tasr, prototype.getLanguage(), context);
DBTraceInstruction created = space.instructionMapSpace.put(tasr, null); DBTraceInstruction created = space.instructionMapSpace.put(tasr, null);
created.set(prototype, context); created.set(prototype, context);

View file

@ -415,25 +415,33 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
forward))); forward)));
} }
protected AddressSetView getCommentAddresses(int commentType, AddressSetView addrSet) {
return new IntersectionAddressSetView(addrSet, program.viewport.unionedAddresses(
s -> program.trace.getCommentAdapter()
.getAddressSetView(Range.singleton(s), e -> e.getType() == commentType)));
}
protected AddressSetView getCommentAddresses(AddressSetView addrSet) {
return new IntersectionAddressSetView(addrSet, program.viewport.unionedAddresses(
s -> program.trace.getCommentAdapter()
.getAddressSetView(Range.singleton(s))));
}
@Override @Override
public CodeUnitIterator getCommentCodeUnitIterator(int commentType, AddressSetView addrSet) { public CodeUnitIterator getCommentCodeUnitIterator(int commentType, AddressSetView addrSet) {
// TODO Auto-generated method stub return new WrappingCodeUnitIterator(
return null; getCodeUnitIterator(getCommentAddresses(commentType, addrSet), true));
} }
@Override @Override
public AddressIterator getCommentAddressIterator(int commentType, AddressSetView addrSet, public AddressIterator getCommentAddressIterator(int commentType, AddressSetView addrSet,
boolean forward) { boolean forward) {
return new IntersectionAddressSetView(addrSet, program.viewport.unionedAddresses( return getCommentAddresses(commentType, addrSet).getAddresses(forward);
s -> program.trace.getCommentAdapter()
.getAddressSetView(Range.singleton(s), e -> e.getType() == commentType)))
.getAddresses(forward);
} }
@Override @Override
public AddressIterator getCommentAddressIterator(AddressSetView addrSet, boolean forward) { public AddressIterator getCommentAddressIterator(AddressSetView addrSet, boolean forward) {
// TODO Auto-generated method stub return getCommentAddresses(addrSet).getAddresses(forward);
return null;
} }
@Override @Override

View file

@ -25,6 +25,7 @@ import javax.help.UnsupportedOperationException;
import com.google.common.collect.Range; import com.google.common.collect.Range;
import generic.NestedIterator;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.lang.Register; import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.Variable; import ghidra.program.model.listing.Variable;
@ -231,13 +232,22 @@ public abstract class AbstractDBTraceProgramViewReferenceManager implements Refe
: (r1, r2) -> -r1.getFromAddress().compareTo(r2.getFromAddress()); : (r1, r2) -> -r1.getFromAddress().compareTo(r2.getFromAddress());
} }
protected Iterator<Reference> getReferenceIteratorForSnap(long snap, Address startAddr) {
AddressIterator addresses =
refs.getReferenceSources(Range.singleton(snap)).getAddresses(startAddr, true);
return NestedIterator.start(addresses, a -> {
return refs.getReferencesFrom(snap, a).iterator();
});
}
@Override @Override
public ReferenceIterator getReferenceIterator(Address startAddr) { public ReferenceIterator getReferenceIterator(Address startAddr) {
if (refs(false) == null) { if (refs(false) == null) {
return new ReferenceIteratorAdapter(Collections.emptyIterator()); return new ReferenceIteratorAdapter(Collections.emptyIterator());
} }
// TODO: This will fail to occlude on equal (src,dst,opIndex) keys
return new ReferenceIteratorAdapter( return new ReferenceIteratorAdapter(
program.viewport.mergedIterator(s -> refs.getReferencesFrom(s, startAddr).iterator(), program.viewport.mergedIterator(s -> getReferenceIteratorForSnap(s, startAddr),
getReferenceFromComparator(true))); getReferenceFromComparator(true)));
} }

View file

@ -49,8 +49,7 @@ import ghidra.trace.database.memory.DBTraceMemoryManager;
import ghidra.trace.database.symbol.DBTraceReference; import ghidra.trace.database.symbol.DBTraceReference;
import ghidra.trace.database.thread.DBTraceThread; import ghidra.trace.database.thread.DBTraceThread;
import ghidra.trace.database.thread.DBTraceThreadManager; import ghidra.trace.database.thread.DBTraceThreadManager;
import ghidra.trace.model.ImmutableTraceAddressSnapRange; import ghidra.trace.model.*;
import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.language.TraceGuestLanguage; import ghidra.trace.model.language.TraceGuestLanguage;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.database.DBOpenMode; import ghidra.util.database.DBOpenMode;
@ -76,6 +75,12 @@ public class ToyDBTraceBuilder implements AutoCloseable {
this.trace = new DBTrace(name, language.getDefaultCompilerSpec(), this); this.trace = new DBTrace(name, language.getDefaultCompilerSpec(), this);
} }
public ToyDBTraceBuilder(Trace trace) {
this.language = trace.getBaseLanguage();
this.trace = (DBTrace) trace;
trace.addConsumer(this);
}
public Address addr(AddressSpace space, long offset) { public Address addr(AddressSpace space, long offset) {
return space.getAddress(offset); return space.getAddress(offset);
} }

View file

@ -50,6 +50,7 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
/** /**
* Constructor for plugin that handles multi-user merge of programs. * Constructor for plugin that handles multi-user merge of programs.
*
* @param tool the tool with the active program to be merged * @param tool the tool with the active program to be merged
* @param mergeManager the merge manager that will control the merge process * @param mergeManager the merge manager that will control the merge process
* @param program the current program * @param program the current program
@ -61,7 +62,8 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
@Override @Override
public MergeManagerProvider createProvider() { public MergeManagerProvider createProvider() {
return new MergeManagerProvider(this, "Merge Programs for " + currentDomainObject.getName()); return new MergeManagerProvider(this,
"Merge Programs for " + currentDomainObject.getName());
} }
@Override @Override
@ -82,6 +84,7 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
/** /**
* Gets the merge manager associated with this plug-in. * Gets the merge manager associated with this plug-in.
*
* @return the merge manager * @return the merge manager
*/ */
@Override @Override
@ -91,6 +94,7 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
/** /**
* Defines and displays a component for resolving merge conflicts. * Defines and displays a component for resolving merge conflicts.
*
* @param component the component * @param component the component
* @param componentID the identifier for this component * @param componentID the identifier for this component
*/ */
@ -101,6 +105,7 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
/** /**
* Sets the merge description at the top of the merge tool. * Sets the merge description at the top of the merge tool.
*
* @param mergeDescription the new description * @param mergeDescription the new description
*/ */
@Override @Override
@ -110,7 +115,9 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
/** /**
* Sets the message below the progress meter in the current phase progress area. * Sets the message below the progress meter in the current phase progress area.
* @param progressDescription the new text message to display. If null, then the default message is displayed. *
* @param progressDescription the new text message to display. If null, then the default message
* is displayed.
*/ */
@Override @Override
void updateProgressDetails(String progressDescription) { void updateProgressDetails(String progressDescription) {
@ -118,7 +125,9 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
} }
/** /**
* Sets the percentage of the progress meter that is filled in for the current phase progress area. * Sets the percentage of the progress meter that is filled in for the current phase progress
* area.
*
* @param currentPercentProgress the percentage of the progress bar to fill in from 0 to 100. * @param currentPercentProgress the percentage of the progress bar to fill in from 0 to 100.
*/ */
@Override @Override
@ -135,8 +144,9 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
} }
/** /**
* Enables/disables the Apply button at the bottom of the merge tool. * Enables/disables the Apply button at the bottom of the merge tool. The Apply button is for
* The Apply button is for applying conflicts. * applying conflicts.
*
* @param state true means enable the button. false means disable it. * @param state true means enable the button. false means disable it.
*/ */
@Override @Override
@ -146,6 +156,7 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
/** /**
* Gets the provider for the merge. * Gets the provider for the merge.
*
* @return the provider * @return the provider
*/ */
@Override @Override
@ -153,22 +164,27 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
return provider; return provider;
} }
@Override
public boolean closeOtherPrograms(boolean ignoreChanges) { public boolean closeOtherPrograms(boolean ignoreChanges) {
return false; return false;
} }
@Override
public boolean closeAllPrograms(boolean ignoreChanges) { public boolean closeAllPrograms(boolean ignoreChanges) {
return false; return false;
} }
@Override
public boolean closeProgram() { public boolean closeProgram() {
return false; return false;
} }
@Override
public boolean closeProgram(Program program, boolean ignoreChanges) { public boolean closeProgram(Program program, boolean ignoreChanges) {
return false; return false;
} }
@Override
public Program[] getAllOpenPrograms() { public Program[] getAllOpenPrograms() {
ProgramMultiUserMergeManager programMergeManager = ProgramMultiUserMergeManager programMergeManager =
(ProgramMultiUserMergeManager) mergeManager; (ProgramMultiUserMergeManager) mergeManager;
@ -178,10 +194,12 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
programMergeManager.getProgram(MergeConstants.ORIGINAL) }; programMergeManager.getProgram(MergeConstants.ORIGINAL) };
} }
@Override
public Program getCurrentProgram() { public Program getCurrentProgram() {
return (Program) currentDomainObject; return (Program) currentDomainObject;
} }
@Override
public Program getProgram(Address addr) { public Program getProgram(Address addr) {
return null; return null;
} }
@ -190,6 +208,7 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
return 0; return 0;
} }
@Override
public boolean isVisible(Program program) { public boolean isVisible(Program program) {
return false; return false;
} }
@ -199,6 +218,7 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
return null; return null;
} }
@Override
public Program openProgram(DomainFile domainFile) { public Program openProgram(DomainFile domainFile) {
return null; return null;
} }
@ -208,29 +228,53 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
return null; return null;
} }
@Override
public Program openProgram(DomainFile df, int version) { public Program openProgram(DomainFile df, int version) {
return null; return null;
} }
@Override
public Program openProgram(DomainFile domainFile, int version, int state) { public Program openProgram(DomainFile domainFile, int version, int state) {
return null; return null;
} }
@Override
public void openProgram(Program program) { public void openProgram(Program program) {
} }
@Override
public void openProgram(Program program, boolean current) { public void openProgram(Program program, boolean current) {
} }
@Override
public void openProgram(Program program, int state) { public void openProgram(Program program, int state) {
} }
@Override
public void releaseProgram(Program program, Object persistentOwner) { public void releaseProgram(Program program, Object persistentOwner) {
} }
@Override
public void saveProgram() {
}
@Override
public void saveProgram(Program program) {
}
@Override
public void saveProgramAs() {
}
@Override
public void saveProgramAs(Program program) {
}
@Override
public void setCurrentProgram(Program p) { public void setCurrentProgram(Program p) {
} }
@Override
public boolean setPersistentOwner(Program program, Object owner) { public boolean setPersistentOwner(Program program, Object owner) {
return false; return false;
} }
@ -238,10 +282,12 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
public void setSearchPriority(Program p, int priority) { public void setSearchPriority(Program p, int priority) {
} }
@Override
public boolean isLocked() { public boolean isLocked() {
return false; return false;
} }
@Override
public void lockDown(boolean state) { public void lockDown(boolean state) {
} }
} }

View file

@ -102,8 +102,7 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
/** /**
* Method called if the plugin supports this domain file. * Method called if the plugin supports this domain file.
* *
* @param data * @param data the data to be used by the running tool
* the data to be used by the running tool
* @return false if data is not a Program object. * @return false if data is not a Program object.
*/ */
@Override @Override
@ -447,9 +446,9 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
} }
/** /**
* This method notifies listening plugins that a programs has been added to * This method notifies listening plugins that a programs has been added to the program manager.
* the program manager. This is not used for actually opening a program from * This is not used for actually opening a program from the database and will act strangely if
* the database and will act strangely if given a closed Program object. * given a closed Program object.
* *
* @see ghidra.app.services.ProgramManager#openProgram(ghidra.program.model.listing.Program) * @see ghidra.app.services.ProgramManager#openProgram(ghidra.program.model.listing.Program)
*/ */
@ -597,8 +596,8 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
} }
/** /**
* Start a transaction if one has not been started; needed when program * Start a transaction if one has not been started; needed when program properties are about to
* properties are about to change from the options editor. * change from the options editor.
*/ */
private void startTransaction(Program currentProgram) { private void startTransaction(Program currentProgram) {
if (transactionID < 0) { if (transactionID < 0) {
@ -685,6 +684,26 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
return openProgram; return openProgram;
} }
@Override
public void saveProgram() {
saveProgram(getCurrentProgram());
}
@Override
public void saveProgram(Program program) {
Swing.runIfSwingOrRunLater(() -> programSaveMgr.saveProgram(program));
}
@Override
public void saveProgramAs() {
saveProgramAs(getCurrentProgram());
}
@Override
public void saveProgramAs(Program program) {
Swing.runIfSwingOrRunLater(() -> programSaveMgr.saveAs(program));
}
/** /**
* Write out my data state. * Write out my data state.
*/ */
@ -1040,13 +1059,4 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
public boolean isManaged(Program program) { public boolean isManaged(Program program) {
return programMgr.contains(program); return programMgr.contains(program);
} }
public void saveProgram(Program program) {
programSaveMgr.saveProgram(program);
}
public void saveProgramAs(Program program) {
programSaveMgr.saveAs(program);
}
} }

View file

@ -26,16 +26,17 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
/** /**
* Service for managing programs. Multiple programs may be open in a tool, but only one is active * Service for managing programs. Multiple programs may be open in a tool, but only one is active at
* at any given time. * any given time.
*/ */
@ServiceInfo(defaultProvider = ProgramManagerPlugin.class, description = "Get the currently open program") @ServiceInfo(
defaultProvider = ProgramManagerPlugin.class,
description = "Get the currently open program")
public interface ProgramManager { public interface ProgramManager {
/** /**
* Program will be open in a Hidden state if not already open. * Program will be open in a Hidden state if not already open. This mode is generally used in
* This mode is generally used in conjunction with a persistent * conjunction with a persistent program owner.
* program owner.
*/ */
public static final int OPEN_HIDDEN = 0; public static final int OPEN_HIDDEN = 0;
@ -45,20 +46,21 @@ public interface ProgramManager {
public static final int OPEN_CURRENT = 1; public static final int OPEN_CURRENT = 1;
/** /**
* Program will be open within the tool but no change will be made * Program will be open within the tool but no change will be made to the currently active
* to the currently active program. If this is the only program * program. If this is the only program open, it will become the currently active program.
* open, it will become the currently active program.
*/ */
public static final int OPEN_VISIBLE = 2; public static final int OPEN_VISIBLE = 2;
/** /**
* Return the program that is currently active. * Return the program that is currently active.
*
* @return may return null if no program is open * @return may return null if no program is open
*/ */
public Program getCurrentProgram(); public Program getCurrentProgram();
/** /**
* Returns true if the specified program is open and considered visible to the user. * Returns true if the specified program is open and considered visible to the user.
*
* @param program the program * @param program the program
* @return true if the specified program is open and considered visible to the user * @return true if the specified program is open and considered visible to the user
*/ */
@ -66,25 +68,27 @@ public interface ProgramManager {
/** /**
* Closes the currently active program * Closes the currently active program
* @return true if the close is successful. *
* false if the close fails or if there is no program currently active. * @return true if the close is successful. false if the close fails or if there is no program
* currently active.
*/ */
public boolean closeProgram(); public boolean closeProgram();
/** /**
* Open the program corresponding to the given url. * Open the program corresponding to the given url.
*
* @param ghidraURL valid server-based program URL * @param ghidraURL valid server-based program URL
* @param state initial open state (OPEN_HIDDEN, OPEN_CURRENT, OPEN_VISIBLE). * @param state initial open state (OPEN_HIDDEN, OPEN_CURRENT, OPEN_VISIBLE). The visibility
* The visibility states will be ignored if the program is already open. * states will be ignored if the program is already open.
* @return null if the user canceled the "open" for the new program or an error * @return null if the user canceled the "open" for the new program or an error occurred and was
* occurred and was displayed. * displayed.
* @see GhidraURL * @see GhidraURL
*/ */
public Program openProgram(URL ghidraURL, int state); public Program openProgram(URL ghidraURL, int state);
/** /**
* Open the program for the given domainFile. Once open it will * Open the program for the given domainFile. Once open it will become the active program.
* become the active program. *
* @param domainFile domain file that has the program * @param domainFile domain file that has the program
* @return null if the user canceled the "open" for the new program * @return null if the user canceled the "open" for the new program
*/ */
@ -93,20 +97,22 @@ public interface ProgramManager {
/** /**
* Open the program for the given domainFile. Once open it will become the active program. * Open the program for the given domainFile. Once open it will become the active program.
* *
* <P>Note: this method functions exactly as {@link #openProgram(DomainFile)} * <P>
* Note: this method functions exactly as {@link #openProgram(DomainFile)}
* *
* @param domainFile domain file that has the program * @param domainFile domain file that has the program
* @param dialogParent unused * @param dialogParent unused
* @return the program * @return the program
* @deprecated deprecated for 10.1; removal for 10.3 or later; use {@link #openProgram(DomainFile)} * @deprecated deprecated for 10.1; removal for 10.3 or later; use
* {@link #openProgram(DomainFile)}
*/ */
@Deprecated @Deprecated
public Program openProgram(DomainFile domainFile, Component dialogParent); public Program openProgram(DomainFile domainFile, Component dialogParent);
/** /**
* Opens the specified version of the program represented by the given DomainFile. This * Opens the specified version of the program represented by the given DomainFile. This method
* method should be used for shared DomainFiles. The newly opened file will be made the * should be used for shared DomainFiles. The newly opened file will be made the active program.
* active program. *
* @param df the DomainFile to open * @param df the DomainFile to open
* @param version the version of the Program to open * @param version the version of the Program to open
* @return the opened program or null if the given version does not exist. * @return the opened program or null if the given version does not exist.
@ -115,29 +121,32 @@ public interface ProgramManager {
/** /**
* Open the program for the given domainFile * Open the program for the given domainFile
*
* @param domainFile domain file that has the program * @param domainFile domain file that has the program
* @param version the version of the Program to open. Specify * @param version the version of the Program to open. Specify DomainFile.DEFAULT_VERSION for
* DomainFile.DEFAULT_VERSION for file update mode. * file update mode.
* @param state initial open state (OPEN_HIDDEN, OPEN_CURRENT, OPEN_VISIBLE). * @param state initial open state (OPEN_HIDDEN, OPEN_CURRENT, OPEN_VISIBLE). The visibility
* The visibility states will be ignored if the program is already open. * states will be ignored if the program is already open.
* @return null if the user canceled the "open" for the new program or an error * @return null if the user canceled the "open" for the new program or an error occurred and was
* occurred and was displayed. * displayed.
*/ */
public Program openProgram(DomainFile domainFile, int version, int state); public Program openProgram(DomainFile domainFile, int version, int state);
/** /**
* Opens the program to the tool. In this case the program is already open, but this tool * Opens the program to the tool. In this case the program is already open, but this tool may
* may not have it registered as open. The program is made the active program. * not have it registered as open. The program is made the active program.
*
* @param program the program to register as open with the tool. * @param program the program to register as open with the tool.
*/ */
public void openProgram(Program program); public void openProgram(Program program);
/** /**
* Opens the program to the tool. In this case the program is already open, but this tool * Opens the program to the tool. In this case the program is already open, but this tool may
* may not have it registered as open. The program is made the active program. * not have it registered as open. The program is made the active program.
*
* @param program the program to register as open with the tool. * @param program the program to register as open with the tool.
* @param current if true, the program is made the current active program. If false, then * @param current if true, the program is made the current active program. If false, then the
* the program is made active only if it the first open program in the tool. * program is made active only if it the first open program in the tool.
* @deprecated use openProgram(Program program, int state) instead. * @deprecated use openProgram(Program program, int state) instead.
*/ */
@Deprecated @Deprecated
@ -145,19 +154,45 @@ public interface ProgramManager {
/** /**
* Open the specified program in the tool. * Open the specified program in the tool.
*
* @param program the program * @param program the program
* @param state initial open state (OPEN_HIDDEN, OPEN_CURRENT, OPEN_VISIBLE). * @param state initial open state (OPEN_HIDDEN, OPEN_CURRENT, OPEN_VISIBLE). The visibility
* The visibility states will be ignored if the program is already open. * states will be ignored if the program is already open.
*/ */
public void openProgram(Program program, int state); public void openProgram(Program program, int state);
/** /**
* Establish a persistent owner on an open program. This will cause the program manager to * Saves the current program, possibly prompting the user for a new name.
* imply make a program hidden if it is closed. */
public void saveProgram();
/**
* Saves the specified program, possibly prompting the user for a new name.
*
* @param program the program
*/
public void saveProgram(Program program);
/**
* Prompts the user to save the current program to a selected file.
*/
public void saveProgramAs();
/**
* Prompts the user to save the specified program to a selected file.
*
* @param program the program
*/
public void saveProgramAs(Program program);
/**
* Establish a persistent owner on an open program. This will cause the program manager to imply
* make a program hidden if it is closed.
*
* @param program the program * @param program the program
* @param owner the owner * @param owner the owner
* @return true if program is open and another object is not already the owner, * @return true if program is open and another object is not already the owner, or the specified
* or the specified owner is already the owner. * owner is already the owner.
* @see #releaseProgram(Program, Object) * @see #releaseProgram(Program, Object)
*/ */
public boolean setPersistentOwner(Program program, Object owner); public boolean setPersistentOwner(Program program, Object owner);
@ -165,35 +200,37 @@ public interface ProgramManager {
/** /**
* Release the persistent ownership of a program. * Release the persistent ownership of a program.
* <p> * <p>
* The program will automatically be closed if it is hidden or was marked as temporary. If * The program will automatically be closed if it is hidden or was marked as temporary. If any
* any of these closures corresponds to a program with changes the user will be given an * of these closures corresponds to a program with changes the user will be given an opportunity
* opportunity to save or keep the program open. * to save or keep the program open.
* <p> * <p>
* If persistentOwner is not the correct owner, the method will have no affect. * If persistentOwner is not the correct owner, the method will have no affect.
*
* @param program the program * @param program the program
* @param persistentOwner the owner defined by {@link #setPersistentOwner(Program, Object)} * @param persistentOwner the owner defined by {@link #setPersistentOwner(Program, Object)}
*/ */
public void releaseProgram(Program program, Object persistentOwner); public void releaseProgram(Program program, Object persistentOwner);
/** /**
* Closes the given program with the option of saving any changes. The exact behavior of * Closes the given program with the option of saving any changes. The exact behavior of this
* this method depends on several factors. First of all, if any other tool has this program * method depends on several factors. First of all, if any other tool has this program open,
* open, then the program is closed for this tool only and the user is not prompted to * then the program is closed for this tool only and the user is not prompted to save the
* save the program regardless of the ignoreChanges flag. Otherwise, if ignoreChanges is * program regardless of the ignoreChanges flag. Otherwise, if ignoreChanges is false and
* false and changes have been made, the user is prompted to save the program. * changes have been made, the user is prompted to save the program.
*
* @param program the program to close. * @param program the program to close.
* @param ignoreChanges if true, the program is closed without saving any changes. * @param ignoreChanges if true, the program is closed without saving any changes.
* @return true if the program was closed. Returns false if the user canceled the close * @return true if the program was closed. Returns false if the user canceled the close while
* while being prompted to save. Also returns false if the program passed in as a parameter * being prompted to save. Also returns false if the program passed in as a parameter is
* is null. * null.
*/ */
boolean closeProgram(Program program, boolean ignoreChanges); boolean closeProgram(Program program, boolean ignoreChanges);
/** /**
* Closes all open programs in this tool except the current program. * Closes all open programs in this tool except the current program. If this tool is the only
* If this tool is the only tool with a program open and that program has changes, * tool with a program open and that program has changes, then the user will be prompted to
* then the user will be prompted to close each such file. * close each such file. (Providing the ignoreChanges flag is false)
* (Providing the ignoreChanges flag is false) *
* @param ignoreChanges if true, the programs will be closed without saving changes. * @param ignoreChanges if true, the programs will be closed without saving changes.
* @return true if all other programs were closed. Returns false if the user canceled the close * @return true if all other programs were closed. Returns false if the user canceled the close
* while being prompted to save. * while being prompted to save.
@ -201,26 +238,29 @@ public interface ProgramManager {
public boolean closeOtherPrograms(boolean ignoreChanges); public boolean closeOtherPrograms(boolean ignoreChanges);
/** /**
* Closes all open programs in this tool. If this tool is the only tool with a program * Closes all open programs in this tool. If this tool is the only tool with a program open and
* open and that program has changes, then the user will be prompted to close each such file. * that program has changes, then the user will be prompted to close each such file. (Providing
* (Providing the ignoreChanges flag is false) * the ignoreChanges flag is false)
*
* @param ignoreChanges if true, the programs will be closed without saving changes. * @param ignoreChanges if true, the programs will be closed without saving changes.
* @return true if all programs were closed. Returns false if the user canceled the close * @return true if all programs were closed. Returns false if the user canceled the close while
* while being prompted to save. * being prompted to save.
*/ */
public boolean closeAllPrograms(boolean ignoreChanges); public boolean closeAllPrograms(boolean ignoreChanges);
/** /**
* Sets the given program to be the current active program in the tool. * Sets the given program to be the current active program in the tool.
*
* @param p the program to make active. * @param p the program to make active.
*/ */
public void setCurrentProgram(Program p); public void setCurrentProgram(Program p);
/** /**
* Returns the first program in the list of open programs that contains the given address. * Returns the first program in the list of open programs that contains the given address.
* Programs are searched in the order they were opened within a given priority. * Programs are searched in the order they were opened within a given priority. Program are
* Program are initially opened with the PRIORITY_NORMAL priority, but can be set to have * initially opened with the PRIORITY_NORMAL priority, but can be set to have PRIORITY_HIGH or
* PRIORITY_HIGH or PRIORITY_LOW. * PRIORITY_LOW.
*
* @param addr the address for which to search. * @param addr the address for which to search.
* @return the first program that can be found to contain the given address. * @return the first program that can be found to contain the given address.
*/ */
@ -228,6 +268,7 @@ public interface ProgramManager {
/** /**
* Returns a list of all open program. * Returns a list of all open program.
*
* @return the programs * @return the programs
*/ */
public Program[] getAllOpenPrograms(); public Program[] getAllOpenPrograms();
@ -235,6 +276,7 @@ public interface ProgramManager {
/** /**
* Allows program manager state to be locked/unlocked. While locked, the program manager will * Allows program manager state to be locked/unlocked. While locked, the program manager will
* not support opening additional programs. * not support opening additional programs.
*
* @param state locked if true, unlocked if false * @param state locked if true, unlocked if false
* @deprecated deprecated for 10.1; removal for 10.3 or later * @deprecated deprecated for 10.1; removal for 10.3 or later
*/ */
@ -243,6 +285,7 @@ public interface ProgramManager {
/** /**
* Returns true if program manager is in the locked state * Returns true if program manager is in the locked state
*
* @return true if program manager is in the locked state * @return true if program manager is in the locked state
* @deprecated deprecated for 10.1; removal for 10.3 or later * @deprecated deprecated for 10.1; removal for 10.3 or later
*/ */

View file

@ -23,8 +23,8 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
/** /**
* A stub of the {@link ProgramManager} interface. This can be used to supply a test program * A stub of the {@link ProgramManager} interface. This can be used to supply a test program manager
* manager or to spy on system internals by overriding methods as needed. * or to spy on system internals by overriding methods as needed.
*/ */
public class TestDummyProgramManager implements ProgramManager { public class TestDummyProgramManager implements ProgramManager {
@ -91,6 +91,26 @@ public class TestDummyProgramManager implements ProgramManager {
// stub // stub
} }
@Override
public void saveProgram() {
// stub
}
@Override
public void saveProgram(Program program) {
// stub
}
@Override
public void saveProgramAs() {
// stub
}
@Override
public void saveProgramAs(Program program) {
// stub
}
@Override @Override
public boolean setPersistentOwner(Program program, Object owner) { public boolean setPersistentOwner(Program program, Object owner) {
// stub // stub

View file

@ -24,8 +24,8 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
/** /**
* A stubbed {@link ProgramManager} that used the 'second program' at the current program. This * A stubbed {@link ProgramManager} that used the 'second program' at the current program. This is
* is used to secondary views in order to install the right program. * used to secondary views in order to install the right program.
*/ */
public class DiffProgramManager implements ProgramManager { public class DiffProgramManager implements ProgramManager {
ProgramDiffPlugin programDiffPlugin; ProgramDiffPlugin programDiffPlugin;
@ -119,6 +119,26 @@ public class DiffProgramManager implements ProgramManager {
// stub // stub
} }
@Override
public void saveProgram() {
// stub
}
@Override
public void saveProgram(Program program) {
// stub
}
@Override
public void saveProgramAs() {
// stub
}
@Override
public void saveProgramAs(Program program) {
// stub
}
@Override @Override
public void setCurrentProgram(Program p) { public void setCurrentProgram(Program p) {
// stub // stub

View file

@ -49,7 +49,6 @@ public class SleighAssembler implements Assembler {
protected Program program; protected Program program;
protected Listing listing; protected Listing listing;
protected Memory memory; protected Memory memory;
protected Disassembler dis;
protected AssemblyParser parser; protected AssemblyParser parser;
protected AssemblyDefaultContext defaultContext; protected AssemblyDefaultContext defaultContext;
protected AssemblyContextGraph ctxGraph; protected AssemblyContextGraph ctxGraph;
@ -71,8 +70,6 @@ public class SleighAssembler implements Assembler {
this.listing = program.getListing(); this.listing = program.getListing();
this.memory = program.getMemory(); this.memory = program.getMemory();
this.dis = Disassembler.getDisassembler(program, TaskMonitor.DUMMY,
DisassemblerMessageListener.IGNORE);
} }
/** /**
@ -113,8 +110,12 @@ public class SleighAssembler implements Assembler {
Address end = at.add(insbytes.length - 1); Address end = at.add(insbytes.length - 1);
listing.clearCodeUnits(at, end, false); listing.clearCodeUnits(at, end, false);
memory.setBytes(at, insbytes); memory.setBytes(at, insbytes);
dis.disassemble(at, new AddressSet(at)); AddressSet set = new AddressSet(at, end);
return listing.getInstructions(new AddressSet(at, end), true); // Creating this at construction causes it to assess memory flags too early.
Disassembler dis = Disassembler.getDisassembler(program, TaskMonitor.DUMMY,
DisassemblerMessageListener.IGNORE);
dis.disassemble(at, set);
return listing.getInstructions(set, true);
} }
@Override @Override

View file

@ -46,9 +46,10 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
/** /**
* Build a new SLEIGH constructor semantic * Build a new SLEIGH constructor semantic
*
* @param cons the SLEIGH constructor * @param cons the SLEIGH constructor
* @param indices the indices of RHS non-terminals in the associated production that represent an * @param indices the indices of RHS non-terminals in the associated production that represent
* operand in the SLEIGH constructor * an operand in the SLEIGH constructor
*/ */
public AssemblyConstructorSemantic(Constructor cons, List<Integer> indices) { public AssemblyConstructorSemantic(Constructor cons, List<Integer> indices) {
this.cons = cons; this.cons = cons;
@ -73,6 +74,7 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
/** /**
* Get the SLEIGH constructor * Get the SLEIGH constructor
*
* @return the constructor * @return the constructor
*/ */
public Constructor getConstructor() { public Constructor getConstructor() {
@ -81,6 +83,7 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
/** /**
* Get the associated encoding patterns for the constructor * Get the associated encoding patterns for the constructor
*
* @return the patterns * @return the patterns
*/ */
public Collection<AssemblyResolvedConstructor> getPatterns() { public Collection<AssemblyResolvedConstructor> getPatterns() {
@ -92,6 +95,7 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
/** /**
* Convert the index of a print piece to its associated operand index * Convert the index of a print piece to its associated operand index
*
* @param printpos position excluding whitespace and string tokens. * @param printpos position excluding whitespace and string tokens.
* @return the operand index * @return the operand index
*/ */
@ -101,6 +105,7 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
/** /**
* Get the list of operand indices in print piece order * Get the list of operand indices in print piece order
*
* @return the list * @return the list
*/ */
public List<Integer> getOperandIndices() { public List<Integer> getOperandIndices() {
@ -111,8 +116,9 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
* Get an iterator over the operand indices * Get an iterator over the operand indices
* *
* If this iterator is advanced for each non-terminal, while simultaneously iterating over the * If this iterator is advanced for each non-terminal, while simultaneously iterating over the
* RHS of the associated production, then this will identify the corresponding operand index * RHS of the associated production, then this will identify the corresponding operand index for
* for each non-terminal * each non-terminal
*
* @return the iterator * @return the iterator
*/ */
public Iterator<Integer> getOperandIndexIterator() { public Iterator<Integer> getOperandIndexIterator() {
@ -142,14 +148,14 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
* than ("specializes") another, i.e., it matches on more bits than another pattern, the more * than ("specializes") another, i.e., it matches on more bits than another pattern, the more
* specific pattern is chosen. Second, if the two are equally special, then the one that occurs * specific pattern is chosen. Second, if the two are equally special, then the one that occurs
* first in the SLEIGH specification is taken. So, during resolution, if a less-special or * first in the SLEIGH specification is taken. So, during resolution, if a less-special or
* later-occurring constructor is chosen, we must prevent continued resolution from matching * later-occurring constructor is chosen, we must prevent continued resolution from matching the
* the more-special or earlier-occurring pattern(s). * more-special or earlier-occurring pattern(s).
* *
* Essentially, this states, "you may choose any value matching my pattern, except those that * Essentially, this states, "you may choose any value matching my pattern, except those that
* match these forbidden patterns." * match these forbidden patterns."
* *
* This takes a given pattern, and searches the rest of the language for any patterns that * This takes a given pattern, and searches the rest of the language for any patterns that would
* would take precedence, and combines them as forbidden patterns with the given pattern. * take precedence, and combines them as forbidden patterns with the given pattern.
* *
* @param pat the given pattern * @param pat the given pattern
* @return the same pattern with forbidden records added * @return the same pattern with forbidden records added
@ -194,7 +200,7 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
// OK, they overlap. Let's see if its a strict subset // OK, they overlap. Let's see if its a strict subset
if (comb.bitsEqual(sibpat)) { if (comb.bitsEqual(sibpat)) {
forbids.add(sibpat.withDescription( forbids.add(sibpat.withDescription(
cons + " forbids " + sibcons + " by pattern specificity")); sibcons + " forbids " + cons + " by pattern specificity"));
return CONTINUE; return CONTINUE;
} }
else if (comb.bitsEqual(pat)) { else if (comb.bitsEqual(pat)) {
@ -205,7 +211,7 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
// Finally, check the line number // Finally, check the line number
if (sibcons.getId() < cons.getId()) { if (sibcons.getId() < cons.getId()) {
forbids.add( forbids.add(
sibpat.withDescription(cons + " forbids " + sibcons + " by rule position")); sibpat.withDescription(sibcons + " forbids " + cons + " by rule position"));
return CONTINUE; return CONTINUE;
} }
@ -219,22 +225,24 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
/** /**
* Solve this constructor's context changes * Solve this constructor's context changes
*
* @param res the combined resolution requirements derived from the subconstructors * @param res the combined resolution requirements derived from the subconstructors
* @param vals any defined symbols (usually {@code inst_start}, and {@code inst_next}) * @param vals any defined symbols (usually {@code inst_start}, and {@code inst_next})
* @param opvals a map from operand index to operand value * @param opvals a map from operand index to operand value
* @return the resolution with context changes applied in reverse, or an error * @return the resolution with context changes applied in reverse, or an error
* *
* Each value in {@code opvals} must either be a numeric value, e.g., an index from a varnode * Each value in {@code opvals} must either be a numeric value, e.g., an index from a
* list, or another {@link AssemblyResolvedConstructor} for a subconstructor operand. * varnode list, or another {@link AssemblyResolvedConstructor} for a subconstructor
* operand.
* *
* It's helpful to think of the SLEIGH disassembly process here. Normally, once the appropriate * It's helpful to think of the SLEIGH disassembly process here. Normally, once the
* constructor has been identified (by matching patterns), its context changes are applied, and * appropriate constructor has been identified (by matching patterns), its context
* then its operands parsed (possibly parsing subconstructor operands). Thus, {@code res} can * changes are applied, and then its operands parsed (possibly parsing subconstructor
* be thought of as the intermediate result between applying context changes and parsing * operands). Thus, {@code res} can be thought of as the intermediate result between
* operands, except in reverse. The output of this method corresponds to the state before * applying context changes and parsing operands, except in reverse. The output of this
* context changes were applied, i.e., immediately after selecting the constructor. Thus, in * method corresponds to the state before context changes were applied, i.e.,
* reverse, the context is solved immediately before applying the selected constructor * immediately after selecting the constructor. Thus, in reverse, the context is solved
* patterns. * immediately before applying the selected constructor patterns.
* *
* @see AssemblyTreeResolver#resolveSelectedChildren(AssemblyProduction, List, List, Collection) * @see AssemblyTreeResolver#resolveSelectedChildren(AssemblyProduction, List, List, Collection)
*/ */
@ -300,10 +308,11 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
* @param outer the state before context changes * @param outer the state before context changes
* @return the state after context changes * @return the state after context changes
* *
* Unlike the usual disassembly process, this method does not take into account any information * Unlike the usual disassembly process, this method does not take into account any
* from the instruction encoding. Any context bits that depend on it are set to unknown * information from the instruction encoding. Any context bits that depend on it are set
* ({@code x}) in the output. This method is used to pre-compute a context transition graph in * to unknown ({@code x}) in the output. This method is used to pre-compute a context
* order to quickly resolve purely-recursive semantics on the root constructor table. * transition graph in order to quickly resolve purely-recursive semantics on the root
* constructor table.
*/ */
public AssemblyResolvedConstructor applyForward(AssemblyResolvedConstructor outer) { public AssemblyResolvedConstructor applyForward(AssemblyResolvedConstructor outer) {
AssemblyResolvedConstructor res = outer; AssemblyResolvedConstructor res = outer;

View file

@ -75,6 +75,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
/** /**
* Constructs a new MemoryMapDB * Constructs a new MemoryMapDB
*
* @param handle the open database handle. * @param handle the open database handle.
* @param addrMap the address map. * @param addrMap the address map.
* @param openMode the open mode for the program. * @param openMode the open mode for the program.
@ -146,14 +147,16 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
} }
/** /**
* Update the <code>allInitializedAddrSet</code> and <code>initializedLoadedAddrSet</code> * Update the <code>allInitializedAddrSet</code> and <code>initializedLoadedAddrSet</code> with
* with relevant initialized addresses from the specified memory block. If block is not * relevant initialized addresses from the specified memory block. If block is not a
* a mapped-block and it may be a source to existing mapped-blocks then * mapped-block and it may be a source to existing mapped-blocks then
* <code>scanAllMappedBlocksIfNeeded</code> should be passed as <code>true</code> unless * <code>scanAllMappedBlocksIfNeeded</code> should be passed as <code>true</code> unless all
* all mapped blocks will be processed separately. * mapped blocks will be processed separately.
*
* @param block memory block * @param block memory block
* @param scanAllMappedBlocksIfNeeded if true and block is initialized and not a mapped block all * @param scanAllMappedBlocksIfNeeded if true and block is initialized and not a mapped block
* mapped blocks will be processed for possible introduction of newly initialized mapped regions. * all mapped blocks will be processed for possible introduction of newly initialized
* mapped regions.
*/ */
private void addBlockAddresses(MemoryBlockDB block, boolean scanAllMappedBlocksIfNeeded) { private void addBlockAddresses(MemoryBlockDB block, boolean scanAllMappedBlocksIfNeeded) {
AddressSet blockSet = new AddressSet(block.getStart(), block.getEnd()); AddressSet blockSet = new AddressSet(block.getStart(), block.getEnd());
@ -193,11 +196,12 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
} }
/** /**
* Update initialized address set for those mapped blocks which map onto the * Update initialized address set for those mapped blocks which map onto the specified block
* specified block which has just completed a transition of its' initialized state. * which has just completed a transition of its' initialized state.
*
* @param block block whose initialized state has changed * @param block block whose initialized state has changed
* @param isInitialized true if block transitioned from uninitialized to initialized, * @param isInitialized true if block transitioned from uninitialized to initialized, else
* else transition is from initialized to uninitialized. * transition is from initialized to uninitialized.
*/ */
private void updateMappedAddresses(MemoryBlockDB block, boolean isInitialized) { private void updateMappedAddresses(MemoryBlockDB block, boolean isInitialized) {
@ -285,6 +289,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
/** /**
* Returns the address factory for the program. * Returns the address factory for the program.
*
* @return program address factory * @return program address factory
*/ */
AddressFactory getAddressFactory() { AddressFactory getAddressFactory() {
@ -293,6 +298,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
/** /**
* Returns the AddressMap from the program. * Returns the AddressMap from the program.
*
* @return program address map * @return program address map
*/ */
AddressMapDB getAddressMap() { AddressMapDB getAddressMap() {
@ -480,8 +486,9 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
} }
/** /**
* Two blocks have been joined producing newBlock. The block which was * Two blocks have been joined producing newBlock. The block which was eliminated can be
* eliminated can be identified using the oldBlockStartAddr. * identified using the oldBlockStartAddr.
*
* @param newBlock new joined memory block * @param newBlock new joined memory block
* @param oldBlockStartAddr original start address of affected block * @param oldBlockStartAddr original start address of affected block
*/ */
@ -798,6 +805,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
/** /**
* Check new block name for validity * Check new block name for validity
*
* @param name new block name * @param name new block name
* @throws IllegalArgumentException if invalid block name specified * @throws IllegalArgumentException if invalid block name specified
*/ */
@ -1246,25 +1254,19 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
} }
/** /**
* Tests if the memory contains a sequence of contiguous bytes that match the * Tests if the memory contains a sequence of contiguous bytes that match the given byte array
* given byte array at all bit positions where the mask contains an "on" bit. * at all bit positions where the mask contains an "on" bit. The test will be something like
* The test will be something like
* *
* for(int i=0;i<bytes.length;i++) { * for(int i=0;i<bytes.length;i++) { if (bytes[i] != memory.getByte(addr+i) &amp; masks[i]) {
* if (bytes[i] != memory.getByte(addr+i) &amp; masks[i]) { * return false; } } return false;
* return false;
* }
* }
* return false;
* *
* @param addr The beginning address in memory to test against. * @param addr The beginning address in memory to test against.
* @param bytes the array of bytes to test for. * @param bytes the array of bytes to test for.
* @param masks the array of masks. (One for each byte in the byte array) * @param masks the array of masks. (One for each byte in the byte array)
* @param forward if true, the matching is going forward, otherwise backward * @param forward if true, the matching is going forward, otherwise backward
* *
* @return 1 if there is a match * @return 1 if there is a match 0 if there is no match -i if no match is found, this is the
* 0 if there is no match * number of bytes that can be safely skipped
* -i if no match is found, this is the number of bytes that can be safely skipped
*/ */
private int match(Address addr, byte[] bytes, byte[] masks, byte[] data, boolean forward) { private int match(Address addr, byte[] bytes, byte[] masks, byte[] data, boolean forward) {
try { try {
@ -1645,10 +1647,11 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
throw new MemoryAccessException( throw new MemoryAccessException(
"Address " + addr.toString(true) + " does not exist in memory"); "Address " + addr.toString(true) + " does not exist in memory");
} }
n -= block.getSize() - addr.subtract(block.getStart()); long advanced = block.getSize() - addr.subtract(block.getStart());
if (n <= 0) { if (advanced >= n) {
break; break;
} }
n -= advanced;
try { try {
addr = block.getEnd().addNoWrap(1); addr = block.getEnd().addNoWrap(1);
} }
@ -1879,8 +1882,9 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
} }
/** /**
* Tests if the given addressSpace (overlay space) is used by any blocks. If not, it * Tests if the given addressSpace (overlay space) is used by any blocks. If not, it removes the
* removes the space. * space.
*
* @param addressSpace overlay address space to be removed * @param addressSpace overlay address space to be removed
*/ */
private void checkRemoveAddressSpace(AddressSpace addressSpace) { private void checkRemoveAddressSpace(AddressSpace addressSpace) {
@ -1930,8 +1934,8 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
} }
/** /**
* Gets the intersected set of addresses between a mapped memory block, and some other * Gets the intersected set of addresses between a mapped memory block, and some other address
* address set. * set.
* *
* @param mappedBlock The mapped memory block to use in the intersection. * @param mappedBlock The mapped memory block to use in the intersection.
* @param set Some other address set to use in the intersection. * @param set Some other address set to use in the intersection.
@ -1954,9 +1958,10 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
} }
/** /**
* Converts the given address range back from the source range back to the mapped range. * Converts the given address range back from the source range back to the mapped range. NOTE:
* NOTE: It is important that the specified mappedSourceRange is restricted to the * It is important that the specified mappedSourceRange is restricted to the mapped source area
* mapped source area of the specified mappedBlock. * of the specified mappedBlock.
*
* @param mappedBlock mapped memory block * @param mappedBlock mapped memory block
* @param mappedSourceRange source range which maps into mappedBlock. * @param mappedSourceRange source range which maps into mappedBlock.
* @return mapped range or null if source range not mapped to block * @return mapped range or null if source range not mapped to block
@ -2225,6 +2230,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
/** /**
* Returns a list of all memory blocks that contain any addresses in the given range * Returns a list of all memory blocks that contain any addresses in the given range
*
* @param start the start address * @param start the start address
* @param end the end address * @param end the end address
* @return a list of all memory blocks that contain any addresses in the given range * @return a list of all memory blocks that contain any addresses in the given range