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/DebuggerConsolePlugin/DebuggerConsolePlugin.html||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/DebuggerInterpreterPlugin/DebuggerInterpreterPlugin.html||GHIDRA||||END|
src/main/help/help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html||GHIDRA||||END|

View file

@ -53,108 +53,113 @@
<tocdef id="Debugger" text="Debugger"
target="help/topics/Debugger/Debugger.html" >
<tocdef id="DebuggerGettingStarted" text="Getting Started"
sortgroup="a"
target="help/topics/Debugger/GettingStarted.html" >
<tocdef id="Launching" text="Launching a Target"
sortgroup="a"
target="help/topics/Debugger/GettingStarted.html#launching" />
</tocdef>
<tocdef id="DebuggerGettingStarted" text="Getting Started"
sortgroup="a"
target="help/topics/Debugger/GettingStarted.html" >
<tocdef id="DebuggerTroubleshooting" text="Troubleshooting"
sortgroup="b"
target="help/topics/Debugger/Troubleshooting.html" />
<tocdef id="Launching" text="Launching a Target"
sortgroup="a"
target="help/topics/Debugger/GettingStarted.html#launching" />
</tocdef>
<tocdef id="DebuggerTargetsPlugin" text="Targets"
sortgroup="c"
target="help/topics/DebuggerTargetsPlugin/DebuggerTargetsPlugin.html" >
<tocdef id="DebuggerTroubleshooting" text="Troubleshooting"
sortgroup="b"
target="help/topics/Debugger/Troubleshooting.html" />
<tocdef id="DebuggerModelServicePlugin" text="Tool Actions"
sortgroup="a"
target="help/topics/DebuggerModelServicePlugin/DebuggerModelServicePlugin.html" />
</tocdef>
<tocdef id="DebuggerTargetsPlugin" text="Targets"
sortgroup="c"
target="help/topics/DebuggerTargetsPlugin/DebuggerTargetsPlugin.html" >
<tocdef id="DebuggerModelServicePlugin" text="Tool Actions"
sortgroup="a"
target="help/topics/DebuggerModelServicePlugin/DebuggerModelServicePlugin.html" />
</tocdef>
<tocdef id="DebuggerConsolePlugin" text="Debug Console"
sortgroup="c1"
target="help/topics/DebuggerConsolePlugin/DebuggerConsolePlugin.html" />
sortgroup="c1"
target="help/topics/DebuggerConsolePlugin/DebuggerConsolePlugin.html" />
<tocdef id="DebuggerObjectsPlugin" text="Commands and Objects"
sortgroup="d"
target="help/topics/DebuggerObjectsPlugin/DebuggerObjectsPlugin.html" />
<tocdef id="DebuggerCopyActionsPlugin" text="Copy Actions"
sortgroup="c2"
target="help/topics/DebuggerCopyActionsPlugin/DebuggerCopyActionsPlugin.html" />
<tocdef id="DebuggerInterpreterPlugin" text="Interpreters"
sortgroup="e"
target="help/topics/DebuggerInterpreterPlugin/DebuggerInterpreterPlugin.html" />
<tocdef id="DebuggerObjectsPlugin" text="Commands and Objects"
sortgroup="d"
target="help/topics/DebuggerObjectsPlugin/DebuggerObjectsPlugin.html" />
<tocdef id="DebuggerThreadsPlugin" text="Threads and Traces"
sortgroup="f"
target="help/topics/DebuggerThreadsPlugin/DebuggerThreadsPlugin.html" />
<tocdef id="DebuggerInterpreterPlugin" text="Interpreters"
sortgroup="e"
target="help/topics/DebuggerInterpreterPlugin/DebuggerInterpreterPlugin.html" />
<tocdef id="DebuggerThreadsPlugin" text="Threads and Traces"
sortgroup="f"
target="help/topics/DebuggerThreadsPlugin/DebuggerThreadsPlugin.html" />
<tocdef id="DebuggerTraceManagerServicePlugin" text="Trace Management"
sortgroup="g"
target="help/topics/DebuggerTraceManagerServicePlugin/DebuggerTraceManagerServicePlugin.html" />
sortgroup="g"
target="help/topics/DebuggerTraceManagerServicePlugin/DebuggerTraceManagerServicePlugin.html" />
<tocdef id="DebuggerEmulationServicePlugin" text="Emulation"
sortgroup="g1"
target="help/topics/DebuggerEmulationServicePlugin/DebuggerEmulationServicePlugin.html" />
<tocdef id="DebuggerRegistersPlugin" text="Registers"
sortgroup="h"
target="help/topics/DebuggerRegistersPlugin/DebuggerRegistersPlugin.html" />
<tocdef id="DebuggerMemoryBytesPlugin" text="Memory"
sortgroup="h1"
target="help/topics/DebuggerMemoryBytesPlugin/DebuggerMemoryBytesPlugin.html" />
<tocdef id="DebuggerMemoryBytesPlugin" text="Memory"
sortgroup="h1"
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"
sortgroup="i"
target="help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html" />
<tocdef id="DebuggerListingPlugin" text="Dynamic Listing"
sortgroup="i"
target="help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html" />
<tocdef id="DebuggerStackPlugin" text="Stack"
sortgroup="j"
target="help/topics/DebuggerStackPlugin/DebuggerStackPlugin.html" />
<tocdef id="DebuggerStackPlugin" text="Stack"
sortgroup="j"
target="help/topics/DebuggerStackPlugin/DebuggerStackPlugin.html" />
<tocdef id="DebuggerBreakpointsPlugin" text="Breakpoints"
sortgroup="k"
target="help/topics/DebuggerBreakpointsPlugin/DebuggerBreakpointsPlugin.html" >
<tocdef id="DebuggerBreakpointsPlugin" text="Breakpoints"
sortgroup="k"
target="help/topics/DebuggerBreakpointsPlugin/DebuggerBreakpointsPlugin.html" >
<tocdef id="DebuggerBreakpointMarkerPlugin" text="In the Listings"
sortgroup="a"
target="help/topics/DebuggerBreakpointMarkerPlugin/DebuggerBreakpointMarkerPlugin.html" />
</tocdef>
<tocdef id="DebuggerBreakpointMarkerPlugin" text="In the Listings"
sortgroup="a"
target="help/topics/DebuggerBreakpointMarkerPlugin/DebuggerBreakpointMarkerPlugin.html" />
</tocdef>
<tocdef id="DebuggerRegionsPlugin" text="Memory Regions"
sortgroup="l"
target="help/topics/DebuggerRegionsPlugin/DebuggerRegionsPlugin.html" />
<tocdef id="DebuggerRegionsPlugin" text="Memory Regions"
sortgroup="l"
target="help/topics/DebuggerRegionsPlugin/DebuggerRegionsPlugin.html" />
<tocdef id="DebuggerTimePlugin" text="Time"
sortgroup="m"
target="help/topics/DebuggerTimePlugin/DebuggerTimePlugin.html" />
<tocdef id="DebuggerTimePlugin" text="Time"
sortgroup="m"
target="help/topics/DebuggerTimePlugin/DebuggerTimePlugin.html" />
<tocdef id="DebuggerModulesPlugin" text="Modules and Sections"
sortgroup="n"
target="help/topics/DebuggerModulesPlugin/DebuggerModulesPlugin.html" >
<tocdef id="DebuggerModulesPlugin" text="Modules and Sections"
sortgroup="n"
target="help/topics/DebuggerModulesPlugin/DebuggerModulesPlugin.html" >
<tocdef id="DebuggerStaticMappingPlugin" text="Static Mappings"
sortgroup="a"
target="help/topics/DebuggerStaticMappingPlugin/DebuggerStaticMappingPlugin.html" />
<tocdef id="DebuggerStaticMappingPlugin" text="Static Mappings"
sortgroup="a"
target="help/topics/DebuggerStaticMappingPlugin/DebuggerStaticMappingPlugin.html" />
</tocdef>
<tocdef id="DebuggerWatchesPlugin" text="Watches"
sortgroup="n"
target="help/topics/DebuggerWatchesPlugin/DebuggerWatchesPlugin.html" />
sortgroup="n"
target="help/topics/DebuggerWatchesPlugin/DebuggerWatchesPlugin.html" />
<tocdef id="DebuggerMemviewPlugin" text="Memview Plot"
sortgroup="o"
target="help/topics/DebuggerMemviewPlugin/DebuggerMemviewPlugin.html" />
sortgroup="o"
target="help/topics/DebuggerMemviewPlugin/DebuggerMemviewPlugin.html" />
<tocdef id="DebuggerPcodeStepperPlugin" text="P-code Stepper"
sortgroup="p"
target="help/topics/DebuggerPcodeStepperPlugin/DebuggerPcodeStepperPlugin.html" />
sortgroup="p"
target="help/topics/DebuggerPcodeStepperPlugin/DebuggerPcodeStepperPlugin.html" />
<tocdef id="DebuggerBots" text="Bots: Workflow Automation"
sortgroup="q"
target="help/topics/DebuggerBots/DebuggerBots.html" />
</tocdef>
<tocdef id="DebuggerBots" text="Bots: Workflow Automation"
sortgroup="q"
target="help/topics/DebuggerBots/DebuggerBots.html" />
</tocdef>
</tocref>
</tocroot>

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
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
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
@ -142,33 +142,32 @@
and/or needs a version upgrade. It will attempt to open the program, allowing Ghidra to prompt
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
there is a selection of addresses in the dynamic listing. It will instruct the recorder to
capture the contents of memory for the selected range(s). Typically, the viewable addresses are
automatically captured &mdash; see the Auto-Read action.</P>
there is a selection of addresses in the dynamic listing. It will instruct the recorder to read
and record the contents of memory for the selected range(s). Typically, the viewable addresses
are automatically read, anyway &mdash; see the Auto-Read action.</P>
<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
memory range(s) displayed in the listing are automatically captured. Like the Capture Memory
action, capture can only occur when the current trace is "at the present" with a live target.
It occurs when the user scrolls the listing, or when the listing is otherwise navigated to a
new location. Note that other components may capture memory, regardless of this listing's
configuration. For example, the recorder typically captures the page of memory pointed to by
the program counter. In other words, this action <EM>cannot</EM> "disable all memory captures."
The options are pluggable, but currently consist of:</P>
memory range(s) displayed in the listing are automatically read and recorded. Like the Read
Memory action, it is only permitted when the current trace is "at the present" with a live
target. It occurs when the user scrolls the listing, or when the listing is otherwise navigated
to a new location. Note that other components may read memory, regardless of this listing's
configuration. For example, the recorder typically reads the page of memory pointed to by the
program counter. In other words, this action <EM>cannot</EM> "disable all memory reads." The
options are pluggable, but currently consist of:</P>
<UL>
<LI>Do Not Read Memory - disables automatic memory capture <EM>for this listing
only</EM>.</LI>
<LI>Do Not Read Memory - disables automatic memory reads <EM>for this listing only</EM>.</LI>
<LI>Read Visible Memory - automatically reads stale ranges that enter this listing's
view.</LI>
<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>
<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
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
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
@ -103,33 +103,32 @@
</TBODY>
</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
there is a selection of addresses in the memory window. It will instruct the recorder to
capture the contents of memory for the selected range(s). Typically, the viewable addresses are
automatically captured &mdash; see the Auto-Read action.</P>
there is a selection of addresses in the memory window. It will instruct the recorder to read
and record the contents of memory for the selected range(s). Typically, the viewable addresses
are automatically read &mdash; see the Auto-Read action.</P>
<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
memory range(s) displayed in the window are automatically captured. Like the Capture Memory
action, capture can only occur when the current trace is "at the present" with a live target.
It occurs when the user scrolls the window, or when the window is otherwise navigated to a new
location. Note that other components may capture memory, regardless of this windows's
configuration. For example, the recorder typically captures the page of memory pointed to by
the program counter. In other words, this action <EM>cannot</EM> "disable all memory captures."
The options are pluggable, but currently consist of:</P>
memory range(s) displayed in the window are automatically read and recorded. Like the Read
Memory action, it is only permitted when the current trace is "at the present" with a live
target. It occurs when the user scrolls the window, or when the window is otherwise navigated
to a new location. Note that other components may read memory, regardless of this windows's
configuration. For example, the recorder typically reads the page of memory pointed to by the
program counter. In other words, this action <EM>cannot</EM> "disable all memory reads." The
options are pluggable, but currently consist of:</P>
<UL>
<LI>Do Not Read Memory - disables automatic memory capture <EM>for this window
only</EM>.</LI>
<LI>Do Not Read Memory - disables automatic memory reads <EM>for this window only</EM>.</LI>
<LI>Read Visible Memory - automatically reads stale ranges that enter this window's
view.</LI>
<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>
<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");
// TODO: Draw a real icon.
ImageIcon ICON_CAPTURE_MEMORY = ICON_REGIONS;
//ResourceManager.loadImage("images/capture-memory.png");
ImageIcon ICON_READ_MEMORY = ICON_REGIONS;
//ResourceManager.loadImage("images/read-memory.png");
// TODO: Draw an icon
ImageIcon ICON_MAP_IDENTICALLY = ResourceManager.loadImage("images/doubleArrow.png");
@ -777,14 +777,15 @@ public interface DebuggerResources {
}
}
abstract class AbstractCaptureSelectedMemoryAction extends DockingAction {
public static final String NAME = "Capture Selected Memory";
public static final Icon ICON = ICON_CAPTURE_MEMORY;
public static final String HELP_ANCHOR = "capture_memory";
abstract class AbstractReadSelectedMemoryAction extends DockingAction {
public static final String NAME = "Read Selected Memory";
public static final Icon ICON = ICON_READ_MEMORY;
public static final String HELP_ANCHOR = "read_memory";
public AbstractCaptureSelectedMemoryAction(Plugin owner) {
public AbstractReadSelectedMemoryAction(Plugin owner) {
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));
}
}
@ -885,7 +886,7 @@ public interface DebuggerResources {
interface AutoReadMemoryAction {
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 NAME_VIS_RO_ONCE = "Read Visible Memory, RO Once";
@ -1081,7 +1082,43 @@ public interface DebuggerResources {
return new ActionBuilder(NAME, ownerName)
.description(DESCRIPTION)
.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));
}
}

View file

@ -27,7 +27,7 @@ import docking.menu.MultiStateDockingAction;
import docking.widgets.EventTrigger;
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
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.utils.BackgroundUtils;
import ghidra.app.services.TraceRecorder;
@ -49,10 +49,10 @@ public abstract class DebuggerReadsMemoryTrait {
protected static final AutoConfigState.ClassHandler<DebuggerReadsMemoryTrait> CONFIG_STATE_HANDLER =
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 CaptureSelectedMemoryAction() {
public ReadSelectedMemoryAction() {
super(plugin);
setToolBarData(new ToolBarData(ICON, GROUP));
setEnabled(false);
@ -89,14 +89,14 @@ public abstract class DebuggerReadsMemoryTrait {
}
}
protected class ForCaptureTraceListener extends TraceDomainObjectListener {
public ForCaptureTraceListener() {
protected class ForReadsTraceListener extends TraceDomainObjectListener {
public ForReadsTraceListener() {
listenFor(TraceSnapshotChangeType.ADDED, this::snapshotAdded);
listenFor(TraceMemoryStateChangeType.CHANGED, this::memStateChanged);
}
private void snapshotAdded(TraceSnapshot snapshot) {
actionCaptureSelected.updateEnabled(null);
actionReadSelected.updateEnabled(null);
}
private void memStateChanged(TraceAddressSnapRange range, TraceMemoryState oldIsNull,
@ -120,7 +120,7 @@ public abstract class DebuggerReadsMemoryTrait {
@Override
public void processMemoryAccessibilityChanged(TraceRecorder recorder) {
Swing.runIfSwingOrRunLater(() -> {
actionCaptureSelected.updateEnabled(null);
actionReadSelected.updateEnabled(null);
});
}
}
@ -137,7 +137,7 @@ public abstract class DebuggerReadsMemoryTrait {
}
protected MultiStateDockingAction<AutoReadMemorySpec> actionAutoRead;
protected CaptureSelectedMemoryAction actionCaptureSelected;
protected ReadSelectedMemoryAction actionReadSelected;
private final AutoReadMemorySpec defaultAutoSpec =
AutoReadMemorySpec.fromConfigName(VisibleROOnceAutoReadMemorySpec.CONFIG_NAME);
@ -149,8 +149,8 @@ public abstract class DebuggerReadsMemoryTrait {
protected final Plugin plugin;
protected final ComponentProvider provider;
protected final ForCaptureTraceListener traceListener =
new ForCaptureTraceListener();
protected final ForReadsTraceListener traceListener =
new ForReadsTraceListener();
protected final ForAccessRecorderListener recorderListener = new ForAccessRecorderListener();
protected final ForVisibilityListener displayListener = new ForVisibilityListener();
@ -257,10 +257,10 @@ public abstract class DebuggerReadsMemoryTrait {
}
}
public DockingAction installCaptureSelectedAction() {
actionCaptureSelected = new CaptureSelectedMemoryAction();
provider.addLocalAction(actionCaptureSelected);
return actionCaptureSelected;
public DockingAction installReadSelectedAction() {
actionReadSelected = new ReadSelectedMemoryAction();
provider.addLocalAction(actionReadSelected);
return actionReadSelected;
}
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.utils.ProgramLocationUtils;
import ghidra.app.plugin.core.debug.utils.ProgramURLUtils;
import ghidra.app.plugin.core.exporter.ExporterDialog;
import ghidra.app.services.*;
import ghidra.app.util.viewer.format.FormatManager;
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.modules.*;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.program.TraceVariableSnapProgramView;
import ghidra.util.HTMLUtilities;
import ghidra.util.Swing;
import ghidra.util.exception.CancelledException;
@ -239,8 +237,7 @@ public class DebuggerListingProvider extends CodeViewerProvider {
protected SyncToStaticListingAction actionSyncToStaticListing;
protected FollowsCurrentThreadAction actionFollowsCurrentThread;
protected MultiStateDockingAction<AutoReadMemorySpec> actionAutoReadMemory;
protected DockingAction actionCaptureSelectedMemory;
protected DockingAction actionExportView;
protected DockingAction actionReadSelectedMemory;
protected DockingAction actionOpenProgram;
protected MultiStateDockingAction<LocationTrackingSpec> actionTrackLocation;
@ -608,12 +605,7 @@ public class DebuggerListingProvider extends CodeViewerProvider {
actionGoTo = goToTrait.installAction();
actionTrackLocation = trackingTrait.installAction();
actionAutoReadMemory = readsMemTrait.installAutoReadAction();
actionCaptureSelectedMemory = readsMemTrait.installCaptureSelectedAction();
actionExportView = ExportTraceViewAction.builder(plugin)
.enabledWhen(ctx -> current.getView() != null)
.onAction(this::activatedExportView)
.buildAndInstallLocal(this);
actionReadSelectedMemory = readsMemTrait.installReadSelectedAction();
actionOpenProgram = OpenProgramAction.builder(plugin)
.withContext(DebuggerOpenProgramActionContext.class)
@ -623,20 +615,6 @@ public class DebuggerListingProvider extends CodeViewerProvider {
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) {
programManager.openProgram(context.getDomainFile(), DomainFile.DEFAULT_VERSION,
ProgramManager.OPEN_CURRENT);

View file

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

View file

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

View file

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

View file

@ -283,7 +283,6 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
private Program program;
private AddressRange staticRange;
private Long shift; // from static image to trace
public MappingEntry(TraceStaticMapping mapping) {
this.mapping = mapping;
@ -309,8 +308,6 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
Address minAddr = opened.getAddressFactory().getAddress(mapping.getStaticAddress());
Address maxAddr = addOrMax(minAddr, mapping.getLength() - 1);
this.staticRange = new AddressRangeImpl(minAddr, maxAddr);
this.shift = mapping.getMinTraceAddress().getOffset() -
staticRange.getMinAddress().getOffset();
return true;
}
return false;
@ -320,7 +317,6 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
if (this.program == closed) {
this.program = null;
this.staticRange = null;
this.shift = null;
return true;
}
return false;
@ -565,7 +561,7 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
}
protected void collectOpenMappedPrograms(AddressRange rng, Range<Long> span,
Map<Program, ShiftAndAddressSetView> result) {
Map<Program, Collection<MappedAddressRange>> result) {
TraceAddressSnapRange tatr = new ImmutableTraceAddressSnapRange(rng, span);
for (Entry<TraceAddressSnapRange, MappingEntry> out : outbound.entrySet()) {
MappingEntry me = out.getValue();
@ -575,16 +571,16 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
if (!out.getKey().intersects(tatr)) {
continue;
}
ShiftAndAddressSetView set = result.computeIfAbsent(me.program,
p -> new ShiftAndAddressSetView(-me.shift, new AddressSet()));
((AddressSet) set.getAddressSetView()).add(me.mapTraceRangeToProgram(rng));
AddressRange srcRng = out.getKey().getRange().intersect(rng);
AddressRange dstRng = me.mapTraceRangeToProgram(rng);
result.computeIfAbsent(me.program, p -> new TreeSet<>())
.add(new MappedAddressRange(srcRng, dstRng));
}
}
public Map<Program, ShiftAndAddressSetView> getOpenMappedViews(AddressSetView set,
public Map<Program, Collection<MappedAddressRange>> getOpenMappedViews(AddressSetView set,
Range<Long> span) {
Map<Program, ShiftAndAddressSetView> result = new HashMap<>();
Map<Program, Collection<MappedAddressRange>> result = new HashMap<>();
for (AddressRange rng : set) {
collectOpenMappedPrograms(rng, span, result);
}
@ -715,7 +711,7 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
}
protected void collectOpenMappedViews(AddressRange rng,
Map<TraceSnap, ShiftAndAddressSetView> result) {
Map<TraceSnap, Collection<MappedAddressRange>> result) {
for (Entry<MappingEntry, Address> inPreceeding : inbound.headMapByValue(
rng.getMaxAddress(), true).entrySet()) {
Address start = inPreceeding.getValue();
@ -726,14 +722,17 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
if (!me.isInProgramRange(rng)) {
continue;
}
ShiftAndAddressSetView set = result.computeIfAbsent(me.getTraceSnap(),
p -> new ShiftAndAddressSetView(me.shift, new AddressSet()));
((AddressSet) set.getAddressSetView()).add(me.mapProgramRangeToTrace(rng));
AddressRange srcRange = me.staticRange.intersect(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) {
Map<TraceSnap, ShiftAndAddressSetView> result = new HashMap<>();
public Map<TraceSnap, Collection<MappedAddressRange>> getOpenMappedViews(
AddressSetView set) {
Map<TraceSnap, Collection<MappedAddressRange>> result = new HashMap<>();
for (AddressRange rng : set) {
collectOpenMappedViews(rng, result);
}
@ -1219,9 +1218,8 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
}
@Override
public Map<Program, ShiftAndAddressSetView> getOpenMappedViews(Trace trace,
AddressSetView set,
long snap) {
public Map<Program, Collection<MappedAddressRange>> getOpenMappedViews(Trace trace,
AddressSetView set, long snap) {
InfoPerTrace info = requireTrackedInfo(trace);
if (info == null) {
return null;
@ -1230,7 +1228,7 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
}
@Override
public Map<TraceSnap, ShiftAndAddressSetView> getOpenMappedViews(Program program,
public Map<TraceSnap, Collection<MappedAddressRange>> getOpenMappedViews(Program program,
AddressSetView set) {
InfoPerProgram info = requireTrackedInfo(program);
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 {
private final long shift;
private final AddressSetView view;
public class MappedAddressRange implements Comparable<MappedAddressRange> {
public ShiftAndAddressSetView(long shift, AddressSetView view) {
this.shift = shift;
this.view = view;
private final AddressRange srcRange;
private final AddressRange dstRange;
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>
* 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
* particular address in this set. Note that since not all addresses from the requested
* 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.
* particular address in the "static" range.
*
* @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() {
return view;
public Address mapSourceToDestination(Address saddr) {
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;
/**
* ======= >>>>>>> d694542c5 (GP-660: Put program filler back in. Need to performance test.) Add
* several static mappings (relocations)
* Add several static mappings (relocations)
*
* <p>
* 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 set the source address set
* @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);
/**
@ -679,9 +784,9 @@ public interface DebuggerStaticMappingService {
*
* @param program the destination program, 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);
/**

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

View file

@ -741,11 +741,11 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
@Test
@Ignore("TODO") // Needs attention, but low priority
// Accessibility listener does not seem to work
public void testActionCaptureSelectedMemory() throws Exception {
public void testActionReadSelectedMemory() throws Exception {
byte[] data = incBlock();
byte[] zero = new byte[data.length];
ByteBuffer buf = ByteBuffer.allocate(data.length);
assertFalse(memBytesProvider.actionCaptureSelectedMemory.isEnabled());
assertFalse(memBytesProvider.actionReadSelectedMemory.isEnabled());
memBytesProvider.setAutoReadMemorySpec(readNone);
// To verify enabled requires live target
@ -760,12 +760,12 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
traceManager.activateTrace(tb.trace);
waitForSwing();
// Still
assertFalse(memBytesProvider.actionCaptureSelectedMemory.isEnabled());
assertFalse(memBytesProvider.actionReadSelectedMemory.isEnabled());
memBytesProvider.setSelection(sel);
waitForSwing();
// Still
assertFalse(memBytesProvider.actionCaptureSelectedMemory.isEnabled());
assertFalse(memBytesProvider.actionReadSelectedMemory.isEnabled());
// Now, simulate the sequence that typically enables the action
createTestModel();
@ -782,21 +782,21 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
// NOTE: recordTargetContainerAndOpenTrace has already activated the trace
// Action is still disabled, because it requires a selection
assertFalse(memBytesProvider.actionCaptureSelectedMemory.isEnabled());
assertFalse(memBytesProvider.actionReadSelectedMemory.isEnabled());
memBytesProvider.setSelection(sel);
waitForSwing();
// 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();
assertEquals(data.length,
trace.getMemoryManager().getBytes(recorder.getSnap(), addr(trace, 0x55550000), buf));
assertArrayEquals(zero, buf.array());
// Verify that the action performs the expected task
performAction(memBytesProvider.actionCaptureSelectedMemory);
performAction(memBytesProvider.actionReadSelectedMemory);
waitForBusyTool(tool);
waitForDomainObject(trace);
@ -811,28 +811,28 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
// Verify that setting the memory inaccessible disables the action
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)
mb.testProcess1.memory.setAccessible(true);
waitForPass(() -> assertTrue(memBytesProvider.actionCaptureSelectedMemory.isEnabled()));
waitForPass(() -> assertTrue(memBytesProvider.actionReadSelectedMemory.isEnabled()));
// Verify that moving into the past disables the action
TraceSnapshot forced = recorder.forceSnapshot();
waitForSwing(); // UI Wants to sync with new snap. Wait....
traceManager.activateSnap(forced.getKey() - 1);
waitForSwing();
assertFalse(memBytesProvider.actionCaptureSelectedMemory.isEnabled());
assertFalse(memBytesProvider.actionReadSelectedMemory.isEnabled());
// Verify that advancing to the present enables the action (assuming a selection)
traceManager.activateSnap(forced.getKey());
waitForSwing();
assertTrue(memBytesProvider.actionCaptureSelectedMemory.isEnabled());
assertTrue(memBytesProvider.actionReadSelectedMemory.isEnabled());
// Verify that stopping the recording disables the action
recorder.stopRecording();
waitForSwing();
assertFalse(memBytesProvider.actionCaptureSelectedMemory.isEnabled());
assertFalse(memBytesProvider.actionReadSelectedMemory.isEnabled());
// 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.services.DebuggerStaticMappingService;
import ghidra.app.services.DebuggerStaticMappingService.ShiftAndAddressSetView;
import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
import ghidra.framework.model.DomainFile;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
@ -339,7 +339,7 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
public void testAddMappingThenTranslateTraceViewToStaticEmpty() throws Exception {
addMapping();
Map<Program, ShiftAndAddressSetView> views =
Map<Program, Collection<MappedAddressRange>> views =
mappingService.getOpenMappedViews(tb.trace, new AddressSet(), 0);
assertTrue(views.isEmpty());
}
@ -360,18 +360,19 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
// After
set.add(dynSpace.getAddress(0xbadbadbadL), dynSpace.getAddress(0xbadbadbadL + 0xff));
Map<Program, ShiftAndAddressSetView> views =
Map<Program, Collection<MappedAddressRange>> views =
mappingService.getOpenMappedViews(tb.trace, set, 0);
assertEquals(1, views.size());
ShiftAndAddressSetView shifted = views.get(program);
assertEquals(0x100000, shifted.getShift());
AddressSetView inStatic = shifted.getAddressSetView();
assertEquals(3, inStatic.getNumAddressRanges());
AddressSet expected = new AddressSet();
expected.add(stSpace.getAddress(0x00200000), stSpace.getAddress(0x002000ff));
expected.add(stSpace.getAddress(0x00200c0d), stSpace.getAddress(0x00200ccc));
expected.add(stSpace.getAddress(0x00201000 - 0x100), stSpace.getAddress(0x00200fff));
assertEquals(expected, inStatic);
Collection<MappedAddressRange> mappedSet = views.get(program);
assertEquals(Set.of(
new MappedAddressRange(tb.range(0x00100000, 0x001000ff),
tb.range(stSpace, 0x00200000, 0x002000ff)),
new MappedAddressRange(tb.range(0x00100c0d, 0x00100ccc),
tb.range(stSpace, 0x00200c0d, 0x00200ccc)),
new MappedAddressRange(tb.range(0x00100f00, 0x00100fff),
tb.range(stSpace, 0x00200f00, 0x00200fff))),
mappedSet);
}
@Test
@ -380,7 +381,7 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
copyTrace();
add2ndMapping();
Map<TraceSnap, ShiftAndAddressSetView> views =
Map<TraceSnap, Collection<MappedAddressRange>> views =
mappingService.getOpenMappedViews(program, new AddressSet());
assertTrue(views.isEmpty());
}
@ -403,30 +404,34 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
// After
set.add(stSpace.getAddress(0xbadbadbadL), stSpace.getAddress(0xbadbadbadL + 0xff));
Map<TraceSnap, ShiftAndAddressSetView> views =
Map<TraceSnap, Collection<MappedAddressRange>> views =
mappingService.getOpenMappedViews(program, set);
Msg.info(this, views);
assertEquals(2, views.size());
ShiftAndAddressSetView shifted1 = views.get(new DefaultTraceSnap(tb.trace, 0));
assertEquals(-0x100000, shifted1.getShift());
AddressSetView in1st = shifted1.getAddressSetView();
assertEquals(5, in1st.getNumAddressRanges());
AddressSetView in2nd = views.get(new DefaultTraceSnap(copy, 0)).getAddressSetView();
assertEquals(3, in2nd.getNumAddressRanges());
Collection<MappedAddressRange> mappedSet1 = views.get(new DefaultTraceSnap(tb.trace, 0));
Collection<MappedAddressRange> mappedSet2 = views.get(new DefaultTraceSnap(copy, 0));
AddressSet expectedIn1st = new AddressSet();
AddressSet expectedIn2nd = new AddressSet();
expectedIn1st.add(dynSpace.getAddress(0x00100000), dynSpace.getAddress(0x001000ff));
expectedIn1st.add(dynSpace.getAddress(0x00100800 - 0x10),
dynSpace.getAddress(0x00100800 + 0xf));
expectedIn1st.add(dynSpace.getAddress(0x00101000 - 0x100), dynSpace.getAddress(0x00100fff));
expectedIn2nd.add(expectedIn1st);
assertEquals(Set.of(
new MappedAddressRange(tb.range(stSpace, 0x00200000, 0x002000ff),
tb.range(0x00100000, 0x001000ff)),
new MappedAddressRange(tb.range(stSpace, 0x002007f0, 0x0020080f),
tb.range(0x001007f0, 0x0010080f)),
new MappedAddressRange(tb.range(stSpace, 0x00200f00, 0x00200fff),
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));
expectedIn1st.add(dynSpace.getAddress(0x00102800 - 0x10), dynSpace.getAddress(0x001027ff));
assertEquals(expectedIn1st, in1st);
assertEquals(expectedIn2nd, in2nd);
assertEquals(Set.of(
new MappedAddressRange(tb.range(stSpace, 0x00200000, 0x002000ff),
tb.range(0x00100000, 0x001000ff)),
new MappedAddressRange(tb.range(stSpace, 0x002007f0, 0x0020080f),
tb.range(0x001007f0, 0x0010080f)),
new MappedAddressRange(tb.range(stSpace, 0x00200f00, 0x00200fff),
tb.range(0x00100f00, 0x00100fff))),
mappedSet2);
}
@Test

View file

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

View file

@ -30,6 +30,7 @@ import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.symbol.*;
import ghidra.trace.database.DBTraceUtils;
import ghidra.trace.database.context.DBTraceRegisterContextManager;
import ghidra.trace.database.context.DBTraceRegisterContextSpace;
import ghidra.trace.database.language.DBTraceGuestLanguage;
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
@ -621,13 +622,12 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
@Override
public BigInteger getValue(Register register, boolean signed) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
DBTraceRegisterContextSpace ctxSpace =
space.trace.getRegisterContextManager().get(space, false);
if (ctxSpace == null) {
DBTraceRegisterContextManager manager = space.trace.getRegisterContextManager();
RegisterValue rv =
manager.getValueWithDefault(getLanguage(), register, getStartSnap(), getAddress());
if (rv == null) {
return null;
}
RegisterValue rv =
ctxSpace.getValue(getLanguage(), register, getStartSnap(), getAddress());
return signed ? rv.getSignedValue() : rv.getUnsignedValue();
}
}
@ -635,12 +635,9 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
@Override
public RegisterValue getRegisterValue(Register register) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
DBTraceRegisterContextSpace ctxSpace =
space.trace.getRegisterContextManager().get(space, false);
if (ctxSpace == null) {
return null;
}
return ctxSpace.getValue(getLanguage(), register, getStartSnap(), getAddress());
DBTraceRegisterContextManager manager = space.trace.getRegisterContextManager();
return manager.getValueWithDefault(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.TraceChangeRecord;
import ghidra.util.LockHold;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@ -66,34 +67,9 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
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,
InstructionPrototype prototype, Instruction protoInstr) {
try {
doSetContexts(lifespan, address, address.addNoWrap(prototype.getLength() - 1),
protoInstr);
Instruction created = doCreate(lifespan, address, prototype, protoInstr);
// copy override settings to replacement instruction
if (protoInstr.isFallThroughOverridden()) {
@ -183,6 +159,27 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
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,
InstructionPrototype prototype, ProcessorContextView context)
throws CodeUnitInsertionException, AddressOverflowException {
@ -214,6 +211,8 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
throw new CodeUnitInsertionException("Code units cannot overlap");
}
doSetContexts(tasr, prototype.getLanguage(), context);
DBTraceInstruction created = space.instructionMapSpace.put(tasr, null);
created.set(prototype, context);

View file

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

View file

@ -25,6 +25,7 @@ import javax.help.UnsupportedOperationException;
import com.google.common.collect.Range;
import generic.NestedIterator;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.Variable;
@ -231,13 +232,22 @@ public abstract class AbstractDBTraceProgramViewReferenceManager implements Refe
: (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
public ReferenceIterator getReferenceIterator(Address startAddr) {
if (refs(false) == null) {
return new ReferenceIteratorAdapter(Collections.emptyIterator());
}
// TODO: This will fail to occlude on equal (src,dst,opIndex) keys
return new ReferenceIteratorAdapter(
program.viewport.mergedIterator(s -> refs.getReferencesFrom(s, startAddr).iterator(),
program.viewport.mergedIterator(s -> getReferenceIteratorForSnap(s, startAddr),
getReferenceFromComparator(true)));
}

View file

@ -49,8 +49,7 @@ import ghidra.trace.database.memory.DBTraceMemoryManager;
import ghidra.trace.database.symbol.DBTraceReference;
import ghidra.trace.database.thread.DBTraceThread;
import ghidra.trace.database.thread.DBTraceThreadManager;
import ghidra.trace.model.ImmutableTraceAddressSnapRange;
import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.*;
import ghidra.trace.model.language.TraceGuestLanguage;
import ghidra.util.Msg;
import ghidra.util.database.DBOpenMode;
@ -76,6 +75,12 @@ public class ToyDBTraceBuilder implements AutoCloseable {
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) {
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.
*
* @param tool the tool with the active program to be merged
* @param mergeManager the merge manager that will control the merge process
* @param program the current program
@ -61,7 +62,8 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
@Override
public MergeManagerProvider createProvider() {
return new MergeManagerProvider(this, "Merge Programs for " + currentDomainObject.getName());
return new MergeManagerProvider(this,
"Merge Programs for " + currentDomainObject.getName());
}
@Override
@ -82,6 +84,7 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
/**
* Gets the merge manager associated with this plug-in.
*
* @return the merge manager
*/
@Override
@ -91,6 +94,7 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
/**
* Defines and displays a component for resolving merge conflicts.
*
* @param component the 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.
*
* @param mergeDescription the new description
*/
@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.
* @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
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.
*/
@Override
@ -135,8 +144,9 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
}
/**
* Enables/disables the Apply button at the bottom of the merge tool.
* The Apply button is for applying conflicts.
* Enables/disables the Apply button at the bottom of the merge tool. The Apply button is for
* applying conflicts.
*
* @param state true means enable the button. false means disable it.
*/
@Override
@ -146,6 +156,7 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
/**
* Gets the provider for the merge.
*
* @return the provider
*/
@Override
@ -153,22 +164,27 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
return provider;
}
@Override
public boolean closeOtherPrograms(boolean ignoreChanges) {
return false;
}
@Override
public boolean closeAllPrograms(boolean ignoreChanges) {
return false;
}
@Override
public boolean closeProgram() {
return false;
}
@Override
public boolean closeProgram(Program program, boolean ignoreChanges) {
return false;
}
@Override
public Program[] getAllOpenPrograms() {
ProgramMultiUserMergeManager programMergeManager =
(ProgramMultiUserMergeManager) mergeManager;
@ -178,10 +194,12 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
programMergeManager.getProgram(MergeConstants.ORIGINAL) };
}
@Override
public Program getCurrentProgram() {
return (Program) currentDomainObject;
}
@Override
public Program getProgram(Address addr) {
return null;
}
@ -190,6 +208,7 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
return 0;
}
@Override
public boolean isVisible(Program program) {
return false;
}
@ -199,6 +218,7 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
return null;
}
@Override
public Program openProgram(DomainFile domainFile) {
return null;
}
@ -208,29 +228,53 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
return null;
}
@Override
public Program openProgram(DomainFile df, int version) {
return null;
}
@Override
public Program openProgram(DomainFile domainFile, int version, int state) {
return null;
}
@Override
public void openProgram(Program program) {
}
@Override
public void openProgram(Program program, boolean current) {
}
@Override
public void openProgram(Program program, int state) {
}
@Override
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) {
}
@Override
public boolean setPersistentOwner(Program program, Object owner) {
return false;
}
@ -238,10 +282,12 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
public void setSearchPriority(Program p, int priority) {
}
@Override
public boolean isLocked() {
return false;
}
@Override
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.
*
* @param data
* the data to be used by the running tool
* @param data the data to be used by the running tool
* @return false if data is not a Program object.
*/
@Override
@ -447,9 +446,9 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
}
/**
* This method notifies listening plugins that a programs has been added to
* the program manager. This is not used for actually opening a program from
* the database and will act strangely if given a closed Program object.
* This method notifies listening plugins that a programs has been added to the program manager.
* This is not used for actually opening a program from the database and will act strangely if
* given a closed Program object.
*
* @see ghidra.app.services.ProgramManager#openProgram(ghidra.program.model.listing.Program)
*/
@ -585,7 +584,7 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
/**
* Set the string chooser property editor on the property that is a filename.
*
* @param options property list
* @param options property list
* @param filePropertyName name of the property that is a filename
*/
private void setPropertyEditor(Options options, String filePropertyName) {
@ -597,8 +596,8 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
}
/**
* Start a transaction if one has not been started; needed when program
* properties are about to change from the options editor.
* Start a transaction if one has not been started; needed when program properties are about to
* change from the options editor.
*/
private void startTransaction(Program currentProgram) {
if (transactionID < 0) {
@ -685,6 +684,26 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
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.
*/
@ -1040,13 +1059,4 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
public boolean isManaged(Program 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;
/**
* Service for managing programs. Multiple programs may be open in a tool, but only one is active
* at any given time.
* Service for managing programs. Multiple programs may be open in a tool, but only one is active at
* 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 {
/**
* Program will be open in a Hidden state if not already open.
* This mode is generally used in conjunction with a persistent
* program owner.
* Program will be open in a Hidden state if not already open. This mode is generally used in
* conjunction with a persistent program owner.
*/
public static final int OPEN_HIDDEN = 0;
@ -45,20 +46,21 @@ public interface ProgramManager {
public static final int OPEN_CURRENT = 1;
/**
* Program will be open within the tool but no change will be made
* to the currently active program. If this is the only program
* open, it will become the currently active program.
* Program will be open within the tool but no change will be made to the currently active
* program. If this is the only program open, it will become the currently active program.
*/
public static final int OPEN_VISIBLE = 2;
/**
* Return the program that is currently active.
*
* @return may return null if no program is open
*/
public Program getCurrentProgram();
/**
* Returns true if the specified program is open and considered visible to the user.
*
* @param program the program
* @return true if the specified program is open and considered visible to the user
*/
@ -66,47 +68,51 @@ public interface ProgramManager {
/**
* 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();
/**
* Open the program corresponding to the given url.
*
* @param ghidraURL valid server-based program URL
* @param state initial open state (OPEN_HIDDEN, OPEN_CURRENT, OPEN_VISIBLE).
* The visibility 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
* occurred and was displayed.
* @param state initial open state (OPEN_HIDDEN, OPEN_CURRENT, OPEN_VISIBLE). The visibility
* 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 occurred and was
* displayed.
* @see GhidraURL
*/
public Program openProgram(URL ghidraURL, int state);
/**
* 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.
*
* @param domainFile domain file that has the program
* @return null if the user canceled the "open" for the new program
*/
public Program openProgram(DomainFile domainFile);
/**
* 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 dialogParent unused
* @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
public Program openProgram(DomainFile domainFile, Component dialogParent);
/**
* Opens the specified version of the program represented by the given DomainFile. This
* method should be used for shared DomainFiles. The newly opened file will be made the
* active program.
* Opens the specified version of the program represented by the given DomainFile. This method
* should be used for shared DomainFiles. The newly opened file will be made the active program.
*
* @param df the DomainFile to open
* @param version the version of the Program to open
* @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
*
* @param domainFile domain file that has the program
* @param version the version of the Program to open. Specify
* DomainFile.DEFAULT_VERSION for file update mode.
* @param state initial open state (OPEN_HIDDEN, OPEN_CURRENT, OPEN_VISIBLE).
* The visibility 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
* occurred and was displayed.
* @param version the version of the Program to open. Specify DomainFile.DEFAULT_VERSION for
* file update mode.
* @param state initial open state (OPEN_HIDDEN, OPEN_CURRENT, OPEN_VISIBLE). The visibility
* 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 occurred and was
* displayed.
*/
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
* may not have it registered as open. The program is made the active program.
* Opens the program to the tool. In this case the program is already open, but this tool may
* not have it registered as open. The program is made the active program.
*
* @param program the program to register as open with the tool.
*/
public void openProgram(Program program);
/**
* Opens the program to the tool. In this case the program is already open, but this tool
* may not have it registered as open. The program is made the active program.
* Opens the program to the tool. In this case the program is already open, but this tool may
* 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 current if true, the program is made the current active program. If false, then
* the program is made active only if it the first open program in the tool.
* @param current if true, the program is made the current active program. If false, then the
* program is made active only if it the first open program in the tool.
* @deprecated use openProgram(Program program, int state) instead.
*/
@Deprecated
@ -145,19 +154,45 @@ public interface ProgramManager {
/**
* Open the specified program in the tool.
*
* @param program the program
* @param state initial open state (OPEN_HIDDEN, OPEN_CURRENT, OPEN_VISIBLE).
* The visibility states will be ignored if the program is already open.
* @param state initial open state (OPEN_HIDDEN, OPEN_CURRENT, OPEN_VISIBLE). The visibility
* states will be ignored if the program is already open.
*/
public void openProgram(Program program, int state);
/**
* Establish a persistent owner on an open program. This will cause the program manager to
* imply make a program hidden if it is closed.
* Saves the current program, possibly prompting the user for a new name.
*/
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 owner the owner
* @return true if program is open and another object is not already the owner,
* or the specified owner is already the owner.
* @return true if program is open and another object is not already the owner, or the specified
* owner is already the owner.
* @see #releaseProgram(Program, Object)
*/
public boolean setPersistentOwner(Program program, Object owner);
@ -165,62 +200,67 @@ public interface ProgramManager {
/**
* Release the persistent ownership of a program.
* <p>
* The program will automatically be closed if it is hidden or was marked as temporary. If
* any of these closures corresponds to a program with changes the user will be given an
* opportunity to save or keep the program open.
* The program will automatically be closed if it is hidden or was marked as temporary. If any
* of these closures corresponds to a program with changes the user will be given an opportunity
* to save or keep the program open.
* <p>
* If persistentOwner is not the correct owner, the method will have no affect.
*
* @param program the program
* @param persistentOwner the owner defined by {@link #setPersistentOwner(Program, Object)}
*/
public void releaseProgram(Program program, Object persistentOwner);
/**
* Closes the given program with the option of saving any changes. The exact behavior of
* this method depends on several factors. First of all, if any other tool has this program
* open, then the program is closed for this tool only and the user is not prompted to
* save the program regardless of the ignoreChanges flag. Otherwise, if ignoreChanges is
* false and changes have been made, the user is prompted to save the program.
* Closes the given program with the option of saving any changes. The exact behavior of this
* method depends on several factors. First of all, if any other tool has this program open,
* then the program is closed for this tool only and the user is not prompted to save the
* program regardless of the ignoreChanges flag. Otherwise, if ignoreChanges is false and
* changes have been made, the user is prompted to save the program.
*
* @param program the program to close.
* @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
* while being prompted to save. Also returns false if the program passed in as a parameter
* is null.
* @return true if the program was closed. Returns false if the user canceled the close while
* being prompted to save. Also returns false if the program passed in as a parameter is
* null.
*/
boolean closeProgram(Program program, boolean ignoreChanges);
/**
* Closes all open programs in this tool except the current program.
* If this tool is the only tool with a program open and that program has changes,
* then the user will be prompted to close each such file.
* (Providing the ignoreChanges flag is false)
* Closes all open programs in this tool except the current program. If this tool is the only
* tool with a program open and that program has changes, then the user will be prompted to
* close each such file. (Providing the ignoreChanges flag is false)
*
* @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
* while being prompted to save.
* while being prompted to save.
*/
public boolean closeOtherPrograms(boolean ignoreChanges);
/**
* Closes all open programs in this tool. If this tool is the only tool with a program
* open and that program has changes, then the user will be prompted to close each such file.
* (Providing the ignoreChanges flag is false)
* Closes all open programs in this tool. If this tool is the only tool with a program open and
* that program has changes, then the user will be prompted to close each such file. (Providing
* the ignoreChanges flag is false)
*
* @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
* while being prompted to save.
* @return true if all programs were closed. Returns false if the user canceled the close while
* being prompted to save.
*/
public boolean closeAllPrograms(boolean ignoreChanges);
/**
* Sets the given program to be the current active program in the tool.
*
* @param p the program to make active.
*/
public void setCurrentProgram(Program p);
/**
* 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.
* Program are initially opened with the PRIORITY_NORMAL priority, but can be set to have
* PRIORITY_HIGH or PRIORITY_LOW.
* Programs are searched in the order they were opened within a given priority. Program are
* initially opened with the PRIORITY_NORMAL priority, but can be set to have PRIORITY_HIGH or
* PRIORITY_LOW.
*
* @param addr the address for which to search.
* @return the first program that can be found to contain the given address.
*/
@ -228,13 +268,15 @@ public interface ProgramManager {
/**
* Returns a list of all open program.
*
* @return the programs
*/
public Program[] getAllOpenPrograms();
/**
* 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.
*
* @param state locked if true, unlocked if false
* @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
*
* @return true if program manager is in the locked state
* @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;
/**
* A stub of the {@link ProgramManager} interface. This can be used to supply a test program
* manager or to spy on system internals by overriding methods as needed.
* A stub of the {@link ProgramManager} interface. This can be used to supply a test program manager
* or to spy on system internals by overriding methods as needed.
*/
public class TestDummyProgramManager implements ProgramManager {
@ -91,6 +91,26 @@ public class TestDummyProgramManager implements ProgramManager {
// 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
public boolean setPersistentOwner(Program program, Object owner) {
// stub

View file

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

View file

@ -49,7 +49,6 @@ public class SleighAssembler implements Assembler {
protected Program program;
protected Listing listing;
protected Memory memory;
protected Disassembler dis;
protected AssemblyParser parser;
protected AssemblyDefaultContext defaultContext;
protected AssemblyContextGraph ctxGraph;
@ -71,8 +70,6 @@ public class SleighAssembler implements Assembler {
this.listing = program.getListing();
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);
listing.clearCodeUnits(at, end, false);
memory.setBytes(at, insbytes);
dis.disassemble(at, new AddressSet(at));
return listing.getInstructions(new AddressSet(at, end), true);
AddressSet set = new AddressSet(at, end);
// 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

View file

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

View file

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