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/DebuggerBreakpointsPlugin/images/breakpoints-make-effective.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/DebuggerConsolePlugin/DebuggerConsolePlugin.html||GHIDRA||||END|
|
src/main/help/help/topics/DebuggerConsolePlugin/DebuggerConsolePlugin.html||GHIDRA||||END|
|
||||||
src/main/help/help/topics/DebuggerConsolePlugin/images/DebuggerConsolePlugin.png||GHIDRA||||END|
|
src/main/help/help/topics/DebuggerConsolePlugin/images/DebuggerConsolePlugin.png||GHIDRA||||END|
|
||||||
|
src/main/help/help/topics/DebuggerCopyActionsPlugin/DebuggerCopyActionsPlugin.html||GHIDRA||||END|
|
||||||
|
src/main/help/help/topics/DebuggerCopyActionsPlugin/images/DebuggerCopyIntoProgramDialog.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/DebuggerEmulationServicePlugin/DebuggerEmulationServicePlugin.html||GHIDRA||||END|
|
src/main/help/help/topics/DebuggerEmulationServicePlugin/DebuggerEmulationServicePlugin.html||GHIDRA||||END|
|
||||||
src/main/help/help/topics/DebuggerInterpreterPlugin/DebuggerInterpreterPlugin.html||GHIDRA||||END|
|
src/main/help/help/topics/DebuggerInterpreterPlugin/DebuggerInterpreterPlugin.html||GHIDRA||||END|
|
||||||
src/main/help/help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html||GHIDRA||||END|
|
src/main/help/help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html||GHIDRA||||END|
|
||||||
|
|
|
@ -52,109 +52,114 @@
|
||||||
<tocref id="Ghidra Functionality">
|
<tocref id="Ghidra Functionality">
|
||||||
<tocdef id="Debugger" text="Debugger"
|
<tocdef id="Debugger" text="Debugger"
|
||||||
target="help/topics/Debugger/Debugger.html" >
|
target="help/topics/Debugger/Debugger.html" >
|
||||||
|
|
||||||
<tocdef id="DebuggerGettingStarted" text="Getting Started"
|
|
||||||
sortgroup="a"
|
|
||||||
target="help/topics/Debugger/GettingStarted.html" >
|
|
||||||
<tocdef id="Launching" text="Launching a Target"
|
|
||||||
sortgroup="a"
|
|
||||||
target="help/topics/Debugger/GettingStarted.html#launching" />
|
|
||||||
</tocdef>
|
|
||||||
|
|
||||||
<tocdef id="DebuggerTroubleshooting" text="Troubleshooting"
|
<tocdef id="DebuggerGettingStarted" text="Getting Started"
|
||||||
sortgroup="b"
|
sortgroup="a"
|
||||||
target="help/topics/Debugger/Troubleshooting.html" />
|
target="help/topics/Debugger/GettingStarted.html" >
|
||||||
|
|
||||||
<tocdef id="DebuggerTargetsPlugin" text="Targets"
|
<tocdef id="Launching" text="Launching a Target"
|
||||||
sortgroup="c"
|
sortgroup="a"
|
||||||
target="help/topics/DebuggerTargetsPlugin/DebuggerTargetsPlugin.html" >
|
target="help/topics/Debugger/GettingStarted.html#launching" />
|
||||||
|
</tocdef>
|
||||||
|
|
||||||
<tocdef id="DebuggerModelServicePlugin" text="Tool Actions"
|
<tocdef id="DebuggerTroubleshooting" text="Troubleshooting"
|
||||||
sortgroup="a"
|
sortgroup="b"
|
||||||
target="help/topics/DebuggerModelServicePlugin/DebuggerModelServicePlugin.html" />
|
target="help/topics/Debugger/Troubleshooting.html" />
|
||||||
</tocdef>
|
|
||||||
|
<tocdef id="DebuggerTargetsPlugin" text="Targets"
|
||||||
|
sortgroup="c"
|
||||||
|
target="help/topics/DebuggerTargetsPlugin/DebuggerTargetsPlugin.html" >
|
||||||
|
|
||||||
|
<tocdef id="DebuggerModelServicePlugin" text="Tool Actions"
|
||||||
|
sortgroup="a"
|
||||||
|
target="help/topics/DebuggerModelServicePlugin/DebuggerModelServicePlugin.html" />
|
||||||
|
</tocdef>
|
||||||
|
|
||||||
<tocdef id="DebuggerConsolePlugin" text="Debug Console"
|
<tocdef id="DebuggerConsolePlugin" text="Debug Console"
|
||||||
sortgroup="c1"
|
sortgroup="c1"
|
||||||
target="help/topics/DebuggerConsolePlugin/DebuggerConsolePlugin.html" />
|
target="help/topics/DebuggerConsolePlugin/DebuggerConsolePlugin.html" />
|
||||||
|
|
||||||
<tocdef id="DebuggerObjectsPlugin" text="Commands and Objects"
|
<tocdef id="DebuggerCopyActionsPlugin" text="Copy Actions"
|
||||||
sortgroup="d"
|
sortgroup="c2"
|
||||||
target="help/topics/DebuggerObjectsPlugin/DebuggerObjectsPlugin.html" />
|
target="help/topics/DebuggerCopyActionsPlugin/DebuggerCopyActionsPlugin.html" />
|
||||||
|
|
||||||
<tocdef id="DebuggerInterpreterPlugin" text="Interpreters"
|
<tocdef id="DebuggerObjectsPlugin" text="Commands and Objects"
|
||||||
sortgroup="e"
|
sortgroup="d"
|
||||||
target="help/topics/DebuggerInterpreterPlugin/DebuggerInterpreterPlugin.html" />
|
target="help/topics/DebuggerObjectsPlugin/DebuggerObjectsPlugin.html" />
|
||||||
|
|
||||||
<tocdef id="DebuggerThreadsPlugin" text="Threads and Traces"
|
<tocdef id="DebuggerInterpreterPlugin" text="Interpreters"
|
||||||
sortgroup="f"
|
sortgroup="e"
|
||||||
target="help/topics/DebuggerThreadsPlugin/DebuggerThreadsPlugin.html" />
|
target="help/topics/DebuggerInterpreterPlugin/DebuggerInterpreterPlugin.html" />
|
||||||
|
|
||||||
|
<tocdef id="DebuggerThreadsPlugin" text="Threads and Traces"
|
||||||
|
sortgroup="f"
|
||||||
|
target="help/topics/DebuggerThreadsPlugin/DebuggerThreadsPlugin.html" />
|
||||||
|
|
||||||
<tocdef id="DebuggerTraceManagerServicePlugin" text="Trace Management"
|
<tocdef id="DebuggerTraceManagerServicePlugin" text="Trace Management"
|
||||||
sortgroup="g"
|
sortgroup="g"
|
||||||
target="help/topics/DebuggerTraceManagerServicePlugin/DebuggerTraceManagerServicePlugin.html" />
|
target="help/topics/DebuggerTraceManagerServicePlugin/DebuggerTraceManagerServicePlugin.html" />
|
||||||
|
|
||||||
<tocdef id="DebuggerEmulationServicePlugin" text="Emulation"
|
<tocdef id="DebuggerEmulationServicePlugin" text="Emulation"
|
||||||
sortgroup="g1"
|
sortgroup="g1"
|
||||||
target="help/topics/DebuggerEmulationServicePlugin/DebuggerEmulationServicePlugin.html" />
|
target="help/topics/DebuggerEmulationServicePlugin/DebuggerEmulationServicePlugin.html" />
|
||||||
|
|
||||||
<tocdef id="DebuggerRegistersPlugin" text="Registers"
|
<tocdef id="DebuggerMemoryBytesPlugin" text="Memory"
|
||||||
sortgroup="h"
|
sortgroup="h1"
|
||||||
target="help/topics/DebuggerRegistersPlugin/DebuggerRegistersPlugin.html" />
|
target="help/topics/DebuggerMemoryBytesPlugin/DebuggerMemoryBytesPlugin.html" />
|
||||||
|
|
||||||
<tocdef id="DebuggerMemoryBytesPlugin" text="Memory"
|
<tocdef id="DebuggerRegistersPlugin" text="Registers"
|
||||||
sortgroup="h1"
|
sortgroup="h"
|
||||||
target="help/topics/DebuggerMemoryBytesPlugin/DebuggerMemoryBytesPlugin.html" />
|
target="help/topics/DebuggerRegistersPlugin/DebuggerRegistersPlugin.html" />
|
||||||
|
|
||||||
<tocdef id="DebuggerListingPlugin" text="Dynamic Listing"
|
<tocdef id="DebuggerListingPlugin" text="Dynamic Listing"
|
||||||
sortgroup="i"
|
sortgroup="i"
|
||||||
target="help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html" />
|
target="help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html" />
|
||||||
|
|
||||||
<tocdef id="DebuggerStackPlugin" text="Stack"
|
<tocdef id="DebuggerStackPlugin" text="Stack"
|
||||||
sortgroup="j"
|
sortgroup="j"
|
||||||
target="help/topics/DebuggerStackPlugin/DebuggerStackPlugin.html" />
|
target="help/topics/DebuggerStackPlugin/DebuggerStackPlugin.html" />
|
||||||
|
|
||||||
<tocdef id="DebuggerBreakpointsPlugin" text="Breakpoints"
|
<tocdef id="DebuggerBreakpointsPlugin" text="Breakpoints"
|
||||||
sortgroup="k"
|
sortgroup="k"
|
||||||
target="help/topics/DebuggerBreakpointsPlugin/DebuggerBreakpointsPlugin.html" >
|
target="help/topics/DebuggerBreakpointsPlugin/DebuggerBreakpointsPlugin.html" >
|
||||||
|
|
||||||
<tocdef id="DebuggerBreakpointMarkerPlugin" text="In the Listings"
|
|
||||||
sortgroup="a"
|
|
||||||
target="help/topics/DebuggerBreakpointMarkerPlugin/DebuggerBreakpointMarkerPlugin.html" />
|
|
||||||
</tocdef>
|
|
||||||
|
|
||||||
<tocdef id="DebuggerRegionsPlugin" text="Memory Regions"
|
<tocdef id="DebuggerBreakpointMarkerPlugin" text="In the Listings"
|
||||||
sortgroup="l"
|
sortgroup="a"
|
||||||
target="help/topics/DebuggerRegionsPlugin/DebuggerRegionsPlugin.html" />
|
target="help/topics/DebuggerBreakpointMarkerPlugin/DebuggerBreakpointMarkerPlugin.html" />
|
||||||
|
|
||||||
<tocdef id="DebuggerTimePlugin" text="Time"
|
|
||||||
sortgroup="m"
|
|
||||||
target="help/topics/DebuggerTimePlugin/DebuggerTimePlugin.html" />
|
|
||||||
|
|
||||||
<tocdef id="DebuggerModulesPlugin" text="Modules and Sections"
|
|
||||||
sortgroup="n"
|
|
||||||
target="help/topics/DebuggerModulesPlugin/DebuggerModulesPlugin.html" >
|
|
||||||
|
|
||||||
<tocdef id="DebuggerStaticMappingPlugin" text="Static Mappings"
|
|
||||||
sortgroup="a"
|
|
||||||
target="help/topics/DebuggerStaticMappingPlugin/DebuggerStaticMappingPlugin.html" />
|
|
||||||
</tocdef>
|
</tocdef>
|
||||||
|
|
||||||
|
<tocdef id="DebuggerRegionsPlugin" text="Memory Regions"
|
||||||
|
sortgroup="l"
|
||||||
|
target="help/topics/DebuggerRegionsPlugin/DebuggerRegionsPlugin.html" />
|
||||||
|
|
||||||
|
<tocdef id="DebuggerTimePlugin" text="Time"
|
||||||
|
sortgroup="m"
|
||||||
|
target="help/topics/DebuggerTimePlugin/DebuggerTimePlugin.html" />
|
||||||
|
|
||||||
|
<tocdef id="DebuggerModulesPlugin" text="Modules and Sections"
|
||||||
|
sortgroup="n"
|
||||||
|
target="help/topics/DebuggerModulesPlugin/DebuggerModulesPlugin.html" >
|
||||||
|
|
||||||
|
<tocdef id="DebuggerStaticMappingPlugin" text="Static Mappings"
|
||||||
|
sortgroup="a"
|
||||||
|
target="help/topics/DebuggerStaticMappingPlugin/DebuggerStaticMappingPlugin.html" />
|
||||||
|
</tocdef>
|
||||||
|
|
||||||
<tocdef id="DebuggerWatchesPlugin" text="Watches"
|
<tocdef id="DebuggerWatchesPlugin" text="Watches"
|
||||||
sortgroup="n"
|
sortgroup="n"
|
||||||
target="help/topics/DebuggerWatchesPlugin/DebuggerWatchesPlugin.html" />
|
target="help/topics/DebuggerWatchesPlugin/DebuggerWatchesPlugin.html" />
|
||||||
|
|
||||||
<tocdef id="DebuggerMemviewPlugin" text="Memview Plot"
|
<tocdef id="DebuggerMemviewPlugin" text="Memview Plot"
|
||||||
sortgroup="o"
|
sortgroup="o"
|
||||||
target="help/topics/DebuggerMemviewPlugin/DebuggerMemviewPlugin.html" />
|
target="help/topics/DebuggerMemviewPlugin/DebuggerMemviewPlugin.html" />
|
||||||
|
|
||||||
<tocdef id="DebuggerPcodeStepperPlugin" text="P-code Stepper"
|
<tocdef id="DebuggerPcodeStepperPlugin" text="P-code Stepper"
|
||||||
sortgroup="p"
|
sortgroup="p"
|
||||||
target="help/topics/DebuggerPcodeStepperPlugin/DebuggerPcodeStepperPlugin.html" />
|
target="help/topics/DebuggerPcodeStepperPlugin/DebuggerPcodeStepperPlugin.html" />
|
||||||
|
|
||||||
<tocdef id="DebuggerBots" text="Bots: Workflow Automation"
|
<tocdef id="DebuggerBots" text="Bots: Workflow Automation"
|
||||||
sortgroup="q"
|
sortgroup="q"
|
||||||
target="help/topics/DebuggerBots/DebuggerBots.html" />
|
target="help/topics/DebuggerBots/DebuggerBots.html" />
|
||||||
</tocdef>
|
</tocdef>
|
||||||
</tocref>
|
</tocref>
|
||||||
</tocroot>
|
</tocroot>
|
||||||
|
|
|
@ -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
|
surprising. For example, disassembling some instructions and then stepping back in time will
|
||||||
cause that disassembly to disappear.</P>
|
cause that disassembly to disappear.</P>
|
||||||
|
|
||||||
<P>Because not all memory is captured, some background coloring is used to indicate the state
|
<P>Because not all memory is recorded, some background coloring is used to indicate the state
|
||||||
of attempted memory reads. Regardless of state, the most-recent contents, as recorded in the
|
of attempted memory reads. Regardless of state, the most-recent contents, as recorded in the
|
||||||
trace, are displayed in the listing, defaulting to 00. "Stale" memory, that is ranges of memory
|
trace, are displayed in the listing, defaulting to 00. "Stale" memory, that is ranges of memory
|
||||||
which have not been read at the current time, are displayed with a darker background. Where
|
which have not been read at the current time, are displayed with a darker background. Where
|
||||||
|
@ -142,33 +142,32 @@
|
||||||
and/or needs a version upgrade. It will attempt to open the program, allowing Ghidra to prompt
|
and/or needs a version upgrade. It will attempt to open the program, allowing Ghidra to prompt
|
||||||
you about the situation.</P>
|
you about the situation.</P>
|
||||||
|
|
||||||
<H3><A name="capture_memory"></A>Capture Memory</H3>
|
<H3><A name="read_memory"></A>Read Memory</H3>
|
||||||
|
|
||||||
<P>This action is available when the current trace is "at the present" with a live target, and
|
<P>This action is available when the current trace is "at the present" with a live target, and
|
||||||
there is a selection of addresses in the dynamic listing. It will instruct the recorder to
|
there is a selection of addresses in the dynamic listing. It will instruct the recorder to read
|
||||||
capture the contents of memory for the selected range(s). Typically, the viewable addresses are
|
and record the contents of memory for the selected range(s). Typically, the viewable addresses
|
||||||
automatically captured — see the Auto-Read action.</P>
|
are automatically read, anyway — see the Auto-Read action.</P>
|
||||||
|
|
||||||
<H3><A name="auto_memory"></A>Auto-Read Memory</H3>
|
<H3><A name="auto_memory"></A>Auto-Read Memory</H3>
|
||||||
|
|
||||||
<P>This action is always available on all dynamic listings. It configures whether or not the
|
<P>This action is always available on all dynamic listings. It configures whether or not the
|
||||||
memory range(s) displayed in the listing are automatically captured. Like the Capture Memory
|
memory range(s) displayed in the listing are automatically read and recorded. Like the Read
|
||||||
action, capture can only occur when the current trace is "at the present" with a live target.
|
Memory action, it is only permitted when the current trace is "at the present" with a live
|
||||||
It occurs when the user scrolls the listing, or when the listing is otherwise navigated to a
|
target. It occurs when the user scrolls the listing, or when the listing is otherwise navigated
|
||||||
new location. Note that other components may capture memory, regardless of this listing's
|
to a new location. Note that other components may read memory, regardless of this listing's
|
||||||
configuration. For example, the recorder typically captures the page of memory pointed to by
|
configuration. For example, the recorder typically reads the page of memory pointed to by the
|
||||||
the program counter. In other words, this action <EM>cannot</EM> "disable all memory captures."
|
program counter. In other words, this action <EM>cannot</EM> "disable all memory reads." The
|
||||||
The options are pluggable, but currently consist of:</P>
|
options are pluggable, but currently consist of:</P>
|
||||||
|
|
||||||
<UL>
|
<UL>
|
||||||
<LI>Do Not Read Memory - disables automatic memory capture <EM>for this listing
|
<LI>Do Not Read Memory - disables automatic memory reads <EM>for this listing only</EM>.</LI>
|
||||||
only</EM>.</LI>
|
|
||||||
|
|
||||||
<LI>Read Visible Memory - automatically reads stale ranges that enter this listing's
|
<LI>Read Visible Memory - automatically reads stale ranges that enter this listing's
|
||||||
view.</LI>
|
view.</LI>
|
||||||
|
|
||||||
<LI>Read Visible Memory, RO Once - (default) behaves like Read Visible Memory, except it will
|
<LI>Read Visible Memory, RO Once - (default) behaves like Read Visible Memory, except it will
|
||||||
neglect to capture read-only ranges that have been captured previously.</LI>
|
neglect read-only ranges that have been read previously.</LI>
|
||||||
</UL>
|
</UL>
|
||||||
|
|
||||||
<H2><A name="colors"></A>Tool Options: Colors</H2>
|
<H2><A name="colors"></A>Tool Options: Colors</H2>
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
limitation is that you cannot use snapshots to display different points in time for the same
|
limitation is that you cannot use snapshots to display different points in time for the same
|
||||||
trace.</P>
|
trace.</P>
|
||||||
|
|
||||||
<P>Because not all memory is captured, some background coloring is used to indicate the state
|
<P>Because not all memory is recorded, some background coloring is used to indicate the state
|
||||||
of attempted memory reads. Regardless of state, the most-recent contents, as recorded in the
|
of attempted memory reads. Regardless of state, the most-recent contents, as recorded in the
|
||||||
trace, are displayed in the window, defaulting to 00. "Stale" memory, that is ranges of memory
|
trace, are displayed in the window, defaulting to 00. "Stale" memory, that is ranges of memory
|
||||||
which have not been read at the current time, are displayed with a darker background. Where
|
which have not been read at the current time, are displayed with a darker background. Where
|
||||||
|
@ -103,33 +103,32 @@
|
||||||
</TBODY>
|
</TBODY>
|
||||||
</TABLE>
|
</TABLE>
|
||||||
|
|
||||||
<H3><A name="capture_memory"></A>Capture Memory</H3>
|
<H3><A name="read_memory"></A>Read Memory</H3>
|
||||||
|
|
||||||
<P>This action is available when the current trace is "at the present" with a live target, and
|
<P>This action is available when the current trace is "at the present" with a live target, and
|
||||||
there is a selection of addresses in the memory window. It will instruct the recorder to
|
there is a selection of addresses in the memory window. It will instruct the recorder to read
|
||||||
capture the contents of memory for the selected range(s). Typically, the viewable addresses are
|
and record the contents of memory for the selected range(s). Typically, the viewable addresses
|
||||||
automatically captured — see the Auto-Read action.</P>
|
are automatically read — see the Auto-Read action.</P>
|
||||||
|
|
||||||
<H3><A name="auto_memory"></A>Auto-Read Memory</H3>
|
<H3><A name="auto_memory"></A>Auto-Read Memory</H3>
|
||||||
|
|
||||||
<P>This action is always available on all memory windows. It configures whether or not the
|
<P>This action is always available on all memory windows. It configures whether or not the
|
||||||
memory range(s) displayed in the window are automatically captured. Like the Capture Memory
|
memory range(s) displayed in the window are automatically read and recorded. Like the Read
|
||||||
action, capture can only occur when the current trace is "at the present" with a live target.
|
Memory action, it is only permitted when the current trace is "at the present" with a live
|
||||||
It occurs when the user scrolls the window, or when the window is otherwise navigated to a new
|
target. It occurs when the user scrolls the window, or when the window is otherwise navigated
|
||||||
location. Note that other components may capture memory, regardless of this windows's
|
to a new location. Note that other components may read memory, regardless of this windows's
|
||||||
configuration. For example, the recorder typically captures the page of memory pointed to by
|
configuration. For example, the recorder typically reads the page of memory pointed to by the
|
||||||
the program counter. In other words, this action <EM>cannot</EM> "disable all memory captures."
|
program counter. In other words, this action <EM>cannot</EM> "disable all memory reads." The
|
||||||
The options are pluggable, but currently consist of:</P>
|
options are pluggable, but currently consist of:</P>
|
||||||
|
|
||||||
<UL>
|
<UL>
|
||||||
<LI>Do Not Read Memory - disables automatic memory capture <EM>for this window
|
<LI>Do Not Read Memory - disables automatic memory reads <EM>for this window only</EM>.</LI>
|
||||||
only</EM>.</LI>
|
|
||||||
|
|
||||||
<LI>Read Visible Memory - automatically reads stale ranges that enter this window's
|
<LI>Read Visible Memory - automatically reads stale ranges that enter this window's
|
||||||
view.</LI>
|
view.</LI>
|
||||||
|
|
||||||
<LI>Read Visible Memory, RO Once - (default) behaves like Read Visible Memory, except it will
|
<LI>Read Visible Memory, RO Once - (default) behaves like Read Visible Memory, except it will
|
||||||
neglect to capture read-only ranges that have been captured previously.</LI>
|
neglect read-only ranges that have been read previously.</LI>
|
||||||
</UL>
|
</UL>
|
||||||
|
|
||||||
<H3><A name="Byte_Viewer_Options"></A>Byte Viewer Options</H3>
|
<H3><A name="Byte_Viewer_Options"></A>Byte Viewer Options</H3>
|
||||||
|
|
|
@ -144,8 +144,8 @@ public interface DebuggerResources {
|
||||||
ImageIcon ICON_AUTOREAD = ResourceManager.loadImage("images/autoread.png");
|
ImageIcon ICON_AUTOREAD = ResourceManager.loadImage("images/autoread.png");
|
||||||
|
|
||||||
// TODO: Draw a real icon.
|
// TODO: Draw a real icon.
|
||||||
ImageIcon ICON_CAPTURE_MEMORY = ICON_REGIONS;
|
ImageIcon ICON_READ_MEMORY = ICON_REGIONS;
|
||||||
//ResourceManager.loadImage("images/capture-memory.png");
|
//ResourceManager.loadImage("images/read-memory.png");
|
||||||
|
|
||||||
// TODO: Draw an icon
|
// TODO: Draw an icon
|
||||||
ImageIcon ICON_MAP_IDENTICALLY = ResourceManager.loadImage("images/doubleArrow.png");
|
ImageIcon ICON_MAP_IDENTICALLY = ResourceManager.loadImage("images/doubleArrow.png");
|
||||||
|
@ -777,14 +777,15 @@ public interface DebuggerResources {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class AbstractCaptureSelectedMemoryAction extends DockingAction {
|
abstract class AbstractReadSelectedMemoryAction extends DockingAction {
|
||||||
public static final String NAME = "Capture Selected Memory";
|
public static final String NAME = "Read Selected Memory";
|
||||||
public static final Icon ICON = ICON_CAPTURE_MEMORY;
|
public static final Icon ICON = ICON_READ_MEMORY;
|
||||||
public static final String HELP_ANCHOR = "capture_memory";
|
public static final String HELP_ANCHOR = "read_memory";
|
||||||
|
|
||||||
public AbstractCaptureSelectedMemoryAction(Plugin owner) {
|
public AbstractReadSelectedMemoryAction(Plugin owner) {
|
||||||
super(NAME, owner.getName());
|
super(NAME, owner.getName());
|
||||||
setDescription("Capture memory for the selected addresses into the trace database");
|
setDescription(
|
||||||
|
"(Re-)read and record memory for the selected addresses into the trace database");
|
||||||
setHelpLocation(new HelpLocation(owner.getName(), HELP_ANCHOR));
|
setHelpLocation(new HelpLocation(owner.getName(), HELP_ANCHOR));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -885,7 +886,7 @@ public interface DebuggerResources {
|
||||||
|
|
||||||
interface AutoReadMemoryAction {
|
interface AutoReadMemoryAction {
|
||||||
String NAME = "Auto-Read Target Memory";
|
String NAME = "Auto-Read Target Memory";
|
||||||
String DESCRIPTION = "Automatically capture visible memory from the live target";
|
String DESCRIPTION = "Automatically read and record visible memory from the live target";
|
||||||
String HELP_ANCHOR = "auto_memory";
|
String HELP_ANCHOR = "auto_memory";
|
||||||
|
|
||||||
String NAME_VIS_RO_ONCE = "Read Visible Memory, RO Once";
|
String NAME_VIS_RO_ONCE = "Read Visible Memory, RO Once";
|
||||||
|
@ -1081,7 +1082,43 @@ public interface DebuggerResources {
|
||||||
return new ActionBuilder(NAME, ownerName)
|
return new ActionBuilder(NAME, ownerName)
|
||||||
.description(DESCRIPTION)
|
.description(DESCRIPTION)
|
||||||
.menuGroup(GROUP)
|
.menuGroup(GROUP)
|
||||||
.menuPath(NAME)
|
.menuPath(DebuggerPluginPackage.NAME, NAME)
|
||||||
|
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CopyIntoProgramAction {
|
||||||
|
String NAME_PAT = "Copy Into %s Program";
|
||||||
|
String DESC_PAT = "Copy the current selection into %s program";
|
||||||
|
String GROUP = GROUP_MAINTENANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CopyIntoCurrentProgramAction extends CopyIntoProgramAction {
|
||||||
|
String NAME = String.format(NAME_PAT, "Current");
|
||||||
|
String DESCRIPTION = String.format(DESC_PAT, "the current");
|
||||||
|
String HELP_ANCHOR = "copy_into_current";
|
||||||
|
|
||||||
|
static ActionBuilder builder(Plugin owner) {
|
||||||
|
String ownerName = owner.getName();
|
||||||
|
return new ActionBuilder(NAME, ownerName)
|
||||||
|
.description(DESCRIPTION)
|
||||||
|
.menuGroup(GROUP)
|
||||||
|
.menuPath(DebuggerPluginPackage.NAME, NAME)
|
||||||
|
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CopyIntoNewProgramAction extends CopyIntoProgramAction {
|
||||||
|
String NAME = String.format(NAME_PAT, "New");
|
||||||
|
String DESCRIPTION = String.format(DESC_PAT, "a new");
|
||||||
|
String HELP_ANCHOR = "copy_into_new";
|
||||||
|
|
||||||
|
static ActionBuilder builder(Plugin owner) {
|
||||||
|
String ownerName = owner.getName();
|
||||||
|
return new ActionBuilder(NAME, ownerName)
|
||||||
|
.description(DESCRIPTION)
|
||||||
|
.menuGroup(GROUP)
|
||||||
|
.menuPath(DebuggerPluginPackage.NAME, NAME)
|
||||||
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
|
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ import docking.menu.MultiStateDockingAction;
|
||||||
import docking.widgets.EventTrigger;
|
import docking.widgets.EventTrigger;
|
||||||
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
|
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
|
||||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
|
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
|
||||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractCaptureSelectedMemoryAction;
|
import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractReadSelectedMemoryAction;
|
||||||
import ghidra.app.plugin.core.debug.gui.action.AutoReadMemorySpec.AutoReadMemorySpecConfigFieldCodec;
|
import ghidra.app.plugin.core.debug.gui.action.AutoReadMemorySpec.AutoReadMemorySpecConfigFieldCodec;
|
||||||
import ghidra.app.plugin.core.debug.utils.BackgroundUtils;
|
import ghidra.app.plugin.core.debug.utils.BackgroundUtils;
|
||||||
import ghidra.app.services.TraceRecorder;
|
import ghidra.app.services.TraceRecorder;
|
||||||
|
@ -49,10 +49,10 @@ public abstract class DebuggerReadsMemoryTrait {
|
||||||
protected static final AutoConfigState.ClassHandler<DebuggerReadsMemoryTrait> CONFIG_STATE_HANDLER =
|
protected static final AutoConfigState.ClassHandler<DebuggerReadsMemoryTrait> CONFIG_STATE_HANDLER =
|
||||||
AutoConfigState.wireHandler(DebuggerReadsMemoryTrait.class, MethodHandles.lookup());
|
AutoConfigState.wireHandler(DebuggerReadsMemoryTrait.class, MethodHandles.lookup());
|
||||||
|
|
||||||
protected class CaptureSelectedMemoryAction extends AbstractCaptureSelectedMemoryAction {
|
protected class ReadSelectedMemoryAction extends AbstractReadSelectedMemoryAction {
|
||||||
public static final String GROUP = DebuggerResources.GROUP_GENERAL;
|
public static final String GROUP = DebuggerResources.GROUP_GENERAL;
|
||||||
|
|
||||||
public CaptureSelectedMemoryAction() {
|
public ReadSelectedMemoryAction() {
|
||||||
super(plugin);
|
super(plugin);
|
||||||
setToolBarData(new ToolBarData(ICON, GROUP));
|
setToolBarData(new ToolBarData(ICON, GROUP));
|
||||||
setEnabled(false);
|
setEnabled(false);
|
||||||
|
@ -89,14 +89,14 @@ public abstract class DebuggerReadsMemoryTrait {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected class ForCaptureTraceListener extends TraceDomainObjectListener {
|
protected class ForReadsTraceListener extends TraceDomainObjectListener {
|
||||||
public ForCaptureTraceListener() {
|
public ForReadsTraceListener() {
|
||||||
listenFor(TraceSnapshotChangeType.ADDED, this::snapshotAdded);
|
listenFor(TraceSnapshotChangeType.ADDED, this::snapshotAdded);
|
||||||
listenFor(TraceMemoryStateChangeType.CHANGED, this::memStateChanged);
|
listenFor(TraceMemoryStateChangeType.CHANGED, this::memStateChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void snapshotAdded(TraceSnapshot snapshot) {
|
private void snapshotAdded(TraceSnapshot snapshot) {
|
||||||
actionCaptureSelected.updateEnabled(null);
|
actionReadSelected.updateEnabled(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void memStateChanged(TraceAddressSnapRange range, TraceMemoryState oldIsNull,
|
private void memStateChanged(TraceAddressSnapRange range, TraceMemoryState oldIsNull,
|
||||||
|
@ -120,7 +120,7 @@ public abstract class DebuggerReadsMemoryTrait {
|
||||||
@Override
|
@Override
|
||||||
public void processMemoryAccessibilityChanged(TraceRecorder recorder) {
|
public void processMemoryAccessibilityChanged(TraceRecorder recorder) {
|
||||||
Swing.runIfSwingOrRunLater(() -> {
|
Swing.runIfSwingOrRunLater(() -> {
|
||||||
actionCaptureSelected.updateEnabled(null);
|
actionReadSelected.updateEnabled(null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,7 +137,7 @@ public abstract class DebuggerReadsMemoryTrait {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected MultiStateDockingAction<AutoReadMemorySpec> actionAutoRead;
|
protected MultiStateDockingAction<AutoReadMemorySpec> actionAutoRead;
|
||||||
protected CaptureSelectedMemoryAction actionCaptureSelected;
|
protected ReadSelectedMemoryAction actionReadSelected;
|
||||||
|
|
||||||
private final AutoReadMemorySpec defaultAutoSpec =
|
private final AutoReadMemorySpec defaultAutoSpec =
|
||||||
AutoReadMemorySpec.fromConfigName(VisibleROOnceAutoReadMemorySpec.CONFIG_NAME);
|
AutoReadMemorySpec.fromConfigName(VisibleROOnceAutoReadMemorySpec.CONFIG_NAME);
|
||||||
|
@ -149,8 +149,8 @@ public abstract class DebuggerReadsMemoryTrait {
|
||||||
protected final Plugin plugin;
|
protected final Plugin plugin;
|
||||||
protected final ComponentProvider provider;
|
protected final ComponentProvider provider;
|
||||||
|
|
||||||
protected final ForCaptureTraceListener traceListener =
|
protected final ForReadsTraceListener traceListener =
|
||||||
new ForCaptureTraceListener();
|
new ForReadsTraceListener();
|
||||||
protected final ForAccessRecorderListener recorderListener = new ForAccessRecorderListener();
|
protected final ForAccessRecorderListener recorderListener = new ForAccessRecorderListener();
|
||||||
protected final ForVisibilityListener displayListener = new ForVisibilityListener();
|
protected final ForVisibilityListener displayListener = new ForVisibilityListener();
|
||||||
|
|
||||||
|
@ -257,10 +257,10 @@ public abstract class DebuggerReadsMemoryTrait {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DockingAction installCaptureSelectedAction() {
|
public DockingAction installReadSelectedAction() {
|
||||||
actionCaptureSelected = new CaptureSelectedMemoryAction();
|
actionReadSelected = new ReadSelectedMemoryAction();
|
||||||
provider.addLocalAction(actionCaptureSelected);
|
provider.addLocalAction(actionReadSelected);
|
||||||
return actionCaptureSelected;
|
return actionReadSelected;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AddressSetDisplayListener getDisplayListener() {
|
public AddressSetDisplayListener getDisplayListener() {
|
||||||
|
|
|
@ -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.gui.modules.DebuggerMissingModuleActionContext;
|
||||||
import ghidra.app.plugin.core.debug.utils.ProgramLocationUtils;
|
import ghidra.app.plugin.core.debug.utils.ProgramLocationUtils;
|
||||||
import ghidra.app.plugin.core.debug.utils.ProgramURLUtils;
|
import ghidra.app.plugin.core.debug.utils.ProgramURLUtils;
|
||||||
import ghidra.app.plugin.core.exporter.ExporterDialog;
|
|
||||||
import ghidra.app.services.*;
|
import ghidra.app.services.*;
|
||||||
import ghidra.app.util.viewer.format.FormatManager;
|
import ghidra.app.util.viewer.format.FormatManager;
|
||||||
import ghidra.app.util.viewer.listingpanel.ListingPanel;
|
import ghidra.app.util.viewer.listingpanel.ListingPanel;
|
||||||
|
@ -72,7 +71,6 @@ import ghidra.program.util.ProgramSelection;
|
||||||
import ghidra.trace.model.Trace;
|
import ghidra.trace.model.Trace;
|
||||||
import ghidra.trace.model.modules.*;
|
import ghidra.trace.model.modules.*;
|
||||||
import ghidra.trace.model.program.TraceProgramView;
|
import ghidra.trace.model.program.TraceProgramView;
|
||||||
import ghidra.trace.model.program.TraceVariableSnapProgramView;
|
|
||||||
import ghidra.util.HTMLUtilities;
|
import ghidra.util.HTMLUtilities;
|
||||||
import ghidra.util.Swing;
|
import ghidra.util.Swing;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
|
@ -239,8 +237,7 @@ public class DebuggerListingProvider extends CodeViewerProvider {
|
||||||
protected SyncToStaticListingAction actionSyncToStaticListing;
|
protected SyncToStaticListingAction actionSyncToStaticListing;
|
||||||
protected FollowsCurrentThreadAction actionFollowsCurrentThread;
|
protected FollowsCurrentThreadAction actionFollowsCurrentThread;
|
||||||
protected MultiStateDockingAction<AutoReadMemorySpec> actionAutoReadMemory;
|
protected MultiStateDockingAction<AutoReadMemorySpec> actionAutoReadMemory;
|
||||||
protected DockingAction actionCaptureSelectedMemory;
|
protected DockingAction actionReadSelectedMemory;
|
||||||
protected DockingAction actionExportView;
|
|
||||||
protected DockingAction actionOpenProgram;
|
protected DockingAction actionOpenProgram;
|
||||||
protected MultiStateDockingAction<LocationTrackingSpec> actionTrackLocation;
|
protected MultiStateDockingAction<LocationTrackingSpec> actionTrackLocation;
|
||||||
|
|
||||||
|
@ -608,12 +605,7 @@ public class DebuggerListingProvider extends CodeViewerProvider {
|
||||||
actionGoTo = goToTrait.installAction();
|
actionGoTo = goToTrait.installAction();
|
||||||
actionTrackLocation = trackingTrait.installAction();
|
actionTrackLocation = trackingTrait.installAction();
|
||||||
actionAutoReadMemory = readsMemTrait.installAutoReadAction();
|
actionAutoReadMemory = readsMemTrait.installAutoReadAction();
|
||||||
actionCaptureSelectedMemory = readsMemTrait.installCaptureSelectedAction();
|
actionReadSelectedMemory = readsMemTrait.installReadSelectedAction();
|
||||||
|
|
||||||
actionExportView = ExportTraceViewAction.builder(plugin)
|
|
||||||
.enabledWhen(ctx -> current.getView() != null)
|
|
||||||
.onAction(this::activatedExportView)
|
|
||||||
.buildAndInstallLocal(this);
|
|
||||||
|
|
||||||
actionOpenProgram = OpenProgramAction.builder(plugin)
|
actionOpenProgram = OpenProgramAction.builder(plugin)
|
||||||
.withContext(DebuggerOpenProgramActionContext.class)
|
.withContext(DebuggerOpenProgramActionContext.class)
|
||||||
|
@ -623,20 +615,6 @@ public class DebuggerListingProvider extends CodeViewerProvider {
|
||||||
contextChanged();
|
contextChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void activatedExportView(ActionContext context) {
|
|
||||||
if (current.getView() == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Avoid odd race conditions by fixing the snap
|
|
||||||
TraceProgramView fixed = current.getView() instanceof TraceVariableSnapProgramView
|
|
||||||
? current.getTrace().getFixedProgramView(current.getSnap())
|
|
||||||
: current.getView();
|
|
||||||
|
|
||||||
ExporterDialog dialog =
|
|
||||||
new ExporterDialog(tool, fixed.getDomainFile(), fixed, getSelection());
|
|
||||||
tool.showDialog(dialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void activatedOpenProgram(DebuggerOpenProgramActionContext context) {
|
private void activatedOpenProgram(DebuggerOpenProgramActionContext context) {
|
||||||
programManager.openProgram(context.getDomainFile(), DomainFile.DEFAULT_VERSION,
|
programManager.openProgram(context.getDomainFile(), DomainFile.DEFAULT_VERSION,
|
||||||
ProgramManager.OPEN_CURRENT);
|
ProgramManager.OPEN_CURRENT);
|
||||||
|
|
|
@ -148,7 +148,7 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
|
||||||
protected DockingAction actionGoTo;
|
protected DockingAction actionGoTo;
|
||||||
protected FollowsCurrentThreadAction actionFollowsCurrentThread;
|
protected FollowsCurrentThreadAction actionFollowsCurrentThread;
|
||||||
protected MultiStateDockingAction<AutoReadMemorySpec> actionAutoReadMemory;
|
protected MultiStateDockingAction<AutoReadMemorySpec> actionAutoReadMemory;
|
||||||
protected DockingAction actionCaptureSelectedMemory;
|
protected DockingAction actionReadSelectedMemory;
|
||||||
protected MultiStateDockingAction<LocationTrackingSpec> actionTrackLocation;
|
protected MultiStateDockingAction<LocationTrackingSpec> actionTrackLocation;
|
||||||
|
|
||||||
protected ForMemoryBytesGoToTrait goToTrait;
|
protected ForMemoryBytesGoToTrait goToTrait;
|
||||||
|
@ -257,7 +257,7 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
|
||||||
actionGoTo = goToTrait.installAction();
|
actionGoTo = goToTrait.installAction();
|
||||||
actionTrackLocation = trackingTrait.installAction();
|
actionTrackLocation = trackingTrait.installAction();
|
||||||
actionAutoReadMemory = readsMemTrait.installAutoReadAction();
|
actionAutoReadMemory = readsMemTrait.installAutoReadAction();
|
||||||
actionCaptureSelectedMemory = readsMemTrait.installCaptureSelectedAction();
|
actionReadSelectedMemory = readsMemTrait.installReadSelectedAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -37,7 +37,7 @@ import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
import utilities.util.IDHashed;
|
import utilities.util.IDHashed;
|
||||||
|
|
||||||
interface LogicalBreakpointInternal extends LogicalBreakpoint {
|
public interface LogicalBreakpointInternal extends LogicalBreakpoint {
|
||||||
public static class ProgramBreakpoint {
|
public static class ProgramBreakpoint {
|
||||||
public static Set<TraceBreakpointKind> kindsFromBookmark(Bookmark mark) {
|
public static Set<TraceBreakpointKind> kindsFromBookmark(Bookmark mark) {
|
||||||
String[] parts = mark.getCategory().split(";");
|
String[] parts = mark.getCategory().split(";");
|
||||||
|
|
|
@ -15,10 +15,11 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.debug.service.emulation;
|
package ghidra.app.plugin.core.debug.service.emulation;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
import ghidra.app.services.DebuggerStaticMappingService;
|
import ghidra.app.services.DebuggerStaticMappingService;
|
||||||
import ghidra.app.services.DebuggerStaticMappingService.ShiftAndAddressSetView;
|
import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
|
||||||
import ghidra.app.services.TraceRecorder;
|
import ghidra.app.services.TraceRecorder;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
|
@ -78,40 +79,44 @@ public class ReadsTargetMemoryPcodeExecutorState
|
||||||
DebuggerStaticMappingService mappingService =
|
DebuggerStaticMappingService mappingService =
|
||||||
tool.getService(DebuggerStaticMappingService.class);
|
tool.getService(DebuggerStaticMappingService.class);
|
||||||
byte[] data = new byte[4096];
|
byte[] data = new byte[4096];
|
||||||
for (Entry<Program, ShiftAndAddressSetView> ent : mappingService
|
for (Entry<Program, Collection<MappedAddressRange>> ent : mappingService
|
||||||
.getOpenMappedViews(trace, unknown, snap)
|
.getOpenMappedViews(trace, unknown, snap)
|
||||||
.entrySet()) {
|
.entrySet()) {
|
||||||
Program program = ent.getKey();
|
Program program = ent.getKey();
|
||||||
ShiftAndAddressSetView shifted = ent.getValue();
|
|
||||||
long shift = shifted.getShift();
|
|
||||||
Memory memory = program.getMemory();
|
Memory memory = program.getMemory();
|
||||||
AddressSetView initialized = memory.getLoadedAndInitializedAddressSet();
|
AddressSetView initialized = memory.getLoadedAndInitializedAddressSet();
|
||||||
AddressSetView toRead = shifted.getAddressSetView().intersect(initialized);
|
|
||||||
Msg.warn(this,
|
|
||||||
"Filling in unknown trace memory in emulator using mapped image: " +
|
|
||||||
program + ": " + toRead);
|
|
||||||
|
|
||||||
for (AddressRange rng : toRead) {
|
Collection<MappedAddressRange> mappedSet = ent.getValue();
|
||||||
long lower = rng.getMinAddress().getOffset();
|
for (MappedAddressRange mappedRng : mappedSet) {
|
||||||
long fullLen = rng.getLength();
|
AddressRange srng = mappedRng.getSourceAddressRange();
|
||||||
while (fullLen > 0) {
|
long shift = mappedRng.getShift();
|
||||||
int len = MathUtilities.unsignedMin(data.length, fullLen);
|
for (AddressRange subsrng : initialized.intersectRange(srng.getMinAddress(),
|
||||||
try {
|
srng.getMaxAddress())) {
|
||||||
int read =
|
Msg.warn(this,
|
||||||
memory.getBytes(space.getAddress(lower), data, 0, len);
|
"Filling in unknown trace memory in emulator using mapped image: " +
|
||||||
if (read < len) {
|
program + ": " + subsrng);
|
||||||
Msg.warn(this,
|
long lower = subsrng.getMinAddress().getOffset();
|
||||||
" Partial read of " + rng + ". Got " + read + " bytes");
|
long fullLen = subsrng.getLength();
|
||||||
|
while (fullLen > 0) {
|
||||||
|
int len = MathUtilities.unsignedMin(data.length, fullLen);
|
||||||
|
try {
|
||||||
|
int read =
|
||||||
|
memory.getBytes(space.getAddress(lower), data, 0, len);
|
||||||
|
if (read < len) {
|
||||||
|
Msg.warn(this,
|
||||||
|
" Partial read of " + subsrng + ". Got " + read +
|
||||||
|
" bytes");
|
||||||
|
}
|
||||||
|
cache.putData(lower - shift, data, 0, read);
|
||||||
}
|
}
|
||||||
cache.putData(lower - shift, data, 0, read);
|
catch (MemoryAccessException | AddressOutOfBoundsException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
lower += len;
|
||||||
|
fullLen -= len;
|
||||||
}
|
}
|
||||||
catch (MemoryAccessException | AddressOutOfBoundsException e) {
|
result = true;
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
lower += len;
|
|
||||||
fullLen -= len;
|
|
||||||
}
|
}
|
||||||
result = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -283,7 +283,6 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
||||||
|
|
||||||
private Program program;
|
private Program program;
|
||||||
private AddressRange staticRange;
|
private AddressRange staticRange;
|
||||||
private Long shift; // from static image to trace
|
|
||||||
|
|
||||||
public MappingEntry(TraceStaticMapping mapping) {
|
public MappingEntry(TraceStaticMapping mapping) {
|
||||||
this.mapping = mapping;
|
this.mapping = mapping;
|
||||||
|
@ -309,8 +308,6 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
||||||
Address minAddr = opened.getAddressFactory().getAddress(mapping.getStaticAddress());
|
Address minAddr = opened.getAddressFactory().getAddress(mapping.getStaticAddress());
|
||||||
Address maxAddr = addOrMax(minAddr, mapping.getLength() - 1);
|
Address maxAddr = addOrMax(minAddr, mapping.getLength() - 1);
|
||||||
this.staticRange = new AddressRangeImpl(minAddr, maxAddr);
|
this.staticRange = new AddressRangeImpl(minAddr, maxAddr);
|
||||||
this.shift = mapping.getMinTraceAddress().getOffset() -
|
|
||||||
staticRange.getMinAddress().getOffset();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -320,7 +317,6 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
||||||
if (this.program == closed) {
|
if (this.program == closed) {
|
||||||
this.program = null;
|
this.program = null;
|
||||||
this.staticRange = null;
|
this.staticRange = null;
|
||||||
this.shift = null;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -565,7 +561,7 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void collectOpenMappedPrograms(AddressRange rng, Range<Long> span,
|
protected void collectOpenMappedPrograms(AddressRange rng, Range<Long> span,
|
||||||
Map<Program, ShiftAndAddressSetView> result) {
|
Map<Program, Collection<MappedAddressRange>> result) {
|
||||||
TraceAddressSnapRange tatr = new ImmutableTraceAddressSnapRange(rng, span);
|
TraceAddressSnapRange tatr = new ImmutableTraceAddressSnapRange(rng, span);
|
||||||
for (Entry<TraceAddressSnapRange, MappingEntry> out : outbound.entrySet()) {
|
for (Entry<TraceAddressSnapRange, MappingEntry> out : outbound.entrySet()) {
|
||||||
MappingEntry me = out.getValue();
|
MappingEntry me = out.getValue();
|
||||||
|
@ -575,16 +571,16 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
||||||
if (!out.getKey().intersects(tatr)) {
|
if (!out.getKey().intersects(tatr)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
AddressRange srcRng = out.getKey().getRange().intersect(rng);
|
||||||
ShiftAndAddressSetView set = result.computeIfAbsent(me.program,
|
AddressRange dstRng = me.mapTraceRangeToProgram(rng);
|
||||||
p -> new ShiftAndAddressSetView(-me.shift, new AddressSet()));
|
result.computeIfAbsent(me.program, p -> new TreeSet<>())
|
||||||
((AddressSet) set.getAddressSetView()).add(me.mapTraceRangeToProgram(rng));
|
.add(new MappedAddressRange(srcRng, dstRng));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<Program, ShiftAndAddressSetView> getOpenMappedViews(AddressSetView set,
|
public Map<Program, Collection<MappedAddressRange>> getOpenMappedViews(AddressSetView set,
|
||||||
Range<Long> span) {
|
Range<Long> span) {
|
||||||
Map<Program, ShiftAndAddressSetView> result = new HashMap<>();
|
Map<Program, Collection<MappedAddressRange>> result = new HashMap<>();
|
||||||
for (AddressRange rng : set) {
|
for (AddressRange rng : set) {
|
||||||
collectOpenMappedPrograms(rng, span, result);
|
collectOpenMappedPrograms(rng, span, result);
|
||||||
}
|
}
|
||||||
|
@ -715,7 +711,7 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void collectOpenMappedViews(AddressRange rng,
|
protected void collectOpenMappedViews(AddressRange rng,
|
||||||
Map<TraceSnap, ShiftAndAddressSetView> result) {
|
Map<TraceSnap, Collection<MappedAddressRange>> result) {
|
||||||
for (Entry<MappingEntry, Address> inPreceeding : inbound.headMapByValue(
|
for (Entry<MappingEntry, Address> inPreceeding : inbound.headMapByValue(
|
||||||
rng.getMaxAddress(), true).entrySet()) {
|
rng.getMaxAddress(), true).entrySet()) {
|
||||||
Address start = inPreceeding.getValue();
|
Address start = inPreceeding.getValue();
|
||||||
|
@ -726,14 +722,17 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
||||||
if (!me.isInProgramRange(rng)) {
|
if (!me.isInProgramRange(rng)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ShiftAndAddressSetView set = result.computeIfAbsent(me.getTraceSnap(),
|
|
||||||
p -> new ShiftAndAddressSetView(me.shift, new AddressSet()));
|
AddressRange srcRange = me.staticRange.intersect(rng);
|
||||||
((AddressSet) set.getAddressSetView()).add(me.mapProgramRangeToTrace(rng));
|
AddressRange dstRange = me.mapProgramRangeToTrace(rng);
|
||||||
|
result.computeIfAbsent(me.getTraceSnap(), p -> new TreeSet<>())
|
||||||
|
.add(new MappedAddressRange(srcRange, dstRange));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<TraceSnap, ShiftAndAddressSetView> getOpenMappedViews(AddressSetView set) {
|
public Map<TraceSnap, Collection<MappedAddressRange>> getOpenMappedViews(
|
||||||
Map<TraceSnap, ShiftAndAddressSetView> result = new HashMap<>();
|
AddressSetView set) {
|
||||||
|
Map<TraceSnap, Collection<MappedAddressRange>> result = new HashMap<>();
|
||||||
for (AddressRange rng : set) {
|
for (AddressRange rng : set) {
|
||||||
collectOpenMappedViews(rng, result);
|
collectOpenMappedViews(rng, result);
|
||||||
}
|
}
|
||||||
|
@ -1219,9 +1218,8 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<Program, ShiftAndAddressSetView> getOpenMappedViews(Trace trace,
|
public Map<Program, Collection<MappedAddressRange>> getOpenMappedViews(Trace trace,
|
||||||
AddressSetView set,
|
AddressSetView set, long snap) {
|
||||||
long snap) {
|
|
||||||
InfoPerTrace info = requireTrackedInfo(trace);
|
InfoPerTrace info = requireTrackedInfo(trace);
|
||||||
if (info == null) {
|
if (info == null) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -1230,7 +1228,7 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<TraceSnap, ShiftAndAddressSetView> getOpenMappedViews(Program program,
|
public Map<TraceSnap, Collection<MappedAddressRange>> getOpenMappedViews(Program program,
|
||||||
AddressSetView set) {
|
AddressSetView set) {
|
||||||
InfoPerProgram info = requireTrackedInfo(program);
|
InfoPerProgram info = requireTrackedInfo(program);
|
||||||
if (info == null) {
|
if (info == null) {
|
||||||
|
|
|
@ -487,27 +487,38 @@ public interface DebuggerStaticMappingService {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <<<<<<< HEAD A {@code (shift,view)} pair for describing sets of mapped addresses
|
* A pair for describing sets of mapped addresses
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Note, the natural order is by the <em>destination</em> address.
|
||||||
*/
|
*/
|
||||||
public class ShiftAndAddressSetView {
|
public class MappedAddressRange implements Comparable<MappedAddressRange> {
|
||||||
private final long shift;
|
|
||||||
private final AddressSetView view;
|
|
||||||
|
|
||||||
public ShiftAndAddressSetView(long shift, AddressSetView view) {
|
private final AddressRange srcRange;
|
||||||
this.shift = shift;
|
private final AddressRange dstRange;
|
||||||
this.view = view;
|
private final int hashCode;
|
||||||
|
private final long shift;
|
||||||
|
|
||||||
|
public MappedAddressRange(AddressRange srcRange, AddressRange dstRange) {
|
||||||
|
this.srcRange = srcRange;
|
||||||
|
this.dstRange = dstRange;
|
||||||
|
this.hashCode = Objects.hash(dstRange, srcRange);
|
||||||
|
this.shift = dstRange.getMinAddress().getOffset() -
|
||||||
|
srcRange.getMinAddress().getOffset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "<MappedRange " + srcRange + "::" + dstRange + ">";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the shift from the source address set to this address set
|
* Get the shift from the source address range to this address range
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* The meaning depends on what returned this view. If this view is the "static" set, then
|
* The meaning depends on what returned this view. If this view is the "static" range, then
|
||||||
* this shift describes what was added to the offset of the "dynamic" address to get a
|
* this shift describes what was added to the offset of the "dynamic" address to get a
|
||||||
* particular address in this set. Note that since not all addresses from the requested
|
* particular address in the "static" range.
|
||||||
* source set may have been mapped, you cannot simply compare min addresses to obtain this
|
|
||||||
* shift. To "map back" to the source address from a destination address in this set,
|
|
||||||
* <em>subtract</em> this shift.
|
|
||||||
*
|
*
|
||||||
* @return the shift
|
* @return the shift
|
||||||
*/
|
*/
|
||||||
|
@ -516,12 +527,107 @@ public interface DebuggerStaticMappingService {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the destination address set view as mapped from the source address set
|
* Map an address in the source range to the corresponding address in the destination range
|
||||||
*
|
*
|
||||||
* @return the address set
|
* @param saddr the source address (not validated)
|
||||||
|
* @return the destination address
|
||||||
*/
|
*/
|
||||||
public AddressSetView getAddressSetView() {
|
public Address mapSourceToDestination(Address saddr) {
|
||||||
return view;
|
return dstRange.getAddressSpace().getAddress(saddr.getOffset() + shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map an address in the destination range to the corresponding address in the source range
|
||||||
|
*
|
||||||
|
* @param daddr the destination address (not validated)
|
||||||
|
* @return the source address
|
||||||
|
*/
|
||||||
|
public Address mapDestinationToSource(Address daddr) {
|
||||||
|
return srcRange.getAddressSpace().getAddress(daddr.getOffset() - shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map a sub-range of the source to the corresponding sub-range of the destination
|
||||||
|
*
|
||||||
|
* @param srng the source sub-range
|
||||||
|
* @return the destination sub-range
|
||||||
|
*/
|
||||||
|
public AddressRange mapSourceToDestination(AddressRange srng) {
|
||||||
|
try {
|
||||||
|
return new AddressRangeImpl(mapSourceToDestination(srng.getMinAddress()),
|
||||||
|
srng.getLength());
|
||||||
|
}
|
||||||
|
catch (AddressOverflowException e) {
|
||||||
|
throw new IllegalArgumentException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map a sub-range of the destination to the corresponding sub-range of the source
|
||||||
|
*
|
||||||
|
* @param drng the destination sub-range
|
||||||
|
* @return the source sub-range
|
||||||
|
*/
|
||||||
|
public AddressRange mapDestinationToSource(AddressRange drng) {
|
||||||
|
try {
|
||||||
|
return new AddressRangeImpl(mapDestinationToSource(drng.getMinAddress()),
|
||||||
|
drng.getLength());
|
||||||
|
}
|
||||||
|
catch (AddressOverflowException e) {
|
||||||
|
throw new IllegalArgumentException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the source address range
|
||||||
|
*
|
||||||
|
* @return the address range
|
||||||
|
*/
|
||||||
|
public AddressRange getSourceAddressRange() {
|
||||||
|
return srcRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the destination address range
|
||||||
|
*
|
||||||
|
* @return the address range
|
||||||
|
*/
|
||||||
|
public AddressRange getDestinationAddressRange() {
|
||||||
|
return dstRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(MappedAddressRange that) {
|
||||||
|
int c;
|
||||||
|
c = this.dstRange.compareTo(that.dstRange);
|
||||||
|
if (c != 0) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
c = this.srcRange.compareTo(that.srcRange);
|
||||||
|
if (c != 0) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (!(obj instanceof MappedAddressRange)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
MappedAddressRange that = (MappedAddressRange) obj;
|
||||||
|
if (!this.dstRange.equals(that.dstRange)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!this.srcRange.equals(that.srcRange)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return hashCode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -570,8 +676,7 @@ public interface DebuggerStaticMappingService {
|
||||||
boolean truncateExisting) throws TraceConflictedMappingException;
|
boolean truncateExisting) throws TraceConflictedMappingException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ======= >>>>>>> d694542c5 (GP-660: Put program filler back in. Need to performance test.) Add
|
* Add several static mappings (relocations)
|
||||||
* several static mappings (relocations)
|
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* This will group the entries by trace and add each's entries in a single transaction. If any
|
* This will group the entries by trace and add each's entries in a single transaction. If any
|
||||||
|
@ -669,9 +774,9 @@ public interface DebuggerStaticMappingService {
|
||||||
* @param trace the source trace
|
* @param trace the source trace
|
||||||
* @param set the source address set
|
* @param set the source address set
|
||||||
* @param snap the source snap
|
* @param snap the source snap
|
||||||
* @return a map of destination programs to corresponding computed destination address sets
|
* @return a map of destination programs to corresponding computed destination address ranges
|
||||||
*/
|
*/
|
||||||
Map<Program, ShiftAndAddressSetView> getOpenMappedViews(Trace trace,
|
Map<Program, Collection<MappedAddressRange>> getOpenMappedViews(Trace trace,
|
||||||
AddressSetView set, long snap);
|
AddressSetView set, long snap);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -679,9 +784,9 @@ public interface DebuggerStaticMappingService {
|
||||||
*
|
*
|
||||||
* @param program the destination program, from which we are mapping back
|
* @param program the destination program, from which we are mapping back
|
||||||
* @param set the destination address set, from which we are mapping back
|
* @param set the destination address set, from which we are mapping back
|
||||||
* @return a map of source traces to corresponding computed source address sets
|
* @return a map of source traces to corresponding computed source address ranges
|
||||||
*/
|
*/
|
||||||
Map<TraceSnap, ShiftAndAddressSetView> getOpenMappedViews(Program program,
|
Map<TraceSnap, Collection<MappedAddressRange>> getOpenMappedViews(Program program,
|
||||||
AddressSetView set);
|
AddressSetView set);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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);
|
tool.getProject().getProjectData().getRootFolder().createFile(obj.getName(), obj, monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void createSnaplessTrace(String langID) throws IOException {
|
||||||
|
tb = new ToyDBTraceBuilder("dynamic-" + name.getMethodName(), langID);
|
||||||
|
}
|
||||||
|
|
||||||
protected void createSnaplessTrace() throws IOException {
|
protected void createSnaplessTrace() throws IOException {
|
||||||
tb = new ToyDBTraceBuilder("dynamic-" + name.getMethodName(), LANGID_TOYBE64);
|
createSnaplessTrace(LANGID_TOYBE64);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addSnapshot(String desc) throws IOException {
|
protected void addSnapshot(String desc) throws IOException {
|
||||||
|
@ -505,16 +509,28 @@ public abstract class AbstractGhidraHeadedDebuggerGUITest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void createTrace() throws IOException {
|
protected void createTrace(String langID) throws IOException {
|
||||||
createSnaplessTrace();
|
createSnaplessTrace(langID);
|
||||||
addSnapshot("First snap");
|
addSnapshot("First snap");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void createAndOpenTrace() throws IOException {
|
protected void createTrace() throws IOException {
|
||||||
createTrace();
|
createTrace(LANGID_TOYBE64);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void useTrace(Trace trace) {
|
||||||
|
tb = new ToyDBTraceBuilder(trace);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void createAndOpenTrace(String langID) throws IOException {
|
||||||
|
createTrace(langID);
|
||||||
traceManager.openTrace(tb.trace);
|
traceManager.openTrace(tb.trace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void createAndOpenTrace() throws IOException {
|
||||||
|
createAndOpenTrace(LANGID_TOYBE64);
|
||||||
|
}
|
||||||
|
|
||||||
protected String getProgramName() {
|
protected String getProgramName() {
|
||||||
return "static-" + getClass().getCanonicalName() + "." + name.getMethodName();
|
return "static-" + getClass().getCanonicalName() + "." + name.getMethodName();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
@Test
|
||||||
@Ignore("TODO") // Needs attention, but low priority
|
@Ignore("TODO") // Needs attention, but low priority
|
||||||
public void testActionCaptureSelectedMemory() throws Exception {
|
public void testActionReadSelectedMemory() throws Exception {
|
||||||
byte[] data = incBlock();
|
byte[] data = incBlock();
|
||||||
byte[] zero = new byte[data.length];
|
byte[] zero = new byte[data.length];
|
||||||
ByteBuffer buf = ByteBuffer.allocate(data.length);
|
ByteBuffer buf = ByteBuffer.allocate(data.length);
|
||||||
assertFalse(listingProvider.actionCaptureSelectedMemory.isEnabled());
|
assertFalse(listingProvider.actionReadSelectedMemory.isEnabled());
|
||||||
listingProvider.setAutoReadMemorySpec(readNone);
|
listingProvider.setAutoReadMemorySpec(readNone);
|
||||||
|
|
||||||
// To verify enabled requires live target
|
// To verify enabled requires live target
|
||||||
|
@ -1002,12 +1002,12 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
||||||
traceManager.activateTrace(tb.trace);
|
traceManager.activateTrace(tb.trace);
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
// Still
|
// Still
|
||||||
assertFalse(listingProvider.actionCaptureSelectedMemory.isEnabled());
|
assertFalse(listingProvider.actionReadSelectedMemory.isEnabled());
|
||||||
|
|
||||||
listingProvider.setSelection(sel);
|
listingProvider.setSelection(sel);
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
// Still
|
// Still
|
||||||
assertFalse(listingProvider.actionCaptureSelectedMemory.isEnabled());
|
assertFalse(listingProvider.actionReadSelectedMemory.isEnabled());
|
||||||
|
|
||||||
// Now, simulate the sequence that typically enables the action
|
// Now, simulate the sequence that typically enables the action
|
||||||
createTestModel();
|
createTestModel();
|
||||||
|
@ -1024,12 +1024,12 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
||||||
|
|
||||||
// NOTE: recordTargetContainerAndOpenTrace has already activated the trace
|
// NOTE: recordTargetContainerAndOpenTrace has already activated the trace
|
||||||
// Action is still disabled, because it requires a selection
|
// Action is still disabled, because it requires a selection
|
||||||
assertFalse(listingProvider.actionCaptureSelectedMemory.isEnabled());
|
assertFalse(listingProvider.actionReadSelectedMemory.isEnabled());
|
||||||
|
|
||||||
listingProvider.setSelection(sel);
|
listingProvider.setSelection(sel);
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
// Now, it should be enabled
|
// Now, it should be enabled
|
||||||
assertTrue(listingProvider.actionCaptureSelectedMemory.isEnabled());
|
assertTrue(listingProvider.actionReadSelectedMemory.isEnabled());
|
||||||
|
|
||||||
// First check nothing captured yet
|
// First check nothing captured yet
|
||||||
buf.clear();
|
buf.clear();
|
||||||
|
@ -1038,7 +1038,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
||||||
assertArrayEquals(zero, buf.array());
|
assertArrayEquals(zero, buf.array());
|
||||||
|
|
||||||
// Verify that the action performs the expected task
|
// Verify that the action performs the expected task
|
||||||
performAction(listingProvider.actionCaptureSelectedMemory);
|
performAction(listingProvider.actionReadSelectedMemory);
|
||||||
waitForBusyTool(tool);
|
waitForBusyTool(tool);
|
||||||
waitForDomainObject(trace);
|
waitForDomainObject(trace);
|
||||||
|
|
||||||
|
@ -1053,28 +1053,28 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
||||||
|
|
||||||
// Verify that setting the memory inaccessible disables the action
|
// Verify that setting the memory inaccessible disables the action
|
||||||
mb.testProcess1.memory.setAccessible(false);
|
mb.testProcess1.memory.setAccessible(false);
|
||||||
waitForPass(() -> assertFalse(listingProvider.actionCaptureSelectedMemory.isEnabled()));
|
waitForPass(() -> assertFalse(listingProvider.actionReadSelectedMemory.isEnabled()));
|
||||||
|
|
||||||
// Verify that setting it accessible re-enables it (assuming we still have selection)
|
// Verify that setting it accessible re-enables it (assuming we still have selection)
|
||||||
mb.testProcess1.memory.setAccessible(true);
|
mb.testProcess1.memory.setAccessible(true);
|
||||||
waitForPass(() -> assertTrue(listingProvider.actionCaptureSelectedMemory.isEnabled()));
|
waitForPass(() -> assertTrue(listingProvider.actionReadSelectedMemory.isEnabled()));
|
||||||
|
|
||||||
// Verify that moving into the past disables the action
|
// Verify that moving into the past disables the action
|
||||||
TraceSnapshot forced = recorder.forceSnapshot();
|
TraceSnapshot forced = recorder.forceSnapshot();
|
||||||
waitForSwing(); // UI Wants to sync with new snap. Wait....
|
waitForSwing(); // UI Wants to sync with new snap. Wait....
|
||||||
traceManager.activateSnap(forced.getKey() - 1);
|
traceManager.activateSnap(forced.getKey() - 1);
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
assertFalse(listingProvider.actionCaptureSelectedMemory.isEnabled());
|
assertFalse(listingProvider.actionReadSelectedMemory.isEnabled());
|
||||||
|
|
||||||
// Verify that advancing to the present enables the action (assuming a selection)
|
// Verify that advancing to the present enables the action (assuming a selection)
|
||||||
traceManager.activateSnap(forced.getKey());
|
traceManager.activateSnap(forced.getKey());
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
assertTrue(listingProvider.actionCaptureSelectedMemory.isEnabled());
|
assertTrue(listingProvider.actionReadSelectedMemory.isEnabled());
|
||||||
|
|
||||||
// Verify that stopping the recording disables the action
|
// Verify that stopping the recording disables the action
|
||||||
recorder.stopRecording();
|
recorder.stopRecording();
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
assertFalse(listingProvider.actionCaptureSelectedMemory.isEnabled());
|
assertFalse(listingProvider.actionReadSelectedMemory.isEnabled());
|
||||||
|
|
||||||
// TODO: When resume recording is implemented, verify action is enabled with selection
|
// TODO: When resume recording is implemented, verify action is enabled with selection
|
||||||
}
|
}
|
||||||
|
|
|
@ -741,11 +741,11 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
|
||||||
@Test
|
@Test
|
||||||
@Ignore("TODO") // Needs attention, but low priority
|
@Ignore("TODO") // Needs attention, but low priority
|
||||||
// Accessibility listener does not seem to work
|
// Accessibility listener does not seem to work
|
||||||
public void testActionCaptureSelectedMemory() throws Exception {
|
public void testActionReadSelectedMemory() throws Exception {
|
||||||
byte[] data = incBlock();
|
byte[] data = incBlock();
|
||||||
byte[] zero = new byte[data.length];
|
byte[] zero = new byte[data.length];
|
||||||
ByteBuffer buf = ByteBuffer.allocate(data.length);
|
ByteBuffer buf = ByteBuffer.allocate(data.length);
|
||||||
assertFalse(memBytesProvider.actionCaptureSelectedMemory.isEnabled());
|
assertFalse(memBytesProvider.actionReadSelectedMemory.isEnabled());
|
||||||
memBytesProvider.setAutoReadMemorySpec(readNone);
|
memBytesProvider.setAutoReadMemorySpec(readNone);
|
||||||
|
|
||||||
// To verify enabled requires live target
|
// To verify enabled requires live target
|
||||||
|
@ -760,12 +760,12 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
|
||||||
traceManager.activateTrace(tb.trace);
|
traceManager.activateTrace(tb.trace);
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
// Still
|
// Still
|
||||||
assertFalse(memBytesProvider.actionCaptureSelectedMemory.isEnabled());
|
assertFalse(memBytesProvider.actionReadSelectedMemory.isEnabled());
|
||||||
|
|
||||||
memBytesProvider.setSelection(sel);
|
memBytesProvider.setSelection(sel);
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
// Still
|
// Still
|
||||||
assertFalse(memBytesProvider.actionCaptureSelectedMemory.isEnabled());
|
assertFalse(memBytesProvider.actionReadSelectedMemory.isEnabled());
|
||||||
|
|
||||||
// Now, simulate the sequence that typically enables the action
|
// Now, simulate the sequence that typically enables the action
|
||||||
createTestModel();
|
createTestModel();
|
||||||
|
@ -782,21 +782,21 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
|
||||||
|
|
||||||
// NOTE: recordTargetContainerAndOpenTrace has already activated the trace
|
// NOTE: recordTargetContainerAndOpenTrace has already activated the trace
|
||||||
// Action is still disabled, because it requires a selection
|
// Action is still disabled, because it requires a selection
|
||||||
assertFalse(memBytesProvider.actionCaptureSelectedMemory.isEnabled());
|
assertFalse(memBytesProvider.actionReadSelectedMemory.isEnabled());
|
||||||
|
|
||||||
memBytesProvider.setSelection(sel);
|
memBytesProvider.setSelection(sel);
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
// Now, it should be enabled
|
// Now, it should be enabled
|
||||||
assertTrue(memBytesProvider.actionCaptureSelectedMemory.isEnabled());
|
assertTrue(memBytesProvider.actionReadSelectedMemory.isEnabled());
|
||||||
|
|
||||||
// First check nothing captured yet
|
// First check nothing recorded yet
|
||||||
buf.clear();
|
buf.clear();
|
||||||
assertEquals(data.length,
|
assertEquals(data.length,
|
||||||
trace.getMemoryManager().getBytes(recorder.getSnap(), addr(trace, 0x55550000), buf));
|
trace.getMemoryManager().getBytes(recorder.getSnap(), addr(trace, 0x55550000), buf));
|
||||||
assertArrayEquals(zero, buf.array());
|
assertArrayEquals(zero, buf.array());
|
||||||
|
|
||||||
// Verify that the action performs the expected task
|
// Verify that the action performs the expected task
|
||||||
performAction(memBytesProvider.actionCaptureSelectedMemory);
|
performAction(memBytesProvider.actionReadSelectedMemory);
|
||||||
waitForBusyTool(tool);
|
waitForBusyTool(tool);
|
||||||
waitForDomainObject(trace);
|
waitForDomainObject(trace);
|
||||||
|
|
||||||
|
@ -811,28 +811,28 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
|
||||||
|
|
||||||
// Verify that setting the memory inaccessible disables the action
|
// Verify that setting the memory inaccessible disables the action
|
||||||
mb.testProcess1.memory.setAccessible(false);
|
mb.testProcess1.memory.setAccessible(false);
|
||||||
waitForPass(() -> assertFalse(memBytesProvider.actionCaptureSelectedMemory.isEnabled()));
|
waitForPass(() -> assertFalse(memBytesProvider.actionReadSelectedMemory.isEnabled()));
|
||||||
|
|
||||||
// Verify that setting it accessible re-enables it (assuming we still have selection)
|
// Verify that setting it accessible re-enables it (assuming we still have selection)
|
||||||
mb.testProcess1.memory.setAccessible(true);
|
mb.testProcess1.memory.setAccessible(true);
|
||||||
waitForPass(() -> assertTrue(memBytesProvider.actionCaptureSelectedMemory.isEnabled()));
|
waitForPass(() -> assertTrue(memBytesProvider.actionReadSelectedMemory.isEnabled()));
|
||||||
|
|
||||||
// Verify that moving into the past disables the action
|
// Verify that moving into the past disables the action
|
||||||
TraceSnapshot forced = recorder.forceSnapshot();
|
TraceSnapshot forced = recorder.forceSnapshot();
|
||||||
waitForSwing(); // UI Wants to sync with new snap. Wait....
|
waitForSwing(); // UI Wants to sync with new snap. Wait....
|
||||||
traceManager.activateSnap(forced.getKey() - 1);
|
traceManager.activateSnap(forced.getKey() - 1);
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
assertFalse(memBytesProvider.actionCaptureSelectedMemory.isEnabled());
|
assertFalse(memBytesProvider.actionReadSelectedMemory.isEnabled());
|
||||||
|
|
||||||
// Verify that advancing to the present enables the action (assuming a selection)
|
// Verify that advancing to the present enables the action (assuming a selection)
|
||||||
traceManager.activateSnap(forced.getKey());
|
traceManager.activateSnap(forced.getKey());
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
assertTrue(memBytesProvider.actionCaptureSelectedMemory.isEnabled());
|
assertTrue(memBytesProvider.actionReadSelectedMemory.isEnabled());
|
||||||
|
|
||||||
// Verify that stopping the recording disables the action
|
// Verify that stopping the recording disables the action
|
||||||
recorder.stopRecording();
|
recorder.stopRecording();
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
assertFalse(memBytesProvider.actionCaptureSelectedMemory.isEnabled());
|
assertFalse(memBytesProvider.actionReadSelectedMemory.isEnabled());
|
||||||
|
|
||||||
// TODO: When resume recording is implemented, verify action is enabled with selection
|
// TODO: When resume recording is implemented, verify action is enabled with selection
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ import com.google.common.collect.Range;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
|
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
|
||||||
import ghidra.app.services.DebuggerStaticMappingService;
|
import ghidra.app.services.DebuggerStaticMappingService;
|
||||||
import ghidra.app.services.DebuggerStaticMappingService.ShiftAndAddressSetView;
|
import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
|
||||||
import ghidra.framework.model.DomainFile;
|
import ghidra.framework.model.DomainFile;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
@ -339,7 +339,7 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
|
||||||
public void testAddMappingThenTranslateTraceViewToStaticEmpty() throws Exception {
|
public void testAddMappingThenTranslateTraceViewToStaticEmpty() throws Exception {
|
||||||
addMapping();
|
addMapping();
|
||||||
|
|
||||||
Map<Program, ShiftAndAddressSetView> views =
|
Map<Program, Collection<MappedAddressRange>> views =
|
||||||
mappingService.getOpenMappedViews(tb.trace, new AddressSet(), 0);
|
mappingService.getOpenMappedViews(tb.trace, new AddressSet(), 0);
|
||||||
assertTrue(views.isEmpty());
|
assertTrue(views.isEmpty());
|
||||||
}
|
}
|
||||||
|
@ -360,18 +360,19 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
|
||||||
// After
|
// After
|
||||||
set.add(dynSpace.getAddress(0xbadbadbadL), dynSpace.getAddress(0xbadbadbadL + 0xff));
|
set.add(dynSpace.getAddress(0xbadbadbadL), dynSpace.getAddress(0xbadbadbadL + 0xff));
|
||||||
|
|
||||||
Map<Program, ShiftAndAddressSetView> views =
|
Map<Program, Collection<MappedAddressRange>> views =
|
||||||
mappingService.getOpenMappedViews(tb.trace, set, 0);
|
mappingService.getOpenMappedViews(tb.trace, set, 0);
|
||||||
assertEquals(1, views.size());
|
assertEquals(1, views.size());
|
||||||
ShiftAndAddressSetView shifted = views.get(program);
|
Collection<MappedAddressRange> mappedSet = views.get(program);
|
||||||
assertEquals(0x100000, shifted.getShift());
|
|
||||||
AddressSetView inStatic = shifted.getAddressSetView();
|
assertEquals(Set.of(
|
||||||
assertEquals(3, inStatic.getNumAddressRanges());
|
new MappedAddressRange(tb.range(0x00100000, 0x001000ff),
|
||||||
AddressSet expected = new AddressSet();
|
tb.range(stSpace, 0x00200000, 0x002000ff)),
|
||||||
expected.add(stSpace.getAddress(0x00200000), stSpace.getAddress(0x002000ff));
|
new MappedAddressRange(tb.range(0x00100c0d, 0x00100ccc),
|
||||||
expected.add(stSpace.getAddress(0x00200c0d), stSpace.getAddress(0x00200ccc));
|
tb.range(stSpace, 0x00200c0d, 0x00200ccc)),
|
||||||
expected.add(stSpace.getAddress(0x00201000 - 0x100), stSpace.getAddress(0x00200fff));
|
new MappedAddressRange(tb.range(0x00100f00, 0x00100fff),
|
||||||
assertEquals(expected, inStatic);
|
tb.range(stSpace, 0x00200f00, 0x00200fff))),
|
||||||
|
mappedSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -380,7 +381,7 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
|
||||||
copyTrace();
|
copyTrace();
|
||||||
add2ndMapping();
|
add2ndMapping();
|
||||||
|
|
||||||
Map<TraceSnap, ShiftAndAddressSetView> views =
|
Map<TraceSnap, Collection<MappedAddressRange>> views =
|
||||||
mappingService.getOpenMappedViews(program, new AddressSet());
|
mappingService.getOpenMappedViews(program, new AddressSet());
|
||||||
assertTrue(views.isEmpty());
|
assertTrue(views.isEmpty());
|
||||||
}
|
}
|
||||||
|
@ -403,30 +404,34 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
|
||||||
// After
|
// After
|
||||||
set.add(stSpace.getAddress(0xbadbadbadL), stSpace.getAddress(0xbadbadbadL + 0xff));
|
set.add(stSpace.getAddress(0xbadbadbadL), stSpace.getAddress(0xbadbadbadL + 0xff));
|
||||||
|
|
||||||
Map<TraceSnap, ShiftAndAddressSetView> views =
|
Map<TraceSnap, Collection<MappedAddressRange>> views =
|
||||||
mappingService.getOpenMappedViews(program, set);
|
mappingService.getOpenMappedViews(program, set);
|
||||||
Msg.info(this, views);
|
Msg.info(this, views);
|
||||||
assertEquals(2, views.size());
|
assertEquals(2, views.size());
|
||||||
ShiftAndAddressSetView shifted1 = views.get(new DefaultTraceSnap(tb.trace, 0));
|
Collection<MappedAddressRange> mappedSet1 = views.get(new DefaultTraceSnap(tb.trace, 0));
|
||||||
assertEquals(-0x100000, shifted1.getShift());
|
Collection<MappedAddressRange> mappedSet2 = views.get(new DefaultTraceSnap(copy, 0));
|
||||||
AddressSetView in1st = shifted1.getAddressSetView();
|
|
||||||
assertEquals(5, in1st.getNumAddressRanges());
|
|
||||||
AddressSetView in2nd = views.get(new DefaultTraceSnap(copy, 0)).getAddressSetView();
|
|
||||||
assertEquals(3, in2nd.getNumAddressRanges());
|
|
||||||
|
|
||||||
AddressSet expectedIn1st = new AddressSet();
|
assertEquals(Set.of(
|
||||||
AddressSet expectedIn2nd = new AddressSet();
|
new MappedAddressRange(tb.range(stSpace, 0x00200000, 0x002000ff),
|
||||||
expectedIn1st.add(dynSpace.getAddress(0x00100000), dynSpace.getAddress(0x001000ff));
|
tb.range(0x00100000, 0x001000ff)),
|
||||||
expectedIn1st.add(dynSpace.getAddress(0x00100800 - 0x10),
|
new MappedAddressRange(tb.range(stSpace, 0x002007f0, 0x0020080f),
|
||||||
dynSpace.getAddress(0x00100800 + 0xf));
|
tb.range(0x001007f0, 0x0010080f)),
|
||||||
expectedIn1st.add(dynSpace.getAddress(0x00101000 - 0x100), dynSpace.getAddress(0x00100fff));
|
new MappedAddressRange(tb.range(stSpace, 0x00200f00, 0x00200fff),
|
||||||
expectedIn2nd.add(expectedIn1st);
|
tb.range(0x00100f00, 0x00100fff)),
|
||||||
|
new MappedAddressRange(tb.range(stSpace, 0x00200000, 0x002000ff),
|
||||||
|
tb.range(0x00102000, 0x001020ff)),
|
||||||
|
new MappedAddressRange(tb.range(stSpace, 0x002007f0, 0x002007ff),
|
||||||
|
tb.range(0x001027f0, 0x001027ff))),
|
||||||
|
mappedSet1);
|
||||||
|
|
||||||
expectedIn1st.add(dynSpace.getAddress(0x00102000), dynSpace.getAddress(0x001020ff));
|
assertEquals(Set.of(
|
||||||
expectedIn1st.add(dynSpace.getAddress(0x00102800 - 0x10), dynSpace.getAddress(0x001027ff));
|
new MappedAddressRange(tb.range(stSpace, 0x00200000, 0x002000ff),
|
||||||
|
tb.range(0x00100000, 0x001000ff)),
|
||||||
assertEquals(expectedIn1st, in1st);
|
new MappedAddressRange(tb.range(stSpace, 0x002007f0, 0x0020080f),
|
||||||
assertEquals(expectedIn2nd, in2nd);
|
tb.range(0x001007f0, 0x0010080f)),
|
||||||
|
new MappedAddressRange(tb.range(stSpace, 0x00200f00, 0x00200fff),
|
||||||
|
tb.range(0x00100f00, 0x00100fff))),
|
||||||
|
mappedSet2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -220,9 +220,7 @@ public class DBTraceDataTypeManager extends DataTypeManagerDB
|
||||||
@Override
|
@Override
|
||||||
public DataOrganization getDataOrganization() {
|
public DataOrganization getDataOrganization() {
|
||||||
if (dataOrganization == null) {
|
if (dataOrganization == null) {
|
||||||
// TODO: Do I need to have a base compiler spec?
|
dataOrganization = trace.getBaseCompilerSpec().getDataOrganization();
|
||||||
dataOrganization =
|
|
||||||
trace.getBaseLanguage().getDefaultCompilerSpec().getDataOrganization();
|
|
||||||
}
|
}
|
||||||
return dataOrganization;
|
return dataOrganization;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ import ghidra.program.model.mem.MemBuffer;
|
||||||
import ghidra.program.model.mem.MemoryAccessException;
|
import ghidra.program.model.mem.MemoryAccessException;
|
||||||
import ghidra.program.model.symbol.*;
|
import ghidra.program.model.symbol.*;
|
||||||
import ghidra.trace.database.DBTraceUtils;
|
import ghidra.trace.database.DBTraceUtils;
|
||||||
|
import ghidra.trace.database.context.DBTraceRegisterContextManager;
|
||||||
import ghidra.trace.database.context.DBTraceRegisterContextSpace;
|
import ghidra.trace.database.context.DBTraceRegisterContextSpace;
|
||||||
import ghidra.trace.database.language.DBTraceGuestLanguage;
|
import ghidra.trace.database.language.DBTraceGuestLanguage;
|
||||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
|
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
|
||||||
|
@ -621,13 +622,12 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
||||||
@Override
|
@Override
|
||||||
public BigInteger getValue(Register register, boolean signed) {
|
public BigInteger getValue(Register register, boolean signed) {
|
||||||
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
|
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
|
||||||
DBTraceRegisterContextSpace ctxSpace =
|
DBTraceRegisterContextManager manager = space.trace.getRegisterContextManager();
|
||||||
space.trace.getRegisterContextManager().get(space, false);
|
RegisterValue rv =
|
||||||
if (ctxSpace == null) {
|
manager.getValueWithDefault(getLanguage(), register, getStartSnap(), getAddress());
|
||||||
|
if (rv == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
RegisterValue rv =
|
|
||||||
ctxSpace.getValue(getLanguage(), register, getStartSnap(), getAddress());
|
|
||||||
return signed ? rv.getSignedValue() : rv.getUnsignedValue();
|
return signed ? rv.getSignedValue() : rv.getUnsignedValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -635,12 +635,9 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
||||||
@Override
|
@Override
|
||||||
public RegisterValue getRegisterValue(Register register) {
|
public RegisterValue getRegisterValue(Register register) {
|
||||||
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
|
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
|
||||||
DBTraceRegisterContextSpace ctxSpace =
|
DBTraceRegisterContextManager manager = space.trace.getRegisterContextManager();
|
||||||
space.trace.getRegisterContextManager().get(space, false);
|
return manager.getValueWithDefault(getLanguage(), register, getStartSnap(),
|
||||||
if (ctxSpace == null) {
|
getAddress());
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return ctxSpace.getValue(getLanguage(), register, getStartSnap(), getAddress());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ import ghidra.trace.model.listing.TraceInstructionsView;
|
||||||
import ghidra.trace.util.OverlappingObjectIterator;
|
import ghidra.trace.util.OverlappingObjectIterator;
|
||||||
import ghidra.trace.util.TraceChangeRecord;
|
import ghidra.trace.util.TraceChangeRecord;
|
||||||
import ghidra.util.LockHold;
|
import ghidra.util.LockHold;
|
||||||
|
import ghidra.util.SystemUtilities;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
@ -66,34 +67,9 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
||||||
this.conflictCodeUnit = conflictCodeUnit;
|
this.conflictCodeUnit = conflictCodeUnit;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void doSetContexts(Range<Long> lifespan, Address min, Address max,
|
|
||||||
ProcessorContextView context) {
|
|
||||||
Language language = space.baseLanguage;
|
|
||||||
Register contextReg = language.getContextBaseRegister();
|
|
||||||
if (contextReg == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
RegisterValue newValue = context.getRegisterValue(contextReg);
|
|
||||||
DBTraceRegisterContextManager ctxMgr = space.trace.getRegisterContextManager();
|
|
||||||
if (Objects.equals(ctxMgr.getDefaultValue(language, contextReg, min), newValue)) {
|
|
||||||
DBTraceRegisterContextSpace ctxSpace = ctxMgr.get(space, false);
|
|
||||||
if (ctxSpace == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ctxSpace.setValue(language, null, lifespan, new AddressRangeImpl(min, max));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
DBTraceRegisterContextSpace ctxSpace = ctxMgr.get(space, true);
|
|
||||||
// TODO: Do not save non-flowing context beyond???
|
|
||||||
ctxSpace.setValue(language, newValue, lifespan, new AddressRangeImpl(min, max));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Instruction doCreateInstruction(Range<Long> lifespan, Address address,
|
protected Instruction doCreateInstruction(Range<Long> lifespan, Address address,
|
||||||
InstructionPrototype prototype, Instruction protoInstr) {
|
InstructionPrototype prototype, Instruction protoInstr) {
|
||||||
try {
|
try {
|
||||||
doSetContexts(lifespan, address, address.addNoWrap(prototype.getLength() - 1),
|
|
||||||
protoInstr);
|
|
||||||
|
|
||||||
Instruction created = doCreate(lifespan, address, prototype, protoInstr);
|
Instruction created = doCreate(lifespan, address, prototype, protoInstr);
|
||||||
// copy override settings to replacement instruction
|
// copy override settings to replacement instruction
|
||||||
if (protoInstr.isFallThroughOverridden()) {
|
if (protoInstr.isFallThroughOverridden()) {
|
||||||
|
@ -183,6 +159,27 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
||||||
super(space, space.instructionMapSpace);
|
super(space, space.instructionMapSpace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void doSetContexts(TraceAddressSnapRange tasr, Language language,
|
||||||
|
ProcessorContextView context) {
|
||||||
|
Register contextReg = language.getContextBaseRegister();
|
||||||
|
if (contextReg == null || contextReg == Register.NO_CONTEXT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RegisterValue newValue = context.getRegisterValue(contextReg);
|
||||||
|
DBTraceRegisterContextManager ctxMgr = space.trace.getRegisterContextManager();
|
||||||
|
if (Objects.equals(ctxMgr.getDefaultValue(language, contextReg, tasr.getX1()), newValue)) {
|
||||||
|
DBTraceRegisterContextSpace ctxSpace = ctxMgr.get(space, false);
|
||||||
|
if (ctxSpace == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ctxSpace.setValue(language, null, tasr.getLifespan(), tasr.getRange());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DBTraceRegisterContextSpace ctxSpace = ctxMgr.get(space, true);
|
||||||
|
// TODO: Do not save non-flowing context beyond???
|
||||||
|
ctxSpace.setValue(language, newValue, tasr.getLifespan(), tasr.getRange());
|
||||||
|
}
|
||||||
|
|
||||||
protected DBTraceInstruction doCreate(Range<Long> lifespan, Address address,
|
protected DBTraceInstruction doCreate(Range<Long> lifespan, Address address,
|
||||||
InstructionPrototype prototype, ProcessorContextView context)
|
InstructionPrototype prototype, ProcessorContextView context)
|
||||||
throws CodeUnitInsertionException, AddressOverflowException {
|
throws CodeUnitInsertionException, AddressOverflowException {
|
||||||
|
@ -214,6 +211,8 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
||||||
throw new CodeUnitInsertionException("Code units cannot overlap");
|
throw new CodeUnitInsertionException("Code units cannot overlap");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
doSetContexts(tasr, prototype.getLanguage(), context);
|
||||||
|
|
||||||
DBTraceInstruction created = space.instructionMapSpace.put(tasr, null);
|
DBTraceInstruction created = space.instructionMapSpace.put(tasr, null);
|
||||||
created.set(prototype, context);
|
created.set(prototype, context);
|
||||||
|
|
||||||
|
|
|
@ -415,25 +415,33 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
|
||||||
forward)));
|
forward)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected AddressSetView getCommentAddresses(int commentType, AddressSetView addrSet) {
|
||||||
|
return new IntersectionAddressSetView(addrSet, program.viewport.unionedAddresses(
|
||||||
|
s -> program.trace.getCommentAdapter()
|
||||||
|
.getAddressSetView(Range.singleton(s), e -> e.getType() == commentType)));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AddressSetView getCommentAddresses(AddressSetView addrSet) {
|
||||||
|
return new IntersectionAddressSetView(addrSet, program.viewport.unionedAddresses(
|
||||||
|
s -> program.trace.getCommentAdapter()
|
||||||
|
.getAddressSetView(Range.singleton(s))));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CodeUnitIterator getCommentCodeUnitIterator(int commentType, AddressSetView addrSet) {
|
public CodeUnitIterator getCommentCodeUnitIterator(int commentType, AddressSetView addrSet) {
|
||||||
// TODO Auto-generated method stub
|
return new WrappingCodeUnitIterator(
|
||||||
return null;
|
getCodeUnitIterator(getCommentAddresses(commentType, addrSet), true));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AddressIterator getCommentAddressIterator(int commentType, AddressSetView addrSet,
|
public AddressIterator getCommentAddressIterator(int commentType, AddressSetView addrSet,
|
||||||
boolean forward) {
|
boolean forward) {
|
||||||
return new IntersectionAddressSetView(addrSet, program.viewport.unionedAddresses(
|
return getCommentAddresses(commentType, addrSet).getAddresses(forward);
|
||||||
s -> program.trace.getCommentAdapter()
|
|
||||||
.getAddressSetView(Range.singleton(s), e -> e.getType() == commentType)))
|
|
||||||
.getAddresses(forward);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AddressIterator getCommentAddressIterator(AddressSetView addrSet, boolean forward) {
|
public AddressIterator getCommentAddressIterator(AddressSetView addrSet, boolean forward) {
|
||||||
// TODO Auto-generated method stub
|
return getCommentAddresses(addrSet).getAddresses(forward);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -25,6 +25,7 @@ import javax.help.UnsupportedOperationException;
|
||||||
|
|
||||||
import com.google.common.collect.Range;
|
import com.google.common.collect.Range;
|
||||||
|
|
||||||
|
import generic.NestedIterator;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.lang.Register;
|
import ghidra.program.model.lang.Register;
|
||||||
import ghidra.program.model.listing.Variable;
|
import ghidra.program.model.listing.Variable;
|
||||||
|
@ -231,13 +232,22 @@ public abstract class AbstractDBTraceProgramViewReferenceManager implements Refe
|
||||||
: (r1, r2) -> -r1.getFromAddress().compareTo(r2.getFromAddress());
|
: (r1, r2) -> -r1.getFromAddress().compareTo(r2.getFromAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Iterator<Reference> getReferenceIteratorForSnap(long snap, Address startAddr) {
|
||||||
|
AddressIterator addresses =
|
||||||
|
refs.getReferenceSources(Range.singleton(snap)).getAddresses(startAddr, true);
|
||||||
|
return NestedIterator.start(addresses, a -> {
|
||||||
|
return refs.getReferencesFrom(snap, a).iterator();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ReferenceIterator getReferenceIterator(Address startAddr) {
|
public ReferenceIterator getReferenceIterator(Address startAddr) {
|
||||||
if (refs(false) == null) {
|
if (refs(false) == null) {
|
||||||
return new ReferenceIteratorAdapter(Collections.emptyIterator());
|
return new ReferenceIteratorAdapter(Collections.emptyIterator());
|
||||||
}
|
}
|
||||||
|
// TODO: This will fail to occlude on equal (src,dst,opIndex) keys
|
||||||
return new ReferenceIteratorAdapter(
|
return new ReferenceIteratorAdapter(
|
||||||
program.viewport.mergedIterator(s -> refs.getReferencesFrom(s, startAddr).iterator(),
|
program.viewport.mergedIterator(s -> getReferenceIteratorForSnap(s, startAddr),
|
||||||
getReferenceFromComparator(true)));
|
getReferenceFromComparator(true)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,8 +49,7 @@ import ghidra.trace.database.memory.DBTraceMemoryManager;
|
||||||
import ghidra.trace.database.symbol.DBTraceReference;
|
import ghidra.trace.database.symbol.DBTraceReference;
|
||||||
import ghidra.trace.database.thread.DBTraceThread;
|
import ghidra.trace.database.thread.DBTraceThread;
|
||||||
import ghidra.trace.database.thread.DBTraceThreadManager;
|
import ghidra.trace.database.thread.DBTraceThreadManager;
|
||||||
import ghidra.trace.model.ImmutableTraceAddressSnapRange;
|
import ghidra.trace.model.*;
|
||||||
import ghidra.trace.model.TraceAddressSnapRange;
|
|
||||||
import ghidra.trace.model.language.TraceGuestLanguage;
|
import ghidra.trace.model.language.TraceGuestLanguage;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.database.DBOpenMode;
|
import ghidra.util.database.DBOpenMode;
|
||||||
|
@ -76,6 +75,12 @@ public class ToyDBTraceBuilder implements AutoCloseable {
|
||||||
this.trace = new DBTrace(name, language.getDefaultCompilerSpec(), this);
|
this.trace = new DBTrace(name, language.getDefaultCompilerSpec(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ToyDBTraceBuilder(Trace trace) {
|
||||||
|
this.language = trace.getBaseLanguage();
|
||||||
|
this.trace = (DBTrace) trace;
|
||||||
|
trace.addConsumer(this);
|
||||||
|
}
|
||||||
|
|
||||||
public Address addr(AddressSpace space, long offset) {
|
public Address addr(AddressSpace space, long offset) {
|
||||||
return space.getAddress(offset);
|
return space.getAddress(offset);
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for plugin that handles multi-user merge of programs.
|
* Constructor for plugin that handles multi-user merge of programs.
|
||||||
|
*
|
||||||
* @param tool the tool with the active program to be merged
|
* @param tool the tool with the active program to be merged
|
||||||
* @param mergeManager the merge manager that will control the merge process
|
* @param mergeManager the merge manager that will control the merge process
|
||||||
* @param program the current program
|
* @param program the current program
|
||||||
|
@ -61,7 +62,8 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MergeManagerProvider createProvider() {
|
public MergeManagerProvider createProvider() {
|
||||||
return new MergeManagerProvider(this, "Merge Programs for " + currentDomainObject.getName());
|
return new MergeManagerProvider(this,
|
||||||
|
"Merge Programs for " + currentDomainObject.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -82,6 +84,7 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the merge manager associated with this plug-in.
|
* Gets the merge manager associated with this plug-in.
|
||||||
|
*
|
||||||
* @return the merge manager
|
* @return the merge manager
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@ -91,6 +94,7 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines and displays a component for resolving merge conflicts.
|
* Defines and displays a component for resolving merge conflicts.
|
||||||
|
*
|
||||||
* @param component the component
|
* @param component the component
|
||||||
* @param componentID the identifier for this component
|
* @param componentID the identifier for this component
|
||||||
*/
|
*/
|
||||||
|
@ -101,6 +105,7 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the merge description at the top of the merge tool.
|
* Sets the merge description at the top of the merge tool.
|
||||||
|
*
|
||||||
* @param mergeDescription the new description
|
* @param mergeDescription the new description
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@ -110,7 +115,9 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the message below the progress meter in the current phase progress area.
|
* Sets the message below the progress meter in the current phase progress area.
|
||||||
* @param progressDescription the new text message to display. If null, then the default message is displayed.
|
*
|
||||||
|
* @param progressDescription the new text message to display. If null, then the default message
|
||||||
|
* is displayed.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
void updateProgressDetails(String progressDescription) {
|
void updateProgressDetails(String progressDescription) {
|
||||||
|
@ -118,7 +125,9 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the percentage of the progress meter that is filled in for the current phase progress area.
|
* Sets the percentage of the progress meter that is filled in for the current phase progress
|
||||||
|
* area.
|
||||||
|
*
|
||||||
* @param currentPercentProgress the percentage of the progress bar to fill in from 0 to 100.
|
* @param currentPercentProgress the percentage of the progress bar to fill in from 0 to 100.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@ -135,8 +144,9 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables/disables the Apply button at the bottom of the merge tool.
|
* Enables/disables the Apply button at the bottom of the merge tool. The Apply button is for
|
||||||
* The Apply button is for applying conflicts.
|
* applying conflicts.
|
||||||
|
*
|
||||||
* @param state true means enable the button. false means disable it.
|
* @param state true means enable the button. false means disable it.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@ -146,6 +156,7 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the provider for the merge.
|
* Gets the provider for the merge.
|
||||||
|
*
|
||||||
* @return the provider
|
* @return the provider
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@ -153,22 +164,27 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
|
||||||
return provider;
|
return provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean closeOtherPrograms(boolean ignoreChanges) {
|
public boolean closeOtherPrograms(boolean ignoreChanges) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean closeAllPrograms(boolean ignoreChanges) {
|
public boolean closeAllPrograms(boolean ignoreChanges) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean closeProgram() {
|
public boolean closeProgram() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean closeProgram(Program program, boolean ignoreChanges) {
|
public boolean closeProgram(Program program, boolean ignoreChanges) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Program[] getAllOpenPrograms() {
|
public Program[] getAllOpenPrograms() {
|
||||||
ProgramMultiUserMergeManager programMergeManager =
|
ProgramMultiUserMergeManager programMergeManager =
|
||||||
(ProgramMultiUserMergeManager) mergeManager;
|
(ProgramMultiUserMergeManager) mergeManager;
|
||||||
|
@ -178,10 +194,12 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
|
||||||
programMergeManager.getProgram(MergeConstants.ORIGINAL) };
|
programMergeManager.getProgram(MergeConstants.ORIGINAL) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Program getCurrentProgram() {
|
public Program getCurrentProgram() {
|
||||||
return (Program) currentDomainObject;
|
return (Program) currentDomainObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Program getProgram(Address addr) {
|
public Program getProgram(Address addr) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -190,6 +208,7 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isVisible(Program program) {
|
public boolean isVisible(Program program) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -199,6 +218,7 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Program openProgram(DomainFile domainFile) {
|
public Program openProgram(DomainFile domainFile) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -208,29 +228,53 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Program openProgram(DomainFile df, int version) {
|
public Program openProgram(DomainFile df, int version) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Program openProgram(DomainFile domainFile, int version, int state) {
|
public Program openProgram(DomainFile domainFile, int version, int state) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void openProgram(Program program) {
|
public void openProgram(Program program) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void openProgram(Program program, boolean current) {
|
public void openProgram(Program program, boolean current) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void openProgram(Program program, int state) {
|
public void openProgram(Program program, int state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void releaseProgram(Program program, Object persistentOwner) {
|
public void releaseProgram(Program program, Object persistentOwner) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveProgram() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveProgram(Program program) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveProgramAs() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveProgramAs(Program program) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void setCurrentProgram(Program p) {
|
public void setCurrentProgram(Program p) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean setPersistentOwner(Program program, Object owner) {
|
public boolean setPersistentOwner(Program program, Object owner) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -238,10 +282,12 @@ public class ProgramMergeManagerPlugin extends MergeManagerPlugin implements Pro
|
||||||
public void setSearchPriority(Program p, int priority) {
|
public void setSearchPriority(Program p, int priority) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isLocked() {
|
public boolean isLocked() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void lockDown(boolean state) {
|
public void lockDown(boolean state) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,8 +102,7 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
||||||
/**
|
/**
|
||||||
* Method called if the plugin supports this domain file.
|
* Method called if the plugin supports this domain file.
|
||||||
*
|
*
|
||||||
* @param data
|
* @param data the data to be used by the running tool
|
||||||
* the data to be used by the running tool
|
|
||||||
* @return false if data is not a Program object.
|
* @return false if data is not a Program object.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@ -447,9 +446,9 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method notifies listening plugins that a programs has been added to
|
* This method notifies listening plugins that a programs has been added to the program manager.
|
||||||
* the program manager. This is not used for actually opening a program from
|
* This is not used for actually opening a program from the database and will act strangely if
|
||||||
* the database and will act strangely if given a closed Program object.
|
* given a closed Program object.
|
||||||
*
|
*
|
||||||
* @see ghidra.app.services.ProgramManager#openProgram(ghidra.program.model.listing.Program)
|
* @see ghidra.app.services.ProgramManager#openProgram(ghidra.program.model.listing.Program)
|
||||||
*/
|
*/
|
||||||
|
@ -585,7 +584,7 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
||||||
/**
|
/**
|
||||||
* Set the string chooser property editor on the property that is a filename.
|
* Set the string chooser property editor on the property that is a filename.
|
||||||
*
|
*
|
||||||
* @param options property list
|
* @param options property list
|
||||||
* @param filePropertyName name of the property that is a filename
|
* @param filePropertyName name of the property that is a filename
|
||||||
*/
|
*/
|
||||||
private void setPropertyEditor(Options options, String filePropertyName) {
|
private void setPropertyEditor(Options options, String filePropertyName) {
|
||||||
|
@ -597,8 +596,8 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start a transaction if one has not been started; needed when program
|
* Start a transaction if one has not been started; needed when program properties are about to
|
||||||
* properties are about to change from the options editor.
|
* change from the options editor.
|
||||||
*/
|
*/
|
||||||
private void startTransaction(Program currentProgram) {
|
private void startTransaction(Program currentProgram) {
|
||||||
if (transactionID < 0) {
|
if (transactionID < 0) {
|
||||||
|
@ -685,6 +684,26 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
||||||
return openProgram;
|
return openProgram;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveProgram() {
|
||||||
|
saveProgram(getCurrentProgram());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveProgram(Program program) {
|
||||||
|
Swing.runIfSwingOrRunLater(() -> programSaveMgr.saveProgram(program));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveProgramAs() {
|
||||||
|
saveProgramAs(getCurrentProgram());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveProgramAs(Program program) {
|
||||||
|
Swing.runIfSwingOrRunLater(() -> programSaveMgr.saveAs(program));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write out my data state.
|
* Write out my data state.
|
||||||
*/
|
*/
|
||||||
|
@ -1040,13 +1059,4 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
||||||
public boolean isManaged(Program program) {
|
public boolean isManaged(Program program) {
|
||||||
return programMgr.contains(program);
|
return programMgr.contains(program);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveProgram(Program program) {
|
|
||||||
programSaveMgr.saveProgram(program);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void saveProgramAs(Program program) {
|
|
||||||
programSaveMgr.saveAs(program);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,16 +26,17 @@ import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service for managing programs. Multiple programs may be open in a tool, but only one is active
|
* Service for managing programs. Multiple programs may be open in a tool, but only one is active at
|
||||||
* at any given time.
|
* any given time.
|
||||||
*/
|
*/
|
||||||
@ServiceInfo(defaultProvider = ProgramManagerPlugin.class, description = "Get the currently open program")
|
@ServiceInfo(
|
||||||
|
defaultProvider = ProgramManagerPlugin.class,
|
||||||
|
description = "Get the currently open program")
|
||||||
public interface ProgramManager {
|
public interface ProgramManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Program will be open in a Hidden state if not already open.
|
* Program will be open in a Hidden state if not already open. This mode is generally used in
|
||||||
* This mode is generally used in conjunction with a persistent
|
* conjunction with a persistent program owner.
|
||||||
* program owner.
|
|
||||||
*/
|
*/
|
||||||
public static final int OPEN_HIDDEN = 0;
|
public static final int OPEN_HIDDEN = 0;
|
||||||
|
|
||||||
|
@ -45,20 +46,21 @@ public interface ProgramManager {
|
||||||
public static final int OPEN_CURRENT = 1;
|
public static final int OPEN_CURRENT = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Program will be open within the tool but no change will be made
|
* Program will be open within the tool but no change will be made to the currently active
|
||||||
* to the currently active program. If this is the only program
|
* program. If this is the only program open, it will become the currently active program.
|
||||||
* open, it will become the currently active program.
|
|
||||||
*/
|
*/
|
||||||
public static final int OPEN_VISIBLE = 2;
|
public static final int OPEN_VISIBLE = 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the program that is currently active.
|
* Return the program that is currently active.
|
||||||
|
*
|
||||||
* @return may return null if no program is open
|
* @return may return null if no program is open
|
||||||
*/
|
*/
|
||||||
public Program getCurrentProgram();
|
public Program getCurrentProgram();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the specified program is open and considered visible to the user.
|
* Returns true if the specified program is open and considered visible to the user.
|
||||||
|
*
|
||||||
* @param program the program
|
* @param program the program
|
||||||
* @return true if the specified program is open and considered visible to the user
|
* @return true if the specified program is open and considered visible to the user
|
||||||
*/
|
*/
|
||||||
|
@ -66,47 +68,51 @@ public interface ProgramManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the currently active program
|
* Closes the currently active program
|
||||||
* @return true if the close is successful.
|
*
|
||||||
* false if the close fails or if there is no program currently active.
|
* @return true if the close is successful. false if the close fails or if there is no program
|
||||||
|
* currently active.
|
||||||
*/
|
*/
|
||||||
public boolean closeProgram();
|
public boolean closeProgram();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open the program corresponding to the given url.
|
* Open the program corresponding to the given url.
|
||||||
|
*
|
||||||
* @param ghidraURL valid server-based program URL
|
* @param ghidraURL valid server-based program URL
|
||||||
* @param state initial open state (OPEN_HIDDEN, OPEN_CURRENT, OPEN_VISIBLE).
|
* @param state initial open state (OPEN_HIDDEN, OPEN_CURRENT, OPEN_VISIBLE). The visibility
|
||||||
* The visibility states will be ignored if the program is already open.
|
* states will be ignored if the program is already open.
|
||||||
* @return null if the user canceled the "open" for the new program or an error
|
* @return null if the user canceled the "open" for the new program or an error occurred and was
|
||||||
* occurred and was displayed.
|
* displayed.
|
||||||
* @see GhidraURL
|
* @see GhidraURL
|
||||||
*/
|
*/
|
||||||
public Program openProgram(URL ghidraURL, int state);
|
public Program openProgram(URL ghidraURL, int state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open the program for the given domainFile. Once open it will
|
* Open the program for the given domainFile. Once open it will become the active program.
|
||||||
* become the active program.
|
*
|
||||||
* @param domainFile domain file that has the program
|
* @param domainFile domain file that has the program
|
||||||
* @return null if the user canceled the "open" for the new program
|
* @return null if the user canceled the "open" for the new program
|
||||||
*/
|
*/
|
||||||
public Program openProgram(DomainFile domainFile);
|
public Program openProgram(DomainFile domainFile);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open the program for the given domainFile. Once open it will become the active program.
|
* Open the program for the given domainFile. Once open it will become the active program.
|
||||||
*
|
*
|
||||||
* <P>Note: this method functions exactly as {@link #openProgram(DomainFile)}
|
* <P>
|
||||||
|
* Note: this method functions exactly as {@link #openProgram(DomainFile)}
|
||||||
*
|
*
|
||||||
* @param domainFile domain file that has the program
|
* @param domainFile domain file that has the program
|
||||||
* @param dialogParent unused
|
* @param dialogParent unused
|
||||||
* @return the program
|
* @return the program
|
||||||
* @deprecated deprecated for 10.1; removal for 10.3 or later; use {@link #openProgram(DomainFile)}
|
* @deprecated deprecated for 10.1; removal for 10.3 or later; use
|
||||||
|
* {@link #openProgram(DomainFile)}
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public Program openProgram(DomainFile domainFile, Component dialogParent);
|
public Program openProgram(DomainFile domainFile, Component dialogParent);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the specified version of the program represented by the given DomainFile. This
|
* Opens the specified version of the program represented by the given DomainFile. This method
|
||||||
* method should be used for shared DomainFiles. The newly opened file will be made the
|
* should be used for shared DomainFiles. The newly opened file will be made the active program.
|
||||||
* active program.
|
*
|
||||||
* @param df the DomainFile to open
|
* @param df the DomainFile to open
|
||||||
* @param version the version of the Program to open
|
* @param version the version of the Program to open
|
||||||
* @return the opened program or null if the given version does not exist.
|
* @return the opened program or null if the given version does not exist.
|
||||||
|
@ -115,29 +121,32 @@ public interface ProgramManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open the program for the given domainFile
|
* Open the program for the given domainFile
|
||||||
|
*
|
||||||
* @param domainFile domain file that has the program
|
* @param domainFile domain file that has the program
|
||||||
* @param version the version of the Program to open. Specify
|
* @param version the version of the Program to open. Specify DomainFile.DEFAULT_VERSION for
|
||||||
* DomainFile.DEFAULT_VERSION for file update mode.
|
* file update mode.
|
||||||
* @param state initial open state (OPEN_HIDDEN, OPEN_CURRENT, OPEN_VISIBLE).
|
* @param state initial open state (OPEN_HIDDEN, OPEN_CURRENT, OPEN_VISIBLE). The visibility
|
||||||
* The visibility states will be ignored if the program is already open.
|
* states will be ignored if the program is already open.
|
||||||
* @return null if the user canceled the "open" for the new program or an error
|
* @return null if the user canceled the "open" for the new program or an error occurred and was
|
||||||
* occurred and was displayed.
|
* displayed.
|
||||||
*/
|
*/
|
||||||
public Program openProgram(DomainFile domainFile, int version, int state);
|
public Program openProgram(DomainFile domainFile, int version, int state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the program to the tool. In this case the program is already open, but this tool
|
* Opens the program to the tool. In this case the program is already open, but this tool may
|
||||||
* may not have it registered as open. The program is made the active program.
|
* not have it registered as open. The program is made the active program.
|
||||||
|
*
|
||||||
* @param program the program to register as open with the tool.
|
* @param program the program to register as open with the tool.
|
||||||
*/
|
*/
|
||||||
public void openProgram(Program program);
|
public void openProgram(Program program);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the program to the tool. In this case the program is already open, but this tool
|
* Opens the program to the tool. In this case the program is already open, but this tool may
|
||||||
* may not have it registered as open. The program is made the active program.
|
* not have it registered as open. The program is made the active program.
|
||||||
|
*
|
||||||
* @param program the program to register as open with the tool.
|
* @param program the program to register as open with the tool.
|
||||||
* @param current if true, the program is made the current active program. If false, then
|
* @param current if true, the program is made the current active program. If false, then the
|
||||||
* the program is made active only if it the first open program in the tool.
|
* program is made active only if it the first open program in the tool.
|
||||||
* @deprecated use openProgram(Program program, int state) instead.
|
* @deprecated use openProgram(Program program, int state) instead.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
|
@ -145,19 +154,45 @@ public interface ProgramManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open the specified program in the tool.
|
* Open the specified program in the tool.
|
||||||
|
*
|
||||||
* @param program the program
|
* @param program the program
|
||||||
* @param state initial open state (OPEN_HIDDEN, OPEN_CURRENT, OPEN_VISIBLE).
|
* @param state initial open state (OPEN_HIDDEN, OPEN_CURRENT, OPEN_VISIBLE). The visibility
|
||||||
* The visibility states will be ignored if the program is already open.
|
* states will be ignored if the program is already open.
|
||||||
*/
|
*/
|
||||||
public void openProgram(Program program, int state);
|
public void openProgram(Program program, int state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Establish a persistent owner on an open program. This will cause the program manager to
|
* Saves the current program, possibly prompting the user for a new name.
|
||||||
* imply make a program hidden if it is closed.
|
*/
|
||||||
|
public void saveProgram();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the specified program, possibly prompting the user for a new name.
|
||||||
|
*
|
||||||
|
* @param program the program
|
||||||
|
*/
|
||||||
|
public void saveProgram(Program program);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prompts the user to save the current program to a selected file.
|
||||||
|
*/
|
||||||
|
public void saveProgramAs();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prompts the user to save the specified program to a selected file.
|
||||||
|
*
|
||||||
|
* @param program the program
|
||||||
|
*/
|
||||||
|
public void saveProgramAs(Program program);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Establish a persistent owner on an open program. This will cause the program manager to imply
|
||||||
|
* make a program hidden if it is closed.
|
||||||
|
*
|
||||||
* @param program the program
|
* @param program the program
|
||||||
* @param owner the owner
|
* @param owner the owner
|
||||||
* @return true if program is open and another object is not already the owner,
|
* @return true if program is open and another object is not already the owner, or the specified
|
||||||
* or the specified owner is already the owner.
|
* owner is already the owner.
|
||||||
* @see #releaseProgram(Program, Object)
|
* @see #releaseProgram(Program, Object)
|
||||||
*/
|
*/
|
||||||
public boolean setPersistentOwner(Program program, Object owner);
|
public boolean setPersistentOwner(Program program, Object owner);
|
||||||
|
@ -165,62 +200,67 @@ public interface ProgramManager {
|
||||||
/**
|
/**
|
||||||
* Release the persistent ownership of a program.
|
* Release the persistent ownership of a program.
|
||||||
* <p>
|
* <p>
|
||||||
* The program will automatically be closed if it is hidden or was marked as temporary. If
|
* The program will automatically be closed if it is hidden or was marked as temporary. If any
|
||||||
* any of these closures corresponds to a program with changes the user will be given an
|
* of these closures corresponds to a program with changes the user will be given an opportunity
|
||||||
* opportunity to save or keep the program open.
|
* to save or keep the program open.
|
||||||
* <p>
|
* <p>
|
||||||
* If persistentOwner is not the correct owner, the method will have no affect.
|
* If persistentOwner is not the correct owner, the method will have no affect.
|
||||||
|
*
|
||||||
* @param program the program
|
* @param program the program
|
||||||
* @param persistentOwner the owner defined by {@link #setPersistentOwner(Program, Object)}
|
* @param persistentOwner the owner defined by {@link #setPersistentOwner(Program, Object)}
|
||||||
*/
|
*/
|
||||||
public void releaseProgram(Program program, Object persistentOwner);
|
public void releaseProgram(Program program, Object persistentOwner);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the given program with the option of saving any changes. The exact behavior of
|
* Closes the given program with the option of saving any changes. The exact behavior of this
|
||||||
* this method depends on several factors. First of all, if any other tool has this program
|
* method depends on several factors. First of all, if any other tool has this program open,
|
||||||
* open, then the program is closed for this tool only and the user is not prompted to
|
* then the program is closed for this tool only and the user is not prompted to save the
|
||||||
* save the program regardless of the ignoreChanges flag. Otherwise, if ignoreChanges is
|
* program regardless of the ignoreChanges flag. Otherwise, if ignoreChanges is false and
|
||||||
* false and changes have been made, the user is prompted to save the program.
|
* changes have been made, the user is prompted to save the program.
|
||||||
|
*
|
||||||
* @param program the program to close.
|
* @param program the program to close.
|
||||||
* @param ignoreChanges if true, the program is closed without saving any changes.
|
* @param ignoreChanges if true, the program is closed without saving any changes.
|
||||||
* @return true if the program was closed. Returns false if the user canceled the close
|
* @return true if the program was closed. Returns false if the user canceled the close while
|
||||||
* while being prompted to save. Also returns false if the program passed in as a parameter
|
* being prompted to save. Also returns false if the program passed in as a parameter is
|
||||||
* is null.
|
* null.
|
||||||
*/
|
*/
|
||||||
boolean closeProgram(Program program, boolean ignoreChanges);
|
boolean closeProgram(Program program, boolean ignoreChanges);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes all open programs in this tool except the current program.
|
* Closes all open programs in this tool except the current program. If this tool is the only
|
||||||
* If this tool is the only tool with a program open and that program has changes,
|
* tool with a program open and that program has changes, then the user will be prompted to
|
||||||
* then the user will be prompted to close each such file.
|
* close each such file. (Providing the ignoreChanges flag is false)
|
||||||
* (Providing the ignoreChanges flag is false)
|
*
|
||||||
* @param ignoreChanges if true, the programs will be closed without saving changes.
|
* @param ignoreChanges if true, the programs will be closed without saving changes.
|
||||||
* @return true if all other programs were closed. Returns false if the user canceled the close
|
* @return true if all other programs were closed. Returns false if the user canceled the close
|
||||||
* while being prompted to save.
|
* while being prompted to save.
|
||||||
*/
|
*/
|
||||||
public boolean closeOtherPrograms(boolean ignoreChanges);
|
public boolean closeOtherPrograms(boolean ignoreChanges);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes all open programs in this tool. If this tool is the only tool with a program
|
* Closes all open programs in this tool. If this tool is the only tool with a program open and
|
||||||
* open and that program has changes, then the user will be prompted to close each such file.
|
* that program has changes, then the user will be prompted to close each such file. (Providing
|
||||||
* (Providing the ignoreChanges flag is false)
|
* the ignoreChanges flag is false)
|
||||||
|
*
|
||||||
* @param ignoreChanges if true, the programs will be closed without saving changes.
|
* @param ignoreChanges if true, the programs will be closed without saving changes.
|
||||||
* @return true if all programs were closed. Returns false if the user canceled the close
|
* @return true if all programs were closed. Returns false if the user canceled the close while
|
||||||
* while being prompted to save.
|
* being prompted to save.
|
||||||
*/
|
*/
|
||||||
public boolean closeAllPrograms(boolean ignoreChanges);
|
public boolean closeAllPrograms(boolean ignoreChanges);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the given program to be the current active program in the tool.
|
* Sets the given program to be the current active program in the tool.
|
||||||
|
*
|
||||||
* @param p the program to make active.
|
* @param p the program to make active.
|
||||||
*/
|
*/
|
||||||
public void setCurrentProgram(Program p);
|
public void setCurrentProgram(Program p);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the first program in the list of open programs that contains the given address.
|
* Returns the first program in the list of open programs that contains the given address.
|
||||||
* Programs are searched in the order they were opened within a given priority.
|
* Programs are searched in the order they were opened within a given priority. Program are
|
||||||
* Program are initially opened with the PRIORITY_NORMAL priority, but can be set to have
|
* initially opened with the PRIORITY_NORMAL priority, but can be set to have PRIORITY_HIGH or
|
||||||
* PRIORITY_HIGH or PRIORITY_LOW.
|
* PRIORITY_LOW.
|
||||||
|
*
|
||||||
* @param addr the address for which to search.
|
* @param addr the address for which to search.
|
||||||
* @return the first program that can be found to contain the given address.
|
* @return the first program that can be found to contain the given address.
|
||||||
*/
|
*/
|
||||||
|
@ -228,13 +268,15 @@ public interface ProgramManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of all open program.
|
* Returns a list of all open program.
|
||||||
|
*
|
||||||
* @return the programs
|
* @return the programs
|
||||||
*/
|
*/
|
||||||
public Program[] getAllOpenPrograms();
|
public Program[] getAllOpenPrograms();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows program manager state to be locked/unlocked. While locked, the program manager will
|
* Allows program manager state to be locked/unlocked. While locked, the program manager will
|
||||||
* not support opening additional programs.
|
* not support opening additional programs.
|
||||||
|
*
|
||||||
* @param state locked if true, unlocked if false
|
* @param state locked if true, unlocked if false
|
||||||
* @deprecated deprecated for 10.1; removal for 10.3 or later
|
* @deprecated deprecated for 10.1; removal for 10.3 or later
|
||||||
*/
|
*/
|
||||||
|
@ -243,6 +285,7 @@ public interface ProgramManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if program manager is in the locked state
|
* Returns true if program manager is in the locked state
|
||||||
|
*
|
||||||
* @return true if program manager is in the locked state
|
* @return true if program manager is in the locked state
|
||||||
* @deprecated deprecated for 10.1; removal for 10.3 or later
|
* @deprecated deprecated for 10.1; removal for 10.3 or later
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -23,8 +23,8 @@ import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A stub of the {@link ProgramManager} interface. This can be used to supply a test program
|
* A stub of the {@link ProgramManager} interface. This can be used to supply a test program manager
|
||||||
* manager or to spy on system internals by overriding methods as needed.
|
* or to spy on system internals by overriding methods as needed.
|
||||||
*/
|
*/
|
||||||
public class TestDummyProgramManager implements ProgramManager {
|
public class TestDummyProgramManager implements ProgramManager {
|
||||||
|
|
||||||
|
@ -91,6 +91,26 @@ public class TestDummyProgramManager implements ProgramManager {
|
||||||
// stub
|
// stub
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveProgram() {
|
||||||
|
// stub
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveProgram(Program program) {
|
||||||
|
// stub
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveProgramAs() {
|
||||||
|
// stub
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveProgramAs(Program program) {
|
||||||
|
// stub
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean setPersistentOwner(Program program, Object owner) {
|
public boolean setPersistentOwner(Program program, Object owner) {
|
||||||
// stub
|
// stub
|
||||||
|
|
|
@ -24,8 +24,8 @@ import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A stubbed {@link ProgramManager} that used the 'second program' at the current program. This
|
* A stubbed {@link ProgramManager} that used the 'second program' at the current program. This is
|
||||||
* is used to secondary views in order to install the right program.
|
* used to secondary views in order to install the right program.
|
||||||
*/
|
*/
|
||||||
public class DiffProgramManager implements ProgramManager {
|
public class DiffProgramManager implements ProgramManager {
|
||||||
ProgramDiffPlugin programDiffPlugin;
|
ProgramDiffPlugin programDiffPlugin;
|
||||||
|
@ -119,6 +119,26 @@ public class DiffProgramManager implements ProgramManager {
|
||||||
// stub
|
// stub
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveProgram() {
|
||||||
|
// stub
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveProgram(Program program) {
|
||||||
|
// stub
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveProgramAs() {
|
||||||
|
// stub
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveProgramAs(Program program) {
|
||||||
|
// stub
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setCurrentProgram(Program p) {
|
public void setCurrentProgram(Program p) {
|
||||||
// stub
|
// stub
|
||||||
|
|
|
@ -49,7 +49,6 @@ public class SleighAssembler implements Assembler {
|
||||||
protected Program program;
|
protected Program program;
|
||||||
protected Listing listing;
|
protected Listing listing;
|
||||||
protected Memory memory;
|
protected Memory memory;
|
||||||
protected Disassembler dis;
|
|
||||||
protected AssemblyParser parser;
|
protected AssemblyParser parser;
|
||||||
protected AssemblyDefaultContext defaultContext;
|
protected AssemblyDefaultContext defaultContext;
|
||||||
protected AssemblyContextGraph ctxGraph;
|
protected AssemblyContextGraph ctxGraph;
|
||||||
|
@ -71,8 +70,6 @@ public class SleighAssembler implements Assembler {
|
||||||
|
|
||||||
this.listing = program.getListing();
|
this.listing = program.getListing();
|
||||||
this.memory = program.getMemory();
|
this.memory = program.getMemory();
|
||||||
this.dis = Disassembler.getDisassembler(program, TaskMonitor.DUMMY,
|
|
||||||
DisassemblerMessageListener.IGNORE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -113,8 +110,12 @@ public class SleighAssembler implements Assembler {
|
||||||
Address end = at.add(insbytes.length - 1);
|
Address end = at.add(insbytes.length - 1);
|
||||||
listing.clearCodeUnits(at, end, false);
|
listing.clearCodeUnits(at, end, false);
|
||||||
memory.setBytes(at, insbytes);
|
memory.setBytes(at, insbytes);
|
||||||
dis.disassemble(at, new AddressSet(at));
|
AddressSet set = new AddressSet(at, end);
|
||||||
return listing.getInstructions(new AddressSet(at, end), true);
|
// Creating this at construction causes it to assess memory flags too early.
|
||||||
|
Disassembler dis = Disassembler.getDisassembler(program, TaskMonitor.DUMMY,
|
||||||
|
DisassemblerMessageListener.IGNORE);
|
||||||
|
dis.disassemble(at, set);
|
||||||
|
return listing.getInstructions(set, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -46,9 +46,10 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a new SLEIGH constructor semantic
|
* Build a new SLEIGH constructor semantic
|
||||||
|
*
|
||||||
* @param cons the SLEIGH constructor
|
* @param cons the SLEIGH constructor
|
||||||
* @param indices the indices of RHS non-terminals in the associated production that represent an
|
* @param indices the indices of RHS non-terminals in the associated production that represent
|
||||||
* operand in the SLEIGH constructor
|
* an operand in the SLEIGH constructor
|
||||||
*/
|
*/
|
||||||
public AssemblyConstructorSemantic(Constructor cons, List<Integer> indices) {
|
public AssemblyConstructorSemantic(Constructor cons, List<Integer> indices) {
|
||||||
this.cons = cons;
|
this.cons = cons;
|
||||||
|
@ -73,6 +74,7 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the SLEIGH constructor
|
* Get the SLEIGH constructor
|
||||||
|
*
|
||||||
* @return the constructor
|
* @return the constructor
|
||||||
*/
|
*/
|
||||||
public Constructor getConstructor() {
|
public Constructor getConstructor() {
|
||||||
|
@ -81,6 +83,7 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the associated encoding patterns for the constructor
|
* Get the associated encoding patterns for the constructor
|
||||||
|
*
|
||||||
* @return the patterns
|
* @return the patterns
|
||||||
*/
|
*/
|
||||||
public Collection<AssemblyResolvedConstructor> getPatterns() {
|
public Collection<AssemblyResolvedConstructor> getPatterns() {
|
||||||
|
@ -92,6 +95,7 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert the index of a print piece to its associated operand index
|
* Convert the index of a print piece to its associated operand index
|
||||||
|
*
|
||||||
* @param printpos position excluding whitespace and string tokens.
|
* @param printpos position excluding whitespace and string tokens.
|
||||||
* @return the operand index
|
* @return the operand index
|
||||||
*/
|
*/
|
||||||
|
@ -101,6 +105,7 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the list of operand indices in print piece order
|
* Get the list of operand indices in print piece order
|
||||||
|
*
|
||||||
* @return the list
|
* @return the list
|
||||||
*/
|
*/
|
||||||
public List<Integer> getOperandIndices() {
|
public List<Integer> getOperandIndices() {
|
||||||
|
@ -111,8 +116,9 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
|
||||||
* Get an iterator over the operand indices
|
* Get an iterator over the operand indices
|
||||||
*
|
*
|
||||||
* If this iterator is advanced for each non-terminal, while simultaneously iterating over the
|
* If this iterator is advanced for each non-terminal, while simultaneously iterating over the
|
||||||
* RHS of the associated production, then this will identify the corresponding operand index
|
* RHS of the associated production, then this will identify the corresponding operand index for
|
||||||
* for each non-terminal
|
* each non-terminal
|
||||||
|
*
|
||||||
* @return the iterator
|
* @return the iterator
|
||||||
*/
|
*/
|
||||||
public Iterator<Integer> getOperandIndexIterator() {
|
public Iterator<Integer> getOperandIndexIterator() {
|
||||||
|
@ -142,17 +148,17 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
|
||||||
* than ("specializes") another, i.e., it matches on more bits than another pattern, the more
|
* than ("specializes") another, i.e., it matches on more bits than another pattern, the more
|
||||||
* specific pattern is chosen. Second, if the two are equally special, then the one that occurs
|
* specific pattern is chosen. Second, if the two are equally special, then the one that occurs
|
||||||
* first in the SLEIGH specification is taken. So, during resolution, if a less-special or
|
* first in the SLEIGH specification is taken. So, during resolution, if a less-special or
|
||||||
* later-occurring constructor is chosen, we must prevent continued resolution from matching
|
* later-occurring constructor is chosen, we must prevent continued resolution from matching the
|
||||||
* the more-special or earlier-occurring pattern(s).
|
* more-special or earlier-occurring pattern(s).
|
||||||
*
|
*
|
||||||
* Essentially, this states, "you may choose any value matching my pattern, except those that
|
* Essentially, this states, "you may choose any value matching my pattern, except those that
|
||||||
* match these forbidden patterns."
|
* match these forbidden patterns."
|
||||||
*
|
*
|
||||||
* This takes a given pattern, and searches the rest of the language for any patterns that
|
* This takes a given pattern, and searches the rest of the language for any patterns that would
|
||||||
* would take precedence, and combines them as forbidden patterns with the given pattern.
|
* take precedence, and combines them as forbidden patterns with the given pattern.
|
||||||
*
|
*
|
||||||
* @param pat the given pattern
|
* @param pat the given pattern
|
||||||
* @return the same pattern with forbidden records added
|
* @return the same pattern with forbidden records added
|
||||||
*/
|
*/
|
||||||
protected AssemblyResolvedConstructor withComputedForbids(AssemblyResolvedConstructor pat) {
|
protected AssemblyResolvedConstructor withComputedForbids(AssemblyResolvedConstructor pat) {
|
||||||
// Forbid anything more specific (or otherwise takes precedence) over me.
|
// Forbid anything more specific (or otherwise takes precedence) over me.
|
||||||
|
@ -194,7 +200,7 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
|
||||||
// OK, they overlap. Let's see if its a strict subset
|
// OK, they overlap. Let's see if its a strict subset
|
||||||
if (comb.bitsEqual(sibpat)) {
|
if (comb.bitsEqual(sibpat)) {
|
||||||
forbids.add(sibpat.withDescription(
|
forbids.add(sibpat.withDescription(
|
||||||
cons + " forbids " + sibcons + " by pattern specificity"));
|
sibcons + " forbids " + cons + " by pattern specificity"));
|
||||||
return CONTINUE;
|
return CONTINUE;
|
||||||
}
|
}
|
||||||
else if (comb.bitsEqual(pat)) {
|
else if (comb.bitsEqual(pat)) {
|
||||||
|
@ -205,7 +211,7 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
|
||||||
// Finally, check the line number
|
// Finally, check the line number
|
||||||
if (sibcons.getId() < cons.getId()) {
|
if (sibcons.getId() < cons.getId()) {
|
||||||
forbids.add(
|
forbids.add(
|
||||||
sibpat.withDescription(cons + " forbids " + sibcons + " by rule position"));
|
sibpat.withDescription(sibcons + " forbids " + cons + " by rule position"));
|
||||||
return CONTINUE;
|
return CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,22 +225,24 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Solve this constructor's context changes
|
* Solve this constructor's context changes
|
||||||
|
*
|
||||||
* @param res the combined resolution requirements derived from the subconstructors
|
* @param res the combined resolution requirements derived from the subconstructors
|
||||||
* @param vals any defined symbols (usually {@code inst_start}, and {@code inst_next})
|
* @param vals any defined symbols (usually {@code inst_start}, and {@code inst_next})
|
||||||
* @param opvals a map from operand index to operand value
|
* @param opvals a map from operand index to operand value
|
||||||
* @return the resolution with context changes applied in reverse, or an error
|
* @return the resolution with context changes applied in reverse, or an error
|
||||||
*
|
*
|
||||||
* Each value in {@code opvals} must either be a numeric value, e.g., an index from a varnode
|
* Each value in {@code opvals} must either be a numeric value, e.g., an index from a
|
||||||
* list, or another {@link AssemblyResolvedConstructor} for a subconstructor operand.
|
* varnode list, or another {@link AssemblyResolvedConstructor} for a subconstructor
|
||||||
|
* operand.
|
||||||
*
|
*
|
||||||
* It's helpful to think of the SLEIGH disassembly process here. Normally, once the appropriate
|
* It's helpful to think of the SLEIGH disassembly process here. Normally, once the
|
||||||
* constructor has been identified (by matching patterns), its context changes are applied, and
|
* appropriate constructor has been identified (by matching patterns), its context
|
||||||
* then its operands parsed (possibly parsing subconstructor operands). Thus, {@code res} can
|
* changes are applied, and then its operands parsed (possibly parsing subconstructor
|
||||||
* be thought of as the intermediate result between applying context changes and parsing
|
* operands). Thus, {@code res} can be thought of as the intermediate result between
|
||||||
* operands, except in reverse. The output of this method corresponds to the state before
|
* applying context changes and parsing operands, except in reverse. The output of this
|
||||||
* context changes were applied, i.e., immediately after selecting the constructor. Thus, in
|
* method corresponds to the state before context changes were applied, i.e.,
|
||||||
* reverse, the context is solved immediately before applying the selected constructor
|
* immediately after selecting the constructor. Thus, in reverse, the context is solved
|
||||||
* patterns.
|
* immediately before applying the selected constructor patterns.
|
||||||
*
|
*
|
||||||
* @see AssemblyTreeResolver#resolveSelectedChildren(AssemblyProduction, List, List, Collection)
|
* @see AssemblyTreeResolver#resolveSelectedChildren(AssemblyProduction, List, List, Collection)
|
||||||
*/
|
*/
|
||||||
|
@ -300,10 +308,11 @@ public class AssemblyConstructorSemantic implements Comparable<AssemblyConstruct
|
||||||
* @param outer the state before context changes
|
* @param outer the state before context changes
|
||||||
* @return the state after context changes
|
* @return the state after context changes
|
||||||
*
|
*
|
||||||
* Unlike the usual disassembly process, this method does not take into account any information
|
* Unlike the usual disassembly process, this method does not take into account any
|
||||||
* from the instruction encoding. Any context bits that depend on it are set to unknown
|
* information from the instruction encoding. Any context bits that depend on it are set
|
||||||
* ({@code x}) in the output. This method is used to pre-compute a context transition graph in
|
* to unknown ({@code x}) in the output. This method is used to pre-compute a context
|
||||||
* order to quickly resolve purely-recursive semantics on the root constructor table.
|
* transition graph in order to quickly resolve purely-recursive semantics on the root
|
||||||
|
* constructor table.
|
||||||
*/
|
*/
|
||||||
public AssemblyResolvedConstructor applyForward(AssemblyResolvedConstructor outer) {
|
public AssemblyResolvedConstructor applyForward(AssemblyResolvedConstructor outer) {
|
||||||
AssemblyResolvedConstructor res = outer;
|
AssemblyResolvedConstructor res = outer;
|
||||||
|
|
|
@ -75,6 +75,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new MemoryMapDB
|
* Constructs a new MemoryMapDB
|
||||||
|
*
|
||||||
* @param handle the open database handle.
|
* @param handle the open database handle.
|
||||||
* @param addrMap the address map.
|
* @param addrMap the address map.
|
||||||
* @param openMode the open mode for the program.
|
* @param openMode the open mode for the program.
|
||||||
|
@ -146,14 +147,16 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the <code>allInitializedAddrSet</code> and <code>initializedLoadedAddrSet</code>
|
* Update the <code>allInitializedAddrSet</code> and <code>initializedLoadedAddrSet</code> with
|
||||||
* with relevant initialized addresses from the specified memory block. If block is not
|
* relevant initialized addresses from the specified memory block. If block is not a
|
||||||
* a mapped-block and it may be a source to existing mapped-blocks then
|
* mapped-block and it may be a source to existing mapped-blocks then
|
||||||
* <code>scanAllMappedBlocksIfNeeded</code> should be passed as <code>true</code> unless
|
* <code>scanAllMappedBlocksIfNeeded</code> should be passed as <code>true</code> unless all
|
||||||
* all mapped blocks will be processed separately.
|
* mapped blocks will be processed separately.
|
||||||
|
*
|
||||||
* @param block memory block
|
* @param block memory block
|
||||||
* @param scanAllMappedBlocksIfNeeded if true and block is initialized and not a mapped block all
|
* @param scanAllMappedBlocksIfNeeded if true and block is initialized and not a mapped block
|
||||||
* mapped blocks will be processed for possible introduction of newly initialized mapped regions.
|
* all mapped blocks will be processed for possible introduction of newly initialized
|
||||||
|
* mapped regions.
|
||||||
*/
|
*/
|
||||||
private void addBlockAddresses(MemoryBlockDB block, boolean scanAllMappedBlocksIfNeeded) {
|
private void addBlockAddresses(MemoryBlockDB block, boolean scanAllMappedBlocksIfNeeded) {
|
||||||
AddressSet blockSet = new AddressSet(block.getStart(), block.getEnd());
|
AddressSet blockSet = new AddressSet(block.getStart(), block.getEnd());
|
||||||
|
@ -193,11 +196,12 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update initialized address set for those mapped blocks which map onto the
|
* Update initialized address set for those mapped blocks which map onto the specified block
|
||||||
* specified block which has just completed a transition of its' initialized state.
|
* which has just completed a transition of its' initialized state.
|
||||||
|
*
|
||||||
* @param block block whose initialized state has changed
|
* @param block block whose initialized state has changed
|
||||||
* @param isInitialized true if block transitioned from uninitialized to initialized,
|
* @param isInitialized true if block transitioned from uninitialized to initialized, else
|
||||||
* else transition is from initialized to uninitialized.
|
* transition is from initialized to uninitialized.
|
||||||
*/
|
*/
|
||||||
private void updateMappedAddresses(MemoryBlockDB block, boolean isInitialized) {
|
private void updateMappedAddresses(MemoryBlockDB block, boolean isInitialized) {
|
||||||
|
|
||||||
|
@ -285,6 +289,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the address factory for the program.
|
* Returns the address factory for the program.
|
||||||
|
*
|
||||||
* @return program address factory
|
* @return program address factory
|
||||||
*/
|
*/
|
||||||
AddressFactory getAddressFactory() {
|
AddressFactory getAddressFactory() {
|
||||||
|
@ -293,6 +298,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the AddressMap from the program.
|
* Returns the AddressMap from the program.
|
||||||
|
*
|
||||||
* @return program address map
|
* @return program address map
|
||||||
*/
|
*/
|
||||||
AddressMapDB getAddressMap() {
|
AddressMapDB getAddressMap() {
|
||||||
|
@ -331,7 +337,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||||
throw new MemoryAccessException(block.getName() + " does not contain range " +
|
throw new MemoryAccessException(block.getName() + " does not contain range " +
|
||||||
start.toString(true) + "-" + endAddr);
|
start.toString(true) + "-" + endAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block.isMapped()) {
|
if (block.isMapped()) {
|
||||||
checkMemoryWriteMappedBlock(block, start, endAddr);
|
checkMemoryWriteMappedBlock(block, start, endAddr);
|
||||||
}
|
}
|
||||||
|
@ -368,7 +374,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||||
mappedEndAddress =
|
mappedEndAddress =
|
||||||
byteMappingScheme.getMappedSourceAddress(mappedRangeMinAddr, endOffset);
|
byteMappingScheme.getMappedSourceAddress(mappedRangeMinAddr, endOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (MemoryBlockDB b : getBlocks(mappedStartAddress, mappedEndAddress)) {
|
for (MemoryBlockDB b : getBlocks(mappedStartAddress, mappedEndAddress)) {
|
||||||
Address minAddr = Address.min(b.getEnd(), mappedEndAddress);
|
Address minAddr = Address.min(b.getEnd(), mappedEndAddress);
|
||||||
Address maxAddr = Address.max(b.getStart(), mappedStartAddress);
|
Address maxAddr = Address.max(b.getStart(), mappedStartAddress);
|
||||||
|
@ -381,9 +387,9 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||||
throws MemoryAccessException {
|
throws MemoryAccessException {
|
||||||
// TODO: could contain uninitialized region which is illegal to write to although block.isInitialized
|
// TODO: could contain uninitialized region which is illegal to write to although block.isInitialized
|
||||||
// may not be of much help since it reflects the first sub-block only - seems like mixing is a bad idea
|
// may not be of much help since it reflects the first sub-block only - seems like mixing is a bad idea
|
||||||
|
|
||||||
checkRangeForInstructions(start, endAddr);
|
checkRangeForInstructions(start, endAddr);
|
||||||
|
|
||||||
// Check all mapped-block address ranges which map onto the range to be modified
|
// Check all mapped-block address ranges which map onto the range to be modified
|
||||||
Collection<MemoryBlockDB> mappedBlocks = nonMappedBlock.getMappedBlocks();
|
Collection<MemoryBlockDB> mappedBlocks = nonMappedBlock.getMappedBlocks();
|
||||||
if (mappedBlocks != null) {
|
if (mappedBlocks != null) {
|
||||||
|
@ -480,8 +486,9 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Two blocks have been joined producing newBlock. The block which was
|
* Two blocks have been joined producing newBlock. The block which was eliminated can be
|
||||||
* eliminated can be identified using the oldBlockStartAddr.
|
* identified using the oldBlockStartAddr.
|
||||||
|
*
|
||||||
* @param newBlock new joined memory block
|
* @param newBlock new joined memory block
|
||||||
* @param oldBlockStartAddr original start address of affected block
|
* @param oldBlockStartAddr original start address of affected block
|
||||||
*/
|
*/
|
||||||
|
@ -798,6 +805,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check new block name for validity
|
* Check new block name for validity
|
||||||
|
*
|
||||||
* @param name new block name
|
* @param name new block name
|
||||||
* @throws IllegalArgumentException if invalid block name specified
|
* @throws IllegalArgumentException if invalid block name specified
|
||||||
*/
|
*/
|
||||||
|
@ -1246,25 +1254,19 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests if the memory contains a sequence of contiguous bytes that match the
|
* Tests if the memory contains a sequence of contiguous bytes that match the given byte array
|
||||||
* given byte array at all bit positions where the mask contains an "on" bit.
|
* at all bit positions where the mask contains an "on" bit. The test will be something like
|
||||||
* The test will be something like
|
|
||||||
*
|
*
|
||||||
* for(int i=0;i<bytes.length;i++) {
|
* for(int i=0;i<bytes.length;i++) { if (bytes[i] != memory.getByte(addr+i) & masks[i]) {
|
||||||
* if (bytes[i] != memory.getByte(addr+i) & masks[i]) {
|
* return false; } } return false;
|
||||||
* return false;
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* return false;
|
|
||||||
*
|
*
|
||||||
* @param addr The beginning address in memory to test against.
|
* @param addr The beginning address in memory to test against.
|
||||||
* @param bytes the array of bytes to test for.
|
* @param bytes the array of bytes to test for.
|
||||||
* @param masks the array of masks. (One for each byte in the byte array)
|
* @param masks the array of masks. (One for each byte in the byte array)
|
||||||
* @param forward if true, the matching is going forward, otherwise backward
|
* @param forward if true, the matching is going forward, otherwise backward
|
||||||
*
|
*
|
||||||
* @return 1 if there is a match
|
* @return 1 if there is a match 0 if there is no match -i if no match is found, this is the
|
||||||
* 0 if there is no match
|
* number of bytes that can be safely skipped
|
||||||
* -i if no match is found, this is the number of bytes that can be safely skipped
|
|
||||||
*/
|
*/
|
||||||
private int match(Address addr, byte[] bytes, byte[] masks, byte[] data, boolean forward) {
|
private int match(Address addr, byte[] bytes, byte[] masks, byte[] data, boolean forward) {
|
||||||
try {
|
try {
|
||||||
|
@ -1645,10 +1647,11 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||||
throw new MemoryAccessException(
|
throw new MemoryAccessException(
|
||||||
"Address " + addr.toString(true) + " does not exist in memory");
|
"Address " + addr.toString(true) + " does not exist in memory");
|
||||||
}
|
}
|
||||||
n -= block.getSize() - addr.subtract(block.getStart());
|
long advanced = block.getSize() - addr.subtract(block.getStart());
|
||||||
if (n <= 0) {
|
if (advanced >= n) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
n -= advanced;
|
||||||
try {
|
try {
|
||||||
addr = block.getEnd().addNoWrap(1);
|
addr = block.getEnd().addNoWrap(1);
|
||||||
}
|
}
|
||||||
|
@ -1879,8 +1882,9 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests if the given addressSpace (overlay space) is used by any blocks. If not, it
|
* Tests if the given addressSpace (overlay space) is used by any blocks. If not, it removes the
|
||||||
* removes the space.
|
* space.
|
||||||
|
*
|
||||||
* @param addressSpace overlay address space to be removed
|
* @param addressSpace overlay address space to be removed
|
||||||
*/
|
*/
|
||||||
private void checkRemoveAddressSpace(AddressSpace addressSpace) {
|
private void checkRemoveAddressSpace(AddressSpace addressSpace) {
|
||||||
|
@ -1930,8 +1934,8 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the intersected set of addresses between a mapped memory block, and some other
|
* Gets the intersected set of addresses between a mapped memory block, and some other address
|
||||||
* address set.
|
* set.
|
||||||
*
|
*
|
||||||
* @param mappedBlock The mapped memory block to use in the intersection.
|
* @param mappedBlock The mapped memory block to use in the intersection.
|
||||||
* @param set Some other address set to use in the intersection.
|
* @param set Some other address set to use in the intersection.
|
||||||
|
@ -1954,9 +1958,10 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the given address range back from the source range back to the mapped range.
|
* Converts the given address range back from the source range back to the mapped range. NOTE:
|
||||||
* NOTE: It is important that the specified mappedSourceRange is restricted to the
|
* It is important that the specified mappedSourceRange is restricted to the mapped source area
|
||||||
* mapped source area of the specified mappedBlock.
|
* of the specified mappedBlock.
|
||||||
|
*
|
||||||
* @param mappedBlock mapped memory block
|
* @param mappedBlock mapped memory block
|
||||||
* @param mappedSourceRange source range which maps into mappedBlock.
|
* @param mappedSourceRange source range which maps into mappedBlock.
|
||||||
* @return mapped range or null if source range not mapped to block
|
* @return mapped range or null if source range not mapped to block
|
||||||
|
@ -2225,9 +2230,10 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of all memory blocks that contain any addresses in the given range
|
* Returns a list of all memory blocks that contain any addresses in the given range
|
||||||
|
*
|
||||||
* @param start the start address
|
* @param start the start address
|
||||||
* @param end the end address
|
* @param end the end address
|
||||||
* @return a list of all memory blocks that contain any addresses in the given range
|
* @return a list of all memory blocks that contain any addresses in the given range
|
||||||
*/
|
*/
|
||||||
List<MemoryBlockDB> getBlocks(Address start, Address end) {
|
List<MemoryBlockDB> getBlocks(Address start, Address end) {
|
||||||
List<MemoryBlockDB> list = new ArrayList<>();
|
List<MemoryBlockDB> list = new ArrayList<>();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue