mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +02:00
GP-1214: Adding copy-into-progarm actions (plugin). Moving export action.
This commit is contained in:
parent
71c476bb6a
commit
1b5384c00c
38 changed files with 3855 additions and 505 deletions
|
@ -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|
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
<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" />
|
||||
|
@ -78,6 +79,10 @@
|
|||
sortgroup="c1"
|
||||
target="help/topics/DebuggerConsolePlugin/DebuggerConsolePlugin.html" />
|
||||
|
||||
<tocdef id="DebuggerCopyActionsPlugin" text="Copy Actions"
|
||||
sortgroup="c2"
|
||||
target="help/topics/DebuggerCopyActionsPlugin/DebuggerCopyActionsPlugin.html" />
|
||||
|
||||
<tocdef id="DebuggerObjectsPlugin" text="Commands and Objects"
|
||||
sortgroup="d"
|
||||
target="help/topics/DebuggerObjectsPlugin/DebuggerObjectsPlugin.html" />
|
||||
|
@ -98,14 +103,14 @@
|
|||
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="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" />
|
||||
|
|
|
@ -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><New Program></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><New Program></B>, and <B><Temporary Program></B> are available
|
||||
for selection. Modifying this option will reset the proposal. Choosing <B><New
|
||||
Program></B> will prompt for a new destination program upon a successful copy. Choosing
|
||||
<B><Temporary Program></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>
|
Binary file not shown.
After Width: | Height: | Size: 37 KiB |
|
@ -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 — 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 — 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>
|
||||
|
|
|
@ -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 — 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 — 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>
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(";");
|
||||
|
|
|
@ -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,22 +79,24 @@ 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);
|
||||
|
||||
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 + ": " + toRead);
|
||||
|
||||
for (AddressRange rng : toRead) {
|
||||
long lower = rng.getMinAddress().getOffset();
|
||||
long fullLen = rng.getLength();
|
||||
program + ": " + subsrng);
|
||||
long lower = subsrng.getMinAddress().getOffset();
|
||||
long fullLen = subsrng.getLength();
|
||||
while (fullLen > 0) {
|
||||
int len = MathUtilities.unsignedMin(data.length, fullLen);
|
||||
try {
|
||||
|
@ -101,7 +104,8 @@ public class ReadsTargetMemoryPcodeExecutorState
|
|||
memory.getBytes(space.getAddress(lower), data, 0, len);
|
||||
if (read < len) {
|
||||
Msg.warn(this,
|
||||
" Partial read of " + rng + ". Got " + read + " bytes");
|
||||
" Partial read of " + subsrng + ". Got " + read +
|
||||
" bytes");
|
||||
}
|
||||
cache.putData(lower - shift, data, 0, read);
|
||||
}
|
||||
|
@ -114,6 +118,7 @@ public class ReadsTargetMemoryPcodeExecutorState
|
|||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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)));
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
*/
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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,25 +68,27 @@ 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
|
||||
*/
|
||||
|
@ -93,20 +97,22 @@ public interface ProgramManager {
|
|||
/**
|
||||
* 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,35 +200,37 @@ 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.
|
||||
|
@ -201,26 +238,29 @@ public interface ProgramManager {
|
|||
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,6 +268,7 @@ public interface ProgramManager {
|
|||
|
||||
/**
|
||||
* Returns a list of all open program.
|
||||
*
|
||||
* @return the programs
|
||||
*/
|
||||
public Program[] getAllOpenPrograms();
|
||||
|
@ -235,6 +276,7 @@ public interface ProgramManager {
|
|||
/**
|
||||
* 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
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) & masks[i]) {
|
||||
* return false;
|
||||
* }
|
||||
* }
|
||||
* return false;
|
||||
* for(int i=0;i<bytes.length;i++) { if (bytes[i] != memory.getByte(addr+i) & 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,6 +2230,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
|
||||
/**
|
||||
* Returns a list of all memory blocks that contain any addresses in the given range
|
||||
*
|
||||
* @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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue