Merge remote-tracking branch 'origin/Ghidra_9.2'

This commit is contained in:
ghidravore 2020-11-02 14:44:34 -05:00
commit b88c949c6b
116 changed files with 12869 additions and 39864 deletions

View file

@ -142,7 +142,6 @@ model {
all{ b -> all{ b ->
def version = b.getApplication().getName() def version = b.getApplication().getName()
println "have binary: " + b
if (version.equals(v33_1)) { if (version.equals(v33_1)) {
if (toolChain in Gcc) { if (toolChain in Gcc) {

View file

@ -6,6 +6,219 @@
</HEAD> </HEAD>
<BODY> <BODY>
<H1 align="center">Ghidra 9.2 Change History (November 2020)</H1>
<blockquote><p><u>New Features</u></p>
<ul>
<li><I>Graphing</I>. A new graph service and implementation was created. The graph service provides basic graphing capabilities. It was also used to generate several different types of graphs including code block graphs, call graphs, and AST graphs. In addition, an export graph service was created that supports various formats. (GP-211)</li>
<li><I>PDB</I>. Added a new, prototype, platform-independent PDB analyzer that processes and applies data types and symbols to a program from a raw (non-XML-converted) PDB file, allowing users to more easily take advantage of PDB information. (GT-3112)</li>
<li><I>Processors</I>. Added M8C SLEIGH processor specification. (GT-3052)</li>
<li><I>Processors</I>. Added support for the RISC-V processor. (GT-3389, Issue #932)</li>
<li><I>Processors</I>. Added support for the Motorola 6809 processor. (GT-3390, Issue #1201)</li>
<li><I>Processors</I>. Added CP1600-series processor support. (GT-3426, Issue #1383)</li>
<li><I>Processors</I>. Added V850 processor module. (GT-3523, Issue #1430)</li>
</ul>
</blockquote>
<blockquote><p><u>Improvements</u></p>
<ul>
<li><I>Analysis</I>. Increased the speed of the Embedded Media Analyzer, which was especially poor for large programs, by doing better checking and reducing the number of passes over the program. (GT-3258)</li>
<li><I>Analysis</I>. Improved the performance of the RTTI analyzer. (GT-3341, Issue #10)</li>
<li><I>Analysis</I>. Updated Auto-analysis to preserve work when encountering recoverable exceptions. (GT-3599)</li>
<li><I>Analysis</I>. Improved efficiency when creating or checking for functions and namespaces which overlap. (GP-21)</li>
<li><I>Analysis</I>. Added partial support of Clang for Windows. (GP-64)</li>
<li><I>Analysis</I>. RTTI structure processing speed has been improved with a faster technique for finding the root RTTI type descriptor. (GP-168, Issue #2075)</li>
<li><I>API</I>. The performance of adding large numbers of data types to the same category has been improved. (GT-3535)</li>
<li><I>API</I>. Added the BigIntegerNumberInputDialog that allows users to enter integer values larger than <code>Integer.MAX_VALUE</code> (2147483647). (GT-3607)</li>
<li><I>API</I>. Made JSON more available using GSON. (GP-89, Issue #1982)</li>
<li><I>Basic Infrastructure</I>. Introduced an extension point <code>priority</code> annotation so users can control extension point ordering. (GT-3350, Issue #1260)</li>
<li><I>Basic Infrastructure</I>. Changed file names in <B>launch.bat</B> to always run executables from System32. (GT-3614, Issue #1599)</li>
<li><I>Basic Infrastructure</I>. Unknown platforms now default to 64-bit. (GT-3615, Issue #1499)</li>
<li><I>Build</I>. Ghidra's native Windows binaries can now be built using Visual Studio 2019. (GT-3277, Issue #999)</li>
<li><I>Build</I>. Extension builds now exclude gradlew artifacts from zip file. (GT-3631, Issue #1763)</li>
<li><I>Build</I>. Reduced the number of duplicated help files among the build jar files. (GP-57, Issue #2144)</li>
<li><I>Build</I>. Git commit hash has been added to <B>application.properties</B> file for every build (not just releases). (GP-67)</li>
<li><I>Contrib</I>. Extensions are now installed to the user's settings directory, not the Ghidra installation directory. (GT-3639, Issue #1960)</li>
<li><I>Data Types</I>. Added mutability data settings (constant, volatile) for Enum datatype. (GT-3415)</li>
<li><I>Data Types</I>. Improved Structure Editor's <B>Edit Component</B> action to work on array pointers. (GP-205, Issue #1633)</li>
<li><I>Decompiler</I>. Added Secondary Highlights to the Decompiler. This feature allows the user to create a highlight for a token to show all occurrences of that token. Further, multiple secondary highlights are allowed at the same time, each using a unique color. See the Decompiler help for more information. (GT-3292, Issue #784)</li>
<li><I>Decompiler</I>. Added heuristics to the Decompiler to better distinguish whether a constant pointer refers to something in the CODE or DATA address space, for Harvard architectures. (GT-3468)</li>
<li><I>Decompiler</I>. Improved Decompiler analysis of local variables with small data types, eliminating unnecessary casts and mask operations. (GT-3525)</li>
<li><I>Decompiler</I>. Documentation for the Decompiler, accessible from within the Code Browser, has been rewritten and extended. (GP-166)</li>
<li><I>Decompiler</I>. The Decompiler can now display the namespace path (or part of it) of symbols it renders. With the default display configuration, the minimal number of path elements necessary are printed to fully resolve the symbol within the current scope. (GP-236)</li>
<li><I>Decompiler</I>. The Decompiler now respects the <B>Charset</B> and <B>Translate</B> settings for string literals it displays. (GP-237)</li>
<li><I>Decompiler</I>. The Decompiler's analysis of array accesses is much improved. It can detect more and varied access patterns produced by optimized code, even if the base offset is not contained in the array. Multi-dimensional arrays are detected as well. (GP-238, Issue #461, #1348)</li>
<li><I>Decompiler</I>. Extended the Decompiler's support for analyzing class methods. The class data type is propagated through the <B>this</B> pointer even in cases where the full prototype of the method is not known. The methods <code>isThisPointer()</code> and <code>isHiddenReturn()</code> are now populated in HighSymbol objects and are accessible in Ghidra scripts. (GP-239, Issue #2151)</li>
<li><I>Decompiler</I>. The Decompiler will now infer a string pointer from a constant that addresses the interior of a string, not just the beginning. (GP-240, Issue #1502)</li>
<li><I>Decompiler</I>. The Decompiler now always prints the full precision of floating-point values, using the minimal number of characters in either fixed point or scientific notation. (GP-241, Issue #778)</li>
<li><I>Decompiler</I>. The Decompiler's <B>Auto Create Structure</B> command now incorporates into new structures data-type information from function prototypes. The <B>Auto Fill in Structure</B> variant of the command will override <B>undefined</B> and other more general data-types with discovered data-types if they are more specific. (GP-242)</li>
<li><I>Demangler</I>. Modified Microsoft Demangler (MDMang) to handle symbols represented by MD5 hash codes when their normal mangled length exceeds 4096. (GT-3409, Issue #1344)</li>
<li><I>Demangler</I>. Upgraded the GNU Demangler to version 2.33.1. Added support for the now-deprecated GNU Demangler version 2.24 to be used as a fallback option for demangling. (GT-3481, Issue #1195, #1308, #1451, #1454)</li>
<li><I>Demangler</I>. The Demangler now more carefully applies information if generic changes have been made. Previously if the function signature had changed in any way from default, the demangler would not attempt to apply any information including the function name. (GP-12)</li>
<li><I>Demangler</I>. Changed MDMang so cast operator names are complete within the qualified function name, effecting what is available from internal API. (GP-13)</li>
<li><I>Demangler</I>. Added additional MDMang Extended Types such as <code>char8_t</code>, <code>char16_t</code>, and <code>char32_t</code>. (GP-14)</li>
<li><I>Documentation</I>. Removed Eclipse BuildShip instructions from the DevGuide. (GT-3634, Issue #1735)</li>
<li><I>FID</I>. Regenerated FunctionID databases. Added support for Visual Studio versions 2017 and 2019. (GP-170)</li>
<li><I>Function Diff</I>. Users may now add functions ad-hoc to existing function comparison panels. (GT-2229)</li>
<li><I>Function Graph</I>. Added <B>Navigation History</B> Tool option for Function Graph to signal it to produce fewer navigation history entries. (GT-3233, Issue #1115)</li>
<li><I>GUI</I>. Users can now view the Function Tag window to see all functions associated with a tag, without having to inspect the Listing. (GT-3054)</li>
<li><I>GUI</I>. Updated the <B>Copy Special</B> action to work on the current address when there is no selection. (GT-3155, Issue #1000)</li>
<li><I>GUI</I>. Significantly improved the performance of filtering trees in the Ghidra GUI. (GT-3225)</li>
<li><I>GUI</I>. Added many optimizations to increase the speed of table sorting and filtering. (GT-3226, Issue #500)</li>
<li><I>GUI</I>. Improved performance of bit view component recently introduced to Structure Editor. (GT-3244, Issue #1141)</li>
<li><I>GUI</I>. Updated usage of timestamps in the UI to be consistent. (GT-3286)</li>
<li><I>GUI</I>. Added tool actions for navigating to the next/previous functions in the navigation history. (GT-3291, Issue #475)</li>
<li><I>GUI</I>. Filtering now works on all tables in the Function Tag window. (GT-3329)</li>
<li><I>GUI</I>. Updated the Ghidra File Chooser so that users can type text into the list and table views in order to quickly jump to a desired file. (GT-3396)</li>
<li><I>GUI</I>. Improved the performance of the Defined Strings table. (GT-3414, Issue #1259)</li>
<li><I>GUI</I>. Updated Ghidra to allow users to set a key binding to perform an equivalent operation to double-clicking the <code>XREF</code> field in the Listing. See the <B>Show Xrefs</B> action in the <B>Tool Options... Key Bindings</B> section. (GT-3446)</li>
<li><I>GUI</I>. Improved mouse wheel scrolling in Listing and Byte Viewers. (GT-3473)</li>
<li><I>GUI</I>. Ghidra's action context mechanism was changed so that actions that modify the program are not accidentally invoked in the wrong context, thus possibly modifying the program in ways the user did not want or without the user knowing that it happened. This also fixed an issue where the navigation history drop-down menu did not represent the locations that would be used if the next/previous buttons were pressed. (GT-3485)</li>
<li><I>GUI</I>. Updated Ghidra tables to defer updating while analysis is running. (GT-3604)</li>
<li><I>GUI</I>. Updated Font Size options to allow the user to set any font size. (GT-3606, Issue #160, #1541)</li>
<li><I>GUI</I>. Added ability to overlay text on an icon. (GP-41)</li>
<li><I>GUI</I>. Updated Ghidra options to allow users to clear default key binding values. (GP-61, Issue #1681)</li>
<li><I>GUI</I>. ToggleDirectionAction button now shows in snapshot windows. (GP-93)</li>
<li><I>GUI</I>. Added a new action to the Symbol Tree to allow users to convert a Namespace to a Class. (GP-225, Issue #2301)</li>
<li><I>Importer</I>. Updated the XML Loader to parse symbol names for namespaces. (GT-3293)</li>
<li><I>Importer:ELF</I>. Added support for processing Android packed ELF Relocation Tables. (GT-3320, Issue #1192)</li>
<li><I>Importer:ELF</I>. Added ELF import opinion for ARM BE8. (GT-3642, Issue #1187)</li>
<li><I>Importer:MachO</I>. DYLD Loader can now load x86_64 DYLD from macOS. (GT-3611, Issue #1566)</li>
<li><I>Importer:PE</I>. Improved parsing of Microsoft ordinal map files produced with <code>DUMPBIN /EXPORTS</code> (see <B>Ghidra/Features/Base/data/symbols/README.txt</B>). (GT-3235)</li>
<li><I>Jython</I>. Upgraded Jython to version 2.7.2. (GP-109)</li>
<li><I>Listing:Data</I>. Improved handling and display of character sequences embedded in operands or integer values. (GT-3347, Issue #1241)</li>
<li><I>Multi-User:Ghidra Server</I>. Added ability to specify initial Ghidra Server user password (<code><B>-a0</B></code> mode only) for the svrAdmin <B>add</B> and <B>reset</B> commands. (GT-3640, Issue #321)</li>
<li><I>Processors</I>. Implemented semantics for <code>vstmia/db</code> <code>vldmia/db</code>, added missing instructions, and fixed shift value for several instructions for the ARM/Thumb NEON instruction set. (GT-2567)</li>
<li><I>Processors</I>. Added the XMEGA variant of the AVR8 processor with general purpose registers moved to a non-memory-mapped register space. (GT-2909)</li>
<li><I>Processors</I>. Added support for x86 <code>SALC</code> instruction. (GT-3367, Issue #1303)</li>
<li><I>Processors</I>. Implemented pcode for 6502 <code>BRK</code> instruction. (GT-3375, Issue #1049)</li>
<li><I>Processors</I>. Implemented x86 <code>PTEST</code> instruction. (GT-3380, Issue #1295)</li>
<li><I>Processors</I>. Added missing instructions to ARM language module. (GT-3394)</li>
<li><I>Processors</I>. Added support for <code>RDRAND</code> and <code>RDSEED</code> instructions to x86-32. (GT-3413)</li>
<li><I>Processors</I>. Improved x86 breakpoint disassembly. (GT-3421, Issue #872)</li>
<li><I>Processors</I>. Added manual index file for the M6809 processor. (GT-3449, Issue #1414)</li>
<li><I>Processors</I>. Corrected issues related to retained instruction context during a language upgrade. In some rare cases this retained context could interfere with the instruction re-disassembly. This context-clearing mechanism is controlled by a new pspec property: <code>resetContextOnUpgrade</code>. (GT-3531)</li>
<li><I>Processors</I>. Updated PIC24/PIC30 index file to match latest manual. Added support for dsPIC33C. (GT-3562)</li>
<li><I>Processors</I>. Updated AVR8 ATmega256 processor model to reflect correct memory layout specification. (GT-933)</li>
<li><I>Processors</I>. Added missing call-fixup to handle call side-effects for 32 bit gcc programs for <code>get_pc_thunk.ax/si</code>. (GP-10)</li>
<li><I>Processors</I>. Added <code>ExitProcess</code> to PEFunctionsThatDoNotReturn. (GP-35)</li>
<li><I>Processors</I>. <B>External Disassembly</B> field in the Listing now shows Thumb disassembly when appropriate TMode context has been established on a memory location. (GP-49)</li>
<li><I>Processors</I>. Changed RISC-V jump instructions to the more appropriate <code>goto</code> instead of <code>call</code>. (GP-54, Issue #2120)</li>
<li><I>Processors</I>. Updated AARCH64 to v8.5, including new MTE instructions. (GP-124)</li>
<li><I>Processors</I>. Added support for floating point params and return for SH4 processor calling conventions. (GP-183, Issue #2218)</li>
<li><I>Processors</I>. Added semantic support for many AARCH64 neon instructions. Addresses for register lanes are now precalculated, reducing the amount of p-code generated. (GP-343)</li>
<li><I>Program API</I>. Improved multi-threaded ProgramDB access performance. (GT-3262)</li>
<li><I>Scripting</I>. Improved <B>ImportSymbolScript.py</B> to import functions in addition to generic labels. (GT-3249, Issue #946)</li>
<li><I>Scripting</I>. Python scripts can now call protected methods from the GhidraScript API. (GT-3334, Issue #1250)</li>
<li><I>Scripting</I>. Updated scripting feature with better change detection, external jar dependencies, and modularity. (GP-4)</li>
<li><I>Scripting</I>. Updated the GhidraDev plugin (v2.1.1) to support Python Debugging when PyDev is installed via the Eclipse <B>dropins</B> directory. (GP-186, Issue #1922)</li>
<li><I>Sleigh</I>. Error messages produced by the SLEIGH compiler have been reformatted to be more consistent in layout as well as more descriptive and more consistent in providing line number information. (GT-3174)</li>
</ul>
</blockquote>
<blockquote><p><u>Bugs</u></p>
<ul>
<li><I>Analysis</I>. Fixed an issue where stored context is initializing the set of registers constantly. (GP-25)</li>
<li><I>Analysis</I>. Fixed an RTTI Analyzer regression when analyzing RTTI0 structures with no RTTI4 references to them. (GP-62, Issue #2153)</li>
<li><I>Analysis</I>. Fixed an issue where the RTTI analyzer was not filling out RTTI3 structures in some cases. (GP-111)</li>
<li><I>API</I>. Fixed NullPointerException when attempting to delete all bookmarks from a script. (GT-3405)</li>
<li><I>API</I>. Updated the Class Searcher so that Extension Points found in the <B>Ghidra/patch</B> directory get loaded. (GT-3547, Issue #1515)</li>
<li><I>Build</I>. Updated dependency fetch script to use HTTPS when downloading CDT. (GP-69, Issue #2173)</li>
<li><I>Build</I>. Fixed resource leak in Ghidra jar builder. (GP-342)</li>
<li><I>Byte Viewer</I>. Fixed Byte Viewer to correctly load the middle-mouse highlight color options change. (GT-3471, Issue #1464, #1465)</li>
<li><I>Data Types</I>. Fixed decoding of static strings that have a character set with a smaller character size than the platform's character size. (GT-3333, Issue #1255)</li>
<li><I>Data Types</I>. Correctly handle Java character sets that do not support the encoding operation. (GT-3407, Issue #1358)</li>
<li><I>Data Types</I>. Fixed bug that caused Data Type Manager Editor key bindings to get deleted. (GT-3411, Issue #1355)</li>
<li><I>Data Types</I>. Updated the DataTypeParser to handle data type names containing templates. (GT-3493, Issue #1417)</li>
<li><I>Data Types</I>. Corrected pointer data type <code>isEquivalent()</code> method to properly check the equivalence of the base data type. The old implementation could cause a pointer to be replaced by a conflicting pointer with the same name whose base datatype is not equivalent. This change has a negative performance impact associated with it and can cause additional conflict datatypes due to the rigid datatype relationships. (GT-3557)</li>
<li><I>Data Types</I>. Improved composite conflict resolution performance and corrected composite merge issues when composite bitfields and/or flexible arrays are present. (GT-3571)</li>
<li><I>Data Types</I>. Corrected size of <code><B>long</B></code> for AARCH64 per LP64 standard. (GP-175)</li>
<li><I>Decompiler</I>. Fixed bug causing the Decompiler to miss symbol references when they are stored to the heap. (GT-3267)</li>
<li><I>Decompiler</I>. Fixed bug in the Decompiler that caused <code>Deleting op with descendants</code> exception. (GT-3506)</li>
<li><I>Decompiler</I>. Decompiler now correctly compensates for integer promotion on shift, division, and remainder operations. (GT-3572)</li>
<li><I>Decompiler</I>. Fixed handling of 64-bit implementations of alloca_probe in the Decompiler. (GT-3576)</li>
<li><I>Decompiler</I>. Default Decompiler options now minimize the risk of losing code when renaming or retyping variables. (GT-3577)</li>
<li><I>Decompiler</I>. The Decompiler no longer inherits a variable name from a subfunction if that variable incorporates additional data-flow unrelated to the subfunction. (GT-3580)</li>
<li><I>Decompiler</I>. Fixed the Decompiler <B>Override Signature</B> action to be enabled on the entire C-code statement. (GT-3636, Issue #1589)</li>
<li><I>Decompiler</I>. Fixed frequent ClassCast and IllegalArgument exceptions when performing <B>Auto Create Structure</B> or <B>Auto Create Class</B> actions in the Decompiler. (GP-119)</li>
<li><I>Decompiler</I>. Fixed a bug in the Decompiler that caused different variables to be assigned the same name in rare instances. (GP-243, Issue #1995)</li>
<li><I>Decompiler</I>. Fixed a bug in the Decompiler that caused <code>PTRSUB off of non-pointer type</code> exceptions. (GP-244, Issue #1826)</li>
<li><I>Decompiler</I>. Fixed a bug in the Decompiler that caused load operations from volatile memory to be removed as dead code. (GP-245, Issue #393, #1832)</li>
<li><I>Decompiler</I>. Fixed a bug causing the Decompiler to miss a stack alias if its offset was, itself, stored on the stack. (GP-246)</li>
<li><I>Decompiler</I>. Fixed a bug causing the Decompiler to lose Equate references to constants passed to functions that were called indirectly. (GP-247)</li>
<li><I>Decompiler</I>. Addressed various situations where the Decompiler unexpectedly removes active instructions as dead code after renaming or retyping a stack location. If the location was really an array element or structure field, renaming forced the Decompiler to treat the location as a distinct variable. Subsequently, the Decompiler thought that indirect references based before the location could not alias any following stack locations, which could then by considered dead. As of the 9.2 release, the Decompiler's renaming action no longer switches an annotation to <code>forcing</code> if it wasn't already. A retyping action, although it is <code>forcing</code>, won't trigger alias blocking for atomic data-types (this is configurable). (GP-248, Issue #524, #873)</li>
<li><I>Decompiler</I>. Fixed decompiler memory issues reported by a community security researcher. (GP-267)</li>
<li><I>Demangler</I>. Fixed the GnuDemangler to parse the full namespace for <code>operator</code> symbols. (GT-3474, Issue #1441, #1448)</li>
<li><I>Demangler</I>. Fixed numerous GNU Demangler parsing issues. Most notable is the added support for C++ Lambda functions. (GT-3545, Issue #1457, #1569)</li>
<li><I>Demangler</I>. Updated the GNU Demangler to correctly parse and apply C++ strings using the <code>unnamed type</code> syntax. (GT-3645)</li>
<li><I>Demangler</I>. Fixed duplicate namespace entry returned from <code>getNamespaceString()</code> on DemangledVariable. (GT-3646, Issue #1729)</li>
<li><I>Demangler</I>. Fixed a GnuDemangler ClassCastException when parsing a <code>typeinfo</code> string containing <code>operator</code> text. (GP-160, Issue #1870, #2267)</li>
<li><I>Demangler</I>. Added <B>stdlib.h</B> include to the GNU Demangler to fix a build issue on some systems. (GP-187, Issue #2294)</li>
<li><I>DWARF</I>. Corrected DWARF relocation handling where the address image base adjustment was factored in twice. (GT-3330)</li>
<li><I>File Formats</I>. Fixed a potential divide-by-zero exception in the EXT4 file system. (GT-3400, Issue #1342)</li>
<li><I>File Formats</I>. Fixed date and time parsing of dates in cdrom <code>iso9660</code> image files. (GT-3451, Issue #1403)</li>
<li><I>Graphing</I>. Fixed a ClassCastException sometimes encountered when performing <B>Select -> Scoped Flow -> Forward Scoped Flow</B>. (GP-180)</li>
<li><I>GUI</I>. Fixed inconsistent behavior with the interactive python interpreter's key bindings. (GT-3282)</li>
<li><I>GUI</I>. Fixed Structure Editor bug that prevented the <B>F2 Edit</B> action from editing the correct table cell after using the arrow keys. (GT-3308, Issue #703)</li>
<li><I>GUI</I>. Updated the Structure Editor so the <B>Delete</B> action is put into a background task to prevent the UI from locking. (GT-3352)</li>
<li><I>GUI</I>. Fixed IndexOutOfBoundsException when invoking column filter on Key Bindings table. (GT-3445)</li>
<li><I>GUI</I>. Fixed the analysis log dialog to not consume all available screen space. (GT-3610)</li>
<li><I>GUI</I>. Fixed issue where <B>Location</B> column, when used in the column filters, resulted in extraneous dialogs popping up. (GT-3623)</li>
<li><I>GUI</I>. Fixed Data Type Preview <B>copy</B> action so that newlines are preserved; updated table export to CSV to escape quotes and commas. (GT-3624)</li>
<li><I>GUI</I>. Fixed tables in Ghidra to copy the text that is rendered. Some tables mistakenly copied the wrong value, such as the Functions Table's Function Signature Column. (GT-3629, Issue #1628)</li>
<li><I>GUI</I>. Structure editor name now updates in title bar and tab when structure is renamed. (GP-19)</li>
<li><I>GUI</I>. Fixed an issue where drag-and-drop import locks the Windows File Explorer source window until the import dialog is closed by the user. (GP-27)</li>
<li><I>GUI</I>. Fixed an issue in GTreeModel where fireNodeChanged had no effect. This could result in stale node information and truncation of the text associated with a node in a GTree. (GP-30)</li>
<li><I>GUI</I>. Fixed an issue where the file chooser directory list truncated filenames with ellipses on HiDPI Windows. (GP-31)</li>
<li><I>GUI</I>. Fixed an uncaught exception when double-clicking on <code>UndefinedFunction_</code> in Decompiler window. (GP-40)</li>
<li><I>GUI</I>. Updated error handling to only show one dialog when a flurry of errors is encountered. (GP-65, Issue #2185)</li>
<li><I>GUI</I>. Fixed an issue where Docking Windows are restored incorrectly if a snapshot is present. (GP-92)</li>
<li><I>GUI</I>. Fixed a File Chooser bug causing a NullPointerException for some users. (GP-171, Issue #1706)</li>
<li><I>GUI</I>. Fixed an issue that caused the script progress bar to appear intermittently. (GP-179, Issue #1819)</li>
<li><I>GUI</I>. Fixed a bug that caused Call Tree nodes to go missing when showing more than one function with the same name. (GP-213, Issue #1682)</li>
<li><I>GUI:Project Window</I>. Fixed Front End <B>copy</B> action to allow for the copy of program names so that users can paste those names into external applications. (GT-3403, Issue #1257)</li>
<li><I>Headless</I>. Headless Ghidra now properly honors the <code><B>-processor</B></code> flag, even if the specified processor is not a valid <B>opinion</B>. (GT-3376, Issue #1311)</li>
<li><I>Importer</I>. Corrected an NeLoader flags parsing error. (GT-3381, Issue #1312)</li>
<li><I>Importer</I>. Fixed the <B>File -> Add to Program...</B> action to not show a memory conflict error when the user is creating an overlay. (GT-3491, Issue #1376)</li>
<li><I>Importer</I>. Updated the XML Importer to apply repeatable comments. (GT-3492, Issue #1423)</li>
<li><I>Importer</I>. Fixed issue in Batch Import where only one item of a selection was removed when attempting to remove a selection of items. (GP-138)</li>
<li><I>Importer</I>. Corrected various issues with processing crushed PNG images. (GP-146, Issue #1854, #1874, #1875, #2252)</li>
<li><I>Importer</I>. Fixed RuntimeException occurrence when trying to load NE programs with unknown resources. (GP-182, Issue #1596, #1713, #2012)</li>
<li><I>Importer</I>. Fixed batch import to handle IllegalArgumentExceptions thrown by loaders. (GP-227, Issue #2328)</li>
<li><I>Importer:ELF</I>. Corrected ELF relocation processing for ARM BE8 (mixed-endian). (GT-3527, Issue #1494)</li>
<li><I>Importer:ELF</I>. Corrected ELF import processing of <code>DT_JMPREL</code> relocations and markup of associated PLT entries. (GP-252, Issue #2334)</li>
<li><I>Importer:PE</I>. Fixed an IndexOutOfBoundsException in the PeLoader that occurred when the size of a section extends past the end of the file. (GT-3433, Issue #1371)</li>
<li><I>Listing:Comments</I>. Fixed bug in Comment field that prevented navigation when clicking on an address or symbol where tabs were present in the comment. (GT-3440)</li>
<li><I>Memory</I>. Fixed bug where sometimes random bytes are inserted instead of <code>0x00</code> when expanding a memory block. (GT-3465)</li>
<li><I>Processors</I>. Corrected the offset in SuperH instructions generated by sign-extending a 20-bit immediate value composed of two sub-fields. (GT-3251, Issue #1161)</li>
<li><I>Processors</I>. Fixed AVR8 addition/subtraction flag macros. (GT-3276)</li>
<li><I>Processors</I>. Corrected <code>XGATE</code> <code>ROR</code> instruction semantics. (GT-3278)</li>
<li><I>Processors</I>. Corrected semantics for SuperH <code>movi20</code> and <code>movi20s</code> instructions. (GT-3337, Issue #1264)</li>
<li><I>Processors</I>. Corrected SuperH floating point instruction token definition. (GT-3340, Issue #1265)</li>
<li><I>Processors</I>. Corrected SuperH <code>movu.b</code> and <code>movu.w</code> instruction semantics. (GT-3345, Issue #1271)</li>
<li><I>Processors</I>. Corrected AVR8 <code>lpm</code> and <code>elpm</code> instruction semantics. (GT-3346, Issue #631)</li>
<li><I>Processors</I>. Corrected pcode for the 6805 <code>BSET</code> instruction. (GT-3366, Issue #1307)</li>
<li><I>Processors</I>. Corrected ARM constructors for instructions <code>vnmla</code>, <code>vnmls</code>, and <code>vnmul</code>. (GT-3368, Issue #1277)</li>
<li><I>Processors</I>. Corrected bit-pattern for ARM <code>vcvt</code> instruction. (GT-3369, Issue #1278)</li>
<li><I>Processors</I>. Corrected TriCore <code>abs</code> instructions. (GT-3379, Issue #1286)</li>
<li><I>Processors</I>. Corrected x86 <code>BT</code> instruction semantics. (GT-3423, Issue #1370)</li>
<li><I>Processors</I>. Fixed issue where CRC16C <code>LOAD</code>/<code>STOR</code> with <code>abs20</code> were not mapped correctly. (GT-3529, Issue #1518)</li>
<li><I>Processors</I>. Fixed M68000 <code>MOVE USP,x</code> and <code>MOVE x,USP</code> opcodes. (GT-3594, Issue #1593)</li>
<li><I>Processors</I>. Fixed the ARM/Thumb <code>TEQ</code> instruction pcode to be an XOR. (GP-23, Issue #1802)</li>
<li><I>Processors</I>. Emulation was broken by a regression in version 9.1.2. Emulation and Sleigh Pcodetests now work correctly. (GP-24, Issue #1579)</li>
<li><I>Processors</I>. Fixed carry flag issue for 6502 <code>CMP</code>, <code>CPX</code>, and <code>CPY</code> instructions. (GP-34)</li>
<li><I>Processors</I>. Corrected the SuperH high-order bit calculation for the <code>rotr</code> instruction. (GP-47)</li>
<li><I>Processors</I>. Corrected ELF ARM relocation processing for type 3 (<code>R_ARM_REL32</code>) and added support for type 42 (<code>R_ARM_PREL31</code>). (GP-164, Issue #2261, #2276)</li>
<li><I>Scripting</I>. Moved Jython cache directory out of <B>tmp</B>. (GP-36)</li>
<li><I>Scripting</I>. Fixed a NoClassDefFoundError when compiling GhidraScript under JDK14. (GP-59, Issue #2152)</li>
<li><I>Scripting</I>. Fixed issues with null result when searching for the script directory. (GP-103, Issue #2187)</li>
<li><I>Scripting</I>. Fixed scripting issue where, if there were non-ASCII characters in the user path, Jython would not work. (GP-204, Issue #1890)</li>
<li><I>Sleigh</I>. Corrected IndexOutOfBoundsException in SLEIGH when doing simple assignment in disassembly actions block. (GT-3382, Issue #745)</li>
<li><I>Symbol Tree</I>. Fixed the Symbol Tree so that clicking an already-selected symbol node will still trigger a Listing navigation. (GT-3436, Issue #453)</li>
<li><I>Symbol Tree</I>. Fixed the Symbol Tree to not continuously rebuild while performing Auto-analysis. (GT-3542)</li>
<li><I>Version Tracking</I>. Fixed Version Tracking <B>Create Manual Match</B> action. (GT-3305, Issue #2215)</li>
<li><I>Version Tracking</I>. Fixed a NullPointerException encountered when changing the Version Tracking options for the <B>Listing Code Comparison</B> when no data was loaded. (GT-3437, Issue #1143)</li>
<li><I>Version Tracking</I>. Fixed Version Tracking exception triggered in the <B>Exact Functions Instructions Match</B> correlator encountered when the two functions being compared differed in their number of instructions. (GT-3438, Issue #1352)</li>
</ul>
</blockquote>
<H1 align="center">Ghidra 9.1.2 Change History (February 2020)</H1> <H1 align="center">Ghidra 9.1.2 Change History (February 2020)</H1>
<blockquote><p><u>Bugs</u></p> <blockquote><p><u>Bugs</u></p>
<ul> <ul>
@ -16,11 +229,11 @@
<li><I>GUI</I>. Corrected processor manual display for Microsoft Windows users, which was not displaying processor manual and was, instead, rendering a blank page in web browser. (GT-3444)</li> <li><I>GUI</I>. Corrected processor manual display for Microsoft Windows users, which was not displaying processor manual and was, instead, rendering a blank page in web browser. (GT-3444)</li>
<li><I>GUI:Bitfield Editor</I>. Added field comment support to composite bitfield editor. (GT-3410)</li> <li><I>GUI:Bitfield Editor</I>. Added field comment support to composite bitfield editor. (GT-3410)</li>
<li><I>Importer:MachO</I>. A MachO loader regression, in Ghidra 9.1.1, when laying down symbols at the correct location, has been fixed. (GT-3487, Issue #1446)</li> <li><I>Importer:MachO</I>. A MachO loader regression, in Ghidra 9.1.1, when laying down symbols at the correct location, has been fixed. (GT-3487, Issue #1446)</li>
<li><I>Languages</I>. Corrected mnemonic for ARM thumb <code>RSB.w</code> instruction. (GT-3420, Issue #1365)</li>
<li><I>Languages</I>. Corrected issue in M68000 with some move instructions not creating correct array assignments. (GT-3429, Issue #1394)</li>
<li><I>Languages</I>. Updated x86 processor manual index file with latest Intel and AMD manuals. (GT-3489, Issue #1078)</li>
<li><I>Multi-User:Ghidra Server</I>. Corrected Ghidra Server remote interface errors that occur when running with Java 11.0.6 (and later) release, which would throw RemoteException <code>"Method is not Remote"</code> errors. (GT-3521, Issue #1440)</li> <li><I>Multi-User:Ghidra Server</I>. Corrected Ghidra Server remote interface errors that occur when running with Java 11.0.6 (and later) release, which would throw RemoteException <code>"Method is not Remote"</code> errors. (GT-3521, Issue #1440)</li>
<li><I>PDB</I>. Corrected PDB XML generation for zero-length classes and structures and resolved various datatype dependency issues encountered during PDB Analysis. Changed line numbers from hex to decimal. (GT-3462, Issue #1410)</li> <li><I>PDB</I>. Corrected PDB XML generation for zero-length classes and structures and resolved various datatype dependency issues encountered during PDB Analysis. Changed line numbers from hex to decimal. (GT-3462, Issue #1410)</li>
<li><I>Processors</I>. Corrected mnemonic for ARM thumb <code>RSB.w</code> instruction. (GT-3420, Issue #1365)</li>
<li><I>Processors</I>. Corrected issue in M68000 with some move instructions not creating correct array assignments. (GT-3429, Issue #1394)</li>
<li><I>Processors</I>. Updated x86 processor manual index file with latest Intel and AMD manuals. (GT-3489, Issue #1078)</li>
</ul> </ul>
</blockquote> </blockquote>
@ -31,6 +244,7 @@
<li><I>Program API</I>. Cached the addresses that correspond to executable memory to improve analysis performance. (GT-3260)</li> <li><I>Program API</I>. Cached the addresses that correspond to executable memory to improve analysis performance. (GT-3260)</li>
</ul> </ul>
</blockquote> </blockquote>
<blockquote><p><u>Bugs</u></p> <blockquote><p><u>Bugs</u></p>
<ul> <ul>
<li><I>Analysis</I>. Fixed a symbol name error that occurred in the Objective-C analyzer. (GT-3321, Issue #1200)</li> <li><I>Analysis</I>. Fixed a symbol name error that occurred in the Objective-C analyzer. (GT-3321, Issue #1200)</li>
@ -42,17 +256,17 @@
<li><I>Decompiler</I>. Fixed issue with enum name strings causing <code>Low-level Error: XML error: syntax error</code> in the decompiler. (GT-3387, Issue #1329)</li> <li><I>Decompiler</I>. Fixed issue with enum name strings causing <code>Low-level Error: XML error: syntax error</code> in the decompiler. (GT-3387, Issue #1329)</li>
<li><I>GUI</I>. Fixed a potential ConcurrentModificationException in the interactive python interpreter. (GT-3280)</li> <li><I>GUI</I>. Fixed a potential ConcurrentModificationException in the interactive python interpreter. (GT-3280)</li>
<li><I>Importer:PE</I>. Fixed an exception in the PeLoader that occurred when the size of the memory block for the headers is larger than the file size. (GT-3344, Issue #1266)</li> <li><I>Importer:PE</I>. Fixed an exception in the PeLoader that occurred when the size of the memory block for the headers is larger than the file size. (GT-3344, Issue #1266)</li>
<li><I>Languages</I>. Corrected Sparc floating point instruction pcode implementation. (GT-3202)</li>
<li><I>Languages</I>. Corrected the semantics of the PowerPC <code>e_cmpi</code> instruction. (GT-3228, Issue #1127)</li>
<li><I>Languages</I>. Corrected bit generation for PowerPC instructions <code>se_bclri</code>, <code>se_bgeni</code>, <code>se_bseti</code>, and <code>se_btsti</code>. (GT-3232, Issue #967)</li>
<li><I>Languages</I>. Corrected register definitions for x86 <code>RDRAND</code> instruction. (GT-3253, Issue #1169)</li>
<li><I>Languages</I>. Corrected signed immediate calculation for some powerPC VLE offsets being incorrect. (GT-3254, Issue #1160)</li>
<li><I>Languages</I>. Resolved issue with x86 escape opcodes preventing certain instruction patterns from decoding. (GT-3256)</li>
<li><I>Languages</I>. Corrected bug in <code>XGATE</code> <code>LDH</code> instruction shifting out high bits. (GT-3268)</li>
<li><I>Languages</I>. Corrected processing of <code>R_MIPS_REL32</code>, <code>R_X86_64_RELATIVE</code>, and <code>R_X86_64_RELATIVE64</code> ELF relocations affecting relocatable binaries which have non-zero section/segment load addresses. (GT-3349)</li>
<li><I>Listing</I>. Fixed missing scroll bar in listing. (GT-3290)</li> <li><I>Listing</I>. Fixed missing scroll bar in listing. (GT-3290)</li>
<li><I>Listing</I>. Fixed issue that was causing a stack trace to be generated when contiguous addresses were cleared for a range greater than <code>Integer.MAX</code>. (GT-3357)</li> <li><I>Listing</I>. Fixed issue that was causing a stack trace to be generated when contiguous addresses were cleared for a range greater than <code>Integer.MAX</code>. (GT-3357)</li>
<li><I>Listing:References</I>. Corrected <B>Create Default Reference</B> action bug which did not handle composite/array data components properly. (GT-3371)</li> <li><I>Listing:References</I>. Corrected <B>Create Default Reference</B> action bug which did not handle composite/array data components properly. (GT-3371)</li>
<li><I>Processors</I>. Corrected Sparc floating point instruction pcode implementation. (GT-3202)</li>
<li><I>Processors</I>. Corrected the semantics of the PowerPC <code>e_cmpi</code> instruction. (GT-3228, Issue #1127)</li>
<li><I>Processors</I>. Corrected bit generation for PowerPC instructions <code>se_bclri</code>, <code>se_bgeni</code>, <code>se_bseti</code>, and <code>se_btsti</code>. (GT-3232, Issue #967)</li>
<li><I>Processors</I>. Corrected register definitions for x86 <code>RDRAND</code> instruction. (GT-3253, Issue #1169)</li>
<li><I>Processors</I>. Corrected signed immediate calculation for some powerPC VLE offsets being incorrect. (GT-3254, Issue #1160)</li>
<li><I>Processors</I>. Resolved issue with x86 escape opcodes preventing certain instruction patterns from decoding. (GT-3256)</li>
<li><I>Processors</I>. Corrected bug in <code>XGATE</code> <code>LDH</code> instruction shifting out high bits. (GT-3268)</li>
<li><I>Processors</I>. Corrected processing of <code>R_MIPS_REL32</code>, <code>R_X86_64_RELATIVE</code>, and <code>R_X86_64_RELATIVE64</code> ELF relocations affecting relocatable binaries which have non-zero section/segment load addresses. (GT-3349)</li>
</ul> </ul>
</blockquote> </blockquote>
@ -64,21 +278,20 @@
<li><I>GUI</I>. Added method for turning off table sorting by control-clicking the only sorted table column. (GT-2763, Issue #87)</li> <li><I>GUI</I>. Added method for turning off table sorting by control-clicking the only sorted table column. (GT-2763, Issue #87)</li>
<li><I>GUI</I>. Hovering on an address will now show where the byte at that address came from in the imported file. (GT-3016, Issue #154)</li> <li><I>GUI</I>. Hovering on an address will now show where the byte at that address came from in the imported file. (GT-3016, Issue #154)</li>
<li><I>Importer:MachO</I>. Added new importer/loader for DYLD-shared cache files. (GT-2343)</li> <li><I>Importer:MachO</I>. Added new importer/loader for DYLD-shared cache files. (GT-2343)</li>
<li><I>Languages</I>. Implemented Intel MCS-96 processor module. (GT-2350)</li>
<li><I>Languages</I>. Added SH1/2/2a sleigh processor specification. (GT-3029, Issue #715)</li>
<li><I>Languages</I>. Added Tricore processor specification. (GT-3041, Issue #567)</li>
<li><I>Languages</I>. Added HCS12X processor specification. (GT-3049)</li>
<li><I>Languages</I>. Added HCS05 and HCS08 sleigh processor specifications. (GT-3050)</li>
<li><I>Languages</I>. Added SH4 sleigh processor specification. (GT-3051, Issue #37)</li>
<li><I>Languages</I>. Added MCS-48 processor specification. (GT-3058, Issue #638)</li>
<li><I>Memory</I>. Added new API to preserve imported program's original bytes and how they map to memory blocks. (GT-2845)</li> <li><I>Memory</I>. Added new API to preserve imported program's original bytes and how they map to memory blocks. (GT-2845)</li>
<li><I>Processors</I>. Implemented Intel MCS-96 processor module. (GT-2350)</li>
<li><I>Processors</I>. Added SH1/2/2a sleigh processor specification. (GT-3029, Issue #715)</li>
<li><I>Processors</I>. Added Tricore processor specification. (GT-3041, Issue #567)</li>
<li><I>Processors</I>. Added HCS12X processor specification. (GT-3049)</li>
<li><I>Processors</I>. Added HCS05 and HCS08 sleigh processor specifications. (GT-3050)</li>
<li><I>Processors</I>. Added SH4 sleigh processor specification. (GT-3051, Issue #37)</li>
<li><I>Processors</I>. Added MCS-48 processor specification. (GT-3058, Issue #638)</li>
<li><I>Program API</I>. Added Bit-field support for structures and unions. <li><I>Program API</I>. Added Bit-field support for structures and unions.
<B>Warning:</B> Version upgrade will be forced on all modified programs and data type archives that are open for update. (GT-557)</li> <B>Warning:</B> Version upgrade will be forced on all modified programs and data type archives that are open for update. (GT-557)</li>
<li><I>Sleigh</I>. Added two new extension modules (SleighDevTools and GnuDisassembler) in support of processor module development. Added support for pcode junit tests which utilize emulation of cross-compiled C test code to verify sleigh pcode (i.e., instruction semantics). The SleighDevTools extension provides the pcode test C source and associated build scripts, as well as external disassembler support for aiding in the validation of disassembled instruction syntax. (GT-3067)</li> <li><I>Sleigh</I>. Added two new extension modules (SleighDevTools and GnuDisassembler) in support of processor module development. Added support for pcode junit tests which utilize emulation of cross-compiled C test code to verify sleigh pcode (i.e., instruction semantics). The SleighDevTools extension provides the pcode test C source and associated build scripts, as well as external disassembler support for aiding in the validation of disassembled instruction syntax. (GT-3067)</li>
</ul> </ul>
</blockquote> </blockquote>
<blockquote><p><u>Improvements</u></p> <blockquote><p><u>Improvements</u></p>
<ul> <ul>
<li><I>Analysis</I>. Added example script, ResolveX86orX64LinuxSyscallsScript.java, for decompiling Linux system calls in x86 and x64. Added syscall-related exercises to <B>Advanced</B> class. (GT-3113)</li> <li><I>Analysis</I>. Added example script, ResolveX86orX64LinuxSyscallsScript.java, for decompiling Linux system calls in x86 and x64. Added syscall-related exercises to <B>Advanced</B> class. (GT-3113)</li>
@ -123,18 +336,6 @@
<li><I>Importer:PE</I>. PeLoader better accounts for section alignment when laying out memory blocks, allowing additional bytes from the file to be loaded into memory. (GT-2827, Issue #327, #418)</li> <li><I>Importer:PE</I>. PeLoader better accounts for section alignment when laying out memory blocks, allowing additional bytes from the file to be loaded into memory. (GT-2827, Issue #327, #418)</li>
<li><I>Importer:PE</I>. Removed out-of-place call to demangler and laying down of types from PeLoader. This fix enables demangling and other analyzers to be applied correctly and in the proper order. (GT-2849)</li> <li><I>Importer:PE</I>. Removed out-of-place call to demangler and laying down of types from PeLoader. This fix enables demangling and other analyzers to be applied correctly and in the proper order. (GT-2849)</li>
<li><I>Importer:PE</I>. PeLoader now adds TLS callback functions as entry points. (GT-2898, Issue #102)</li> <li><I>Importer:PE</I>. PeLoader now adds TLS callback functions as entry points. (GT-2898, Issue #102)</li>
<li><I>Languages</I>. Added new Task Monitor service to better handle user experience when there are delays in building languages. (GT-2376)</li>
<li><I>Languages</I>. Corrected ARM/Thumb instruction parsing for Thumb <code>bl</code> and <code>add</code> instructions. (GT-2744, Issue #362)</li>
<li><I>Languages</I>. Added AVR8 manual index file. (GT-2828, Issue #346)</li>
<li><I>Languages</I>. Improved support for ARM on Windows. (GT-2880)</li>
<li><I>Languages</I>. M68000 <code>LSL.W</code>, <code>ASL.B</code>, <code>LSL.B</code>, and <code>ASL.W</code> instructions now correctly set the <code>CF</code> flag. (GT-2907, Issue #619)</li>
<li><I>Languages</I>. Updated x86 manual index files. (GT-2943, Issue #366)</li>
<li><I>Languages</I>. Improved macro label-related error reporting in slaspec files. (GT-2995, Issue #522)</li>
<li><I>Languages</I>. Added MIPS special <code>0x1f</code> patterns. (GT-3005, Issue #709)</li>
<li><I>Languages</I>. Added proper updating of the <code>X</code> condition flag register for the M68000 processor <code>lsl</code> and <code>lsr</code> instructions. (GT-3137, Issue #983)</li>
<li><I>Languages</I>. Implemented PowerPc VLE Interrupt Handler Efficiency Instructions. (GT-3143, Issue #935)</li>
<li><I>Languages</I>. Ghidra now correctly models SPARC 64-bit <B>stack bias</B>. (GT-3201)</li>
<li><I>Languages</I>. Updated AVR32 instruction manual index to latest version. (GT-712)</li>
<li><I>Listing</I>. Updated Listing to support horizontal scrolling by holding the <B>Shift</B> key when using the mouse wheel. (GT-3105, Issue #451)</li> <li><I>Listing</I>. Updated Listing to support horizontal scrolling by holding the <B>Shift</B> key when using the mouse wheel. (GT-3105, Issue #451)</li>
<li><I>Listing:References</I>. Created new <B>overriding</B> reference types, which improve and extend the ability to override calls, jumps, and callothers. (GT-2885)</li> <li><I>Listing:References</I>. Created new <B>overriding</B> reference types, which improve and extend the ability to override calls, jumps, and callothers. (GT-2885)</li>
<li><I>Multi-User</I>. Added a script to allow repository admins the ability to terminate multiple file checkouts belonging to an individual user on a shared project. (GT-2893)</li> <li><I>Multi-User</I>. Added a script to allow repository admins the ability to terminate multiple file checkouts belonging to an individual user on a shared project. (GT-2893)</li>
@ -143,6 +344,18 @@
<li><I>PDB</I>. Now using HTTPS for Microsoft symbol server URL. (GT-2819, Issue #369)</li> <li><I>PDB</I>. Now using HTTPS for Microsoft symbol server URL. (GT-2819, Issue #369)</li>
<li><I>PDB</I>. PDB processing can now store data types that contain forward slashes under a CategoryPath. (GT-2974, Issue #94, #182)</li> <li><I>PDB</I>. PDB processing can now store data types that contain forward slashes under a CategoryPath. (GT-2974, Issue #94, #182)</li>
<li><I>PDB</I>. PDB Analyzer no longer automatically includes the PDB path specified in the program's PE header when searching for the PDB. However, the filename in this path is considered during the search. The analyzer's <B>Unsafe: Include PE PDB Path in PDB Search</B> option allows the user to revert to the original PDB search algorithm. (GT-3076, Issue #277)</li> <li><I>PDB</I>. PDB Analyzer no longer automatically includes the PDB path specified in the program's PE header when searching for the PDB. However, the filename in this path is considered during the search. The analyzer's <B>Unsafe: Include PE PDB Path in PDB Search</B> option allows the user to revert to the original PDB search algorithm. (GT-3076, Issue #277)</li>
<li><I>Processors</I>. Added new Task Monitor service to better handle user experience when there are delays in building languages. (GT-2376)</li>
<li><I>Processors</I>. Corrected ARM/Thumb instruction parsing for Thumb <code>bl</code> and <code>add</code> instructions. (GT-2744, Issue #362)</li>
<li><I>Processors</I>. Added AVR8 manual index file. (GT-2828, Issue #346)</li>
<li><I>Processors</I>. Improved support for ARM on Windows. (GT-2880)</li>
<li><I>Processors</I>. M68000 <code>LSL.W</code>, <code>ASL.B</code>, <code>LSL.B</code>, and <code>ASL.W</code> instructions now correctly set the <code>CF</code> flag. (GT-2907, Issue #619)</li>
<li><I>Processors</I>. Updated x86 manual index files. (GT-2943, Issue #366)</li>
<li><I>Processors</I>. Improved macro label-related error reporting in slaspec files. (GT-2995, Issue #522)</li>
<li><I>Processors</I>. Added MIPS special <code>0x1f</code> patterns. (GT-3005, Issue #709)</li>
<li><I>Processors</I>. Added proper updating of the <code>X</code> condition flag register for the M68000 processor <code>lsl</code> and <code>lsr</code> instructions. (GT-3137, Issue #983)</li>
<li><I>Processors</I>. Implemented PowerPc VLE Interrupt Handler Efficiency Instructions. (GT-3143, Issue #935)</li>
<li><I>Processors</I>. Ghidra now correctly models SPARC 64-bit <B>stack bias</B>. (GT-3201)</li>
<li><I>Processors</I>. Updated AVR32 instruction manual index to latest version. (GT-712)</li>
<li><I>Program API</I>. Added SHA256 hash to Program metadata and API. (GT-2753, Issue #331)</li> <li><I>Program API</I>. Added SHA256 hash to Program metadata and API. (GT-2753, Issue #331)</li>
<li><I>Scripting</I>. Updated Script Table Chooser Dialog: to fix bug with tracking work items, to add new API methods for item removal and dialog closed notification, and to prevent the same item from being worked on more than once. (GT-2724, Issue #307)</li> <li><I>Scripting</I>. Updated Script Table Chooser Dialog: to fix bug with tracking work items, to add new API methods for item removal and dialog closed notification, and to prevent the same item from being worked on more than once. (GT-2724, Issue #307)</li>
<li><I>Scripting</I>. Fixed MultiInstructionMemReference Ghidra script to place the reference correctly on instructions with a delay slot. (GT-2906)</li> <li><I>Scripting</I>. Fixed MultiInstructionMemReference Ghidra script to place the reference correctly on instructions with a delay slot. (GT-2906)</li>
@ -216,25 +429,6 @@
<li><I>Importer:ELF</I>. Improved ELF MIPS support for GP-relative relocations encountered in PIC compiled binaries. Also added support for <code>R_MIPS_RPREL32</code> relocation. (GT-3026, Issue #764)</li> <li><I>Importer:ELF</I>. Improved ELF MIPS support for GP-relative relocations encountered in PIC compiled binaries. Also added support for <code>R_MIPS_RPREL32</code> relocation. (GT-3026, Issue #764)</li>
<li><I>Importer:ELF</I>. ELF x86-64 relocations <code>R_X86_64_GOT32</code>, <code>R_X86_64_PLT32</code>, <code>R_X86_64_SIZE32</code>, <code>R_X86_64_SIZE64</code>, and <code>R_X86_64_GOTPC32</code> have been fixed to relocate correctly. Additional ELF x86-64 relocations, found mostly in unlinked <B>.o</B> files, have been added. (GT-3089, Issue #910)</li> <li><I>Importer:ELF</I>. ELF x86-64 relocations <code>R_X86_64_GOT32</code>, <code>R_X86_64_PLT32</code>, <code>R_X86_64_SIZE32</code>, <code>R_X86_64_SIZE64</code>, and <code>R_X86_64_GOTPC32</code> have been fixed to relocate correctly. Additional ELF x86-64 relocations, found mostly in unlinked <B>.o</B> files, have been added. (GT-3089, Issue #910)</li>
<li><I>Importer:PE</I>. Fixed a problem in the PeLoader that would result in section names being incorrectly used as primary symbols. This could result in function names being wrong. (GT-3195, Issue #761, #1051)</li> <li><I>Importer:PE</I>. Fixed a problem in the PeLoader that would result in section names being incorrectly used as primary symbols. This could result in function names being wrong. (GT-3195, Issue #761, #1051)</li>
<li><I>Languages</I>. Utilized <code>FLOAT_NEG</code> pcodeop to simplify PowerPC <code>fneg</code> instructions. (GT-2781, Issue #387)</li>
<li><I>Languages</I>. Added 6502 <code>I</code> status bit save and restore. (GT-2826, Issue #469)</li>
<li><I>Languages</I>. Corrected alternate register definitions in z80 processor. (GT-2876, Issue #520)</li>
<li><I>Languages</I>. Reviewed all processor modules for GhidraSleighEditor syntax errors. (GT-2902)</li>
<li><I>Languages</I>. Added support for <code>RD</code>, <code>WR</code>, <code>FS</code>, and <code>GSBASE</code> instructions in x86. (GT-2940, Issue #554, #555)</li>
<li><I>Languages</I>. Added fixes for sign extension of <code>ADD</code>, <code>AND</code>, <code>CMP</code>, and <code>SUB</code> instructions on x86-64bit. (GT-2955, Issue #881)</li>
<li><I>Languages</I>. Updated PIC-30 division pcode to correct decompilation issue. (GT-3008)</li>
<li><I>Languages</I>. Fixed x86 <code>AAM</code> instruction. (GT-3015)</li>
<li><I>Languages</I>. Corrected x86 decode of <code>MOVBE</code> instruction. (GT-3039, Issue #822)</li>
<li><I>Languages</I>. Corrected M68000 <code>mov3q</code> instruction decode and semantics. (GT-3080, Issue #905)</li>
<li><I>Languages</I>. The JVM instruction <code>I2D</code> now correctly pushes an 8-byte double on the stack. (GT-3081)</li>
<li><I>Languages</I>. Fixed problem displaying processor manuals in Windows Firefox. (GT-3084)</li>
<li><I>Languages</I>. Encoding of <code>MOV</code> into debug registers has been relaxed. (GT-3117)</li>
<li><I>Languages</I>. Corrected behavior of PowerPC <code>vectorPermute</code> pcodeop for emulation. (GT-3148)</li>
<li><I>Languages</I>. Corrected MIPS relocation computation for <code>R_MIPS_26</code>, <code>R_MIPS16_26</code>, and <code>R_MICROMIPS_26_S1</code>. (GT-3154, Issue #1001)</li>
<li><I>Languages</I>. Corrected the bit patterns for PowerPC VLE <code>rlwimi</code> and <code>rlwinm</code> instructions. (GT-3159, Issue #752)</li>
<li><I>Languages</I>. Corrected instruction semantics for AARCH64 <code>BLR</code> instruction. (GT-3191)</li>
<li><I>Languages</I>. Corrected fall-through override semantics for cases where pcode simply drops into the next address. (GT-3196, Issue #1083)</li>
<li><I>Languages</I>. Corrected the semantics of the PowerPC <code>se_bmaski</code> instruction. (GT-3230, Issue #1123)</li>
<li><I>Listing</I>. Fixed potential infinite loop when editing long comments. (GT-2824, Issue #437)</li> <li><I>Listing</I>. Fixed potential infinite loop when editing long comments. (GT-2824, Issue #437)</li>
<li><I>Listing</I>. Fixed potential ClassCastException in Listing comments. (GT-3023)</li> <li><I>Listing</I>. Fixed potential ClassCastException in Listing comments. (GT-3023)</li>
<li><I>Listing</I>. Cursor in the listing now stays in the proper column after editing a field. (GT-3045, Issue #702)</li> <li><I>Listing</I>. Cursor in the listing now stays in the proper column after editing a field. (GT-3045, Issue #702)</li>
@ -248,6 +442,25 @@
<li><I>PDB</I>. Addressed memory leaks and string handling issues in <B>pdb.exe</B>. (GT-2975, Issue #674, #597, #598, #599, #600)</li> <li><I>PDB</I>. Addressed memory leaks and string handling issues in <B>pdb.exe</B>. (GT-2975, Issue #674, #597, #598, #599, #600)</li>
<li><I>PDB</I>. Can now recover stack variables from more recent Visual Studio version PDBs. (GT-3014)</li> <li><I>PDB</I>. Can now recover stack variables from more recent Visual Studio version PDBs. (GT-3014)</li>
<li><I>PDB</I>. Fixed PDB validation logic, which caused a more severe error message to be created, masking the real issue. (GT-3209, Issue #198, #1024)</li> <li><I>PDB</I>. Fixed PDB validation logic, which caused a more severe error message to be created, masking the real issue. (GT-3209, Issue #198, #1024)</li>
<li><I>Processors</I>. Utilized <code>FLOAT_NEG</code> pcodeop to simplify PowerPC <code>fneg</code> instructions. (GT-2781, Issue #387)</li>
<li><I>Processors</I>. Added 6502 <code>I</code> status bit save and restore. (GT-2826, Issue #469)</li>
<li><I>Processors</I>. Corrected alternate register definitions in z80 processor. (GT-2876, Issue #520)</li>
<li><I>Processors</I>. Reviewed all processor modules for GhidraSleighEditor syntax errors. (GT-2902)</li>
<li><I>Processors</I>. Added support for <code>RD</code>, <code>WR</code>, <code>FS</code>, and <code>GSBASE</code> instructions in x86. (GT-2940, Issue #554, #555)</li>
<li><I>Processors</I>. Added fixes for sign extension of <code>ADD</code>, <code>AND</code>, <code>CMP</code>, and <code>SUB</code> instructions on x86-64bit. (GT-2955, Issue #881)</li>
<li><I>Processors</I>. Updated PIC-30 division pcode to correct decompilation issue. (GT-3008)</li>
<li><I>Processors</I>. Fixed x86 <code>AAM</code> instruction. (GT-3015)</li>
<li><I>Processors</I>. Corrected x86 decode of <code>MOVBE</code> instruction. (GT-3039, Issue #822)</li>
<li><I>Processors</I>. Corrected M68000 <code>mov3q</code> instruction decode and semantics. (GT-3080, Issue #905)</li>
<li><I>Processors</I>. The JVM instruction <code>I2D</code> now correctly pushes an 8-byte double on the stack. (GT-3081)</li>
<li><I>Processors</I>. Fixed problem displaying processor manuals in Windows Firefox. (GT-3084)</li>
<li><I>Processors</I>. Encoding of <code>MOV</code> into debug registers has been relaxed. (GT-3117)</li>
<li><I>Processors</I>. Corrected behavior of PowerPC <code>vectorPermute</code> pcodeop for emulation. (GT-3148)</li>
<li><I>Processors</I>. Corrected MIPS relocation computation for <code>R_MIPS_26</code>, <code>R_MIPS16_26</code>, and <code>R_MICROMIPS_26_S1</code>. (GT-3154, Issue #1001)</li>
<li><I>Processors</I>. Corrected the bit patterns for PowerPC VLE <code>rlwimi</code> and <code>rlwinm</code> instructions. (GT-3159, Issue #752)</li>
<li><I>Processors</I>. Corrected instruction semantics for AARCH64 <code>BLR</code> instruction. (GT-3191)</li>
<li><I>Processors</I>. Corrected fall-through override semantics for cases where pcode simply drops into the next address. (GT-3196, Issue #1083)</li>
<li><I>Processors</I>. Corrected the semantics of the PowerPC <code>se_bmaski</code> instruction. (GT-3230, Issue #1123)</li>
<li><I>Program API</I>. Corrected parameter storage which failed to properly refresh after undo/redo. (GT-3130, Issue #960)</li> <li><I>Program API</I>. Corrected parameter storage which failed to properly refresh after undo/redo. (GT-3130, Issue #960)</li>
<li><I>Program API</I>. Corrected function parameter ordinal numbering when more than one auto-parameter is present. (GT-3214)</li> <li><I>Program API</I>. Corrected function parameter ordinal numbering when more than one auto-parameter is present. (GT-3214)</li>
<li><I>Project Manager</I>. Fixed a problem with creating Ghidra projects in Windows root directories (e.g., Z:\). (GT-2585)</li> <li><I>Project Manager</I>. Fixed a problem with creating Ghidra projects in Windows root directories (e.g., Z:\). (GT-2585)</li>
@ -262,109 +475,78 @@
</blockquote> </blockquote>
<H1 align="center">Ghidra 9.0.4 Change History (May 2019)</H1> <H1 align="center">Ghidra 9.0.4 Change History (May 2019)</H1>
<blockquote><p><u>Bugs</u></p>
<blockquote><p><u>Bugs</u></p></blockquote>
<blockquote>
<ul> <ul>
<li><I>Multi-User:Ghidra Server</I>. Corrected severe script error in svrAdmin.bat introduced with 9.0.3 build.</li>
<li><I>Multi-User:Ghidra Server.</I> Corrected severe script error in svrAdmin.bat introduced with 9.0.3 build.</li> <li><I>GUI</I>. Restored the default 'p' key binding for creating pointers within the listing display.</li>
<li><I>GUI.</I> Restored the default 'p' key binding for creating pointers within the listing display.</li>
</ul> </ul>
</blockquote> </blockquote>
<H1 align="center">Ghidra 9.0.3 Change History (April 2019)</H1> <H1 align="center">Ghidra 9.0.3 Change History (April 2019)</H1>
<blockquote><p><u>New Features</u></p>
<blockquote><p><u>New Feature</u></p></blockquote>
<blockquote>
<ul> <ul>
<li><I>GUI</I>. Function tags are now viewable from Functions Window table using new column.</li>
<li><I>GUI.</I> Function tags are now viewable from Functions Window table using new column.</li>
</ul> </ul>
</blockquote> </blockquote>
<blockquote><p><u>Improvements</u></p></blockquote> <blockquote><p><u>Improvements</u></p>
<blockquote>
<ul> <ul>
<li><I>Decompiler</I>. Improved modeling of CFG on Windows 10. (Issue #340)</li>
<li><I>Decompiler.</I> Improved modeling of CFG on Windows 10. (Issue #340)</li> <li><I>Patcher</I>. Renamed patch directory to /Ghidra/patch and added README.txt that explains how the patch directory is used.</li>
<li><I>Patcher.</I> Renamed patch directory to /Ghidra/patch and added README.txt that explains how the patch directory is used.</li> <li><I>Search</I>. Updated the Decompiler Data Type Finder to find references to inside of nested array access in a line of Decompiler C output. (Issue #416)</li>
<li><I>Search.</I> Updated the Decompiler Data Type Finder to find references to inside of nested array access in a line of Decompiler C output. (Issue #416)</li> <li><I>Sleigh</I>. Improved error reporting for SLEIGH compiler. (Issue #364)</li>
<li><I>Sleigh.</I> Improved error reporting for SLEIGH compiler. (Issue #364)</li>
</ul> </ul>
</blockquote> </blockquote>
<blockquote><p><u>Bugs</u></p></blockquote> <blockquote><p><u>Bugs</u></p>
<blockquote>
<ul> <ul>
<li><I>Analysis</I>. Code that checks for thunks no longer throws an exception if the PC is not set for the processor.</li>
<li><I>Analysis.</I> Code that checks for thunks no longer throws an exception if the PC is not set for the processor.</li> <li><I>Analysis</I>. Made a fix to enable Apply button when changing tool options. (Issue #40)</li>
<li><I>Analysis.</I> Made a fix to enable Apply button when changing tool options. (Issue #40)</li> <li><I>Data Types</I>. Fixed concurrent modification exception when replacing one datatype for another that results in some other datatype being renamed. </li>
<li><I>Data Types.</I> Fixed concurrent modification exception when replacing one datatype for another that results in some other datatype being renamed. </li> <li><I>Decompiler</I>. Fixed dynamic variables and equates in 16-bit x86 programs. (Issue #336)</li>
<li><I>Decompiler.</I> Fixed dynamic variables and equates in 16-bit x86 programs. (Issue #336)</li> <li><I>Decompiler:Java</I>. Fixed DEX decompilation regression issue. (Issue #350)</li>
<li><I>Decompiler:Java.</I> Fixed DEX decompilation regression issue. (Issue #350)</li> <li><I>Eclipse Integration</I>. Fixed exception in Eclipse GhidraDev plugin that occurred when performing certain actions on a Ghidra project that was imported from a previously exported Archive File. (Issues #283, #383)</li>
<li><I>Eclipse Integration.</I> Fixed exception in Eclipse GhidraDev plugin that occurred when performing certain actions on a Ghidra project that was imported from a previously exported Archive File. (Issues #283, #383)</li> <li><I>GUI</I>. Improved documentation on how to deal with HiDPI monitor issues in Linux. In the <I>&lt;ghidra_installation&gt;</I>/support/launch.properties file, change VMARGS=-Dsun.java2d.xrender from false to true.</li>
<li><I>GUI.</I> Improved documentation on how to deal with HiDPI monitor issues in Linux. In the <I>&lt;ghidra_installation&gt;</I>/support/launch.properties file, change VMARGS=-Dsun.java2d.xrender from false to true.</li> <li><I>Importer</I>. Fixed an exception that occurred when batch importing APK files. (Issue #426)</li>
<li><I>Importer.</I> Fixed an exception that occurred when batch importing APK files. (Issue #426)</li> <li><I>Multi-User:Ghidra Server</I>. Restored ability to execute svrAdmin script in development mode. </li>
<li><I>Languages.</I> The 6502 Zero page indexed addressing has been corrected to only access the Zero page. (Issue #201)</li> <li><I>Processors</I>. The 6502 Zero page indexed addressing has been corrected to only access the Zero page. (Issue #201)</li>
<li><I>Languages.</I> The 68000 BCD arithmetic instructions now have pcode semantics that allow disassembly to continue. (Issue #227)</li> <li><I>Processors</I>. The 68000 BCD arithmetic instructions now have pcode semantics that allow disassembly to continue. (Issue #227)</li>
<li><I>Multi-User:Ghidra Server.</I> Restored ability to execute svrAdmin script in development mode. </li> <li><I>Search</I>. Fixed NullPointerException in Decompiler Data Type Reference Finder. (Issue #407)</li>
<li><I>Search.</I> Fixed NullPointerException in Decompiler Data Type Reference Finder. (Issue #407)</li>
</ul> </ul>
</blockquote> </blockquote>
<H1 align="center">Ghidra 9.0.2 Change History (April 2019)</H1> <H1 align="center">Ghidra 9.0.2 Change History (April 2019)</H1>
<blockquote><p><u>Bugs</u></p>
<blockquote><p><u>Bugs</u></p></blockquote>
<blockquote>
<ul> <ul>
<li><I>Analysis</I>. Constant reference analysis boundary controls for speculative references has been fixed. Speculative references are references created from computed constants passed as parameters, stored to a location, or from indexed offsets from a register. (Issue #228)</li>
<li><I>Analysis.</I> Constant reference analysis boundary controls for speculative references has been fixed. Speculative references are references created from computed constants passed as parameters, stored to a location, or from indexed offsets from a register. (Issue #228)</li> <li><I>Decompiler</I>. Fixed rendering bug in the Decompiler when the "Find" dialog is closed. (Issue #282) </li>
<li><I>Decompiler</I>. Fixed decompiler handling of Function Definition data types. (Issue #247) </li>
<li><I>Decompiler. </I> Fixed rendering bug in the Decompiler when the "Find" dialog is closed. (Issue #282) </li> <li><I>Decompiler</I>. Fixed "Free Varnode" exception in RuleConditionalMove. (Issue #294) </li>
<li><I>Decompiler. </I> Fixed decompiler handling of Function Definition data types. (Issue #247) </li> <li><I>Diff</I>. Fixed exceptions that can occur in the Diff View for programs with overlays. </li>
<li><I>Documentation</I>. Corrected the spelling of "listener" throughout the source code. (Issue #235) </li>
<li><I>Decompiler. </I> Fixed "Free Varnode" exception in RuleConditionalMove. (Issue #294) </li> <li><I>Exporter</I>. Exporting a selection as Intel Hex will now allow a selection of any length. Previously this was restricted to multiples of 16 bytes. (Issue #260) </li>
<li><I>GUI</I>. Fixed exception that occurs after disabling MyProgramChangesDisplayPlugin. </li>
<li><I>Diff. </I> Fixed exceptions that can occur in the Diff View for programs with overlays. </li> <li><I>GUI</I>. Updated the "Open Program" dialog to disallow file drop operations. (Issue #252)
<li><I>Multi-User:Ghidra Server</I>. Corrected bug introduced into ghidraSvr.bat which could prevent Ghidra Server startup (Issue #279) </li>
<li><I>Documentation. </I> Corrected the spelling of "listener" throughout the source code. (Issue #235) </li> <li><I>Processors</I>. The ARM Thumb CMP.W and LSL instructions have been changed to correctly decode. There are still issues to work out with Unpredictable execution when Rd is the PC. (Issue #280) </li>
<li><I>Exporter. </I> Exporting a selection as Intel Hex will now allow a selection of any length. Previously this was restricted to multiples of 16 bytes. (Issue #260) </li> <li><I>Scripting</I>. MultiInstructionMemReference script has been corrected to consider input and output registers when placing a reference on an instruction.</li>
<li><I>GUI. </I> Fixed exception that occurs after disabling MyProgramChangesDisplayPlugin. </li>
<li><I>GUI.</I> Updated the "Open Program" dialog to disallow file drop operations. (Issue #252)
<li><I>Languages. </I> The ARM Thumb CMP.W and LSL isntructions have been changed to correctly decode. There are still issues to work out with Unpredictable execution when Rd is the PC. (Issue #280) </li>
<li><I>Multi-User:Ghidra Server. </I> Corrected bug introduced into ghidraSvr.bat which could prevent Ghidra Server startup (Issue #279) </li>
<li><I>Scripting.</I> MultiInstructionMemReference script has been corrected to consider input and output registers when placing a reference on an instruction.</li>
</ul> </ul>
</blockquote> </blockquote>
<blockquote><p><u>Security</u></p></blockquote> <blockquote><p><u>Security</u></p>
<blockquote>
<ul> <ul>
<li><I>Basic Infrastructure</I>. Added a property to support/launch.properties to prevent log4j from using jansi.dll on Windows. (Issue #286) </li>
<li><I>Basic Infrastructure. </I> Added a property to support/launch.properties to prevent log4j from using jansi.dll on Windows. (Issue #286) </li>
</ul> </ul>
</blockquote> </blockquote>
<H1 align="center">Ghidra 9.0.1 Change History (March 2019)</H1> <H1 align="center">Ghidra 9.0.1 Change History (March 2019)</H1>
<blockquote><p><u>New Features</u></p>
<blockquote><p><u>New Features</u></p></blockquote>
<blockquote>
<ul> <ul>
<li><I>Scripting</I>. Created a script to show all equates within the current selection. (Issue #111)</li> <li><I>Scripting</I>. Created a script to show all equates within the current selection. (Issue #111)</li>
</ul> </ul>
</blockquote> </blockquote>
<blockquote><p><u>Improvements</u></p></blockquote> <blockquote><p><u>Improvements</u></p>
<blockquote>
<ul> <ul>
<li><I>Basic Infrastructure</I>. Updated commons-compress library to version 1.18. (Issue #171)</li> <li><I>Basic Infrastructure</I>. Updated commons-compress library to version 1.18. (Issue #171)</li>
<li><I>Eclipse Integration</I>. Ghidra now connects to the Eclipse GhidraDev plugin on 127.0.0.1 rather than localhost.</li> <li><I>Eclipse Integration</I>. Ghidra now connects to the Eclipse GhidraDev plugin on 127.0.0.1 rather than localhost.</li>
@ -372,24 +554,23 @@
<li><I>GUI</I>. Fixed Options Dialog slow scrolling speed. (Issue #27)</li> <li><I>GUI</I>. Fixed Options Dialog slow scrolling speed. (Issue #27)</li>
<li><I>Importer:ELF</I>. Corrected bug in ELF loader which can improperly process the GOT, PLT and relocations <li><I>Importer:ELF</I>. Corrected bug in ELF loader which can improperly process the GOT, PLT and relocations
when multiple symbol tables exist within the ELF binary. (Issue #52)</li> when multiple symbol tables exist within the ELF binary. (Issue #52)</li>
<li><I>Languages</I>. Added ARM/Thumb SRS instruction decodes for undefined modes. (Issue #216)</li>
<li><I>Multi-User:Ghidra Server</I>. Corrected the Ghidra Server service wrapper (YAJSW) configuration for <li><I>Multi-User:Ghidra Server</I>. Corrected the Ghidra Server service wrapper (YAJSW) configuration for
Mac OS X to prevent a startup timeout condition which could occur.</li> Mac OS X to prevent a startup timeout condition which could occur.</li>
<li><I>Processors</I>. Added ARM/Thumb SRS instruction decodes for undefined modes. (Issue #216)</li>
</ul> </ul>
</blockquote> </blockquote>
<blockquote><p><u>Bugs</u></p></blockquote> <blockquote><p><u>Bugs</u></p>
<blockquote>
<ul> <ul>
<li><I>API</I>. Fixed equals method on Varnode class. (Issue #97)</li> <li><I>API</I>. Fixed equals method on Varnode class. (Issue #97)</li>
<li><I>API</I>. Fixed a bug in MaskImpl.comlementMask(). (Issue #187)</li> <li><I>API</I>. Fixed a bug in MaskImpl.comlementMask(). (Issue #187)</li>
<li><I>Basic Infrasturcture</I>. Fixed special character handling in idaxml.py. (Issue #75)</li> <li><I>Basic Infrastructure</I>. Fixed special character handling in idaxml.py. (Issue #75)</li>
<li><I>Basic Infrastructure</I>. Ghidra now forces the locale to en_US by default. Only the en_US is currently supported. <li><I>Basic Infrastructure</I>. Ghidra now forces the locale to en_US by default. Only the en_US is currently supported.
This fixes certain unexpected exceptions. (Issue #209)</li> This fixes certain unexpected exceptions. (Issue #209)</li>
<li><I>Diff</I>. Fixed exceptions occasionally encountered when starting a Diff session. (Issue #211)</li> <li><I>Diff</I>. Fixed exceptions occasionally encountered when starting a Diff session. (Issue #211)</li>
<li><I>Documentation</I>. Fixed javadoc search box redirecting to broken links. (Issue #129)</li> <li><I>Documentation</I>. Fixed javadoc search box redirecting to broken links. (Issue #129)</li>
<li><I>Function Graph</I>. Fixed Function Graph exception when generating tooltip. (Issue #65)</li> <li><I>Function Graph</I>. Fixed Function Graph exception when generating tooltip. (Issue #65)</li>
<li><I>GUI</I>. Updted window placement to keep windows on screen. (Issue #41)</li> <li><I>GUI</I>. Updated window placement to keep windows on screen. (Issue #41)</li>
<li><I>GUI</I>. Add/Edit References dialog now restricts users to creating refs in valid memory address spaces.</li> <li><I>GUI</I>. Add/Edit References dialog now restricts users to creating refs in valid memory address spaces.</li>
<li><I>GUI</I>. Fixed exception when exiting Ghidra while a table is being edited. (Issue #51)</li> <li><I>GUI</I>. Fixed exception when exiting Ghidra while a table is being edited. (Issue #51)</li>
<li><I>GUI</I>. Fixed some touchpad scrolling issues. (Issue #2)</li> <li><I>GUI</I>. Fixed some touchpad scrolling issues. (Issue #2)</li>
@ -398,28 +579,26 @@
<li><I>GUI</I>. Toolbar buttons now respond to fast clicking.</li> <li><I>GUI</I>. Toolbar buttons now respond to fast clicking.</li>
<li><I>Importer:MachO</I>. The MachoLoader can now find import libraries found in Universal Binary files. (Issue #136)</li> <li><I>Importer:MachO</I>. The MachoLoader can now find import libraries found in Universal Binary files. (Issue #136)</li>
<li><I>Importer:PE</I>. The PeLoader now correctly parses the GuardCFFunctionTable when entries are more than 4 bytes each. (Issue #220)</li> <li><I>Importer:PE</I>. The PeLoader now correctly parses the GuardCFFunctionTable when entries are more than 4 bytes each. (Issue #220)</li>
<li><I>Languages</I>. Added missing PowerPC VLE conditional branch instructions: e_bdnz and e_bdz. (Issue #103)</li>
<li><I>Languages</I>. Fixed instruction semantics for several instructions and added Control Flow Enforcement, NOP variants, CMP variants, UD1, and
prefixed call instructions to X86 processor specification. (Issues #22, #53, #158, #157)</li>
<li><I>Languages</I>. The 68000 MOVE instruction now correctly sets the CF and VF flags. (Issue #163)</li>
<li><I>Languages</I>. Added four missing MOVEM instruction variants to the 68000 processor. (Issue #219)</li>
<li><I>Languages</I>. An incorrect usage of X instead of Y in indexed mode for the 6502 has been corrected.(Issue #201)</li>
<li><I>Languages</I>. Added support for ARM Thumb half BL instruction on processor variants prior to v6. (Issue #39)</li>
<li><I>Multi-User:Ghidra Server</I>. Removed support for native OS authentication from Ghidra Server (removed modes -a2 and -a3) <li><I>Multi-User:Ghidra Server</I>. Removed support for native OS authentication from Ghidra Server (removed modes -a2 and -a3)
due to incompatibility with newer OS releases including Windows 10 and Windows Server 2016. Re-introduction of this will be due to incompatibility with newer OS releases including Windows 10 and Windows Server 2016. Re-introduction of this will be
considered for a future release.</li> considered for a future release.</li>
<li><I>PDB</I>. Corrected NPE error when processing PDB files. (Issues #138, #188)</li> <li><I>PDB</I>. Corrected NPE error when processing PDB files. (Issues #138, #188)</li>
<li><I>Processors</I>. Added missing PowerPC VLE conditional branch instructions: e_bdnz and e_bdz. (Issue #103)</li>
<li><I>Processors</I>. Fixed instruction semantics for several instructions and added Control Flow Enforcement, NOP variants, CMP variants, UD1, and
prefixed call instructions to X86 processor specification. (Issues #22, #53, #158, #157)</li>
<li><I>Processors</I>. The 68000 MOVE instruction now correctly sets the CF and VF flags. (Issue #163)</li>
<li><I>Processors</I>. Added four missing MOVEM instruction variants to the 68000 processor. (Issue #219)</li>
<li><I>Processors</I>. An incorrect usage of X instead of Y in indexed mode for the 6502 has been corrected.(Issue #201)</li>
<li><I>Processors</I>. Added support for ARM Thumb half BL instruction on processor variants prior to v6. (Issue #39)</li>
<li><I>Scripting</I>. Fixed a bug in ImportSymbolsScript.py that prevented it from running. (Issue #170)</li> <li><I>Scripting</I>. Fixed a bug in ImportSymbolsScript.py that prevented it from running. (Issue #170)</li>
</ul> </ul>
</blockquote> </blockquote>
<blockquote><p><u>Security</u></p></blockquote> <blockquote><p><u>Security</u></p>
<blockquote>
<ul> <ul>
<li><I>Basic Infrastructure</I>. Running Ghidra in debug mode no longer opens remotely accessible ports by default. (Issue #6)</li> <li><I>Basic Infrastructure</I>. Running Ghidra in debug mode no longer opens remotely accessible ports by default. (Issue #6)</li>
<li><I>GUI</I>. The Defined Strings plugin no longer renders HTML in its table. (Issue #45)</li> <li><I>GUI</I>. The Defined Strings plugin no longer renders HTML in its table. (Issue #45)</li>
<li><I>Project Manager</I>. Fixed an XXE vulnerability affeting projects and many other saved components. (Issue #71)</li> <li><I>Project Manager</I>. Fixed an XXE vulnerability affecting projects and many other saved components. (Issue #71)</li>
</li>
</ul> </ul>
</blockquote> </blockquote>

View file

@ -443,6 +443,8 @@ src/main/help/help/topics/GhidraServer/GhidraServer.htm||GHIDRA||||END|
src/main/help/help/topics/Glossary/glossary.htm||GHIDRA||||END| src/main/help/help/topics/Glossary/glossary.htm||GHIDRA||||END|
src/main/help/help/topics/Glossary/images/BigEndian.png||GHIDRA||reviewed||END| src/main/help/help/topics/Glossary/images/BigEndian.png||GHIDRA||reviewed||END|
src/main/help/help/topics/Glossary/images/LittleEndian.png||GHIDRA||reviewed||END| src/main/help/help/topics/Glossary/images/LittleEndian.png||GHIDRA||reviewed||END|
src/main/help/help/topics/Graph/GraphIntro.html||GHIDRA||||END|
src/main/help/help/topics/Graph/GraphServicesIntro.html||GHIDRA||||END|
src/main/help/help/topics/HeadlessAnalyzer/HeadlessAnalyzer.htm||GHIDRA||||END| src/main/help/help/topics/HeadlessAnalyzer/HeadlessAnalyzer.htm||GHIDRA||||END|
src/main/help/help/topics/ImporterPlugin/images/About_pdb.png||GHIDRA||reviewed||END| src/main/help/help/topics/ImporterPlugin/images/About_pdb.png||GHIDRA||reviewed||END|
src/main/help/help/topics/ImporterPlugin/images/BatchImportDialog.png||GHIDRA||||END| src/main/help/help/topics/ImporterPlugin/images/BatchImportDialog.png||GHIDRA||||END|

View file

@ -150,7 +150,9 @@
<tocdef id="FileSystem Browser" text="FileSystem Browser" target="help/topics/FileSystemBrowserPlugin/FileSystemBrowserPlugin.html" /> <tocdef id="FileSystem Browser" text="FileSystem Browser" target="help/topics/FileSystemBrowserPlugin/FileSystemBrowserPlugin.html" />
<tocdef id="Graphing" text="Graphing" /> <tocdef id="Graphing" text="Graphing" target="help/topics/Graph/GraphIntro.html">
<tocdef id="Graph Services" text="Graph Services" target="help/topics/Graph/GraphServicesIntro.html"/>
</tocdef>
<tocdef id="Program Tree" text="Program Tree" target="help/topics/ProgramTreePlugin/program_tree.htm" > <tocdef id="Program Tree" text="Program Tree" target="help/topics/ProgramTreePlugin/program_tree.htm" >
<tocdef id="Folders and Fragments" sortgroup="a" text="Folders and Fragments" target="help/topics/ProgramTreePlugin/program_tree.htm#FoldersAndFragments" /> <tocdef id="Folders and Fragments" sortgroup="a" text="Folders and Fragments" target="help/topics/ProgramTreePlugin/program_tree.htm#FoldersAndFragments" />

View file

@ -0,0 +1,24 @@
<!DOCTYPE doctype PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN">
<HTML>
<HEAD>
<TITLE>Graphing</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>Graphing</H1>
<BLOCKQUOTE>
<P>
This section contains all help related to the creation and display of Graphs. Content will
appear inside of this section as plugins are added. To see the available graph features,
see the <B>Graph</b> menu on the toolbar.
</P>
</BLOCKQUOTE>
</BODY>
</HTML>

View file

@ -0,0 +1,25 @@
<!DOCTYPE doctype PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN">
<HTML>
<HEAD>
<TITLE>Graph Services</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>Graph Services</H1>
<BLOCKQUOTE>
<P>
This section contains all help related to the graph services that provide capabilities for
generated graphs, such as displaying and exporting. Content will appear inside of this '
section as plugins are added. To see the available graph features,
see the <B>Graph</b> menu on the toolbar.
</P>
</BLOCKQUOTE>
</BODY>
</HTML>

View file

@ -120,6 +120,10 @@ public abstract class AddressBasedGraphDisplayListener
return null; return null;
} }
String id = getVertexId(address); String id = getVertexId(address);
if (id == null) {
return null;
}
return graphDisplay.getGraph().getVertex(id); return graphDisplay.getGraph().getVertex(id);
} }
@ -202,6 +206,9 @@ public abstract class AddressBasedGraphDisplayListener
private void handleSymbolAddedOrRenamed(Address address, Symbol symbol) { private void handleSymbolAddedOrRenamed(Address address, Symbol symbol) {
AttributedVertex vertex = getVertex(address); AttributedVertex vertex = getVertex(address);
if (vertex == null) {
return;
}
graphDisplay.updateVertexName(vertex, symbol.getName()); graphDisplay.updateVertexName(vertex, symbol.getName());
} }

View file

@ -147,6 +147,7 @@ public class FdeTable {
// this is an indirect reference to code from the table, // this is an indirect reference to code from the table,
// so tag reference as an indirect code flow // so tag reference as an indirect code flow
// TODO: This should be a CODE flow, leaving as INDIRECTION until refactor
prog.getReferenceManager().addMemoryReference(locComponentAddr, locAddr, prog.getReferenceManager().addMemoryReference(locComponentAddr, locAddr,
RefType.INDIRECTION, RefType.INDIRECTION,
SourceType.ANALYSIS, 0); SourceType.ANALYSIS, 0);

View file

@ -71,6 +71,7 @@ public final class ObjectiveC1_TypeEncodings {
public final static char _C_BYCOPY = 'O'; public final static char _C_BYCOPY = 'O';
public final static char _C_BYREF = 'R'; public final static char _C_BYREF = 'R';
public final static char _C_ONEWAY = 'V'; public final static char _C_ONEWAY = 'V';
public final static char _C_ATOMIC = 'A';
private final static String ANONYMOUS_PREFIX = "Anonymous"; private final static String ANONYMOUS_PREFIX = "Anonymous";
@ -410,8 +411,13 @@ public final class ObjectiveC1_TypeEncodings {
DataType dt = parseDataType(buffer); DataType dt = parseDataType(buffer);
return new TypedefDataType("ONEWAY " + dt.getDisplayName(), dt); return new TypedefDataType("ONEWAY " + dt.getDisplayName(), dt);
} }
case _C_ATOMIC: {
buffer.deleteCharAt(0);
DataType dt = parseDataType(buffer);
return new TypedefDataType("ATOMIC " + dt.getDisplayName(), dt);
} }
throw new UnsupportedOperationException("unrecognized format type: " + buffer.charAt(0)); }
throw new UnsupportedOperationException("Unsupported Objective C type encoding: " + buffer.charAt(0));
} }
private Union parseBitFields(StringBuffer buffer) { private Union parseBitFields(StringBuffer buffer) {

View file

@ -202,8 +202,7 @@ public class PcodeFormatter {
formatVarnodeTpl(program, opcode, -1, output, lineList); formatVarnodeTpl(program, opcode, -1, output, lineList);
lineList.add(EQUALS); lineList.add(EQUALS);
} }
Color color = Color color = (opcode == PcodeOp.UNIMPLEMENTED) ? Color.RED : Color.BLUE.darker();
(opcode == PcodeOp.UNIMPLEMENTED) ? Color.RED : Color.BLUE.darker();
lineList.add(new AttributedString(PcodeOp.getMnemonic(opcode), color, metrics)); lineList.add(new AttributedString(PcodeOp.getMnemonic(opcode), color, metrics));
VarnodeTpl[] inputs = op.getInput(); VarnodeTpl[] inputs = op.getInput();
for (int i = 0; i < inputs.length; i++) { for (int i = 0; i < inputs.length; i++) {
@ -262,7 +261,7 @@ public class PcodeFormatter {
formatConstant(offset, size, lineList); formatConstant(offset, size, lineList);
} }
else if (space.isUniqueSpace()) { else if (space.isUniqueSpace()) {
formatUnique(offset, size, opIndex < 0, lineList); formatUnique(offset, size, lineList);
} }
else { else {
formatAddress(program, space.getSpaceId(), offset, size, lineList); formatAddress(program, space.getSpaceId(), offset, size, lineList);
@ -284,26 +283,22 @@ public class PcodeFormatter {
private void formatRaw(AddressSpace space, ConstTpl offset, ConstTpl size, private void formatRaw(AddressSpace space, ConstTpl offset, ConstTpl size,
List<AttributedString> lineList) { List<AttributedString> lineList) {
// same format as the Varnode.toString // same format as the Varnode.toString
String str = String str = "(" + space.getName() + ", 0x" + Long.toHexString(offset.getReal()) + ", " +
"(" + space.getName() + ", 0x" + Long.toHexString(offset.getReal()) + ", " +
size.getReal() + ")"; size.getReal() + ")";
lineList.add(new AttributedString(str, Color.BLUE, metrics)); lineList.add(new AttributedString(str, Color.BLUE, metrics));
} }
private void formatUnique(ConstTpl offset, ConstTpl size, boolean isOutput, private void formatUnique(ConstTpl offset, ConstTpl size, List<AttributedString> lineList) {
List<AttributedString> lineList) {
if (offset.getType() != ConstTpl.REAL) { if (offset.getType() != ConstTpl.REAL) {
throw new RuntimeException("Unsupported unique offset type: " + offset.getType()); throw new RuntimeException("Unsupported unique offset type: " + offset.getType());
} }
if (size.getType() != ConstTpl.REAL) { if (size.getType() != ConstTpl.REAL) {
throw new RuntimeException("Unsupported unique size type: " + size.getType()); throw new RuntimeException("Unsupported unique size type: " + size.getType());
} }
lineList.add(new AttributedString("$U" + Long.toHexString(offset.getReal()), localColor, lineList.add(
metrics)); new AttributedString("$U" + Long.toHexString(offset.getReal()), localColor, metrics));
if (isOutput) {
formatSize(size, lineList); formatSize(size, lineList);
} }
}
private void formatAddress(Program program, AddressSpace addrSpace, ConstTpl offset, private void formatAddress(Program program, AddressSpace addrSpace, ConstTpl offset,
ConstTpl size, List<AttributedString> lineList) { ConstTpl size, List<AttributedString> lineList) {
@ -314,8 +309,8 @@ public class PcodeFormatter {
long offsetValue = offset.getReal(); long offsetValue = offset.getReal();
if (addrSpace == null) { if (addrSpace == null) {
lineList.add(STAR); lineList.add(STAR);
lineList.add(new AttributedString("0x" + Long.toHexString(offsetValue), addressColor, lineList.add(
metrics)); new AttributedString("0x" + Long.toHexString(offsetValue), addressColor, metrics));
if (size.getType() != ConstTpl.J_CURSPACE_SIZE) { if (size.getType() != ConstTpl.J_CURSPACE_SIZE) {
formatSize(size, lineList); formatSize(size, lineList);
} }
@ -328,7 +323,8 @@ public class PcodeFormatter {
lineList.add(new AttributedString(reg.getName(), registerColor, metrics)); lineList.add(new AttributedString(reg.getName(), registerColor, metrics));
if (reg.getMinimumByteSize() > sizeValue) { if (reg.getMinimumByteSize() > sizeValue) {
lineList.add(COLON); lineList.add(COLON);
lineList.add(new AttributedString(Integer.toString(sizeValue), this.scalarColor, metrics)); lineList.add(
new AttributedString(Integer.toString(sizeValue), this.scalarColor, metrics));
} }
return; return;
} }
@ -396,7 +392,8 @@ public class PcodeFormatter {
} }
private boolean formatLabelInput(VarnodeTpl input0, List<AttributedString> lineList) { private boolean formatLabelInput(VarnodeTpl input0, List<AttributedString> lineList) {
if (input0.getSpace().isConstSpace() && input0.getOffset().getType() == ConstTpl.J_RELATIVE) { if (input0.getSpace().isConstSpace() &&
input0.getOffset().getType() == ConstTpl.J_RELATIVE) {
String label = "<" + input0.getOffset().getReal() + ">"; String label = "<" + input0.getOffset().getReal() + ">";
lineList.add(new AttributedString(label, Color.BLUE, metrics)); lineList.add(new AttributedString(label, Color.BLUE, metrics));
return true; return true;

View file

@ -15,14 +15,14 @@
*/ */
package ghidra.test; package ghidra.test;
import java.util.ArrayList;
import java.util.List;
import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramBuilder;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryAccessException; import ghidra.program.model.mem.MemoryAccessException;
import java.util.ArrayList;
import java.util.List;
public class ToyProgramBuilder extends ProgramBuilder { public class ToyProgramBuilder extends ProgramBuilder {
private static final String TOY_LANGUAGE_ID_BE = "Toy:BE:32:builder"; private static final String TOY_LANGUAGE_ID_BE = "Toy:BE:32:builder";
@ -106,7 +106,7 @@ public class ToyProgramBuilder extends ProgramBuilder {
/** /**
* Get address in default ram space * Get address in default ram space
* @param offset address offset * @param offset address offset
* @return address * @return the address
*/ */
public Address getAddress(long offset) { public Address getAddress(long offset) {
return defaultSpace.getAddress(offset); return defaultSpace.getAddress(offset);

View file

@ -170,18 +170,24 @@ public class UndefinedFunction implements Function {
while (iterator.hasNext() && !monitor.isCancelled()) { while (iterator.hasNext() && !monitor.isCancelled()) {
CodeBlockReference blockReference = iterator.next(); CodeBlockReference blockReference = iterator.next();
FlowType flowType = blockReference.getFlowType(); FlowType flowType = blockReference.getFlowType();
if (flowType.isCall()) if (flowType.isCall()) {
continue; // Don't follow call edges for within-function analysis continue; // Don't follow call edges for within-function analysis
}
if (flowType.isIndirect()) {
continue; // Don't follow improper use of Indirect reference
}
count += 1; // Count the existence of source that is NOT a call count += 1; // Count the existence of source that is NOT a call
Address sourceAddr = blockReference.getSourceAddress(); Address sourceAddr = blockReference.getSourceAddress();
if (visitedAddresses.contains(sourceAddr)) if (visitedAddresses.contains(sourceAddr)) {
continue; // Already visited this block continue; // Already visited this block
}
visitedAddresses.addRange(sourceAddr, sourceAddr); visitedAddresses.addRange(sourceAddr, sourceAddr);
worklist.add(blockReference.getSourceBlock()); worklist.add(blockReference.getSourceBlock());
} }
if (count == 0) // We found a block with no incoming edges, a likely function start if (count == 0) {
return curblock; return curblock;
} }
}
return null; return null;
} }
@ -469,8 +475,9 @@ public class UndefinedFunction implements Function {
@Override @Override
public void setReturnType(DataType type, SourceType source) { public void setReturnType(DataType type, SourceType source) {
if (type == DataType.DEFAULT) if (type == DataType.DEFAULT) {
return; return;
}
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View file

@ -94,6 +94,7 @@ class Funcdata {
void splitUses(Varnode *vn); ///< Make all reads of the given Varnode unique void splitUses(Varnode *vn); ///< Make all reads of the given Varnode unique
Varnode *cloneVarnode(const Varnode *vn); ///< Clone a Varnode (between copies of the function) Varnode *cloneVarnode(const Varnode *vn); ///< Clone a Varnode (between copies of the function)
void destroyVarnode(Varnode *vn); ///< Delete the given Varnode from \b this function void destroyVarnode(Varnode *vn); ///< Delete the given Varnode from \b this function
void coverVarnodes(SymbolEntry *entry,vector<Varnode *> &list);
// Low level op functions // Low level op functions
void opZeroMulti(PcodeOp *op); ///< Transform trivial CPUI_MULTIEQUAL to CPUI_COPY void opZeroMulti(PcodeOp *op); ///< Transform trivial CPUI_MULTIEQUAL to CPUI_COPY
// Low level block functions // Low level block functions

View file

@ -1376,6 +1376,35 @@ Address Funcdata::findDisjointCover(Varnode *vn,int4 &sz)
return addr; return addr;
} }
/// \brief Make sure every Varnode in the given list has a Symbol it will link to
///
/// This is used when Varnodes overlap a locked Symbol but extend beyond it.
/// An existing Symbol is passed in with a list of possibly overextending Varnodes.
/// The list is in Address order. We check that each Varnode has a Symbol that
/// overlaps its first byte (to guarantee a link). If one doesn't exist it is created.
/// \param entry is the existing Symbol entry
/// \param list is the list of Varnodes
void Funcdata::coverVarnodes(SymbolEntry *entry,vector<Varnode *> &list)
{
Scope *scope = entry->getSymbol()->getScope();
for(int4 i=0;i<list.size();++i) {
Varnode *vn = list[i];
// We only need to check once for all Varnodes at the same Address
// Of these, pick the biggest Varnode
if (i+1<list.size() && list[i+1]->getAddr() == vn->getAddr())
continue;
Address usepoint = vn->getUsePoint(*this);
SymbolEntry *overlapEntry = scope->findContainer(vn->getAddr(), vn->getSize(), usepoint);
if (overlapEntry == (SymbolEntry *)0) {
int4 diff = (int4)(vn->getOffset() - entry->getAddr().getOffset());
ostringstream s;
s << entry->getSymbol()->getName() << '_' << diff;
scope->addSymbol(s.str(),vn->getHigh()->getType(),vn->getAddr(),usepoint);
}
}
}
/// Search for \e addrtied Varnodes whose storage falls in the global Scope, then /// Search for \e addrtied Varnodes whose storage falls in the global Scope, then
/// build a new global Symbol if one didn't exist before. /// build a new global Symbol if one didn't exist before.
void Funcdata::mapGlobals(void) void Funcdata::mapGlobals(void)
@ -1386,6 +1415,7 @@ void Funcdata::mapGlobals(void)
Varnode *vn,*maxvn; Varnode *vn,*maxvn;
Datatype *ct; Datatype *ct;
uint4 flags; uint4 flags;
vector<Varnode *> uncoveredVarnodes;
bool inconsistentuse = false; bool inconsistentuse = false;
iter = vbank.beginLoc(); // Go through all varnodes for this space iter = vbank.beginLoc(); // Go through all varnodes for this space
@ -1398,10 +1428,16 @@ void Funcdata::mapGlobals(void)
maxvn = vn; maxvn = vn;
Address addr = vn->getAddr(); Address addr = vn->getAddr();
Address endaddr = addr + vn->getSize(); Address endaddr = addr + vn->getSize();
uncoveredVarnodes.clear();
while(iter != enditer) { while(iter != enditer) {
vn = *iter; vn = *iter;
if (!vn->isPersist()) break; if (!vn->isPersist()) break;
if (vn->getAddr() < endaddr) { if (vn->getAddr() < endaddr) {
// Varnodes at the same base address will get linked to the Symbol at that address
// even if the size doesn't match, but we check for internal Varnodes that
// do not have an attached Symbol as these won't get linked to anything
if (vn->getAddr() != addr && vn->getSymbolEntry() == (SymbolEntry *)0)
uncoveredVarnodes.push_back(vn);
endaddr = vn->getAddr() + vn->getSize(); endaddr = vn->getAddr() + vn->getSize();
if (vn->getSize() > maxvn->getSize()) if (vn->getSize() > maxvn->getSize())
maxvn = vn; maxvn = vn;
@ -1429,8 +1465,11 @@ void Funcdata::mapGlobals(void)
Varnode::addrtied|Varnode::persist); Varnode::addrtied|Varnode::persist);
discover->addSymbol(symbolname,ct,addr,usepoint); discover->addSymbol(symbolname,ct,addr,usepoint);
} }
else if ((addr.getOffset()+ct->getSize())-1 > (entry->getAddr().getOffset()+entry->getSize()) -1) else if ((addr.getOffset()+ct->getSize())-1 > (entry->getAddr().getOffset()+entry->getSize()) -1) {
inconsistentuse = true; inconsistentuse = true;
if (!uncoveredVarnodes.empty()) // Provide Symbols for any uncovered internal Varnodes
coverVarnodes(entry, uncoveredVarnodes);
}
} }
if (inconsistentuse) if (inconsistentuse)
warningHeader("Globals starting with '_' overlap smaller symbols at the same address"); warningHeader("Globals starting with '_' overlap smaller symbols at the same address");

View file

@ -1987,7 +1987,7 @@ bool SleighCompile::undefinePreprocValue(const string &nm)
// Functions needed by the parser // Functions needed by the parser
TokenSymbol *SleighCompile::defineToken(string *name,uintb *sz) TokenSymbol *SleighCompile::defineToken(string *name,uintb *sz,int4 endian)
{ {
uint4 size = *sz; uint4 size = *sz;
@ -1998,7 +1998,12 @@ TokenSymbol *SleighCompile::defineToken(string *name,uintb *sz)
} }
else else
size = size/8; size = size/8;
Token *newtoken = new Token(*name,size,isBigEndian(),tokentable.size()); bool isBig;
if (endian ==0)
isBig = isBigEndian();
else
isBig = (endian > 0);
Token *newtoken = new Token(*name,size,isBig,tokentable.size());
tokentable.push_back(newtoken); tokentable.push_back(newtoken);
delete name; delete name;
TokenSymbol *res = new TokenSymbol(newtoken); TokenSymbol *res = new TokenSymbol(newtoken);

View file

@ -261,7 +261,7 @@ public:
bool undefinePreprocValue(const string &nm); bool undefinePreprocValue(const string &nm);
// Parser functions // Parser functions
TokenSymbol *defineToken(string *name,uintb *sz); TokenSymbol *defineToken(string *name,uintb *sz,int4 endian);
void addTokenField(TokenSymbol *sym,FieldQuality *qual); void addTokenField(TokenSymbol *sym,FieldQuality *qual);
bool addContextField(VarnodeSymbol *sym,FieldQuality *qual); bool addContextField(VarnodeSymbol *sym,FieldQuality *qual);
void newSpace(SpaceQuality *qual); void newSpace(SpaceQuality *qual);

View file

@ -181,7 +181,9 @@ aligndef: DEFINE_KEY ALIGN_KEY '=' INTEGER ';' { slgh->setAlignment(*$4); delete
; ;
tokendef: tokenprop ';' {} tokendef: tokenprop ';' {}
; ;
tokenprop: DEFINE_KEY TOKEN_KEY STRING '(' INTEGER ')' { $$ = slgh->defineToken($3,$5); } tokenprop: DEFINE_KEY TOKEN_KEY STRING '(' INTEGER ')' { $$ = slgh->defineToken($3,$5,0); }
| DEFINE_KEY TOKEN_KEY STRING '(' INTEGER ')' ENDIAN_KEY '=' LITTLE_KEY { $$ = slgh->defineToken($3,$5,-1); }
| DEFINE_KEY TOKEN_KEY STRING '(' INTEGER ')' ENDIAN_KEY '=' BIG_KEY { $$ = slgh->defineToken($3,$5,1); }
| tokenprop fielddef { $$ = $1; slgh->addTokenField($1,$2); } | tokenprop fielddef { $$ = $1; slgh->addTokenField($1,$2); }
| DEFINE_KEY TOKEN_KEY anysymbol { string errmsg=$3->getName()+": redefined as a token"; yyerror(errmsg.c_str()); YYERROR; } | DEFINE_KEY TOKEN_KEY anysymbol { string errmsg=$3->getName()+": redefined as a token"; yyerror(errmsg.c_str()); YYERROR; }
; ;

View file

@ -494,7 +494,7 @@ int4 scan_number(char *numtext,YYSTYPE *lval,bool signednum)
[(),\-] { yylval.ch = yytext[0]; return yytext[0]; } [(),\-] { yylval.ch = yytext[0]; return yytext[0]; }
\: { BEGIN(print); slgh->calcContextLayout(); yylval.ch = yytext[0]; return yytext[0]; } \: { BEGIN(print); slgh->calcContextLayout(); yylval.ch = yytext[0]; return yytext[0]; }
\{ { BEGIN(sem); yylval.ch = yytext[0]; return yytext[0]; } \{ { BEGIN(sem); yylval.ch = yytext[0]; return yytext[0]; }
#.*$ #.*
[\r\ \t\v]+ [\r\ \t\v]+
\n { slgh->nextLine(); } \n { slgh->nextLine(); }
macro { BEGIN(macroblock); return MACRO_KEY; } macro { BEGIN(macroblock); return MACRO_KEY; }
@ -540,7 +540,7 @@ with { BEGIN(pattern); withsection = 1; slgh->calcContextLayout(); return WITH
<defblock>values { return VALUES_KEY; } <defblock>values { return VALUES_KEY; }
<defblock>variables { return VARIABLES_KEY; } <defblock>variables { return VARIABLES_KEY; }
<defblock>pcodeop { return PCODEOP_KEY; } <defblock>pcodeop { return PCODEOP_KEY; }
<defblock>#.*$ <defblock>#.*
<defblock>[a-zA-Z_.][a-zA-Z0-9_.]* { return find_symbol(); } <defblock>[a-zA-Z_.][a-zA-Z0-9_.]* { return find_symbol(); }
<defblock>[0-9]|[1-9][0-9]+ { return scan_number(yytext,&yylval,false); } <defblock>[0-9]|[1-9][0-9]+ { return scan_number(yytext,&yylval,false); }
<defblock>0x[0-9a-fA-F]+ { return scan_number(yytext,&yylval,false); } <defblock>0x[0-9a-fA-F]+ { return scan_number(yytext,&yylval,false); }
@ -582,7 +582,7 @@ with { BEGIN(pattern); withsection = 1; slgh->calcContextLayout(); return WITH
<pattern>\| { yylval.ch = yytext[0]; return (actionon==0) ? yytext[0] : OP_OR; } <pattern>\| { yylval.ch = yytext[0]; return (actionon==0) ? yytext[0] : OP_OR; }
<pattern>\^ { return OP_XOR; } <pattern>\^ { return OP_XOR; }
<pattern>[=(),:;+\-*/~<>] { yylval.ch = yytext[0]; return yytext[0]; } <pattern>[=(),:;+\-*/~<>] { yylval.ch = yytext[0]; return yytext[0]; }
<pattern>#.*$ <pattern>#.*
<pattern>[a-zA-Z_.][a-zA-Z0-9_.]* { return find_symbol(); } <pattern>[a-zA-Z_.][a-zA-Z0-9_.]* { return find_symbol(); }
<pattern>[0-9]|[1-9][0-9]+ { return scan_number(yytext,&yylval,true); } <pattern>[0-9]|[1-9][0-9]+ { return scan_number(yytext,&yylval,true); }
<pattern>0x[0-9a-fA-F]+ { return scan_number(yytext,&yylval,true); } <pattern>0x[0-9a-fA-F]+ { return scan_number(yytext,&yylval,true); }
@ -648,7 +648,7 @@ with { BEGIN(pattern); withsection = 1; slgh->calcContextLayout(); return WITH
<sem>build { return BUILD_KEY; } <sem>build { return BUILD_KEY; }
<sem>local { return LOCAL_KEY; } <sem>local { return LOCAL_KEY; }
<sem>[=(),:\[\];!&|^+\-*/%~<>] { yylval.ch = yytext[0]; return yytext[0]; } <sem>[=(),:\[\];!&|^+\-*/%~<>] { yylval.ch = yytext[0]; return yytext[0]; }
<sem>#.*$ <sem>#.*
<sem>[a-zA-Z_.][a-zA-Z0-9_.]* { return find_symbol(); } <sem>[a-zA-Z_.][a-zA-Z0-9_.]* { return find_symbol(); }
<sem>[0-9]|[1-9][0-9]+ { return scan_number(yytext,&yylval,false); } <sem>[0-9]|[1-9][0-9]+ { return scan_number(yytext,&yylval,false); }
<sem>0x[0-9a-fA-F]+ { return scan_number(yytext,&yylval,false); } <sem>0x[0-9a-fA-F]+ { return scan_number(yytext,&yylval,false); }

View file

@ -4,7 +4,7 @@
<title>SLEIGH</title> <title>SLEIGH</title>
<subtitle>A Language for Rapid Processor Specification</subtitle> <subtitle>A Language for Rapid Processor Specification</subtitle>
<pubdate>Originally published December 16, 2005</pubdate> <pubdate>Originally published December 16, 2005</pubdate>
<releaseinfo>Last updated September 5, 2019</releaseinfo> <releaseinfo>Last updated October 28, 2020</releaseinfo>
</info> </info>
<simplesect> <simplesect>
<info> <info>
@ -573,13 +573,14 @@ define endian=little;
</programlisting> </programlisting>
</informalexample> </informalexample>
This defines how the processor interprets contiguous sequences of This defines how the processor interprets contiguous sequences of
bytes as integers. It effects how integer fields within an instruction bytes as integers or other values and globally affects values across
are interpreted (see <xref linkend="sleigh_defining_tokens"/>), and all address spaces. It also affects how integer fields
it also effects the details of how the processor is supposed to within an instruction are interpreted, (see <xref linkend="sleigh_defining_tokens"/>),
implement atomic operations like integer addition and integer although it is possible to override this setting in the rare case that endianess is
compare. The specification designer should only need to worry about different for data versus instruction encoding.
these details when labeling instruction fields, otherwise the The specification designer generally only needs to worry about
specification language will hide endianess issues. endianess when labeling instruction fields and when defining overlapping registers,
otherwise the specification language hides endianess issues.
</para> </para>
</sect2> </sect2>
<sect2> <sect2>
@ -966,7 +967,7 @@ individual <emphasis>constructor</emphasis> (defined in <xref linkend="sleigh_co
defines a local scope for operand names. As with most languages, a defines a local scope for operand names. As with most languages, a
local symbol with the same name as a global local symbol with the same name as a global
symbol <emphasis>hides</emphasis> the global symbol while that scope symbol <emphasis>hides</emphasis> the global symbol while that scope
is in affect. is in effect.
</para> </para>
</sect2> </sect2>
<sect2 id="sleigh_predefined_symbols"> <sect2 id="sleigh_predefined_symbols">
@ -1057,8 +1058,22 @@ there are one or more field declarations specifying the name of the
field and the range of bits within the token making up the field. The field and the range of bits within the token making up the field. The
size of a field does <emphasis>not</emphasis> need to be a multiple of size of a field does <emphasis>not</emphasis> need to be a multiple of
8. The range is inclusive where the least significant bit in the token 8. The range is inclusive where the least significant bit in the token
is labeled 0. The endianess of the processor will effect this labeling is labeled 0. When defining tokens that are bigger than 1 byte, the
when defining tokens that are bigger than 1 byte. After each field global endianess setting (See <xref linkend="sleigh_endianess_definition"/>)
will affect this labeling. Although it is rarely required, it is possible to override
the global endianess setting for a specific token by appending either the qualifier
<emphasis role="bold">endian=little</emphasis> or <emphasis role="bold">endian=big</emphasis>
immediately after the token name and size. For instance:
<informalexample>
<programlisting>
define token instr ( 32 ) endian=little op0=(0,15) <emphasis role="weak">...</emphasis>
</programlisting>
</informalexample>
The token <emphasis>instr</emphasis> is overridden to be little endian.
This override applies to all fields defined for the token but affects no other tokens.
</para>
<para>
After each field
declaration, there can be zero or more of the following attribute declaration, there can be zero or more of the following attribute
keywords: keywords:
<informalexample> <informalexample>
@ -2023,7 +2038,7 @@ assignment to such a variable changes the context in which the current
instruction is being disassembled and can potentially have a drastic instruction is being disassembled and can potentially have a drastic
effect on how the rest of the instruction is disassembled. An effect on how the rest of the instruction is disassembled. An
assignment of this form is considered local to the instruction and assignment of this form is considered local to the instruction and
will not effect how other instructions are parsed. The context will not affect how other instructions are parsed. The context
variable is reset to its original value before parsing other variable is reset to its original value before parsing other
instructions. The disassembly action may also contain one or instructions. The disassembly action may also contain one or
more <emphasis role="bold">globalset</emphasis> directives, which more <emphasis role="bold">globalset</emphasis> directives, which
@ -2547,7 +2562,7 @@ the table symbol <emphasis>mode</emphasis>. When this constructor is
matched, as part of a more complicated instruction, the matched, as part of a more complicated instruction, the
symbol <emphasis>mode</emphasis> will represent the original semantic symbol <emphasis>mode</emphasis> will represent the original semantic
value of <emphasis>reg</emphasis> but with the standard post-increment value of <emphasis>reg</emphasis> but with the standard post-increment
side effect. side-effect.
</para> </para>
<para> <para>
The table symbol associated with the constructor becomes The table symbol associated with the constructor becomes
@ -3724,7 +3739,7 @@ blr is opcode=35 &amp; reg=15 &amp; LRset=1 { return [lr]; }
An alternative to the <emphasis role="bold">noflow</emphasis> attribute is to simply issue An alternative to the <emphasis role="bold">noflow</emphasis> attribute is to simply issue
multiple directives within a single constructor, so an explicit end to a context change multiple directives within a single constructor, so an explicit end to a context change
can be given. The value of the variable exported to the global state can be given. The value of the variable exported to the global state
is the one in affect at the point where the directive is issued. Thus, is the one in effect at the point where the directive is issued. Thus,
after one <emphasis role="bold">globalset</emphasis>, the same context after one <emphasis role="bold">globalset</emphasis>, the same context
variable can be assigned a different value, followed by variable can be assigned a different value, followed by
another <emphasis role="bold">globalset</emphasis> for a different another <emphasis role="bold">globalset</emphasis> for a different
@ -3735,7 +3750,7 @@ Because context in SLEIGH is controlled by a disassembly process,
there are some basic caveats to the use of there are some basic caveats to the use of
the <emphasis role="bold">globalset</emphasis> directive. With the <emphasis role="bold">globalset</emphasis> directive. With
<emphasis>flowing</emphasis> context changes, <emphasis>flowing</emphasis> context changes,
there is no guarantee of what global state will be in affect at a there is no guarantee of what global state will be in effect at a
particular address. During disassembly, at any given particular address. During disassembly, at any given
point, the process may not have uncovered all the relevant directives, point, the process may not have uncovered all the relevant directives,
and the known directives may not necessarily be consistent. In and the known directives may not necessarily be consistent. In

View file

@ -526,6 +526,9 @@ public class DecompileCallback {
private Namespace getNameSpaceByID(long id) { private Namespace getNameSpaceByID(long id) {
Symbol namespaceSym = program.getSymbolTable().getSymbol(id); Symbol namespaceSym = program.getSymbolTable().getSymbol(id);
if (namespaceSym == null) {
return null;
}
Object namespace = namespaceSym.getObject(); Object namespace = namespaceSym.getObject();
if (namespace instanceof Namespace) { if (namespace instanceof Namespace) {
return (Namespace) namespace; return (Namespace) namespace;

View file

@ -123,13 +123,7 @@ public class ASTGraphTask extends Task {
graphType == GraphType.DATA_FLOW_GRAPH ? "AST Data Flow" : "AST Control Flow"; graphType == GraphType.DATA_FLOW_GRAPH ? "AST Data Flow" : "AST Control Flow";
description = description + " for " + hfunction.getFunction().getName(); description = description + " for " + hfunction.getFunction().getName();
display.setGraph(graph, description, false, monitor); display.setGraph(graph, description, false, monitor);
// set the graph location setGraphLocation(display, displayListener);
if (location != null) {
AttributedVertex vertex = displayListener.getVertex(location);
// update graph location, but don't have it send out event
display.setFocusedVertex(vertex, EventTrigger.INTERNAL_ONLY);
}
} }
catch (GraphException e) { catch (GraphException e) {
Msg.showError(this, null, "Graph Error", e.getMessage()); Msg.showError(this, null, "Graph Error", e.getMessage());
@ -140,6 +134,20 @@ public class ASTGraphTask extends Task {
} }
private void setGraphLocation(GraphDisplay display, ASTGraphDisplayListener displayListener) {
if (location == null) {
return;
}
AttributedVertex vertex = displayListener.getVertex(location);
if (vertex == null) {
return; // location not in graph
}
// update graph location, but don't have it send out event
display.setFocusedVertex(vertex, EventTrigger.INTERNAL_ONLY);
}
protected void createDataFlowGraph(AttributedGraph graph, TaskMonitor monitor) protected void createDataFlowGraph(AttributedGraph graph, TaskMonitor monitor)
throws CancelledException { throws CancelledException {
Iterator<PcodeOpAST> opIter = hfunction.getPcodeOps(); Iterator<PcodeOpAST> opIter = hfunction.getPcodeOps();

View file

@ -1091,7 +1091,8 @@ class FGActionManager {
AddressSet subtraction = provider.getCurrentProgramSelection().subtract(functionBody); AddressSet subtraction = provider.getCurrentProgramSelection().subtract(functionBody);
ProgramSelection programSelectionWithoutGraphBody = new ProgramSelection(subtraction); ProgramSelection programSelectionWithoutGraphBody = new ProgramSelection(subtraction);
plugin.getTool().firePluginEvent(new ProgramSelectionPluginEvent("Spoof!", plugin.getTool()
.firePluginEvent(new ProgramSelectionPluginEvent("Spoof!",
programSelectionWithoutGraphBody, provider.getCurrentProgram())); programSelectionWithoutGraphBody, provider.getCurrentProgram()));
} }
@ -1161,8 +1162,10 @@ class FGActionManager {
private void makeSelectionFromAddresses(AddressSet addresses) { private void makeSelectionFromAddresses(AddressSet addresses) {
ProgramSelection selection = new ProgramSelection(addresses); ProgramSelection selection = new ProgramSelection(addresses);
plugin.getTool().firePluginEvent( plugin.getTool()
new ProgramSelectionPluginEvent("Spoof!", selection, provider.getCurrentProgram())); .firePluginEvent(
new ProgramSelectionPluginEvent("Spoof!", selection,
provider.getCurrentProgram()));
} }
private void ungroupVertices(Set<GroupedFunctionGraphVertex> groupVertices) { private void ungroupVertices(Set<GroupedFunctionGraphVertex> groupVertices) {

View file

@ -1,7 +1,7 @@
EXCLUDE FROM GHIDRA JAR: true EXCLUDE FROM GHIDRA JAR: true
MODULE FILE LICENSE: lib/jungrapht-visualization-1.0-RC9.jar BSD MODULE FILE LICENSE: lib/jungrapht-visualization-1.0.jar BSD
MODULE FILE LICENSE: lib/jungrapht-layout-1.0-RC9.jar BSD MODULE FILE LICENSE: lib/jungrapht-layout-1.0.jar BSD
MODULE FILE LICENSE: lib/jgrapht-core-1.5.0.jar LGPL 2.1 MODULE FILE LICENSE: lib/jgrapht-core-1.5.0.jar LGPL 2.1
MODULE FILE LICENSE: lib/jgrapht-io-1.5.0.jar LGPL 2.1 MODULE FILE LICENSE: lib/jgrapht-io-1.5.0.jar LGPL 2.1
MODULE FILE LICENSE: lib/jheaps-0.13.jar Apache License 2.0 MODULE FILE LICENSE: lib/jheaps-0.13.jar Apache License 2.0

View file

@ -11,10 +11,8 @@ eclipse.project.name = 'Features Graph Services'
dependencies { dependencies {
compile project(":Base") compile project(":Base")
// compile "com.github.tomnelson:jungrapht-visualization:1.0-SNAPSHOT" compile "com.github.tomnelson:jungrapht-visualization:1.0"
// compile "com.github.tomnelson:jungrapht-layout:1.0-SNAPSHOT" compile "com.github.tomnelson:jungrapht-layout:1.0"
compile "com.github.tomnelson:jungrapht-visualization:1.0-RC9"
compile "com.github.tomnelson:jungrapht-layout:1.0-RC9"
compile "org.jgrapht:jgrapht-core:1.5.0" compile "org.jgrapht:jgrapht-core:1.5.0"
// not using jgrapht-io code that depends on antlr, so exclude antlr // not using jgrapht-io code that depends on antlr, so exclude antlr

View file

@ -50,11 +50,9 @@
<tocroot> <tocroot>
<tocref id="Graphing"> <tocref id="Graph Services">
<tocdef id="Graph Services" text="Graph Services">
<tocdef id="Default Graph Display" text="Default Graph Display" target="help/topics/GraphServices/GraphDisplay.htm" /> <tocdef id="Default Graph Display" text="Default Graph Display" target="help/topics/GraphServices/GraphDisplay.htm" />
<tocdef id="Exporting a Graph" text="Exporting a Graph" target="help/topics/GraphServices/GraphExport.htm" /> <tocdef id="Exporting a Graph" text="Exporting a Graph" target="help/topics/GraphServices/GraphExport.htm" />
</tocdef>
</tocref> </tocref>
</tocroot> </tocroot>

View file

@ -5,103 +5,245 @@
<META name="generator" content= <META name="generator" content=
"HTML Tidy for Java (vers. 2009-12-01), see jtidy.sourceforge.net"> "HTML Tidy for Java (vers. 2009-12-01), see jtidy.sourceforge.net">
<TITLE>Graphing</TITLE> <TITLE>Graph Display</TITLE>
<META http-equiv="Content-Type" content="text/html; charset=windows-1252"> <META http-equiv="Content-Type" content="text/html; charset=windows-1252">
<LINK rel="stylesheet" type="text/css" href="../../shared/Frontpage.css"> <LINK rel="stylesheet" type="text/css" href="../../shared/Frontpage.css">
</HEAD> </HEAD>
<BODY lang="EN-US"> <BODY lang="EN-US">
<A name="Default_Graph_Display"/> <A name="Default_Graph_Display">
<H1>Default Graph Display</H1> <H1>Default Graph Display</H1>
<H2>Visualization of a Graph</H2>
<BLOCKQUOTE> <BLOCKQUOTE>
<P>The visualization display will show the graph in a new window or in a new tab of a previously created graph window.</P> <P>The visualization display will show the graph in a new window or in a new tab of a
<BLOCKQUOTE> previously created graph window.</P>
<BLOCKQUOTE>
<P align="left"><IMG src="images/DefaultGraphDisplay.png" border="1"></P> <CENTER>
<TABLE border="0" width="100%">
<TR>
<TD width="100%" align="center"><IMG alt="" border="1" src=
"images/DefaultGraphDisplay.png"></TD>
</TR>
</TABLE>
</CENTER>
</BLOCKQUOTE> </BLOCKQUOTE>
</BLOCKQUOTE>
</BLOCKQUOTE> <H2>Manipulating the Graph</H2>
<H2>Manipulating the Graph:</H2>
<ul> <UL>
<li>MouseButton1+drag will translate the display in the x and y axis</li> <LI>Dragging in the graph or on any unselected vertices will pan the graph (translate the
<li>Mouse Wheel will zoom in and out</li> display in the x and y axis)</LI>
<li>CTRL+Mouse Wheel will zoom in and out in the X-Axis only</li>
<li>ALT+Mouse Wheel will zoom in and out in the Y-Axis only</li> <LI>Dragging a selected vertex will reposition all selected vertices</LI>
<li>Ctrl+MouseButton1 will select a vertex or edge</li>
<ul> <LI>Using the <CODE>Mouse Wheel</CODE> will zoom the graph in and out</LI>
<li>Shift+Ctrl+MouseButton1 over an unselected vertex will add that vertex to the selection</li>
<li>Shift+Ctrl+MouseButton1 over a previously selected vertex will remove that vertex from the selection</li> <LI><CODE>Control+Mouse Wheel</CODE> will zoom the graph in and out on the X-Axis only</LI>
</ul>
<li>Ctrl+MouseButton1+drag on an empty area will create a rectangular area and select enclosed vertices</li> <LI><CODE>ALT+Mouse Wheel</CODE> will zoom the graph in and out in the Y-Axis only</LI>
<li>Ctrl+MouseButton1+drag over a vertex will reposition all selected vertices</li>
</ul> <LI><CODE>Ctrl+Click</CODE> will select a vertex
<UL>
<LI><CODE>Ctrl+Click</CODE> over an unselected vertex will add that vertex to the
selection</LI>
<LI><CODE>Ctrl+Click</CODE> over a previously selected vertex will remove that vertex
from the selection</LI>
</UL>
</LI>
<LI><CODE>Ctrl+drag</CODE> on an empty area will create a rectangular area and select
enclosed vertices</LI>
</UL>
<H2>Toolbar Buttons</H2> <H2>Toolbar Buttons</H2>
<P><A name="Scroll_To_Selection"/> The <IMG src="images/locationIn.gif"> toggle button, when 'set' will cause a focused vertex (red arrow) to be scrolled to the center of the view</P>
<P><A name="Free_Form_Selection"/>The <IMG src="images/Lasso.png" width="16" height="16"> toggle button, when 'set' will allow the user to draw a free-form shape that encloses the vertices they wish to select.</P>
<P><A name="SatelliteView"/>The <IMG src="images/sat2.png" width="16" height="16"> toggle button, when 'set' will open a satellite mini view of the graph in the lower right corner. The mini-view can be manipulated with the mouse to affect the main view</P>
<P><A name="Reset_View"/>The <IMG src="images/reload3.png"> button will reset any visual transformations on the graph and center it at a best-effort size</P>
<P><A name="View_Magnifier"/>The <IMG src="images/magnifier.png"> toggle button, when 'set' will open a rectangular magnification lens in the graph view</P>
<BLOCKQUOTE><BLOCKQUOTE>
<ul>
<li>MouseButton1 click-drag on the lens center circle to move the magnifier lens</li>
<li>MouseButton1 click-draw on a lens edge diamond to resize the magnifier lens </li>
<li>MouseButton1 click on the upper-right circle-cross to dispose of the magnifier lens</li>
<li>MouseWheel will change the magnification of the lens</li>
</ul>
</BLOCKQUOTE></BLOCKQUOTE>
<P><A name="Show_Filters"/>The <IMG src="images/filter_on.png"> button will open a Filter dialog. Select buttons in the dialog to hide specific vertices or edges in the display. <P><A name="Scroll_To_Selection">
The Filter dialog buttons are created by examining the graph vertex/edge properties to discover candidates for filtering.</P></BLOCKQUOTE></BLOCKQUOTE> The <IMG alt="" src="images/locationIn.gif"> toggle button, when 'set' will cause a focused
vertex (the vertex with the red arrow) to be moved to the center of the view</P>
<P><A name="Arrangement"/>The <IMG src="images/katomic.png" width="16" height="16"> Arrangement menu is used to select one of several graph layout algorithms.</P> <P><A name="Free_Form_Selection">
<BLOCKQUOTE><BLOCKQUOTE> The <IMG alt="" src="images/Lasso.png" width="16" height="16"> toggle button, when 'set' will
allow the user to draw a free-form shape that encloses the vertices they wish to select.</P>
<ul> <P><A name="SatelliteView">
<li><A name="Compact_Hierarchical"/><B>Compact Hierarchical</B> is the <b>TidierTree Layout Algorithm</b>. It builds a tree structure and attempts to reduce horizontal space.</li> The <IMG alt="" src="images/network-wireless-16.png" width="16" height="16"> toggle button,
<li><A name="Hierarchical"/><B>Hierarchical</B> is a basic Tree algorithm. It prioritizes 'important' edges while constructing the tree.</li> when 'set' will open a satellite mini view of the graph in the lower right corner. The
<li><A name="Compact Radial"/><B>Compact Radial</B> is the <b>TidierTree Layout Algorithm</b> with the root(s) at the center and child vertices radiating outwards.</li> mini-view can be manipulated with the mouse to affect the main view</P>
<li><B>Hierarchical MinCross</B> is the <b>Sugiyama Layout Algorithm</b>. It attempts to route edges around vertices in order to reduce crossing.There are four layering algorithms:</li>
<ul> <P><A name="Reset_View">
<li><A name="Hierarchical_MinCross_Top_Down"/><B>Top Down</B> - biases the vertices to the top</li> The <IMG alt="" src="images/reload3.png"> button will reset any visual transformations on the
<li><A name="Hierarchical_MinCross_Longest_Path"/><B>Longest Path</B> - biases the vertices to the bottom</li> graph and center it at a best-effort size</P>
<li><A name="Hierarchical_MinCross_Network_Simplex"/><B>Network Simplex</B> - layers after finding an 'optimal tree'</li>
<li><A name="Hierarchical_MinCross_Coffman_Graham"/><B>Coffman Graham</B> - biases the vertices using a scheduling algorithm to minimize length</li> <P><A name="View_Magnifier">
</ul> The <IMG alt="" src="images/magnifier.png"> toggle button, when 'set' will open a rectangular
magnification lens in the graph view</P>
<BLOCKQUOTE>
<BLOCKQUOTE>
<UL>
<LI>MouseButton1 click-drag on the lens center circle to move the magnifier lens</LI>
<LI>MouseButton1 click-draw on a lens edge diamond to resize the magnifier lens</LI>
<LI>MouseButton1 click on the upper-right circle-cross to dispose of the magnifier
lens</LI>
<LI>MouseWheel will change the magnification of the lens</LI>
</UL>
</BLOCKQUOTE>
</BLOCKQUOTE>
<P><A name="Show_Filters">
The <IMG alt="" src="Icons.CONFIGURE_FILTER_ICON"> button will open a Filter dialog. Select
buttons in the dialog to hide specific vertices or edges in the display. The Filter dialog
buttons are created by examining the graph vertex/edge properties to discover candidates for
filtering.</P>
<P><A name="Arrangement">
The <IMG alt="" src="images/katomic.png" width="16" height="16"> Arrangement menu is used to
select one of several graph layout algorithms.</P>
<BLOCKQUOTE>
<BLOCKQUOTE>
<UL>
<LI><A name="Compact_Hierarchical">
<B>Compact Hierarchical</B> is the <B>TidierTree Layout Algorithm</B>. It builds a tree
structure and attempts to reduce horizontal space.</LI>
<LI><A name="Hierarchical">
<B>Hierarchical</B> is a basic Tree algorithm. It prioritizes 'important' edges while
constructing the tree.</LI>
<LI><A name="Compact Radial">
<B>Compact Radial</B> is the <B>TidierTree Layout Algorithm</B> with the root(s) at the
center and child vertices radiating outwards.</LI>
<LI><B>Hierarchical MinCross</B> is the <B>Sugiyama Layout Algorithm</B>. It attempts to
route edges around vertices in order to reduce crossing.There are four layering
algorithms:</LI>
<LI style="list-style: none">
<UL>
<LI><A name="Hierarchical_MinCross_Top_Down">
<B>Top Down</B> - biases the vertices to the top</LI>
<LI><A name="Hierarchical_MinCross_Longest_Path">
<B>Longest Path</B> - biases the vertices to the bottom</LI>
<LI><A name="Hierarchical_MinCross_Network_Simplex">
<B>Network Simplex</B> - layers after finding an 'optimal tree'</LI>
<LI><A name="Hierarchical_MinCross_Coffman_Graham">
<B>Coffman Graham</B> - biases the vertices using a scheduling algorithm to minimize
length</LI>
</UL>
</LI>
<LI><A name="Circle">
<B>Circle</B> will arrange vertices in a Circle. If there are not too many edges (less
than specified in the jungrapht.circle.reduceEdgeCrossingMaxEdges property with a default
of 200), it will attempt to reduce edge crossing by rearranging the vertices.</LI>
<LI><A name="Force_Balanced">
<B>Force Balanced</B> is a <B>Force Directed Layout Algorithm</B> using the the <B>Kamada
Kawai</B> approach. It attempts to balance the graph by considering vertices and edge
connections.</LI>
<LI><A name="Force_Directed">
<B>Force Directed</B> is a <B>Force Directed Layout Algorithm</B> using the
<B>Fructermann Reingold</B> approach. It pushes unconnected vertices apart and draws
connected vertices together.</LI>
<LI><A name="Radial">
<B>Radial</B> is a Tree structure with the root(s) at the center and child vertices
radiating outwards.</LI>
<LI><A name="Balloon">
<B>Balloon</B> is a Tree structure with the root(s) at the centers of circles in a radial
pattern</LI>
<LI><A name="Gem__Graph_Embedder_">
<B>GEM</B> is a Force Directed layout with locally separated components</LI>
</UL>
</BLOCKQUOTE>
</BLOCKQUOTE>
<li><A name="Circle"/><B>Circle</B> will arrange vertices in a Circle. If there are not too many edges (less than specified in the jungrapht.circle.reduceEdgeCrossingMaxEdges property with a default of 200), it will attempt to reduce edge crossing by rearranging the vertices.</li>
<li><A name="Force_Balanced"/><B>Force Balanced</B> is a <b>Force Directed Layout Algorithm</b> using the the <b>Kamada Kawai</b> approach. It attempts to balance the graph by considering vertices and edge connections.</li>
<li><A name="Force_Directed"/><B>Force Directed</B> is a <b>Force Directed Layout Algorithm</b> using the <b>Fructermann Reingold</b> approach. It pushes unconnected vertices apart and draws connected vertices together.</li>
<li><A name="Radial"/><B>Radial</B> is a Tree structure with the root(s) at the center and child vertices radiating outwards.</li>
<li><A name="Balloon"/><B>Balloon</B> is a Tree structure with the root(s) at the centers of circles in a radial pattern</li>
<li><A name="Gem__Graph_Embedder_"/><B>GEM</B> is a Force Directed layout with locally separated components </li>
</ul>
</BLOCKQUOTE></BLOCKQUOTE>
<H2>Popup Actions</H2> <H2>Popup Actions</H2>
<BLOCKQUOTE> <BLOCKQUOTE>
<H3>Standard Popup Actions</H3> <H3>Standard Popup Actions</H3>
<ul>
<li><A name="Hide Selected"/><B>Hide Selected</B> - Causes the display to not show selected vertices. </li> <UL>
<li><A name="Hide Unselected"/><B>Hide Unselected</B> - Causes the display to not show unselected vertices.</li> <LI><A name="Hide_Selected">
<li><A name="Invert Selection"/><B>Invert Selection</B> - Unselects all selected nodes and selects all unselected nodes.</li> <B>Hide Selected</B> - Causes the display to not show selected vertices.</LI>
<li><A name="Grow Selection From Sources"/><B>Grow Selection From Sources</B> - Adds to the selection all vertices that have outgoing edges to the current selection.</li>
<li><A name="Grow Selection To Targets"/><B>Grow Selection To Targets</B> - Adds to the selection all vertices that have incoming edges from the current selection.</li> <LI><A name="Hide_Unselected">
<li><A name="Create Subgraph"/><B>Display Selected As New Graph</B> - Creates a new graph and display from the currently selected vertices.</li> <B>Hide Unselected</B> - Causes the display to not show unselected vertices.</LI>
</ul>
<LI><A name="Invert_Selection">
<B>Invert Selection</B> - Unselects all selected nodes and selects all unselected
nodes.</LI>
<LI><A name="Grow_Selection From Sources">
<B>Grow Selection From Sources</B> - Adds to the selection all vertices that have outgoing
edges to the current selection.</LI>
<LI><A name="Grow_Selection To Targets">
<B>Grow Selection To Targets</B> - Adds to the selection all vertices that have incoming
edges from the current selection.</LI>
<LI><A name="Clear_Selection">
<B>Clear Selection</B> - Clears all edge and vertex selection.</LI>
<LI><A name="Create_Subgraph">
<B>Display Selected As New Graph</B> - Creates a new graph and display from the currently
selected vertices.</LI>
<LI><A name="Display_Popup_Windows">
<B>Display Popup Windows</B> - When toggled off no tooltip popups will be displayed.</LI>
</UL>
<H3>Vertex Popup Actions</H3> <H3>Vertex Popup Actions</H3>
<ul>
<li><A name="Select Vertex"/><B>Select Vertex</B> - Selects the vertex that this action was invoked on.</li> <UL>
<li><A name="Deselect Vertex"/><B>Deselect Vertex</B> - Deselects the vertex that this action was invoked on.</li> <LI><A name="Select_Vertex">
</ul> <B>Select Vertex</B> - Selects the vertex that this action was invoked on.</LI>
<LI><A name="Deselect_Vertex">
<B>Deselect Vertex</B> - Deselects the vertex that this action was invoked on.</LI>
</UL>
<H3>Edge Popup Actions</H3> <H3>Edge Popup Actions</H3>
<ul>
<li><A name="Edge Source"/><B>Go To Edge Source</B> - Makes this edge's source vertex be the focused vertex.</li> <UL>
<li><A name="Edge Target"/><B>Go To Edge Target</B> - Makes this edge's destination vertex be the focused vertex.</li> <LI><A name="Edge_Source">
<li><A name="Select Edge"/><B>Select Edge</B> - Add this edge and its associated vertices to the selection</li> <B>Go To Edge Source</B> - Makes this edge's source vertex be the focused vertex.</LI>
<li><A name="Deselect Edge"/><B>Deselect Edge</B> - Removes this edge and its associated vertices from the selection </li>
</ul> <LI><A name="Edge_Target">
<B>Go To Edge Target</B> - Makes this edge's destination vertex be the focused vertex.</LI>
<LI><A name="Select_Edge">
<B>Select Edge</B> - Add this edge and its associated vertices to the selection</LI>
<LI><A name="Deselect_Edge">
<B>Deselect Edge</B> - Removes this edge and its associated vertices from the
selection</LI>
</UL>
</BLOCKQUOTE> </BLOCKQUOTE>
<P class="providedbyplugin">Provided By:&nbsp; <I>GraphDisplayBrokerPlugin</I></P>
<P class="relatedtopic">Related Topics:</P>
<UL>
<LI><A href="help/topics/GraphServices/GraphExport.htm">Graph Export</A></LI>
</UL><BR>
<BR>
</BODY> </BODY>
</HTML> </HTML>

View file

@ -11,19 +11,22 @@
</HEAD> </HEAD>
<BODY lang="EN-US"> <BODY lang="EN-US">
<A name="Default Graph Exporter"/> <A NAME="Graph_Exporter"/>
<H1>Graph Export Service</H1> <H1>Graph Export Service</H1>
<H2> Export Dialog </H2> <H2> Export Dialog </H2>
<P> Whenever a graph is generated and the graph output is set to <B>Graph Export</B>, then the <P> Whenever a graph is generated and the graph output is set to <B>Graph Export</B>, then the
following graph export dialog is displayed: </P> following graph export dialog is displayed: </P>
<BR> <BR>
<BLOCKQUOTE>
<BLOCKQUOTE>
<P align="left"><IMG src="images/ExportDialog.png"></P> <CENTER>
</BLOCKQUOTE> <TABLE border="0" width="100%">
</BLOCKQUOTE> <TR>
<TD width="100%" align="center"><IMG alt="" border="1" src=
"images/ExportDialog.png"></TD>
</TR>
</TABLE>
</CENTER>
<BR> <BR>
<BLOCKQUOTE> <BLOCKQUOTE>
<P>The Export Graph dialog offers a choice of the following graph formats:</P> <P>The Export Graph dialog offers a choice of the following graph formats:</P>
@ -49,5 +52,16 @@
<p>The <b>Ok</b> button will marshal the graph to the selected file in the selected format and close the dialog.</p> <p>The <b>Ok</b> button will marshal the graph to the selected file in the selected format and close the dialog.</p>
<p>The <b>Cancel</b> button will close the dialog and perform no other action.</p> <p>The <b>Cancel</b> button will close the dialog and perform no other action.</p>
<P class="providedbyplugin">Provided By:&nbsp; <I>GraphDisplayBrokerPlugin</I></P>
<P class="relatedtopic">Related Topics:</P>
<UL>
<LI><A href="help/topics/GraphServices/GraphDisplay.htm">Default Graph Display</A></LI>
</UL>
<BR>
<BR>
</BODY> </BODY>
</HTML> </HTML>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Before After
Before After

View file

@ -73,6 +73,6 @@ public class ExportAttributedGraphDisplayProvider implements GraphDisplayProvide
@Override @Override
public HelpLocation getHelpLocation() { public HelpLocation getHelpLocation() {
return new HelpLocation("GraphServices", "Default_Graph_Exporter"); return new HelpLocation("GraphServices", "Graph_Exporter");
} }
} }

View file

@ -74,7 +74,7 @@ public class GraphExporterDialog extends DialogComponentProvider {
addWorkPanel(buildWorkPanel()); addWorkPanel(buildWorkPanel());
addOKButton(); addOKButton();
addCancelButton(); addCancelButton();
setHelpLocation(new HelpLocation("ExporterPlugin", "Exporter_Dialog")); setHelpLocation(new HelpLocation("GraphServices", "Graph_Exporter"));
validate(); validate();
} }

View file

@ -27,10 +27,10 @@ final class DefaultDisplayGraphIcons {
private DefaultDisplayGraphIcons() { private DefaultDisplayGraphIcons() {
} }
public static final Icon SATELLITE_VIEW_ICON = Icons.get("images/sat2.png"); public static final Icon SATELLITE_VIEW_ICON = Icons.get("images/network-wireless-16.png");
public static final Icon VIEW_MAGNIFIER_ICON = Icons.get("images/magnifier.png"); public static final Icon VIEW_MAGNIFIER_ICON = Icons.get("images/magnifier.png");
public static final Icon PROGRAM_GRAPH_ICON = Icons.get("images/redspheregraph.png"); public static final Icon PROGRAM_GRAPH_ICON = Icons.get("images/redspheregraph.png");
public static final Icon LAYOUT_ALGORITHM_ICON = Icons.get("images/katomic.png"); public static final Icon LAYOUT_ALGORITHM_ICON = Icons.get("images/katomic.png");
public static final Icon LASSO_ICON = Icons.get("images/Lasso.png"); public static final Icon LASSO_ICON = Icons.get("images/Lasso.png");
public static final Icon FILTER_ICON = Icons.get("images/filter_on.png"); public static final Icon FILTER_ICON = Icons.CONFIGURE_FILTER_ICON;
} }

View file

@ -23,7 +23,6 @@ import java.awt.event.*;
import java.awt.geom.Point2D; import java.awt.geom.Point2D;
import java.util.*; import java.util.*;
import java.util.List; import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -32,6 +31,7 @@ import javax.swing.*;
import javax.swing.event.AncestorEvent; import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener; import javax.swing.event.AncestorListener;
import org.apache.commons.lang3.StringUtils;
import org.jgrapht.Graph; import org.jgrapht.Graph;
import org.jgrapht.graph.AsSubgraph; import org.jgrapht.graph.AsSubgraph;
import org.jungrapht.visualization.*; import org.jungrapht.visualization.*;
@ -58,9 +58,13 @@ import docking.action.ToggleDockingAction;
import docking.action.builder.*; import docking.action.builder.*;
import docking.menu.ActionState; import docking.menu.ActionState;
import docking.widgets.EventTrigger; import docking.widgets.EventTrigger;
import generic.util.WindowUtilities;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.graph.AttributeFilters; import ghidra.graph.AttributeFilters;
import ghidra.graph.job.GraphJobRunner; import ghidra.graph.job.GraphJobRunner;
import ghidra.graph.viewer.popup.*;
import ghidra.graph.visualization.mouse.JgtPluggableGraphMouse;
import ghidra.graph.visualization.mouse.JgtUtils;
import ghidra.service.graph.*; import ghidra.service.graph.*;
import ghidra.util.*; import ghidra.util.*;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
@ -73,19 +77,18 @@ import resources.Icons;
*/ */
public class DefaultGraphDisplay implements GraphDisplay { public class DefaultGraphDisplay implements GraphDisplay {
public static final String FAVORED_EDGE = "Fall-Through"; private static final String ACTION_OWNER = "GraphServices";
private static final int MAX_NODES = Integer.getInteger("maxNodes", 10000);
public static final Dimension PREFERRED_VIEW_SIZE = new Dimension(1000, 1000);
public static final Dimension PREFERRED_LAYOUT_SIZE = new Dimension(3000, 3000);
Logger log = Logger.getLogger(DefaultGraphDisplay.class.getName()); private static final String FAVORED_EDGE = "Fall-Through";
private static final int MAX_NODES = Integer.getInteger("maxNodes", 10000);
private static final Dimension PREFERRED_VIEW_SIZE = new Dimension(1000, 1000);
private static final Dimension PREFERRED_LAYOUT_SIZE = new Dimension(3000, 3000);
private Logger log = Logger.getLogger(DefaultGraphDisplay.class.getName());
private GraphDisplayListener listener = new DummyGraphDisplayListener(); private GraphDisplayListener listener = new DummyGraphDisplayListener();
private String title; private String title;
/**
* the {@link Graph} to visualize
*/
private AttributedGraph graph; private AttributedGraph graph;
/** /**
@ -94,99 +97,71 @@ public class DefaultGraphDisplay implements GraphDisplay {
private final int displayId; private final int displayId;
/** /**
* the delegate viewer to display the ProgramGraph * The delegate viewer to display the ProgramGraph
*/ */
private final VisualizationViewer<AttributedVertex, AttributedEdge> viewer; private final VisualizationViewer<AttributedVertex, AttributedEdge> viewer;
/** /**
* the {@link PluginTool} * The {@link PluginTool}
*/ */
private final PluginTool pluginTool; private final PluginTool pluginTool;
/**
* the "owner name" for action - mainly affects default help location
*/
private final String actionOwnerName = "GraphServices";
/**
* provides the component for the {@link GraphDisplay}
*/
private final DefaultGraphDisplayComponentProvider componentProvider; private final DefaultGraphDisplayComponentProvider componentProvider;
/** /**
* whether to ensure the focused vertex is visible, scrolling if necessary * Whether to ensure the focused vertex is visible, scrolling if necessary
* the visualization in order to center the selected vertex * the visualization in order to center the selected vertex
* or the center of the set of selected vertices * or the center of the set of selected vertices
*/ */
private boolean ensureVertexIsVisible = false; private boolean ensureVertexIsVisible = false;
/** /**
* allows selection of various {@link LayoutAlgorithm} ('arrangements') * Allows selection of various {@link LayoutAlgorithm} ('arrangements')
*/ */
private final LayoutTransitionManager layoutTransitionManager; private final LayoutTransitionManager layoutTransitionManager;
/** /**
* provides graph displays for supplied graphs * Provides graph displays for supplied graphs
*/ */
private final DefaultGraphDisplayProvider graphDisplayProvider; private final DefaultGraphDisplayProvider graphDisplayProvider;
/** /**
* the vertex that has been nominated to be 'focused' in the graph display and listing * the vertex that has been nominated to be 'focused' in the graph display and listing
*/ */
private AttributedVertex focusedVertex; private AttributedVertex focusedVertex;
/**
* Runs animation jobs for updating the display
*/
private final GraphJobRunner jobRunner = new GraphJobRunner(); private final GraphJobRunner jobRunner = new GraphJobRunner();
/** /**
* a satellite view that shows in the lower left corner as a birds-eye view of the graph display * a satellite view that shows in the lower left corner as a birds-eye view of the graph display
*/ */
private final SatelliteVisualizationViewer<AttributedVertex, AttributedEdge> satelliteViewer; private final SatelliteVisualizationViewer<AttributedVertex, AttributedEdge> satelliteViewer;
/**
* generated filters on edges
*/
private AttributeFilters edgeFilters;
/**
* generated filters on vertices
*/
private AttributeFilters vertexFilters;
/**
* a dialog populated with generated vertex/edge filters
*/
private FilterDialog filterDialog; private FilterDialog filterDialog;
/** private AttributeFilters edgeFilters;
* holds the vertex icons (instead of recomputing them) private AttributeFilters vertexFilters;
*/
private GhidraIconCache iconCache; private GhidraIconCache iconCache;
/** /**
* multi-selection is done in a free-form traced shape instead of a rectangle * Multi-selection is done in a free-form traced shape instead of a rectangle
*/ */
private boolean freeFormSelection; private boolean freeFormSelection;
/** /**
* Handles the popup * Handles all mouse interaction
*/ */
private GhidraGraphMouse graphMouse; private JgtPluggableGraphMouse graphMouse;
/**
* Will accept a {@link Graph} and use it to create a new graph display in
* a new tab or new window
*/
Consumer<Graph<AttributedVertex, AttributedEdge>> subgraphConsumer = g -> {
try {
AttributedGraph attributedGraph = new AttributedGraph();
g.vertexSet().forEach(attributedGraph::addVertex);
g.edgeSet().forEach(e -> {
AttributedVertex source = g.getEdgeSource(e);
AttributedVertex target = g.getEdgeTarget(e);
attributedGraph.addEdge(source, target, e);
});
displaySubGraph(attributedGraph);
}
catch (CancelledException e) {
// noop
}
};
private ToggleDockingAction hideSelectedAction; private ToggleDockingAction hideSelectedAction;
private ToggleDockingAction hideUnselectedAction; private ToggleDockingAction hideUnselectedAction;
private SwitchableSelectionItemListener switchableSelectionListener; private SwitchableSelectionItemListener switchableSelectionListener;
private ToggleDockingAction togglePopupsAction;
private PopupRegulator<AttributedVertex, AttributedEdge> popupRegulator;
/** /**
* Create the initial display, the graph-less visualization viewer, and its controls * Create the initial display, the graph-less visualization viewer, and its controls
* @param displayProvider provides a {@link PluginTool} for Docking features * @param displayProvider provides a {@link PluginTool} for Docking features
@ -202,6 +177,9 @@ public class DefaultGraphDisplay implements GraphDisplay {
componentProvider = new DefaultGraphDisplayComponentProvider(this, pluginTool); componentProvider = new DefaultGraphDisplayComponentProvider(this, pluginTool);
componentProvider.addToTool(); componentProvider.addToTool();
satelliteViewer = createSatelliteViewer(viewer); satelliteViewer = createSatelliteViewer(viewer);
if (graphDisplayProvider.getDefaultSatelliteState()) {
viewer.getComponent().add(satelliteViewer.getComponent());
}
layoutTransitionManager = layoutTransitionManager =
new LayoutTransitionManager(viewer, this::isRoot); new LayoutTransitionManager(viewer, this::isRoot);
@ -221,7 +199,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
.builder(viewer.getRenderContext().getVertexBoundsFunction()) .builder(viewer.getRenderContext().getVertexBoundsFunction())
.build()); .build());
graphMouse = new GhidraGraphMouse(componentProvider, viewer); graphMouse = new JgtPluggableGraphMouse(this);
createToolbarActions(); createToolbarActions();
createPopupActions(); createPopupActions();
@ -297,7 +275,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
private void createToolbarActions() { private void createToolbarActions() {
// create a toggle for 'scroll to selected vertex' // create a toggle for 'scroll to selected vertex'
new ToggleActionBuilder("Scroll To Selection", actionOwnerName) new ToggleActionBuilder("Scroll To Selection", ACTION_OWNER)
.toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON) .toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON)
.description("Ensure that the 'focused' vertex is visible") .description("Ensure that the 'focused' vertex is visible")
.selected(true) .selected(true)
@ -309,7 +287,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
// create a toggle for enabling 'free-form' selection: selection is // create a toggle for enabling 'free-form' selection: selection is
// inside of a traced shape instead of a rectangle // inside of a traced shape instead of a rectangle
new ToggleActionBuilder("Free-Form Selection", actionOwnerName) new ToggleActionBuilder("Free-Form Selection", ACTION_OWNER)
.toolBarIcon(DefaultDisplayGraphIcons.LASSO_ICON) .toolBarIcon(DefaultDisplayGraphIcons.LASSO_ICON)
.description("Trace Free-Form Shape to select multiple vertices (CTRL-click-drag)") .description("Trace Free-Form Shape to select multiple vertices (CTRL-click-drag)")
.selected(false) .selected(false)
@ -318,13 +296,14 @@ public class DefaultGraphDisplay implements GraphDisplay {
.buildAndInstallLocal(componentProvider); .buildAndInstallLocal(componentProvider);
// create an icon button to display the satellite view // create an icon button to display the satellite view
new ToggleActionBuilder("SatelliteView", actionOwnerName).description("Show Satellite View") new ToggleActionBuilder("SatelliteView", ACTION_OWNER).description("Show Satellite View")
.toolBarIcon(DefaultDisplayGraphIcons.SATELLITE_VIEW_ICON) .toolBarIcon(DefaultDisplayGraphIcons.SATELLITE_VIEW_ICON)
.onAction(this::toggleSatellite) .onAction(this::toggleSatellite)
.selected(graphDisplayProvider.getDefaultSatelliteState())
.buildAndInstallLocal(componentProvider); .buildAndInstallLocal(componentProvider);
// create an icon button to reset the view transformations to identity (scaled to layout) // create an icon button to reset the view transformations to identity (scaled to layout)
new ActionBuilder("Reset View", actionOwnerName) new ActionBuilder("Reset View", ACTION_OWNER)
.description("Reset all view transforms to center graph in display") .description("Reset all view transforms to center graph in display")
.toolBarIcon(Icons.REFRESH_ICON) .toolBarIcon(Icons.REFRESH_ICON)
.onAction(context -> viewer.scaleToLayout()) .onAction(context -> viewer.scaleToLayout())
@ -332,7 +311,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
// create a button to show the view magnify lens // create a button to show the view magnify lens
LensSupport<LensGraphMouse> magnifyViewSupport = createMagnifier(); LensSupport<LensGraphMouse> magnifyViewSupport = createMagnifier();
ToggleDockingAction lensToggle = new ToggleActionBuilder("View Magnifier", actionOwnerName) ToggleDockingAction lensToggle = new ToggleActionBuilder("View Magnifier", ACTION_OWNER)
.description("Show View Magnifier") .description("Show View Magnifier")
.toolBarIcon(DefaultDisplayGraphIcons.VIEW_MAGNIFIER_ICON) .toolBarIcon(DefaultDisplayGraphIcons.VIEW_MAGNIFIER_ICON)
.onAction(context -> magnifyViewSupport.activate( .onAction(context -> magnifyViewSupport.activate(
@ -343,90 +322,91 @@ public class DefaultGraphDisplay implements GraphDisplay {
componentProvider.addLocalAction(lensToggle); componentProvider.addLocalAction(lensToggle);
// create an action button to show a dialog with generated filters // create an action button to show a dialog with generated filters
new ActionBuilder("Show Filters", actionOwnerName).description("Show Graph Filters") new ActionBuilder("Show Filters", ACTION_OWNER).description("Show Graph Filters")
.toolBarIcon(DefaultDisplayGraphIcons.FILTER_ICON) .toolBarIcon(DefaultDisplayGraphIcons.FILTER_ICON)
.onAction(context -> showFilterDialog()) .onAction(context -> showFilterDialog())
.buildAndInstallLocal(componentProvider); .buildAndInstallLocal(componentProvider);
// create a menu with graph layout algorithm selections // create a menu with graph layout algorithm selections
new MultiStateActionBuilder<String>("Arrangement", actionOwnerName) List<ActionState<String>> layoutActionStates = getLayoutActionStates();
.description("Select Layout Arrangement Algorithm") new MultiStateActionBuilder<String>("Arrangement", ACTION_OWNER)
.description("Arrangement: " + layoutActionStates.get(0).getName())
.toolBarIcon(DefaultDisplayGraphIcons.LAYOUT_ALGORITHM_ICON) .toolBarIcon(DefaultDisplayGraphIcons.LAYOUT_ALGORITHM_ICON)
.fireFirstAction(false) .fireFirstAction(false)
.onActionStateChanged((s, t) -> layoutChanged(s.getName())) .onActionStateChanged((s, t) -> layoutChanged(s.getName()))
.addStates(getLayoutActionStates()) .addStates(layoutActionStates)
.buildAndInstallLocal(componentProvider); .buildAndInstallLocal(componentProvider);
} }
private void createPopupActions() { private void createPopupActions() {
new ActionBuilder("Select Vertex", actionOwnerName) new ActionBuilder("Select Vertex", ACTION_OWNER)
.popupMenuPath("Select Vertex") .popupMenuPath("Select Vertex")
.popupMenuGroup("selection", "1") .popupMenuGroup("selection", "1")
.withContext(VertexGraphActionContext.class) .withContext(VertexGraphActionContext.class)
.enabledWhen(c -> !viewer.getSelectedVertexState().isSelected(c.getClickedVertex())) .enabledWhen(c -> !isSelected(c.getClickedVertex()))
.onAction(c -> viewer.getSelectedVertexState().select(c.getClickedVertex())) .onAction(c -> viewer.getSelectedVertexState().select(c.getClickedVertex()))
.buildAndInstallLocal(componentProvider); .buildAndInstallLocal(componentProvider);
new ActionBuilder("Deselect Vertex", actionOwnerName) new ActionBuilder("Deselect Vertex", ACTION_OWNER)
.popupMenuPath("Deselect Vertex") .popupMenuPath("Deselect Vertex")
.popupMenuGroup("selection", "2") .popupMenuGroup("selection", "2")
.withContext(VertexGraphActionContext.class) .withContext(VertexGraphActionContext.class)
.enabledWhen(c -> viewer.getSelectedVertexState().isSelected(c.getClickedVertex())) .enabledWhen(c -> isSelected(c.getClickedVertex()))
.onAction(c -> viewer.getSelectedVertexState().deselect(c.getClickedVertex())) .onAction(c -> viewer.getSelectedVertexState().deselect(c.getClickedVertex()))
.buildAndInstallLocal(componentProvider); .buildAndInstallLocal(componentProvider);
new ActionBuilder("Select Edge", actionOwnerName) new ActionBuilder("Select Edge", ACTION_OWNER)
.popupMenuPath("Select Edge") .popupMenuPath("Select Edge")
.popupMenuGroup("selection", "1") .popupMenuGroup("selection", "1")
.withContext(EdgeGraphActionContext.class) .withContext(EdgeGraphActionContext.class)
.enabledWhen(c -> !viewer.getSelectedEdgeState().isSelected(c.getClickedEdge())) .enabledWhen(c -> !isSelected(c.getClickedEdge()))
.onAction(c -> selectEdge(c.getClickedEdge())) .onAction(c -> selectEdge(c.getClickedEdge()))
.buildAndInstallLocal(componentProvider); .buildAndInstallLocal(componentProvider);
new ActionBuilder("Deselect Edge", actionOwnerName) new ActionBuilder("Deselect Edge", ACTION_OWNER)
.popupMenuPath("Deselect Edge") .popupMenuPath("Deselect Edge")
.popupMenuGroup("selection", "2") .popupMenuGroup("selection", "2")
.withContext(EdgeGraphActionContext.class) .withContext(EdgeGraphActionContext.class)
.enabledWhen(c -> viewer.getSelectedEdgeState().isSelected(c.getClickedEdge())) .enabledWhen(c -> isSelected(c.getClickedEdge()))
.onAction(c -> deselectEdge(c.getClickedEdge())) .onAction(c -> deselectEdge(c.getClickedEdge()))
.buildAndInstallLocal(componentProvider); .buildAndInstallLocal(componentProvider);
new ActionBuilder("Edge Source", actionOwnerName) new ActionBuilder("Edge Source", ACTION_OWNER)
.popupMenuPath("Go To Edge Source") .popupMenuPath("Go To Edge Source")
.popupMenuGroup("Go To") .popupMenuGroup("Go To")
.withContext(EdgeGraphActionContext.class) .withContext(EdgeGraphActionContext.class)
.onAction(c -> setFocusedVertex(graph.getEdgeSource(c.getClickedEdge()))) .onAction(c -> setFocusedVertex(graph.getEdgeSource(c.getClickedEdge())))
.buildAndInstallLocal(componentProvider); .buildAndInstallLocal(componentProvider);
new ActionBuilder("Edge Target", actionOwnerName) new ActionBuilder("Edge Target", ACTION_OWNER)
.popupMenuPath("Go To Edge Target") .popupMenuPath("Go To Edge Target")
.popupMenuGroup("Go To") .popupMenuGroup("Go To")
.withContext(EdgeGraphActionContext.class) .withContext(EdgeGraphActionContext.class)
.onAction(c -> setFocusedVertex(graph.getEdgeTarget(c.getClickedEdge()))) .onAction(c -> setFocusedVertex(graph.getEdgeTarget(c.getClickedEdge())))
.buildAndInstallLocal(componentProvider); .buildAndInstallLocal(componentProvider);
hideSelectedAction = new ToggleActionBuilder("Hide Selected", actionOwnerName) hideSelectedAction = new ToggleActionBuilder("Hide Selected", ACTION_OWNER)
.popupMenuPath("Hide Selected") .popupMenuPath("Hide Selected")
.popupMenuGroup("z", "1") .popupMenuGroup("z", "1")
.description("Toggles whether or not to show selected vertices and edges") .description("Toggles whether or not to show selected vertices and edges")
.onAction(c -> manageVertexDisplay()) .onAction(c -> manageVertexDisplay())
.buildAndInstallLocal(componentProvider); .buildAndInstallLocal(componentProvider);
hideUnselectedAction = new ToggleActionBuilder("Hide Unselected", actionOwnerName) hideUnselectedAction = new ToggleActionBuilder("Hide Unselected", ACTION_OWNER)
.popupMenuPath("Hide Unselected") .popupMenuPath("Hide Unselected")
.popupMenuGroup("z", "2") .popupMenuGroup("z", "2")
.description("Toggles whether or not to show selected vertices and edges") .description("Toggles whether or not to show selected vertices and edges")
.onAction(c -> manageVertexDisplay()) .onAction(c -> manageVertexDisplay())
.buildAndInstallLocal(componentProvider); .buildAndInstallLocal(componentProvider);
new ActionBuilder("Invert Selection", actionOwnerName) new ActionBuilder("Invert Selection", ACTION_OWNER)
.popupMenuPath("Invert Selection") .popupMenuPath("Invert Selection")
.popupMenuGroup("z", "3") .popupMenuGroup("z", "3")
.description("Inverts the current selection") .description("Inverts the current selection")
.onAction(c -> invertSelection()) .onAction(c -> invertSelection())
.buildAndInstallLocal(componentProvider); .buildAndInstallLocal(componentProvider);
new ActionBuilder("Grow Selection To Targets", actionOwnerName) new ActionBuilder("Grow Selection To Targets", ACTION_OWNER)
.popupMenuPath("Grow Selection To Targets") .popupMenuPath("Grow Selection To Targets")
.popupMenuGroup("z", "4") .popupMenuGroup("z", "4")
.description("Extends the current selection by including the target vertex " + .description("Extends the current selection by including the target vertex " +
@ -436,7 +416,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
.onAction(c -> growSelection(getTargetVerticesFromSelected())) .onAction(c -> growSelection(getTargetVerticesFromSelected()))
.buildAndInstallLocal(componentProvider); .buildAndInstallLocal(componentProvider);
new ActionBuilder("Grow Selection From Sources", actionOwnerName) new ActionBuilder("Grow Selection From Sources", ACTION_OWNER)
.popupMenuPath("Grow Selection From Sources") .popupMenuPath("Grow Selection From Sources")
.popupMenuGroup("z", "4") .popupMenuGroup("z", "4")
.description("Extends the current selection by including the target vertex " + .description("Extends the current selection by including the target vertex " +
@ -446,13 +426,49 @@ public class DefaultGraphDisplay implements GraphDisplay {
.onAction(c -> growSelection(getSourceVerticesFromSelected())) .onAction(c -> growSelection(getSourceVerticesFromSelected()))
.buildAndInstallLocal(componentProvider); .buildAndInstallLocal(componentProvider);
new ActionBuilder("Create Subgraph", actionOwnerName) new ActionBuilder("Clear Selection", ACTION_OWNER)
.popupMenuPath("Display Selected as New Graph") .popupMenuPath("Clear Selection")
.popupMenuGroup("z", "5") .popupMenuGroup("z", "5")
.keyBinding("escape")
.enabledWhen(c -> hasSelection())
.onAction(c -> clearSelection())
.buildAndInstallLocal(componentProvider);
new ActionBuilder("Create Subgraph", ACTION_OWNER)
.popupMenuPath("Display Selected as New Graph")
.popupMenuGroup("zz", "5")
.description("Creates a subgraph from the selected nodes") .description("Creates a subgraph from the selected nodes")
.enabledWhen(c -> !viewer.getSelectedVertexState().getSelected().isEmpty()) .enabledWhen(c -> !viewer.getSelectedVertexState().getSelected().isEmpty())
.onAction(c -> createAndDisplaySubGraph()) .onAction(c -> createAndDisplaySubGraph())
.buildAndInstallLocal(componentProvider); .buildAndInstallLocal(componentProvider);
togglePopupsAction = new ToggleActionBuilder("Display Popup Windows", ACTION_OWNER)
.popupMenuPath("Display Popup Windows")
.popupMenuGroup("zz", "1")
.description("Toggles whether or not to show popup windows, such as tool tips")
.selected(true)
.onAction(c -> popupRegulator.setPopupsVisible(togglePopupsAction.isSelected()))
.buildAndInstallLocal(componentProvider);
popupRegulator.setPopupsVisible(togglePopupsAction.isSelected());
}
private void clearSelection() {
viewer.getSelectedVertexState().clear();
viewer.getSelectedEdgeState().clear();
}
private boolean hasSelection() {
return !(viewer.getSelectedVertexState().getSelected().isEmpty() &&
viewer.getSelectedEdgeState().getSelected().isEmpty());
}
private boolean isSelected(AttributedVertex v) {
return viewer.getSelectedVertexState().isSelected(v);
}
private boolean isSelected(AttributedEdge e) {
return viewer.getSelectedEdgeState().isSelected(e);
} }
private void createAndDisplaySubGraph() { private void createAndDisplaySubGraph() {
@ -540,7 +556,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
for (String layoutName : names) { for (String layoutName : names) {
ActionState<String> state = new ActionState<>(layoutName, ActionState<String> state = new ActionState<>(layoutName,
DefaultDisplayGraphIcons.LAYOUT_ALGORITHM_ICON, layoutName); DefaultDisplayGraphIcons.LAYOUT_ALGORITHM_ICON, layoutName);
state.setHelpLocation(new HelpLocation(actionOwnerName, layoutName)); state.setHelpLocation(new HelpLocation(ACTION_OWNER, layoutName));
actionStates.add(state); actionStates.add(state);
} }
return actionStates; return actionStates;
@ -576,7 +592,9 @@ public class DefaultGraphDisplay implements GraphDisplay {
* @param context information about the event * @param context information about the event
*/ */
private void toggleSatellite(ActionContext context) { private void toggleSatellite(ActionContext context) {
if (((AbstractButton) context.getSourceObject()).isSelected()) { boolean selected = ((AbstractButton) context.getSourceObject()).isSelected();
graphDisplayProvider.setDefaultSatelliteState(selected);
if (selected) {
viewer.getComponent().add(satelliteViewer.getComponent()); viewer.getComponent().add(satelliteViewer.getComponent());
} }
else { else {
@ -585,18 +603,6 @@ public class DefaultGraphDisplay implements GraphDisplay {
viewer.repaint(); viewer.repaint();
} }
private void displaySubGraph(Graph<AttributedVertex, AttributedEdge> subGraph)
throws CancelledException {
GraphDisplay graphDisplay = graphDisplayProvider.getGraphDisplay(false, TaskMonitor.DUMMY);
graphDisplay.setGraph((AttributedGraph) subGraph, "SubGraph", false, TaskMonitor.DUMMY);
graphDisplay.setGraphDisplayListener(listener);
}
/**
* create a SatelliteViewer for the Visualization
* @param parentViewer the main visualization 'parent' of the satellite view
* @return a new SatelliteVisualizationViewer
*/
private SatelliteVisualizationViewer<AttributedVertex, AttributedEdge> createSatelliteViewer( private SatelliteVisualizationViewer<AttributedVertex, AttributedEdge> createSatelliteViewer(
VisualizationViewer<AttributedVertex, AttributedEdge> parentViewer) { VisualizationViewer<AttributedVertex, AttributedEdge> parentViewer) {
Dimension viewerSize = parentViewer.getSize(); Dimension viewerSize = parentViewer.getSize();
@ -625,9 +631,6 @@ public class DefaultGraphDisplay implements GraphDisplay {
return satellite; return satellite;
} }
/**
* close this graph display
*/
@Override @Override
public void close() { public void close() {
graphDisplayProvider.remove(this); graphDisplayProvider.remove(this);
@ -638,10 +641,6 @@ public class DefaultGraphDisplay implements GraphDisplay {
componentProvider.closeComponent(); componentProvider.closeComponent();
} }
/**
* accept a {@code GraphDisplayListener}
* @param listener the listener to be notified
*/
@Override @Override
public void setGraphDisplayListener(GraphDisplayListener listener) { public void setGraphDisplayListener(GraphDisplayListener listener) {
if (this.listener != null) { if (this.listener != null) {
@ -673,7 +672,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
viewer.getSelectedVertexState().addItemListener(switchableSelectionListener); viewer.getSelectedVertexState().addItemListener(switchableSelectionListener);
} }
protected void setFocusedVertex(AttributedVertex vertex) { public void setFocusedVertex(AttributedVertex vertex) {
setFocusedVertex(vertex, EventTrigger.API_CALL); setFocusedVertex(vertex, EventTrigger.API_CALL);
} }
@ -709,15 +708,6 @@ public class DefaultGraphDisplay implements GraphDisplay {
return true; return true;
} }
/**
* transform the supplied {@code AttributedVertex}s to a List of their ids
* @param selectedVertices the collections of vertices.
* @return a list of vertex ids
*/
private List<String> toVertexIds(Collection<AttributedVertex> selectedVertices) {
return selectedVertices.stream().map(AttributedVertex::getId).collect(Collectors.toList());
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private Collection<AttributedVertex> getVertices(Object item) { private Collection<AttributedVertex> getVertices(Object item) {
if (item instanceof Collection) { if (item instanceof Collection) {
@ -770,7 +760,6 @@ public class DefaultGraphDisplay implements GraphDisplay {
} }
} }
/** /**
* set the {@link AttributedGraph} for visualization * set the {@link AttributedGraph} for visualization
* @param attributedGraph the {@link AttributedGraph} to visualize * @param attributedGraph the {@link AttributedGraph} to visualize
@ -795,8 +784,8 @@ public class DefaultGraphDisplay implements GraphDisplay {
} }
/** /**
* Determines if a vertex is a root. For our purpose, a root either has no incomming edges * Determines if a vertex is a root. For our purpose, a root either has no incoming edges
* or has at least one outgoing "favored" edge and no incomming "favored" edge * or has at least one outgoing "favored" edge and no incoming "favored" edge
* @param vertex the vertex to test if it is a root * @param vertex the vertex to test if it is a root
* @return true if the vertex is a root * @return true if the vertex is a root
*/ */
@ -994,8 +983,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
return new Point2D.Double(p.x, p.y); return new Point2D.Double(p.x, p.y);
} }
// they did not pick a vertex to center, so // they did not pick a vertex to center, so just center the graph
// just center the graph
Point2D center = viewer.getCenter(); Point2D center = viewer.getCenter();
Point p = Point.of(center.getX(), center.getY()); Point p = Point.of(center.getX(), center.getY());
return new Point2D.Double(p.x, p.y); return new Point2D.Double(p.x, p.y);
@ -1057,9 +1045,16 @@ public class DefaultGraphDisplay implements GraphDisplay {
} }
}); });
// We control tooltips with the PopupRegulator. Use null values to disable the default
// tool tip mechanism
vv.setVertexToolTipFunction(v -> null);
vv.setEdgeToolTipFunction(e -> null);
vv.setToolTipText(null);
PopupSource<AttributedVertex, AttributedEdge> popupSource = new GraphDisplayPopupSource(vv);
popupRegulator = new PopupRegulator<>(popupSource);
this.iconCache = new GhidraIconCache(); this.iconCache = new GhidraIconCache();
vv.setVertexToolTipFunction(AttributedVertex::getHtmlString);
vv.setEdgeToolTipFunction(AttributedEdge::getHtmlString);
RenderContext<AttributedVertex, AttributedEdge> renderContext = vv.getRenderContext(); RenderContext<AttributedVertex, AttributedEdge> renderContext = vv.getRenderContext();
// set up the shape and color functions // set up the shape and color functions
@ -1098,7 +1093,6 @@ public class DefaultGraphDisplay implements GraphDisplay {
renderContext.setArrowFillPaintFunction( renderContext.setArrowFillPaintFunction(
e -> renderContext.getSelectedEdgeState().isSelected(e) ? Color.red e -> renderContext.getSelectedEdgeState().isSelected(e) ? Color.red
: Colors.getColor(e)); : Colors.getColor(e));
vv.setToolTipText("");
// assign the shapes to the modal renderer // assign the shapes to the modal renderer
ModalRenderer<AttributedVertex, AttributedEdge> modalRenderer = vv.getRenderer(); ModalRenderer<AttributedVertex, AttributedEdge> modalRenderer = vv.getRenderer();
@ -1191,13 +1185,13 @@ public class DefaultGraphDisplay implements GraphDisplay {
public ActionContext getActionContext(MouseEvent e) { public ActionContext getActionContext(MouseEvent e) {
AttributedVertex pickedVertex = graphMouse.getPickedVertex(e); AttributedVertex pickedVertex = JgtUtils.getVertex(e, viewer);
if (pickedVertex != null) { if (pickedVertex != null) {
return new VertexGraphActionContext(componentProvider, graph, getSelectedVertices(), return new VertexGraphActionContext(componentProvider, graph, getSelectedVertices(),
focusedVertex, pickedVertex); focusedVertex, pickedVertex);
} }
AttributedEdge pickedEdge = graphMouse.getPickedEdge(e); AttributedEdge pickedEdge = JgtUtils.getEdge(e, viewer);
if (pickedEdge != null) { if (pickedEdge != null) {
return new EdgeGraphActionContext(componentProvider, graph, getSelectedVertices(), return new EdgeGraphActionContext(componentProvider, graph, getSelectedVertices(),
focusedVertex, pickedEdge); focusedVertex, pickedEdge);
@ -1208,7 +1202,6 @@ public class DefaultGraphDisplay implements GraphDisplay {
} }
/** /**
* Use the hide selected action states to determine what vertices are shown: * Use the hide selected action states to determine what vertices are shown:
* <ul> * <ul>
@ -1261,4 +1254,135 @@ public class DefaultGraphDisplay implements GraphDisplay {
}); });
} }
// class passed to the PopupRegulator to help construct info popups for the graph
private class GraphDisplayPopupSource implements PopupSource<AttributedVertex, AttributedEdge> {
private VisualizationViewer<AttributedVertex, AttributedEdge> vv;
public GraphDisplayPopupSource(VisualizationViewer<AttributedVertex, AttributedEdge> vv) {
this.vv = vv;
}
@Override
public ToolTipInfo<?> getToolTipInfo(MouseEvent event) {
// check for a vertex hit first, otherwise, we get edge hits when we are hovering
// over a vertex, due to how edges are interpreted as existing all the way to the
// center point of a vertex
AttributedVertex vertex = getVertex(event);
if (vertex != null) {
return new VertexToolTipInfo(vertex, event);
}
AttributedEdge edge = getEdge(event);
if (edge != null) {
return new EdgeToolTipInfo(edge, event);
}
// no vertex or edge hit; just create a basic info that is essentially a null-object
// placeholder to prevent NPEs
return new VertexToolTipInfo(vertex, event);
}
@Override
public AttributedVertex getVertex(MouseEvent event) {
LayoutModel<AttributedVertex> layoutModel =
vv.getVisualizationModel().getLayoutModel();
Point2D p = vv.getTransformSupport().inverseTransform(vv, event.getPoint());
AttributedVertex vertex =
vv.getPickSupport().getVertex(layoutModel, p.getX(), p.getY());
return vertex;
}
@Override
public AttributedEdge getEdge(MouseEvent event) {
LayoutModel<AttributedVertex> layoutModel =
vv.getVisualizationModel().getLayoutModel();
Point2D p = vv.getTransformSupport().inverseTransform(vv, event.getPoint());
AttributedEdge edge = vv.getPickSupport().getEdge(layoutModel, p.getX(), p.getY());
return edge;
}
@Override
public void addMouseMotionListener(MouseMotionListener l) {
vv.getComponent().addMouseMotionListener(l);
}
@Override
public void repaint() {
vv.repaint();
}
@Override
public Window getPopupParent() {
return WindowUtilities.windowForComponent(vv.getComponent());
}
}
private class VertexToolTipInfo extends ToolTipInfo<AttributedVertex> {
VertexToolTipInfo(AttributedVertex vertex, MouseEvent event) {
super(event, vertex);
}
@Override
public JComponent createToolTipComponent() {
if (graphObject == null) {
return null;
}
String toolTip = graphObject.getHtmlString();
if (StringUtils.isBlank(toolTip)) {
return null;
}
JToolTip jToolTip = new JToolTip();
jToolTip.setTipText(toolTip);
return jToolTip;
}
@Override
protected void emphasize() {
// this graph display does not have a notion of emphasizing
}
@Override
protected void deEmphasize() {
// this graph display does not have a notion of emphasizing
}
}
private class EdgeToolTipInfo extends ToolTipInfo<AttributedEdge> {
EdgeToolTipInfo(AttributedEdge edge, MouseEvent event) {
super(event, edge);
}
@Override
protected JComponent createToolTipComponent() {
if (graphObject == null) {
return null;
}
String toolTip = graphObject.getHtmlString();
if (StringUtils.isBlank(toolTip)) {
return null;
}
JToolTip jToolTip = new JToolTip();
jToolTip.setTipText(toolTip);
return jToolTip;
}
@Override
protected void emphasize() {
// this graph display does not have a notion of emphasizing
}
@Override
protected void deEmphasize() {
// this graph display does not have a notion of emphasizing
}
}
} }

View file

@ -19,6 +19,7 @@ import java.util.HashSet;
import java.util.Set; import java.util.Set;
import ghidra.framework.options.Options; import ghidra.framework.options.Options;
import ghidra.framework.options.PreferenceState;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.service.graph.GraphDisplay; import ghidra.service.graph.GraphDisplay;
import ghidra.service.graph.GraphDisplayProvider; import ghidra.service.graph.GraphDisplayProvider;
@ -28,10 +29,14 @@ import ghidra.util.task.TaskMonitor;
public class DefaultGraphDisplayProvider implements GraphDisplayProvider { public class DefaultGraphDisplayProvider implements GraphDisplayProvider {
private static final String PREFERENCES_KEY = "GRAPH_DISPLAY_SERVICE";
private static final String DEFAULT_SATELLITE_STATE = "DEFAULT_SATELLITE_STATE";
private final Set<DefaultGraphDisplay> displays = new HashSet<>(); private final Set<DefaultGraphDisplay> displays = new HashSet<>();
private PluginTool pluginTool; private PluginTool pluginTool;
private Options options; private Options options;
private int displayCounter = 1; private int displayCounter = 1;
private boolean defaultSatelliteState;
private PreferenceState preferences;
@Override @Override
public String getName() { public String getName() {
@ -66,6 +71,12 @@ public class DefaultGraphDisplayProvider implements GraphDisplayProvider {
public void initialize(PluginTool tool, Options graphOptions) { public void initialize(PluginTool tool, Options graphOptions) {
this.pluginTool = tool; this.pluginTool = tool;
this.options = graphOptions; this.options = graphOptions;
preferences = pluginTool.getWindowManager().getPreferenceState(PREFERENCES_KEY);
if (preferences == null) {
preferences = new PreferenceState();
pluginTool.getWindowManager().putPreferenceState(PREFERENCES_KEY, preferences);
}
defaultSatelliteState = preferences.getBoolean(DEFAULT_SATELLITE_STATE, false);
} }
/** /**
@ -105,4 +116,14 @@ public class DefaultGraphDisplayProvider implements GraphDisplayProvider {
displays.remove(defaultGraphDisplay); displays.remove(defaultGraphDisplay);
} }
boolean getDefaultSatelliteState() {
return defaultSatelliteState;
}
void setDefaultSatelliteState(boolean b) {
defaultSatelliteState = b;
preferences.putBoolean(DEFAULT_SATELLITE_STATE, b);
}
} }

View file

@ -0,0 +1,311 @@
/* ###
* 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.graph.visualization.mouse;
import java.awt.Cursor;
import java.awt.event.*;
import org.jungrapht.visualization.SatelliteVisualizationViewer;
import org.jungrapht.visualization.VisualizationViewer;
import org.jungrapht.visualization.control.AbstractGraphMousePlugin;
import org.jungrapht.visualization.selection.MutableSelectedState;
/**
* Graph mouse plugin base class.
*
* Usage Notes:
* <ul>
* <li>We clear state on mouseReleased() and mouseExited(), since we will get
* at least one of those calls</li>
* </ul>
* @param <V> the vertex type
* @param <E> the edge type
*/
//@formatter:off
public abstract class AbstractJgtGraphMousePlugin<V, E>
extends AbstractGraphMousePlugin
implements MouseListener, MouseMotionListener {
//@formatter:on
protected boolean isHandlingMouseEvents;
protected V selectedVertex;
protected E selectedEdge;
public AbstractJgtGraphMousePlugin() {
this(InputEvent.BUTTON1_DOWN_MASK);
}
public AbstractJgtGraphMousePlugin(int selectionModifiers) {
super(selectionModifiers);
}
public VisualizationViewer<V, E> getViewer(MouseEvent e) {
VisualizationViewer<V, E> viewer = getGraphViewer(e);
return viewer;
}
/**
* Returns the <b>primary/master</b> graph viewer.
*
* @param e the mouse event from which to get the viewer
* @return the viewer
*/
@SuppressWarnings("unchecked")
public VisualizationViewer<V, E> getGraphViewer(MouseEvent e) {
VisualizationViewer<V, E> viewer = (VisualizationViewer<V, E>) e.getSource();
// is this the satellite viewer?
if (viewer instanceof SatelliteVisualizationViewer) {
return ((SatelliteVisualizationViewer<V, E>) viewer).getMaster();
}
return viewer;
}
/**
* Returns the satellite graph viewer. This assumes that the mouse event originated from
* the satellite viewer.
*
* @param e the mouse event from which to get the viewer
* @return the viewer
*/
@SuppressWarnings("unchecked")
public SatelliteVisualizationViewer<V, E> getSatelliteGraphViewer(MouseEvent e) {
VisualizationViewer<V, E> viewer = (VisualizationViewer<V, E>) e.getSource();
// is this the satellite viewer?
if (viewer instanceof SatelliteVisualizationViewer) {
return (SatelliteVisualizationViewer<V, E>) viewer;
}
throw new IllegalStateException("Do not have a satellite GraphViewer");
}
/**
* Signals to perform any cleanup when this plugin is going away
*/
public void dispose() {
// stub
}
/**
* Checks the given mouse event to see if it is a valid event for selecting a vertex at the
* mouse location. If so, then the vertex is selected in this mouse handler and the event
* is consumed.
* @param e the event
* @return true if a vertex was selected
*/
protected boolean checkForVertex(MouseEvent e) {
if (!checkModifiers(e)) {
selectedVertex = null;
return false;
}
VisualizationViewer<V, E> vv = getViewer(e);
selectedVertex = JgtUtils.getVertex(e, vv);
if (selectedVertex == null) {
return false;
}
e.consume();
return true;
}
/**
* Checks the given mouse event to see if it is a valid event for selecting an edge at the
* mouse location. If so, then the edge is selected in this mouse handler and the event
* is consumed.
* @param e the event
* @return true if an edge was selected
*/
protected boolean checkForEdge(MouseEvent e) {
if (!checkModifiers(e) || isOverVertex(e)) {
selectedEdge = null;
return false;
}
VisualizationViewer<V, E> vv = getViewer(e);
selectedEdge = JgtUtils.getEdge(e, vv);
if (selectedEdge == null) {
return false;
}
e.consume();
isHandlingMouseEvents = true;
return true;
}
/**
* Selects the given vertex
* @param vertex the vertex
* @param viewer the graph viewer
* @return true if the vertex is selected
*/
protected boolean selectVertex(V vertex, VisualizationViewer<V, E> viewer) {
MutableSelectedState<V> selectedVertexState = viewer.getSelectedVertexState();
if (selectedVertexState == null) {
return false;
}
selectedVertexState.isSelected(vertex);
if (selectedVertexState.isSelected(vertex) == false) {
selectedVertexState.clear();
selectedVertexState.select(vertex, true);
}
return true;
}
/**
* Selects the given edge
* @param edge the edge
* @param viewer the graph viewer
* @return true if the edge is selected
*/
protected boolean selectEdge(E edge, VisualizationViewer<V, E> viewer) {
MutableSelectedState<E> selectedVertexState = viewer.getSelectedEdgeState();
if (selectedVertexState == null) {
return false;
}
selectedVertexState.isSelected(edge);
if (selectedVertexState.isSelected(edge) == false) {
selectedVertexState.clear();
selectedVertexState.select(edge, true);
}
return true;
}
/**
* Returns true if the location of the mouse event is over a vertex
* @param e the event
* @return true if the location of the mouse event is over a vertex
*/
protected boolean isOverVertex(MouseEvent e) {
return getVertex(e) != null;
}
/**
* Returns the vertex if the mouse event is over a vertex
* @param e the event
* @return a vertex or null
*/
protected V getVertex(MouseEvent e) {
VisualizationViewer<V, E> viewer = getViewer(e);
return JgtUtils.getVertex(e, viewer);
}
/**
* Returns true if the location of the mouse event is over a edge
* @param e the event
* @return true if the location of the mouse event is over a edge
*/
protected boolean isOverEdge(MouseEvent e) {
VisualizationViewer<V, E> viewer = getViewer(e);
E edge = JgtUtils.getEdge(e, viewer);
if (edge == null) {
return false;
}
return !isOverVertex(e);
}
protected void installCursor(Cursor newCursor, MouseEvent e) {
VisualizationViewer<V, E> viewer = getViewer(e);
viewer.setCursor(newCursor);
}
protected boolean shouldShowCursor(MouseEvent e) {
return isOverVertex(e); // to showing cursor over vertices
}
@Override
public void mousePressed(MouseEvent e) {
if (!checkModifiers(e)) {
return;
}
// override this method to do stuff
}
@Override
public void mouseClicked(MouseEvent e) {
if (!isHandlingMouseEvents) {
return;
}
e.consume();
resetState();
}
protected void resetState() {
isHandlingMouseEvents = false;
selectedVertex = null;
selectedEdge = null;
}
@Override
public void mouseDragged(MouseEvent e) {
if (!isHandlingMouseEvents) {
return;
}
e.consume();
resetState();
}
@Override
public void mouseMoved(MouseEvent e) {
if (isHandlingMouseEvents) {
e.consume();
}
// only "turn on" the cursor; resetting is handled elsewhere (in the mouse driver)
if (shouldShowCursor(e)) {
installCursor(cursor, e);
e.consume();
}
}
@Override
public void mouseReleased(MouseEvent e) {
if (isHandlingMouseEvents) {
e.consume();
}
if (shouldShowCursor(e)) {
installCursor(cursor, e);
}
}
@Override
public void mouseEntered(MouseEvent e) {
if (shouldShowCursor(e)) {
installCursor(cursor, e);
e.consume();
}
}
@Override
public void mouseExited(MouseEvent e) {
installCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR), e);
}
}

View file

@ -0,0 +1,55 @@
/* ###
* 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.graph.visualization.mouse;
import java.awt.Cursor;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import org.jungrapht.visualization.VisualizationViewer;
import org.jungrapht.visualization.control.AbstractGraphMousePlugin;
/**
* Restores the cursor after other graph mouse operations.
*
* Future: this is copied from the Visual Graph counterpart--consolidate these
*
* @param <V> the vertex type
* @param <E> the edge type
*/
public class JgtCursorRestoringPlugin<V, E> extends AbstractGraphMousePlugin
implements MouseMotionListener {
public JgtCursorRestoringPlugin() {
super(0);
}
@Override
public void mouseDragged(MouseEvent e) {
// don't care
}
@Override
public void mouseMoved(MouseEvent e) {
installCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR), e);
}
@SuppressWarnings("unchecked")
private void installCursor(Cursor newCursor, MouseEvent e) {
VisualizationViewer<V, E> viewer = (VisualizationViewer<V, E>) e.getSource();
viewer.setCursor(newCursor);
}
}

View file

@ -0,0 +1,113 @@
/* ###
* 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.graph.visualization.mouse;
import java.awt.Cursor;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import org.jgrapht.Graph;
import org.jungrapht.visualization.VisualizationViewer;
import org.jungrapht.visualization.layout.model.Point;
import org.jungrapht.visualization.selection.MutableSelectedState;
import ghidra.graph.visualization.CenterAnimationJob;
/**
* Mouse plugin to allow for edge navigation
*
* @param <V> the vertex type
* @param <E> the edge type
*/
public class JgtEdgeNavigationPlugin<V, E> extends AbstractJgtGraphMousePlugin<V, E> {
public JgtEdgeNavigationPlugin() {
this.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
}
@Override
public void mousePressed(MouseEvent e) {
if (!checkModifiers(e)) {
return;
}
if (e.getClickCount() != 2) {
return;
}
checkForEdge(e); // this will select an edge if we can and store off the edge
}
@Override
public void mouseClicked(MouseEvent e) {
if (!isHandlingMouseEvents) {
return;
}
E edge = selectedEdge; // save off before we reset
e.consume();
resetState();
// on double-clicks we go to the vertex in the current edge direction unless that vertex
// is already selected, then we go to the other vertex
VisualizationViewer<V, E> viewer = getViewer(e);
MutableSelectedState<V> selectedState = viewer.getSelectedVertexState();
Graph<V, E> graph = viewer.getVisualizationModel().getGraph();
V end = graph.getEdgeTarget(edge);
if (!selectedState.isSelected(end)) {
pickAndShowVertex(end, selectedState, viewer);
return;
}
// the destination was picked, go the other direction
V source = graph.getEdgeSource(edge);
pickAndShowVertex(source, selectedState, viewer);
}
private void pickAndShowVertex(V vertex, MutableSelectedState<V> selectedVertexState,
VisualizationViewer<V, E> viewer) {
// TODO animate; this requires a single view updater
Point2D existingCenter = viewer.getRenderContext()
.getMultiLayerTransformer()
.inverseTransform(viewer.getCenter());
Point vp = viewer.getVisualizationModel().getLayoutModel().get(vertex);
Point2D newCenter = new Point2D.Double(vp.x, vp.y);
CenterAnimationJob job = new CenterAnimationJob(viewer, existingCenter, newCenter);
job.finished();
selectedVertexState.clear();
selectedVertexState.select(vertex);
/*
VisualGraphViewUpdater<V, E> updater = viewer.getViewUpdater();
updater.moveVertexToCenterWithAnimation(vertex, isBusy -> {
// pick the vertex after the animation has run
if (!isBusy) {
GPickedState<V> pickedStateWrapper = (GPickedState<V>) selectedVertexState;
pickedStateWrapper.pickToActivate(vertex);
}
});
*/
}
@Override
protected boolean shouldShowCursor(MouseEvent e) {
return isOverEdge(e);
}
}

View file

@ -0,0 +1,70 @@
/* ###
* 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.graph.visualization.mouse;
import java.awt.event.InputEvent;
import org.jungrapht.visualization.control.*;
import docking.DockingUtils;
import ghidra.graph.visualization.DefaultGraphDisplay;
import ghidra.service.graph.AttributedEdge;
import ghidra.service.graph.AttributedVertex;
/**
* Pluggable graph mouse for jungrapht
*/
public class JgtPluggableGraphMouse extends DefaultGraphMouse<AttributedVertex, AttributedEdge> {
private DefaultGraphDisplay graphDisplay;
// TODO we should not need the graph display for any mouse plugins, but the API is net yet
// robust enough to communicate fully without it
public JgtPluggableGraphMouse(DefaultGraphDisplay graphDisplay) {
super(DefaultGraphMouse.<AttributedVertex, AttributedEdge> builder());
this.graphDisplay = graphDisplay;
}
@Override
public void loadPlugins() {
//
// Note: the order of these additions matters, as an event will flow to each plugin until
// it is handled.
//
// edge
add(new JgtEdgeNavigationPlugin<AttributedVertex, AttributedEdge>());
add(new JgtVertexFocusingPlugin<AttributedVertex, AttributedEdge>(graphDisplay));
// scaling
add(new ScalingGraphMousePlugin(new CrossoverScalingControl(), 0, in, out));
// the grab/pan feature
add(new JgtTranslatingPlugin<AttributedVertex, AttributedEdge>());
add(new SelectingGraphMousePlugin<AttributedVertex, AttributedEdge>(
InputEvent.BUTTON1_DOWN_MASK,
0,
DockingUtils.CONTROL_KEY_MODIFIER_MASK));
// cursor cleanup
add(new JgtCursorRestoringPlugin<AttributedVertex, AttributedEdge>());
setPluginsLoaded();
}
}

View file

@ -0,0 +1,177 @@
/* ###
* 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.graph.visualization.mouse;
import java.awt.Cursor;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import org.jungrapht.visualization.*;
import org.jungrapht.visualization.MultiLayerTransformer.Layer;
import org.jungrapht.visualization.control.TranslatingGraphMousePlugin;
import org.jungrapht.visualization.transform.MutableTransformer;
/**
* Note: this class is based on {@link TranslatingGraphMousePlugin}.
* <p>
* TranslatingGraphMousePlugin uses a MouseButtonOne press and drag gesture to translate
* the graph display in the x and y direction. The default MouseButtonOne modifier can be overridden
* to cause a different mouse gesture to translate the display.
*
* @param <V> the vertex type
* @param <E> the edge type
*/
public class JgtTranslatingPlugin<V, E>
extends AbstractJgtGraphMousePlugin<V, E> {
private boolean panning;
private boolean isHandlingEvent;
public JgtTranslatingPlugin() {
this(InputEvent.BUTTON1_DOWN_MASK);
}
public JgtTranslatingPlugin(int modifiers) {
super(modifiers);
this.cursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR);
}
@Override
public void mousePressed(MouseEvent e) {
boolean accepted = checkModifiers(e) && isInDraggingArea(e);
if (!accepted) {
return;
}
down = e.getPoint();
VisualizationViewer<V, E> viewer = getGraphViewer(e);
viewer.setCursor(cursor);
isHandlingEvent = true;
e.consume();
}
@Override
public void mouseReleased(MouseEvent e) {
boolean wasHandlingEvent = isHandlingEvent;
isHandlingEvent = false;
down = null;
installCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR), e);
// NOTE: we are only consuming the event here if we actually did pan...this allows follow-on
// mouse handlers to process the mouseReleased() event. This is a bit odd and not the
// normal event processing (which is to consume all related events).
if (wasHandlingEvent && panning) {
e.consume();
}
panning = false;
}
@Override
public void mouseDragged(MouseEvent e) {
boolean accepted = checkModifiers(e);
if (!accepted) {
return;
}
if (!isHandlingEvent) {
return;
}
panning = true;
VisualizationViewer<V, E> viewer = getGraphViewer(e);
RenderContext<V, E> context = viewer.getRenderContext();
MultiLayerTransformer multiLayerTransformer = context.getMultiLayerTransformer();
MutableTransformer layoutTransformer = multiLayerTransformer.getTransformer(Layer.LAYOUT);
viewer.setCursor(cursor);
Point2D downPoint = multiLayerTransformer.inverseTransform(down);
Point2D p = multiLayerTransformer.inverseTransform(e.getPoint());
float dx = (float) (p.getX() - downPoint.getX());
float dy = (float) (p.getY() - downPoint.getY());
layoutTransformer.translate(dx, dy);
down.x = e.getX();
down.y = e.getY();
e.consume();
viewer.repaint();
}
@Override
public void mouseClicked(MouseEvent e) {
// don't care
}
@Override
public void mouseEntered(MouseEvent e) {
if (isHandlingEvent) {
return;
}
if (!isInDraggingArea(e)) {
return;
}
if (!checkModifiersForCursor(e)) {
return;
}
installCursor(cursor, e);
}
@Override
public void mouseExited(MouseEvent e) {
installCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR), e);
}
@Override
public void mouseMoved(MouseEvent e) {
if (!checkModifiersForCursor(e)) {
return;
}
if (isHandlingEvent) {
e.consume();
}
if (isInDraggingArea(e)) {
installCursor(cursor, e);
e.consume();
}
}
private boolean checkModifiersForCursor(MouseEvent e) {
if (e.getModifiersEx() == 0) {
return true;
}
return false;
}
//==================================================================================================
// Private methods
//==================================================================================================
private boolean isInDraggingArea(MouseEvent e) {
return !(isOverVertex(e) || isOverEdge(e));
}
@Override
public void installCursor(Cursor newCursor, MouseEvent e) {
VisualizationViewer<V, E> viewer = getViewer(e);
viewer.setCursor(newCursor);
}
}

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package ghidra.graph.visualization; package ghidra.graph.visualization.mouse;
import static org.jungrapht.visualization.VisualizationServer.*; import static org.jungrapht.visualization.VisualizationServer.*;
@ -22,42 +22,86 @@ import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import org.jungrapht.visualization.VisualizationViewer; import org.jungrapht.visualization.VisualizationViewer;
import org.jungrapht.visualization.control.*; import org.jungrapht.visualization.control.GraphElementAccessor;
import org.jungrapht.visualization.control.TransformSupport;
import org.jungrapht.visualization.layout.model.LayoutModel; import org.jungrapht.visualization.layout.model.LayoutModel;
import org.jungrapht.visualization.selection.ShapePickSupport; import org.jungrapht.visualization.selection.ShapePickSupport;
import docking.ComponentProvider;
import ghidra.service.graph.AttributedEdge;
import ghidra.service.graph.AttributedVertex;
/** /**
* An extension of the jungrapht DefaultGraphMouse. This class has references to * Keeper of shared logic for jungrapht handling
* <ul>
* <li>a {@link VisualizationViewer} (to access the Graph and LayoutModel)
*
*/ */
public class GhidraGraphMouse extends DefaultGraphMouse<AttributedVertex, AttributedEdge> { public class JgtUtils {
private static final String PICK_AREA_SIZE_PROPERTY = PREFIX + "pickAreaSize"; private static final String PICK_AREA_SIZE_PROPERTY = PREFIX + "pickAreaSize";
private VisualizationViewer<AttributedVertex, AttributedEdge> viewer;
private int pickSize;
/** /**
* create an instance with default values * Returns the edge under the given mouse event
* @param componentProvider the graph component provider *
* @param viewer the graph viewer component * @param <V> the vertex type
* @param <E> the edge type
* @param e the event
* @param viewer the graph viewer
* @return the edge
*/ */
GhidraGraphMouse(ComponentProvider componentProvider, public static <V, E> E getEdge(MouseEvent e, VisualizationViewer<V, E> viewer) {
VisualizationViewer<AttributedVertex, AttributedEdge> viewer) { if (e == null) {
return null;
super(DefaultGraphMouse.<AttributedVertex, AttributedEdge> builder());
this.viewer = viewer;
pickSize = Integer.getInteger(GhidraGraphMouse.PICK_AREA_SIZE_PROPERTY, 4);
} }
private Rectangle2D getFootprint(MouseEvent e) { Rectangle2D footprintRectangle = getFootprint(e);
LayoutModel<V> layoutModel = viewer.getVisualizationModel().getLayoutModel();
GraphElementAccessor<V, E> pickSupport = viewer.getPickSupport();
if (pickSupport == null) {
return null;
}
if (pickSupport instanceof ShapePickSupport) {
ShapePickSupport<V, E> shapePickSupport =
(ShapePickSupport<V, E>) pickSupport;
return shapePickSupport.getEdge(layoutModel, footprintRectangle);
}
TransformSupport<V, E> transformSupport =
viewer.getTransformSupport();
Point2D layoutPoint = transformSupport.inverseTransform(viewer, e.getPoint());
return pickSupport.getEdge(layoutModel, layoutPoint.getX(), layoutPoint.getY());
}
/**
* Returns the vertex under the given mouse event
*
* @param <V> the vertex type
* @param <E> the edge type
* @param e the event
* @param viewer the graph viewer
* @return the vertex
*/
public static <V, E> V getVertex(MouseEvent e, VisualizationViewer<V, E> viewer) {
if (e == null) {
return null;
}
Rectangle2D footprintRectangle = getFootprint(e);
LayoutModel<V> layoutModel = viewer.getVisualizationModel().getLayoutModel();
GraphElementAccessor<V, E> pickSupport = viewer.getPickSupport();
if (pickSupport == null) {
return null;
}
if (pickSupport instanceof ShapePickSupport) {
ShapePickSupport<V, E> shapePickSupport =
(ShapePickSupport<V, E>) pickSupport;
return shapePickSupport.getVertex(layoutModel, footprintRectangle);
}
TransformSupport<V, E> transformSupport =
viewer.getTransformSupport();
Point2D layoutPoint = transformSupport.inverseTransform(viewer, e.getPoint());
return pickSupport.getVertex(layoutModel, layoutPoint.getX(), layoutPoint.getY());
}
private static Rectangle2D getFootprint(MouseEvent e) {
int pickSize = Integer.getInteger(PICK_AREA_SIZE_PROPERTY, 4);
return new Rectangle2D.Float( return new Rectangle2D.Float(
e.getPoint().x - pickSize / 2f, e.getPoint().x - pickSize / 2f,
e.getPoint().y - pickSize / 2f, e.getPoint().y - pickSize / 2f,
@ -65,44 +109,4 @@ public class GhidraGraphMouse extends DefaultGraphMouse<AttributedVertex, Attrib
pickSize); pickSize);
} }
AttributedEdge getPickedEdge(MouseEvent e) {
if (e == null) {
return null;
}
Rectangle2D footprintRectangle = getFootprint(e);
LayoutModel<AttributedVertex> layoutModel = viewer.getVisualizationModel().getLayoutModel();
GraphElementAccessor<AttributedVertex, AttributedEdge> pickSupport =
viewer.getPickSupport();
if (pickSupport instanceof ShapePickSupport) {
ShapePickSupport<AttributedVertex, AttributedEdge> shapePickSupport =
(ShapePickSupport<AttributedVertex, AttributedEdge>) pickSupport;
return shapePickSupport.getEdge(layoutModel, footprintRectangle);
}
TransformSupport<AttributedVertex, AttributedEdge> transformSupport =
viewer.getTransformSupport();
Point2D layoutPoint = transformSupport.inverseTransform(viewer, e.getPoint());
return pickSupport.getEdge(layoutModel, layoutPoint.getX(), layoutPoint.getY());
}
AttributedVertex getPickedVertex(MouseEvent e) {
if (e == null) {
return null;
}
Rectangle2D footprintRectangle = getFootprint(e);
LayoutModel<AttributedVertex> layoutModel = viewer.getVisualizationModel().getLayoutModel();
GraphElementAccessor<AttributedVertex, AttributedEdge> pickSupport =
viewer.getPickSupport();
if (pickSupport instanceof ShapePickSupport) {
ShapePickSupport<AttributedVertex, AttributedEdge> shapePickSupport =
(ShapePickSupport<AttributedVertex, AttributedEdge>) pickSupport;
return shapePickSupport.getVertex(layoutModel, footprintRectangle);
}
TransformSupport<AttributedVertex, AttributedEdge> transformSupport =
viewer.getTransformSupport();
Point2D layoutPoint = transformSupport.inverseTransform(viewer, e.getPoint());
return pickSupport.getVertex(layoutModel, layoutPoint.getX(), layoutPoint.getY());
}
} }

View file

@ -0,0 +1,57 @@
/* ###
* 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.graph.visualization.mouse;
import java.awt.event.MouseEvent;
import ghidra.graph.visualization.DefaultGraphDisplay;
import ghidra.service.graph.AttributedVertex;
/**
* A mouse plugin to focus a vertex when clicked
*
* @param <V> the vertex type
* @param <E> the edge type
*/
public class JgtVertexFocusingPlugin<V, E> extends AbstractJgtGraphMousePlugin<V, E> {
private DefaultGraphDisplay graphDisplay;
public JgtVertexFocusingPlugin(DefaultGraphDisplay graphDisplay) {
this.graphDisplay = graphDisplay;
}
@Override
public void mousePressed(MouseEvent e) {
if (!checkModifiers(e)) {
return;
}
selectedVertex = getVertex(e);
}
@Override
public void mouseClicked(MouseEvent e) {
if (selectedVertex == null) {
return;
}
graphDisplay.setFocusedVertex((AttributedVertex) selectedVertex);
// Note: do not consume the event. We will just focus our vertex, regardless of further
// mouse event processing.
}
}

View file

@ -62,3 +62,10 @@ jungrapht.initialDimensionVertexDensity=0.3f
jungrapht.minScale=0.001 jungrapht.minScale=0.001
jungrapht.maxScale=1.0 jungrapht.maxScale=1.0
11:45 AM
# not using spatial data structures for edges (fixed in versions after 1.0)
jungrapht.edgeSpatialSupport=NONE
jungrapht.vertexSpatialSupport=NONE

View file

@ -26,7 +26,6 @@ import ghidra.app.util.opinion.PeLoader;
import ghidra.app.util.pdb.PdbLocator; import ghidra.app.util.pdb.PdbLocator;
import ghidra.framework.options.OptionType; import ghidra.framework.options.OptionType;
import ghidra.framework.options.Options; import ghidra.framework.options.Options;
import ghidra.framework.preferences.Preferences;
import ghidra.program.model.address.AddressSetView; import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.util.Msg; import ghidra.util.Msg;
@ -208,15 +207,8 @@ public class PdbAnalyzer extends AbstractAnalyzer {
@Override @Override
public void registerOptions(Options options, Program program) { public void registerOptions(Options options, Program program) {
String pdbStorageLocation = symbolsRepositoryDir = PdbLocator.getDefaultPdbSymbolsDir();
Preferences.getProperty(PdbParser.PDB_STORAGE_PROPERTY, null, true);
if (pdbStorageLocation != null) {
File pdbDirectory = new File(pdbStorageLocation);
if (pdbDirectory.isDirectory()) {
symbolsRepositoryDir = pdbDirectory;
}
}
options.registerOption(SYMBOLPATH_OPTION_NAME, OptionType.FILE_TYPE, symbolsRepositoryDir, options.registerOption(SYMBOLPATH_OPTION_NAME, OptionType.FILE_TYPE, symbolsRepositoryDir,
null, SYMBOLPATH_OPTION_DESCRIPTION); null, SYMBOLPATH_OPTION_DESCRIPTION);
@ -227,12 +219,11 @@ public class PdbAnalyzer extends AbstractAnalyzer {
@Override @Override
public void optionsChanged(Options options, Program program) { public void optionsChanged(Options options, Program program) {
symbolsRepositoryDir = File symbolsDir = options.getFile(SYMBOLPATH_OPTION_NAME, symbolsRepositoryDir);
options.getFile(SYMBOLPATH_OPTION_NAME, PdbLocator.DEFAULT_SYMBOLS_DIR); if (!symbolsDir.equals(symbolsRepositoryDir)) {
symbolsRepositoryDir = symbolsDir;
Preferences.setProperty(PdbParser.PDB_STORAGE_PROPERTY, PdbLocator.setDefaultPdbSymbolsDir(symbolsDir);
symbolsRepositoryDir != null ? symbolsRepositoryDir.getAbsolutePath() : null); }
Preferences.store();
includePeSpecifiedPdbPath = includePeSpecifiedPdbPath =
options.getBoolean(OPTION_NAME_INCLUDE_PE_PDB_PATH, includePeSpecifiedPdbPath); options.getBoolean(OPTION_NAME_INCLUDE_PE_PDB_PATH, includePeSpecifiedPdbPath);

View file

@ -22,6 +22,7 @@ import java.util.Date;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import ghidra.app.services.*; import ghidra.app.services.*;
//import ghidra.app.util.bin.format.pdb.PdbParser;
import ghidra.app.util.bin.format.pdb.PdbParserConstants; import ghidra.app.util.bin.format.pdb.PdbParserConstants;
import ghidra.app.util.bin.format.pdb2.pdbreader.*; import ghidra.app.util.bin.format.pdb2.pdbreader.*;
import ghidra.app.util.importer.MessageLog; import ghidra.app.util.importer.MessageLog;
@ -84,7 +85,6 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer {
private static final String OPTION_NAME_SYMBOLPATH = "Symbol Repository Path"; private static final String OPTION_NAME_SYMBOLPATH = "Symbol Repository Path";
private static final String OPTION_DESCRIPTION_SYMBOLPATH = private static final String OPTION_DESCRIPTION_SYMBOLPATH =
"Directory path to root of Microsoft Symbol Repository Directory"; "Directory path to root of Microsoft Symbol Repository Directory";
private File DEFAULT_SYMBOLS_DIR = PdbLocator.DEFAULT_SYMBOLS_DIR;
private File symbolsRepositoryDir; private File symbolsRepositoryDir;
// Include the PE-Header-Specified PDB path for searching for appropriate PDB file. // Include the PE-Header-Specified PDB path for searching for appropriate PDB file.
@ -356,14 +356,18 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer {
@Override @Override
public void registerOptions(Options options, Program program) { public void registerOptions(Options options, Program program) {
symbolsRepositoryDir = PdbLocator.getDefaultPdbSymbolsDir();
// PDB file location information // PDB file location information
if (developerMode) {
options.registerOption(OPTION_NAME_DO_FORCELOAD, Boolean.FALSE, null, options.registerOption(OPTION_NAME_DO_FORCELOAD, Boolean.FALSE, null,
OPTION_DESCRIPTION_DO_FORCELOAD); OPTION_DESCRIPTION_DO_FORCELOAD);
options.registerOption(OPTION_NAME_FORCELOAD_FILE, OptionType.FILE_TYPE, options.registerOption(OPTION_NAME_FORCELOAD_FILE, OptionType.FILE_TYPE,
DEFAULT_FORCE_LOAD_FILE, DEFAULT_FORCE_LOAD_FILE, null, OPTION_DESCRIPTION_FORCELOAD_FILE);
null, OPTION_DESCRIPTION_FORCELOAD_FILE); }
options.registerOption(OPTION_NAME_SYMBOLPATH, OptionType.FILE_TYPE, DEFAULT_SYMBOLS_DIR, options.registerOption(OPTION_NAME_SYMBOLPATH, OptionType.FILE_TYPE,
null, OPTION_DESCRIPTION_SYMBOLPATH); symbolsRepositoryDir, null, OPTION_DESCRIPTION_SYMBOLPATH);
options.registerOption(OPTION_NAME_INCLUDE_PE_PDB_PATH, includePeSpecifiedPdbPath, null, options.registerOption(OPTION_NAME_INCLUDE_PE_PDB_PATH, includePeSpecifiedPdbPath, null,
OPTION_DESCRIPTION_INCLUDE_PE_PDB_PATH); OPTION_DESCRIPTION_INCLUDE_PE_PDB_PATH);
@ -380,9 +384,9 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer {
// PdbApplicatorOptions // PdbApplicatorOptions
getPdbApplicatorOptions(); getPdbApplicatorOptions();
if (developerMode) {
options.registerOption(OPTION_NAME_PROCESSING_RESTRICTIONS, restrictions, null, options.registerOption(OPTION_NAME_PROCESSING_RESTRICTIONS, restrictions, null,
OPTION_DESCRIPTION_PROCESSING_RESTRICTIONS); OPTION_DESCRIPTION_PROCESSING_RESTRICTIONS);
if (developerMode) {
options.registerOption(OPTION_NAME_APPLY_CODE_SCOPE_BLOCK_COMMENTS, options.registerOption(OPTION_NAME_APPLY_CODE_SCOPE_BLOCK_COMMENTS,
applyCodeScopeBlockComments, null, applyCodeScopeBlockComments, null,
OPTION_DESCRIPTION_APPLY_CODE_SCOPE_BLOCK_COMMENTS); OPTION_DESCRIPTION_APPLY_CODE_SCOPE_BLOCK_COMMENTS);
@ -414,11 +418,16 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer {
@Override @Override
public void optionsChanged(Options options, Program program) { public void optionsChanged(Options options, Program program) {
if (developerMode) {
doForceLoad = options.getBoolean(OPTION_NAME_DO_FORCELOAD, doForceLoad); doForceLoad = options.getBoolean(OPTION_NAME_DO_FORCELOAD, doForceLoad);
forceLoadFile = options.getFile(OPTION_NAME_FORCELOAD_FILE, forceLoadFile); forceLoadFile = options.getFile(OPTION_NAME_FORCELOAD_FILE, forceLoadFile);
}
symbolsRepositoryDir = options.getFile(OPTION_NAME_SYMBOLPATH, DEFAULT_SYMBOLS_DIR); File symbolsDir = options.getFile(OPTION_NAME_SYMBOLPATH, symbolsRepositoryDir);
if (!symbolsDir.equals(symbolsRepositoryDir)) {
symbolsRepositoryDir = symbolsDir;
PdbLocator.setDefaultPdbSymbolsDir(symbolsDir);
}
includePeSpecifiedPdbPath = includePeSpecifiedPdbPath =
options.getBoolean(OPTION_NAME_INCLUDE_PE_PDB_PATH, includePeSpecifiedPdbPath); options.getBoolean(OPTION_NAME_INCLUDE_PE_PDB_PATH, includePeSpecifiedPdbPath);
@ -434,8 +443,8 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer {
setPdbReaderOptions(); setPdbReaderOptions();
// PdbApplicatorOptions // PdbApplicatorOptions
restrictions = options.getEnum(OPTION_NAME_PROCESSING_RESTRICTIONS, restrictions);
if (developerMode) { if (developerMode) {
restrictions = options.getEnum(OPTION_NAME_PROCESSING_RESTRICTIONS, restrictions);
applyCodeScopeBlockComments = options.getBoolean( applyCodeScopeBlockComments = options.getBoolean(
OPTION_NAME_APPLY_CODE_SCOPE_BLOCK_COMMENTS, applyCodeScopeBlockComments); OPTION_NAME_APPLY_CODE_SCOPE_BLOCK_COMMENTS, applyCodeScopeBlockComments);
// Mechanism to apply instruction labels is not yet implemented-> does nothing // Mechanism to apply instruction labels is not yet implemented-> does nothing

View file

@ -54,8 +54,6 @@ public class PdbParser {
private static final String README_FILENAME = private static final String README_FILENAME =
Application.getInstallationDirectory() + "\\docs\\README_PDB.html"; Application.getInstallationDirectory() + "\\docs\\README_PDB.html";
public final static String PDB_STORAGE_PROPERTY = "PDB Storage Directory";
static final String STRUCTURE_KIND = "Structure"; static final String STRUCTURE_KIND = "Structure";
static final String UNION_KIND = "Union"; static final String UNION_KIND = "Union";

View file

@ -27,6 +27,7 @@ import ghidra.app.util.importer.LibrarySearchPathManager;
import ghidra.app.util.importer.MessageLog; import ghidra.app.util.importer.MessageLog;
import ghidra.framework.OperatingSystem; import ghidra.framework.OperatingSystem;
import ghidra.framework.Platform; import ghidra.framework.Platform;
import ghidra.framework.preferences.Preferences;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.SystemUtilities; import ghidra.util.SystemUtilities;
@ -79,6 +80,8 @@ public class PdbLocator {
public final static File WINDOWS_SYMBOLS_DIR = public final static File WINDOWS_SYMBOLS_DIR =
onWindows ? new File("C:/WINDOWS/Symbols") : null; onWindows ? new File("C:/WINDOWS/Symbols") : null;
public final static String PDB_SYMBOLS_DIR_PREFERENCE = "PDB Storage Directory";
private File symbolsRepositoryDir; private File symbolsRepositoryDir;
/** /**
* Only holds identifies in PDBs up until a matching one was found--nothing beyond that. * Only holds identifies in PDBs up until a matching one was found--nothing beyond that.
@ -677,4 +680,21 @@ public class PdbLocator {
return true; return true;
} }
public static File getDefaultPdbSymbolsDir() {
String pdbStorageLocation = Preferences.getProperty(PDB_SYMBOLS_DIR_PREFERENCE, null, true);
File defaultSymbolsDir = DEFAULT_SYMBOLS_DIR;
if (pdbStorageLocation != null) {
File pdbDirectory = new File(pdbStorageLocation);
if (pdbDirectory.isDirectory()) {
defaultSymbolsDir = pdbDirectory;
}
}
return defaultSymbolsDir;
}
public static void setDefaultPdbSymbolsDir(File symbolsDir) {
Preferences.setProperty(PDB_SYMBOLS_DIR_PREFERENCE, symbolsDir.getAbsolutePath());
Preferences.store();
}
} }

View file

@ -59,6 +59,10 @@ public abstract class AbstractComplexTypeApplier extends MsTypeApplier {
return ((AbstractComplexMsType) msType).getMsProperty().isForwardReference(); return ((AbstractComplexMsType) msType).getMsProperty().isForwardReference();
} }
boolean isNested() {
return ((AbstractComplexMsType) msType).getMsProperty().isNestedClass();
}
boolean isFinal() { boolean isFinal() {
return ((AbstractComplexMsType) msType).getMsProperty().isSealed(); return ((AbstractComplexMsType) msType).getMsProperty().isSealed();
} }

View file

@ -135,7 +135,15 @@ public class CompositeTypeApplier extends AbstractComplexTypeApplier {
@Override @Override
void resolve() { void resolve() {
if (!isForwardReference()) {
// NOTE: Until we know better we do not want to explicitly
// apply nested composite datatypes and allow them to be
// created as-needed (e.g., function definition). This is
// done to minimize duplication of anonymous/unnamed nested
// composites since the parent composite reconsruction performed
// by DefaultCompositeMember will generate such nested composites.
if (!isForwardReference() && !isNested()) {
super.resolve(); super.resolve();
} }
} }

View file

@ -112,9 +112,9 @@ public class TypeApplierFactory {
applier = applier =
new MemberFunctionTypeApplier(applicator, (MemberFunction16MsType) type); new MemberFunctionTypeApplier(applicator, (MemberFunction16MsType) type);
break; break;
case VtShapeMsType.PDB_ID: // case VtShapeMsType.PDB_ID:
applier = new VtShapeTypeApplier(applicator, (VtShapeMsType) type); // applier = new VtShapeTypeApplier(applicator, (VtShapeMsType) type);
break; // break;
// case Cobol016MsType.PDB_ID: // case Cobol016MsType.PDB_ID:
// // Not evaluated/implemented yet. // // Not evaluated/implemented yet.
// break; // break;

View file

@ -35,6 +35,7 @@ import ghidra.app.services.DataTypeManagerService;
import ghidra.app.util.bin.format.pdb.PdbException; import ghidra.app.util.bin.format.pdb.PdbException;
import ghidra.app.util.bin.format.pdb.PdbParser; import ghidra.app.util.bin.format.pdb.PdbParser;
import ghidra.app.util.bin.format.pdb.PdbParser.PdbFileType; import ghidra.app.util.bin.format.pdb.PdbParser.PdbFileType;
import ghidra.app.util.pdb.PdbLocator;
import ghidra.app.util.pdb.PdbProgramAttributes; import ghidra.app.util.pdb.PdbProgramAttributes;
import ghidra.app.util.pdb.pdbapplicator.PdbApplicatorRestrictions; import ghidra.app.util.pdb.pdbapplicator.PdbApplicatorRestrictions;
import ghidra.framework.Application; import ghidra.framework.Application;
@ -399,16 +400,7 @@ public class PdbSymbolServerPlugin extends Plugin {
testDirectory = localDir; testDirectory = localDir;
} }
else { else {
String userHome = System.getProperty("user.home"); testDirectory = PdbLocator.getDefaultPdbSymbolsDir();
String storedLocalDir =
Preferences.getProperty(PdbParser.PDB_STORAGE_PROPERTY, userHome, true);
testDirectory = new File(storedLocalDir);
if (!testDirectory.exists()) {
testDirectory = new File(userHome);
}
} }
final File storedDirectory = testDirectory; final File storedDirectory = testDirectory;
@ -444,7 +436,7 @@ public class PdbSymbolServerPlugin extends Plugin {
throw new CancelledException(); throw new CancelledException();
} }
Preferences.setProperty(PdbParser.PDB_STORAGE_PROPERTY, chosenDir[0].getAbsolutePath()); PdbLocator.setDefaultPdbSymbolsDir(chosenDir[0]);
return chosenDir[0]; return chosenDir[0];
} }

View file

@ -13,7 +13,7 @@
<BODY lang="EN-US"> <BODY lang="EN-US">
<H1>Graphing the Program</H1> <H1>Graphing the Program</H1>
<H3>Graph Output</H3> <H2>Graph Output</H2>
<P>To display or export a graph, Ghidra supports multiple graph services. Ghidra has two <P>To display or export a graph, Ghidra supports multiple graph services. Ghidra has two
built-in graph services; one to display a graph and one to export a graph. Before invoking built-in graph services; one to display a graph and one to export a graph. Before invoking
@ -21,7 +21,7 @@
will direct the output of the graph function to the active graph service. To select a graph will direct the output of the graph function to the active graph service. To select a graph
service, use the <B>Graph<IMG src="../../shared/arrow.gif">Graph Output</B></LI> menu. service, use the <B>Graph<IMG src="../../shared/arrow.gif">Graph Output</B></LI> menu.
<H3>Graph types</H3> <H2>Graph types</H2>
<P>Program control flow Graphs can be created and then shown using an appropriate graph service. <P>Program control flow Graphs can be created and then shown using an appropriate graph service.
A control flow graph is a representation of the flow from one portion of the code to A control flow graph is a representation of the flow from one portion of the code to
another. The nodes of the graph represent blocks of code and the edges represent flow between another. The nodes of the graph represent blocks of code and the edges represent flow between
@ -49,7 +49,7 @@
<P>Selection and Location events are synchronized between each <P>Selection and Location events are synchronized between each
graph and the other windows in the tool. graph and the other windows in the tool.
<H3>Selection</H3> <H2>Selection</H2>
<P>The current selection within the graph display is represented by a red box around selected <P>The current selection within the graph display is represented by a red box around selected
nodes as shown below on the node labeled "00408133". A node is selected if any addresses it represents are contained within the nodes as shown below on the node labeled "00408133". A node is selected if any addresses it represents are contained within the
@ -74,7 +74,7 @@
from the basic blocks found within the selected subroutine.</P> from the basic blocks found within the selected subroutine.</P>
</BLOCKQUOTE> </BLOCKQUOTE>
<H3>Location</H3> <H2>Location</H2>
<P>The node containing the current address location is marked with a large red arrow as shown <P>The node containing the current address location is marked with a large red arrow as shown
below on the graph node labeled "00408133".</P> below on the graph node labeled "00408133".</P>
@ -95,7 +95,7 @@
current address location within Ghidra to change to the minimum address represented by the current address location within Ghidra to change to the minimum address represented by the
graph node.</P> graph node.</P>
<H3>Graph Representation</H3> <H2>Graph Representation</H2>
<P>By Default, the graphs use the following icons and colors to represent the nodes and edges.</P> <P>By Default, the graphs use the following icons and colors to represent the nodes and edges.</P>
@ -220,98 +220,6 @@
</DIV> </DIV>
</CENTER> </CENTER>
<H2><A name="Graph_Block_Flow"></A>Block Flow Graph</H2>
<P>A Block Flow Graph consists of nodes that represent Basic blocks of contiguous instructions.
Basic blocks are broken up by any instruction that causes a change in execution flow. All Jump,
Call, Branch, and Return instructions can cause the execution flow to change. Arithmetic and
store/load instructions do not break a Basic block because they do not change the execution
flow. A labeled instruction will always start a block regardless of the instruction type.</P>
<P>For example:</P>
<P align="center"><IMG src="images/BasicBlockExampleCode.png"></P>
<P>Would generate the following graph:</P>
<P align="center"><IMG src="images/BasicBlockGraph.png">
</P>
<BLOCKQUOTE>
<P><IMG src="../../shared/note.png"> If there is a current selection, the nodes and edges
will be restricted to blocks of code that fall within the selection.</P>
</BLOCKQUOTE>
<P>To Graph Block Flow Using the default model,</P>
<OL>
<LI>Select <B>Graph<IMG src="../../shared/arrow.gif"> Block Flow</B></LI>
<LI>A new graph window is created</LI>
</OL>
<H2><A name="Graph_Code_Flow"></A>Graph Code Flow</H2>
<P align="left">A Code Flow Graph is an extension of a <A href="#Graph_Block_Flow">Block Flow
Graph</A> in which each graph node (i.e., vertex) contains the list of instructions contained
within the associated block. The list of instructions are passed to the graph as the vertex
label.</P>
<P align="center"><BR>
<BR>
<IMG src="images/CodeBlockGraph.png"></P>
<H2><A name="Graph_Calls_Using_Default_Model"></A>Graph Calls</H2>
<P>A graph of the call instruction flow from one subroutine to another can be created with
<B>Graph<IMG src="../../shared/arrow.gif"> Calls</B>. The graph is created using the default
Call Model. Several Subroutine Models are available. Each model provides a slightly
different perspective on what constitutes a subroutine.</P>
<BLOCKQUOTE>
<BLOCKQUOTE>
<P><IMG src="../../shared/note.png"> If there is a current selection, the nodes and edges
will be restricted to blocks of code that fall within the selection.</P>
</BLOCKQUOTE>
<P>To Graph Calls Using the default model,</P>
<OL>
<LI>Select <B>Graph<IMG src="../../shared/arrow.gif"> Calls</B></LI>
<LI>A new graph window is created</LI>
</OL>
<P><A name="Graph_Calls_Using_Model"></A>To Graph Calls Using a specific model*,</P>
<OL>
<LI>Select <B>Graph<IMG src="../../shared/arrow.gif"> Calls Using Model<IMG src=
"../../shared/arrow.gif"></B> &lt;<I><B>a Call Model</B></I>&gt;</LI>
<LI>
Select one of
<UL>
<LI>Isolated Entry Model</LI>
<LI>Multiple Entry Model</LI>
<LI>Overlapped Code Model</LI>
<LI>Partitioned Code Model</LI>
</UL>
</LI>
<LI>A new graph window is created</LI>
</OL>
<BLOCKQUOTE>
<P><IMG src="../../shared/note.png"> *For a more thorough description of each Call Block
Model (i.e., Subroutine Model), see <A href="help/topics/BlockModel/Block_Model.htm">Block
Models</A>. The specific list of models presented to the user may vary depending upon the
set of block models configured into the tool.</P>
</BLOCKQUOTE>
</BLOCKQUOTE>
<H2><A name="Reuse_Graph"></A>Reuse Graph</H2> <H2><A name="Reuse_Graph"></A>Reuse Graph</H2>
@ -380,6 +288,123 @@
current address location changes.</P> current address location changes.</P>
<BR>
<BR>
<BR>
<BR>
<BR>
<BR>
<BR>
<BR>
<HR WIDTH="90%">
<BR>
<BR>
<H2><A name="Graph_Block_Flow"></A>Block Flow Graph</H2>
<P>A Block Flow Graph consists of nodes that represent Basic blocks of contiguous instructions.
Basic blocks are broken up by any instruction that causes a change in execution flow. All Jump,
Call, Branch, and Return instructions can cause the execution flow to change. Arithmetic and
store/load instructions do not break a Basic block because they do not change the execution
flow. A labeled instruction will always start a block regardless of the instruction type.</P>
<P>For example:</P>
<P align="center"><IMG src="images/BasicBlockExampleCode.png"></P>
<P>Would generate the following graph:</P>
<P align="center"><IMG src="images/BasicBlockGraph.png">
</P>
<BLOCKQUOTE>
<P><IMG src="../../shared/note.png"> If there is a current selection, the nodes and edges
will be restricted to blocks of code that fall within the selection.</P>
</BLOCKQUOTE>
<P>To Graph Block Flow Using the default model,</P>
<OL>
<LI>Select <B>Graph<IMG src="../../shared/arrow.gif"> Block Flow</B></LI>
<LI>A new graph window is created</LI>
</OL>
<BLOCKQUOTE>
<H3><A NAME="Rename_Vertex"></A>Rename Vertex</H3>
<BLOCKQUOTE>
<P>Allows the user to rename the symbol represented by the given vertex.
</P>
</BLOCKQUOTE>
</BLOCKQUOTE>
<H2><A name="Graph_Code_Flow"></A>Graph Code Flow</H2>
<P align="left">A Code Flow Graph is an extension of a <A href="#Graph_Block_Flow">Block Flow
Graph</A> in which each graph node (i.e., vertex) contains the list of instructions contained
within the associated block. The list of instructions are passed to the graph as the vertex
label.</P>
<P align="center"><BR>
<BR>
<IMG src="images/CodeBlockGraph.png"></P>
<H2><A name="Graph_Calls_Using_Default_Model"></A>Graph Calls</H2>
<P>A graph of the call instruction flow from one subroutine to another can be created with
<B>Graph<IMG src="../../shared/arrow.gif"> Calls</B>. The graph is created using the default
Call Model. Several Subroutine Models are available. Each model provides a slightly
different perspective on what constitutes a subroutine.</P>
<BLOCKQUOTE>
<BLOCKQUOTE>
<P><IMG src="../../shared/note.png"> If there is a current selection, the nodes and edges
will be restricted to blocks of code that fall within the selection.</P>
</BLOCKQUOTE>
<P>To Graph Calls Using the default model,</P>
<OL>
<LI>Select <B>Graph<IMG src="../../shared/arrow.gif"> Calls</B></LI>
<LI>A new graph window is created</LI>
</OL>
<P><A name="Graph_Calls_Using_Model"></A>To Graph Calls Using a specific model*,</P>
<OL>
<LI>Select <B>Graph<IMG src="../../shared/arrow.gif"> Calls Using Model<IMG src=
"../../shared/arrow.gif"></B> &lt;<I><B>a Call Model</B></I>&gt;</LI>
<LI>
Select one of
<UL>
<LI>Isolated Entry Model</LI>
<LI>Multiple Entry Model</LI>
<LI>Overlapped Code Model</LI>
<LI>Partitioned Code Model</LI>
</UL>
</LI>
<LI>A new graph window is created</LI>
</OL>
<BLOCKQUOTE>
<P><IMG src="../../shared/note.png"> *For a more thorough description of each Call Block
Model (i.e., Subroutine Model), see <A href="help/topics/BlockModel/Block_Model.htm">Block
Models</A>. The specific list of models presented to the user may vary depending upon the
set of block models configured into the tool.</P>
</BLOCKQUOTE>
</BLOCKQUOTE>
<BR> <BR>
<BR> <BR>
<BR> <BR>

View file

@ -106,7 +106,7 @@ public class BlockGraphTask extends Task {
private CodeBlockModel blockModel; private CodeBlockModel blockModel;
private AddressSetView selection; private AddressSetView selection;
private ProgramLocation location; private ProgramLocation location;
private GraphDisplayProvider graphService; private GraphDisplayProvider graphProvider;
private boolean reuseGraph; private boolean reuseGraph;
private boolean appendGraph; private boolean appendGraph;
private PluginTool tool; private PluginTool tool;
@ -116,7 +116,7 @@ public class BlockGraphTask extends Task {
public BlockGraphTask(String actionName, boolean graphEntryPointNexus, boolean showCode, public BlockGraphTask(String actionName, boolean graphEntryPointNexus, boolean showCode,
boolean reuseGraph, boolean appendGraph, PluginTool tool, ProgramSelection selection, boolean reuseGraph, boolean appendGraph, PluginTool tool, ProgramSelection selection,
ProgramLocation location, CodeBlockModel blockModel, ProgramLocation location, CodeBlockModel blockModel,
GraphDisplayProvider graphService) { GraphDisplayProvider graphProvider) {
super("Graph Program", true, false, true); super("Graph Program", true, false, true);
this.actionName = actionName; this.actionName = actionName;
@ -127,7 +127,7 @@ public class BlockGraphTask extends Task {
this.appendGraph = appendGraph; this.appendGraph = appendGraph;
this.tool = tool; this.tool = tool;
this.blockModel = blockModel; this.blockModel = blockModel;
this.graphService = graphService; this.graphProvider = graphProvider;
this.colorizingService = tool.getService(ColorizingService.class); this.colorizingService = tool.getService(ColorizingService.class);
this.selection = selection; this.selection = selection;
this.location = location; this.location = location;
@ -142,7 +142,7 @@ public class BlockGraphTask extends Task {
AttributedGraph graph = createGraph(); AttributedGraph graph = createGraph();
monitor.setMessage("Generating Graph..."); monitor.setMessage("Generating Graph...");
try { try {
GraphDisplay display = graphService.getGraphDisplay(reuseGraph, monitor); GraphDisplay display = graphProvider.getGraphDisplay(reuseGraph, monitor);
BlockModelGraphDisplayListener listener = BlockModelGraphDisplayListener listener =
new BlockModelGraphDisplayListener(tool, blockModel, display); new BlockModelGraphDisplayListener(tool, blockModel, display);
display.setGraphDisplayListener(listener); display.setGraphDisplayListener(listener);

View file

@ -26,6 +26,7 @@ import ghidra.program.model.block.*;
import ghidra.program.model.symbol.Symbol; import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolTable; import ghidra.program.model.symbol.SymbolTable;
import ghidra.service.graph.*; import ghidra.service.graph.*;
import ghidra.util.HelpLocation;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -47,6 +48,7 @@ public class BlockModelGraphDisplayListener extends AddressBasedGraphDisplayList
display.addAction(new ActionBuilder("Rename Vertex", "Block Graph") display.addAction(new ActionBuilder("Rename Vertex", "Block Graph")
.popupMenuPath("Rename Vertex") .popupMenuPath("Rename Vertex")
.withContext(VertexGraphActionContext.class) .withContext(VertexGraphActionContext.class)
.helpLocation(new HelpLocation("ProgramGraphPlugin", "Rename Vertex"))
// only enable action when vertex corresponds to an address // only enable action when vertex corresponds to an address
.enabledWhen(c -> getAddress(c.getClickedVertex().getId()) != null) .enabledWhen(c -> getAddress(c.getClickedVertex().getId()) != null)
.onAction(this::updateVertexName) .onAction(this::updateVertexName)

View file

@ -30,6 +30,10 @@ import ghidra.program.model.mem.MemoryAccessException;
import ghidra.test.*; import ghidra.test.*;
public class AbstractBlockGraphTest extends AbstractGhidraHeadedIntegrationTest { public class AbstractBlockGraphTest extends AbstractGhidraHeadedIntegrationTest {
protected static final String CALLER_FUNCTION_ADDRESS = "01002200";
protected static final String SIMPLE_FUNCTION_ADDRESS = "01002239";
protected PluginTool tool; protected PluginTool tool;
protected ProgramDB program; protected ProgramDB program;
protected TestEnv env; protected TestEnv env;
@ -37,10 +41,6 @@ public class AbstractBlockGraphTest extends AbstractGhidraHeadedIntegrationTest
private ToyProgramBuilder builder; private ToyProgramBuilder builder;
protected CodeBrowserPlugin codeBrowser; protected CodeBrowserPlugin codeBrowser;
protected Address addr(long addr) {
return builder.getAddress(addr);
}
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
@ -50,7 +50,6 @@ public class AbstractBlockGraphTest extends AbstractGhidraHeadedIntegrationTest
tool = env.getTool(); tool = env.getTool();
initializeTool(); initializeTool();
} }
@After @After
@ -80,14 +79,15 @@ public class AbstractBlockGraphTest extends AbstractGhidraHeadedIntegrationTest
builder = new ToyProgramBuilder("sample", true); builder = new ToyProgramBuilder("sample", true);
builder.createMemory("caller", "0x01002200", 8); builder.createMemory("caller", "0x01002200", 8);
builder.createMemory("simple", "0x01002239", 8); builder.createMemory("simple", "0x01002239", 8);
builder.createMemory("not_graphed", "0x01002300", 8);
buildCallerFunction(builder); buildCallerFunction();
buildSimpleFunction(builder); buildSimpleFunction();
program = builder.getProgram(); program = builder.getProgram();
} }
private void buildCallerFunction(ToyProgramBuilder builder) throws MemoryAccessException { private void buildCallerFunction() throws MemoryAccessException {
// just a function that calls another // just a function that calls another
builder.addBytesNOP("0x01002200", 1); builder.addBytesNOP("0x01002200", 1);
builder.addBytesCall("0x01002201", "0x01002239");// jump to C builder.addBytesCall("0x01002201", "0x01002239");// jump to C
@ -98,7 +98,7 @@ public class AbstractBlockGraphTest extends AbstractGhidraHeadedIntegrationTest
builder.createLabel("0x01002200", "entry");// function label builder.createLabel("0x01002200", "entry");// function label
} }
private void buildSimpleFunction(ToyProgramBuilder builder) throws MemoryAccessException { private void buildSimpleFunction() throws MemoryAccessException {
// just a function to render in the graph so that we can clear out settings/cache // just a function to render in the graph so that we can clear out settings/cache
// 01002239 // 01002239
@ -128,4 +128,12 @@ public class AbstractBlockGraphTest extends AbstractGhidraHeadedIntegrationTest
builder.createLabel("0x01002239", "simple");// function label builder.createLabel("0x01002239", "simple");// function label
} }
protected Address addr(long addr) {
return builder.getAddress(addr);
}
protected Address addr(String addressString) {
return builder.addr(addressString);
}
} }

View file

@ -34,17 +34,16 @@
<SPLIT_NODE WIDTH="1475" HEIGHT="596" DIVIDER_LOCATION="632" ORIENTATION="HORIZONTAL"> <SPLIT_NODE WIDTH="1475" HEIGHT="596" DIVIDER_LOCATION="632" ORIENTATION="HORIZONTAL">
<SPLIT_NODE WIDTH="950" HEIGHT="985" DIVIDER_LOCATION="894" ORIENTATION="HORIZONTAL"> <SPLIT_NODE WIDTH="950" HEIGHT="985" DIVIDER_LOCATION="894" ORIENTATION="HORIZONTAL">
<COMPONENT_NODE TOP_INFO="0"> <COMPONENT_NODE TOP_INFO="0">
<COMPONENT_INFO NAME="Listing" OWNER="CodeBrowserPlugin" TITLE="Listing: notepad.exe.2" ACTIVE="true" GROUP="" INSTANCE_ID="2777482860928188961" /> <COMPONENT_INFO NAME="Listing" OWNER="CodeBrowserPlugin" TITLE="Listing:" ACTIVE="true" GROUP="Core" INSTANCE_ID="2777482860928188961" />
<COMPONENT_INFO NAME="Test Listing" OWNER="Test Listing" TITLE="Test Listing" ACTIVE="false" GROUP="" INSTANCE_ID="2733632016422332683" />
</COMPONENT_NODE> </COMPONENT_NODE>
<COMPONENT_NODE TOP_INFO="0"> <COMPONENT_NODE TOP_INFO="0">
<COMPONENT_INFO NAME="Listing" OWNER="CodeBrowserPlugin" TITLE="[Listing: notepad.exe]" ACTIVE="false" GROUP="disconnected" INSTANCE_ID="2738354194536315852" /> <COMPONENT_INFO NAME="Listing" OWNER="CodeBrowserPlugin" TITLE="Listing:" ACTIVE="false" GROUP="disconnected" INSTANCE_ID="2738354194536315852" />
</COMPONENT_NODE> </COMPONENT_NODE>
</SPLIT_NODE> </SPLIT_NODE>
<SPLIT_NODE WIDTH="355" HEIGHT="1097" DIVIDER_LOCATION="542" ORIENTATION="VERTICAL"> <SPLIT_NODE WIDTH="355" HEIGHT="1097" DIVIDER_LOCATION="542" ORIENTATION="VERTICAL">
<SPLIT_NODE WIDTH="541" HEIGHT="596" DIVIDER_LOCATION="546" ORIENTATION="VERTICAL"> <SPLIT_NODE WIDTH="541" HEIGHT="596" DIVIDER_LOCATION="546" ORIENTATION="VERTICAL">
<COMPONENT_NODE TOP_INFO="0"> <COMPONENT_NODE TOP_INFO="0">
<COMPONENT_INFO NAME="Decompiler" OWNER="DecompilePlugin" TITLE="Decompiler" ACTIVE="true" GROUP="" INSTANCE_ID="2777482860928188962" /> <COMPONENT_INFO NAME="Decompiler" OWNER="DecompilePlugin" TITLE="Decompile" ACTIVE="true" GROUP="" INSTANCE_ID="2777482860928188962" />
</COMPONENT_NODE> </COMPONENT_NODE>
<COMPONENT_NODE TOP_INFO="0"> <COMPONENT_NODE TOP_INFO="0">
<COMPONENT_INFO NAME="Version Tracking Matches for Destination" OWNER="VTPlugin" TITLE="Version Tracking Matches for Destination" ACTIVE="true" GROUP="VTResults" INSTANCE_ID="2777482861815284257" /> <COMPONENT_INFO NAME="Version Tracking Matches for Destination" OWNER="VTPlugin" TITLE="Version Tracking Matches for Destination" ACTIVE="true" GROUP="VTResults" INSTANCE_ID="2777482861815284257" />
@ -56,8 +55,7 @@
</SPLIT_NODE> </SPLIT_NODE>
</SPLIT_NODE> </SPLIT_NODE>
<COMPONENT_NODE TOP_INFO="0"> <COMPONENT_NODE TOP_INFO="0">
<COMPONENT_INFO NAME="Decompiler" OWNER="Decompiler" TITLE="Decompile" ACTIVE="false" GROUP="" INSTANCE_ID="0" /> <COMPONENT_INFO NAME="References from " OWNER="ReferencesPlugin" TITLE="References from" ACTIVE="false" GROUP="" INSTANCE_ID="0" />
<COMPONENT_INFO NAME="References from " OWNER="ReferencesPlugin" TITLE="References from notepad.exe@01001004" ACTIVE="false" GROUP="" INSTANCE_ID="0" />
<COMPONENT_INFO NAME="Label References Provider" OWNER="LocationReferencesPlugin" TITLE="References to s__0100a000" ACTIVE="false" GROUP="" INSTANCE_ID="2738698427373098901" /> <COMPONENT_INFO NAME="Label References Provider" OWNER="LocationReferencesPlugin" TITLE="References to s__0100a000" ACTIVE="false" GROUP="" INSTANCE_ID="2738698427373098901" />
</COMPONENT_NODE> </COMPONENT_NODE>
</SPLIT_NODE> </SPLIT_NODE>
@ -186,7 +184,7 @@
</WINDOW_NODE> </WINDOW_NODE>
<WINDOW_NODE X_POS="454" Y_POS="326" WIDTH="464" HEIGHT="513"> <WINDOW_NODE X_POS="454" Y_POS="326" WIDTH="464" HEIGHT="513">
<COMPONENT_NODE TOP_INFO="0"> <COMPONENT_NODE TOP_INFO="0">
<COMPONENT_INFO NAME="References Editor " OWNER="ReferencesPlugin" TITLE="References Editor @ 0100566d (notepad.exe)" ACTIVE="false" GROUP="" INSTANCE_ID="2738698427373098900" /> <COMPONENT_INFO NAME="References Editor " OWNER="ReferencesPlugin" TITLE="References Editor" ACTIVE="false" GROUP="" INSTANCE_ID="2738698427373098900" />
</COMPONENT_NODE> </COMPONENT_NODE>
</WINDOW_NODE> </WINDOW_NODE>
<WINDOW_NODE X_POS="0" Y_POS="0" WIDTH="0" HEIGHT="0"> <WINDOW_NODE X_POS="0" Y_POS="0" WIDTH="0" HEIGHT="0">
@ -196,7 +194,7 @@
</WINDOW_NODE> </WINDOW_NODE>
<WINDOW_NODE X_POS="397" Y_POS="209" WIDTH="597" HEIGHT="433"> <WINDOW_NODE X_POS="397" Y_POS="209" WIDTH="597" HEIGHT="433">
<COMPONENT_NODE TOP_INFO="0"> <COMPONENT_NODE TOP_INFO="0">
<COMPONENT_INFO NAME="Structure Editor" OWNER="DataTypeManagerPlugin" TITLE="Structure Editor: IMAGE_RESOURCE_DIRECTORY_ENTRY_NameStruct.conflict (notepad.exe)" ACTIVE="false" GROUP="" INSTANCE_ID="2739607423924196364" /> <COMPONENT_INFO NAME="Structure Editor" OWNER="DataTypeManagerPlugin" TITLE="Structure Editor: " ACTIVE="false" GROUP="" INSTANCE_ID="2739607423924196364" />
</COMPONENT_NODE> </COMPONENT_NODE>
</WINDOW_NODE> </WINDOW_NODE>
<WINDOW_NODE X_POS="171" Y_POS="155" WIDTH="940" HEIGHT="642"> <WINDOW_NODE X_POS="171" Y_POS="155" WIDTH="940" HEIGHT="642">
@ -211,17 +209,17 @@
</WINDOW_NODE> </WINDOW_NODE>
<WINDOW_NODE X_POS="-1287" Y_POS="271" WIDTH="1069" HEIGHT="677"> <WINDOW_NODE X_POS="-1287" Y_POS="271" WIDTH="1069" HEIGHT="677">
<COMPONENT_NODE TOP_INFO="0"> <COMPONENT_NODE TOP_INFO="0">
<COMPONENT_INFO NAME="Enum Editor" OWNER="DataTypeManagerPlugin" TITLE="enum (notepad.exe)" ACTIVE="false" GROUP="" INSTANCE_ID="2736721927148945968" /> <COMPONENT_INFO NAME="Enum Editor" OWNER="DataTypeManagerPlugin" TITLE="enum " ACTIVE="false" GROUP="" INSTANCE_ID="2736721927148945968" />
</COMPONENT_NODE> </COMPONENT_NODE>
</WINDOW_NODE> </WINDOW_NODE>
<WINDOW_NODE X_POS="385" Y_POS="260" WIDTH="488" HEIGHT="350"> <WINDOW_NODE X_POS="385" Y_POS="260" WIDTH="488" HEIGHT="350">
<COMPONENT_NODE TOP_INFO="0"> <COMPONENT_NODE TOP_INFO="0">
<COMPONENT_INFO NAME="Union Editor" OWNER="DataTypeManagerPlugin" TITLE="Union Editor: union (calc.exe)" ACTIVE="false" GROUP="" INSTANCE_ID="2650648146038267899" /> <COMPONENT_INFO NAME="Union Editor" OWNER="DataTypeManagerPlugin" TITLE="Union Editor: union (x.exe)" ACTIVE="false" GROUP="" INSTANCE_ID="2650648146038267899" />
</COMPONENT_NODE> </COMPONENT_NODE>
</WINDOW_NODE> </WINDOW_NODE>
<WINDOW_NODE X_POS="-752" Y_POS="237" WIDTH="753" HEIGHT="554"> <WINDOW_NODE X_POS="-752" Y_POS="237" WIDTH="753" HEIGHT="554">
<COMPONENT_NODE TOP_INFO="0"> <COMPONENT_NODE TOP_INFO="0">
<COMPONENT_INFO NAME="Bytes" OWNER="ByteViewerPlugin" TITLE="Bytes : notepad.exe.2" ACTIVE="false" GROUP="" INSTANCE_ID="2777482860928188968" /> <COMPONENT_INFO NAME="Bytes" OWNER="ByteViewerPlugin" TITLE="Bytes : " ACTIVE="false" GROUP="" INSTANCE_ID="2777482860928188968" />
</COMPONENT_NODE> </COMPONENT_NODE>
</WINDOW_NODE> </WINDOW_NODE>
<WINDOW_NODE X_POS="443" Y_POS="383" WIDTH="477" HEIGHT="374"> <WINDOW_NODE X_POS="443" Y_POS="383" WIDTH="477" HEIGHT="374">

View file

@ -34,18 +34,18 @@
<SPLIT_NODE WIDTH="1475" HEIGHT="551" DIVIDER_LOCATION="632" ORIENTATION="HORIZONTAL"> <SPLIT_NODE WIDTH="1475" HEIGHT="551" DIVIDER_LOCATION="632" ORIENTATION="HORIZONTAL">
<SPLIT_NODE WIDTH="950" HEIGHT="985" DIVIDER_LOCATION="894" ORIENTATION="HORIZONTAL"> <SPLIT_NODE WIDTH="950" HEIGHT="985" DIVIDER_LOCATION="894" ORIENTATION="HORIZONTAL">
<COMPONENT_NODE TOP_INFO="0"> <COMPONENT_NODE TOP_INFO="0">
<COMPONENT_INFO NAME="Listing" OWNER="CodeBrowserPlugin" TITLE="Listing: WallaceSrc" ACTIVE="true" GROUP="" INSTANCE_ID="2769903913093334120" /> <COMPONENT_INFO NAME="Listing" OWNER="CodeBrowserPlugin" TITLE="Listing" ACTIVE="true" GROUP="Core" INSTANCE_ID="2769903913093334120" />
<COMPONENT_INFO NAME="Test Listing" OWNER="Test Listing" TITLE="Test Listing" ACTIVE="false" GROUP="" INSTANCE_ID="2733632016422332683" /> <COMPONENT_INFO NAME="Test Listing" OWNER="Test Listing" TITLE="Test Listing" ACTIVE="false" GROUP="" INSTANCE_ID="2733632016422332683" />
</COMPONENT_NODE> </COMPONENT_NODE>
<COMPONENT_NODE TOP_INFO="0"> <COMPONENT_NODE TOP_INFO="0">
<COMPONENT_INFO NAME="Listing" OWNER="CodeBrowserPlugin" TITLE="[Listing: notepad.exe]" ACTIVE="false" GROUP="disconnected" INSTANCE_ID="2738354194536315852" /> <COMPONENT_INFO NAME="Listing" OWNER="CodeBrowserPlugin" TITLE="Listing" ACTIVE="false" GROUP="disconnected" INSTANCE_ID="2738354194536315852" />
</COMPONENT_NODE> </COMPONENT_NODE>
</SPLIT_NODE> </SPLIT_NODE>
<SPLIT_NODE WIDTH="355" HEIGHT="1097" DIVIDER_LOCATION="542" ORIENTATION="VERTICAL"> <SPLIT_NODE WIDTH="355" HEIGHT="1097" DIVIDER_LOCATION="542" ORIENTATION="VERTICAL">
<SPLIT_NODE WIDTH="541" HEIGHT="551" DIVIDER_LOCATION="546" ORIENTATION="VERTICAL"> <SPLIT_NODE WIDTH="541" HEIGHT="551" DIVIDER_LOCATION="546" ORIENTATION="VERTICAL">
<SPLIT_NODE WIDTH="541" HEIGHT="551" DIVIDER_LOCATION="546" ORIENTATION="VERTICAL"> <SPLIT_NODE WIDTH="541" HEIGHT="551" DIVIDER_LOCATION="546" ORIENTATION="VERTICAL">
<COMPONENT_NODE TOP_INFO="0"> <COMPONENT_NODE TOP_INFO="0">
<COMPONENT_INFO NAME="Decompiler" OWNER="DecompilePlugin" TITLE="Decompile: Gadget" ACTIVE="true" GROUP="" INSTANCE_ID="2769903892362986615" /> <COMPONENT_INFO NAME="Decompiler" OWNER="DecompilePlugin" TITLE="Decompile" ACTIVE="true" GROUP="" INSTANCE_ID="2769903892362986615" />
</COMPONENT_NODE> </COMPONENT_NODE>
<COMPONENT_NODE TOP_INFO="0"> <COMPONENT_NODE TOP_INFO="0">
<COMPONENT_INFO NAME="Version Tracking Matches for Source" OWNER="VTPlugin" TITLE="Version Tracking Matches for Source" ACTIVE="true" GROUP="VTResults" INSTANCE_ID="2769903913525347441" /> <COMPONENT_INFO NAME="Version Tracking Matches for Source" OWNER="VTPlugin" TITLE="Version Tracking Matches for Source" ACTIVE="true" GROUP="VTResults" INSTANCE_ID="2769903913525347441" />
@ -61,8 +61,7 @@
</SPLIT_NODE> </SPLIT_NODE>
</SPLIT_NODE> </SPLIT_NODE>
<COMPONENT_NODE TOP_INFO="0"> <COMPONENT_NODE TOP_INFO="0">
<COMPONENT_INFO NAME="Decompiler" OWNER="Decompiler" TITLE="Decompile" ACTIVE="false" GROUP="" INSTANCE_ID="0" /> <COMPONENT_INFO NAME="References from " OWNER="ReferencesPlugin" TITLE="References" ACTIVE="false" GROUP="" INSTANCE_ID="0" />
<COMPONENT_INFO NAME="References from " OWNER="ReferencesPlugin" TITLE="References from notepad.exe@01001004" ACTIVE="false" GROUP="" INSTANCE_ID="0" />
<COMPONENT_INFO NAME="Label References Provider" OWNER="LocationReferencesPlugin" TITLE="References to s__0100a000" ACTIVE="false" GROUP="" INSTANCE_ID="2738698427373098901" /> <COMPONENT_INFO NAME="Label References Provider" OWNER="LocationReferencesPlugin" TITLE="References to s__0100a000" ACTIVE="false" GROUP="" INSTANCE_ID="2738698427373098901" />
</COMPONENT_NODE> </COMPONENT_NODE>
</SPLIT_NODE> </SPLIT_NODE>
@ -191,7 +190,7 @@
</WINDOW_NODE> </WINDOW_NODE>
<WINDOW_NODE X_POS="454" Y_POS="326" WIDTH="464" HEIGHT="513"> <WINDOW_NODE X_POS="454" Y_POS="326" WIDTH="464" HEIGHT="513">
<COMPONENT_NODE TOP_INFO="0"> <COMPONENT_NODE TOP_INFO="0">
<COMPONENT_INFO NAME="References Editor " OWNER="ReferencesPlugin" TITLE="References Editor @ 0100566d (notepad.exe)" ACTIVE="false" GROUP="" INSTANCE_ID="2738698427373098900" /> <COMPONENT_INFO NAME="References Editor " OWNER="ReferencesPlugin" TITLE="References Editor" ACTIVE="false" GROUP="" INSTANCE_ID="2738698427373098900" />
</COMPONENT_NODE> </COMPONENT_NODE>
</WINDOW_NODE> </WINDOW_NODE>
<WINDOW_NODE X_POS="0" Y_POS="0" WIDTH="0" HEIGHT="0"> <WINDOW_NODE X_POS="0" Y_POS="0" WIDTH="0" HEIGHT="0">
@ -201,7 +200,7 @@
</WINDOW_NODE> </WINDOW_NODE>
<WINDOW_NODE X_POS="397" Y_POS="209" WIDTH="597" HEIGHT="433"> <WINDOW_NODE X_POS="397" Y_POS="209" WIDTH="597" HEIGHT="433">
<COMPONENT_NODE TOP_INFO="0"> <COMPONENT_NODE TOP_INFO="0">
<COMPONENT_INFO NAME="Structure Editor" OWNER="DataTypeManagerPlugin" TITLE="Structure Editor: IMAGE_RESOURCE_DIRECTORY_ENTRY_NameStruct.conflict (notepad.exe)" ACTIVE="false" GROUP="" INSTANCE_ID="2739607423924196364" /> <COMPONENT_INFO NAME="Structure Editor" OWNER="DataTypeManagerPlugin" TITLE="Structure Editor" ACTIVE="false" GROUP="" INSTANCE_ID="2739607423924196364" />
</COMPONENT_NODE> </COMPONENT_NODE>
</WINDOW_NODE> </WINDOW_NODE>
<WINDOW_NODE X_POS="171" Y_POS="155" WIDTH="940" HEIGHT="642"> <WINDOW_NODE X_POS="171" Y_POS="155" WIDTH="940" HEIGHT="642">
@ -216,17 +215,17 @@
</WINDOW_NODE> </WINDOW_NODE>
<WINDOW_NODE X_POS="-1287" Y_POS="271" WIDTH="1069" HEIGHT="677"> <WINDOW_NODE X_POS="-1287" Y_POS="271" WIDTH="1069" HEIGHT="677">
<COMPONENT_NODE TOP_INFO="0"> <COMPONENT_NODE TOP_INFO="0">
<COMPONENT_INFO NAME="Enum Editor" OWNER="DataTypeManagerPlugin" TITLE="enum (notepad.exe)" ACTIVE="false" GROUP="" INSTANCE_ID="2736721927148945968" /> <COMPONENT_INFO NAME="Enum Editor" OWNER="DataTypeManagerPlugin" TITLE="enum " ACTIVE="false" GROUP="" INSTANCE_ID="2736721927148945968" />
</COMPONENT_NODE> </COMPONENT_NODE>
</WINDOW_NODE> </WINDOW_NODE>
<WINDOW_NODE X_POS="385" Y_POS="260" WIDTH="488" HEIGHT="350"> <WINDOW_NODE X_POS="385" Y_POS="260" WIDTH="488" HEIGHT="350">
<COMPONENT_NODE TOP_INFO="0"> <COMPONENT_NODE TOP_INFO="0">
<COMPONENT_INFO NAME="Union Editor" OWNER="DataTypeManagerPlugin" TITLE="Union Editor: union (calc.exe)" ACTIVE="false" GROUP="" INSTANCE_ID="2650648146038267899" /> <COMPONENT_INFO NAME="Union Editor" OWNER="DataTypeManagerPlugin" TITLE="Union Editor: union" ACTIVE="false" GROUP="" INSTANCE_ID="2650648146038267899" />
</COMPONENT_NODE> </COMPONENT_NODE>
</WINDOW_NODE> </WINDOW_NODE>
<WINDOW_NODE X_POS="-752" Y_POS="237" WIDTH="753" HEIGHT="554"> <WINDOW_NODE X_POS="-752" Y_POS="237" WIDTH="753" HEIGHT="554">
<COMPONENT_NODE TOP_INFO="0"> <COMPONENT_NODE TOP_INFO="0">
<COMPONENT_INFO NAME="Bytes" OWNER="ByteViewerPlugin" TITLE="Bytes : WallaceSrc" ACTIVE="false" GROUP="" INSTANCE_ID="2769903913093334115" /> <COMPONENT_INFO NAME="Bytes" OWNER="ByteViewerPlugin" TITLE="Bytes" ACTIVE="false" GROUP="" INSTANCE_ID="2769903913093334115" />
</COMPONENT_NODE> </COMPONENT_NODE>
</WINDOW_NODE> </WINDOW_NODE>
<WINDOW_NODE X_POS="443" Y_POS="383" WIDTH="477" HEIGHT="374"> <WINDOW_NODE X_POS="443" Y_POS="383" WIDTH="477" HEIGHT="374">

View file

@ -931,17 +931,19 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
* @param rootXMLElement JDOM element from which to extract the state information. * @param rootXMLElement JDOM element from which to extract the state information.
*/ */
public void restoreFromXML(Element rootXMLElement) { public void restoreFromXML(Element rootXMLElement) {
Element rootNodeElement = rootXMLElement.getChild(RootNode.ROOT_NODE_ELEMENT_NAME); restoreWindowDataFromXml(rootXMLElement);
restoreWindowDataFromXml(rootNodeElement);
// load the tool preferences
restorePreferencesFromXML(rootXMLElement); restorePreferencesFromXML(rootXMLElement);
} }
/** /**
* Restore to the docking window manager the layout and positioning information from XML. * Restore to the docking window manager the layout and positioning information from XML.
* @param windowData The XML element containing the above information. * @param rootXMLElement JDOM element from which to extract the state information.
*/ */
public void restoreWindowDataFromXml(Element windowData) { public void restoreWindowDataFromXml(Element rootXMLElement) {
Element windowData = rootXMLElement.getChild(RootNode.ROOT_NODE_ELEMENT_NAME);
if (windowData == null) {
return;
}
// //
// Clear our focus history, as we are changing placeholders' providers, so the old focus // Clear our focus history, as we are changing placeholders' providers, so the old focus
// is no longer relevant. // is no longer relevant.
@ -1576,7 +1578,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
return toolPreferencesElement; return toolPreferencesElement;
} }
private void restorePreferencesFromXML(Element rootElement) { public void restorePreferencesFromXML(Element rootElement) {
Element toolPreferencesElement = rootElement.getChild(TOOL_PREFERENCES_XML_NAME); Element toolPreferencesElement = rootElement.getChild(TOOL_PREFERENCES_XML_NAME);
if (toolPreferencesElement == null) { if (toolPreferencesElement == null) {
return; return;

View file

@ -1128,9 +1128,13 @@ public class GraphComponent<V extends VisualVertex, E extends VisualEdge<V>, G e
private V selectedVertex; private V selectedVertex;
@SuppressWarnings("deprecation") // deprecated until we fix the checkModifiers() code
public VertexClickMousePlugin() { public VertexClickMousePlugin() {
super(InputEvent.BUTTON1_MASK); super(InputEvent.BUTTON1_DOWN_MASK);
}
@Override
public boolean checkModifiers(MouseEvent e) {
return e.getModifiersEx() == modifiers;
} }
@Override @Override
@ -1203,6 +1207,5 @@ public class GraphComponent<V extends VisualVertex, E extends VisualEdge<V>, G e
public void mouseExited(MouseEvent e) { public void mouseExited(MouseEvent e) {
// stub // stub
} }
} }
} }

View file

@ -16,13 +16,13 @@
package ghidra.graph.viewer; package ghidra.graph.viewer;
import java.awt.*; import java.awt.*;
import java.awt.event.*; import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Point2D; import java.awt.geom.Point2D;
import java.util.function.Consumer; import java.util.function.Consumer;
import javax.swing.*; import javax.swing.*;
import docking.widgets.PopupWindow;
import edu.uci.ics.jung.algorithms.layout.Layout; import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.visualization.VisualizationViewer; import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.picking.MultiPickedState; import edu.uci.ics.jung.visualization.picking.MultiPickedState;
@ -35,6 +35,7 @@ import ghidra.graph.viewer.event.mouse.*;
import ghidra.graph.viewer.event.picking.GPickedState; import ghidra.graph.viewer.event.picking.GPickedState;
import ghidra.graph.viewer.layout.VisualGraphLayout; import ghidra.graph.viewer.layout.VisualGraphLayout;
import ghidra.graph.viewer.options.VisualGraphOptions; import ghidra.graph.viewer.options.VisualGraphOptions;
import ghidra.graph.viewer.popup.*;
import ghidra.graph.viewer.renderer.VisualGraphRenderer; import ghidra.graph.viewer.renderer.VisualGraphRenderer;
import ghidra.util.layout.PairLayout; import ghidra.util.layout.PairLayout;
@ -62,9 +63,7 @@ public class GraphViewer<V extends VisualVertex, E extends VisualEdge<V>>
private Consumer<GraphViewer<V, E>> initializedListener; private Consumer<GraphViewer<V, E>> initializedListener;
private PopupRegulator popupRegulator = new PopupRegulator(); private PopupRegulator<V, E> popupRegulator;
private PopupWindow popupWindow;
private boolean showPopups = true;
private VertexTooltipProvider<V, E> vertexTooltipProvider = new DummyTooltipProvider(); private VertexTooltipProvider<V, E> vertexTooltipProvider = new DummyTooltipProvider();
protected VisualGraphOptions options; protected VisualGraphOptions options;
@ -88,6 +87,8 @@ public class GraphViewer<V extends VisualVertex, E extends VisualEdge<V>>
PickedState<V> pickedState = getPickedVertexState(); PickedState<V> pickedState = getPickedVertexState();
gPickedState = new GPickedState<>((MultiPickedState<V>) pickedState); gPickedState = new GPickedState<>((MultiPickedState<V>) pickedState);
setPickedVertexState(gPickedState); setPickedVertexState(gPickedState);
popupRegulator = new PopupRegulator<V, E>(new GraphViewerPopupSource());
} }
private void buildUpdater() { private void buildUpdater() {
@ -271,23 +272,16 @@ public class GraphViewer<V extends VisualVertex, E extends VisualEdge<V>>
// Popups and Tooltips // Popups and Tooltips
//================================================================================================== //==================================================================================================
public void setPopupsVisible(boolean visible) { /*package*/ void setPopupDelay(int delayMs) {
this.showPopups = visible; popupRegulator.setPopupDelay(delayMs);
if (!showPopups) {
hidePopupTooltips();
} }
public void setPopupsVisible(boolean visible) {
popupRegulator.setPopupsVisible(visible);
} }
/*package*/ boolean isPopupShowing() { /*package*/ boolean isPopupShowing() {
return popupWindow != null && popupWindow.isShowing(); return popupRegulator.isPopupShowing();
}
private void hidePopupTooltips() {
if (popupWindow != null && popupWindow.isShowing()) {
popupWindow.hide();
// don't call dispose, or we don't get our componentHidden() callback
// popupWindow.dispose();
}
} }
@Override @Override
@ -317,49 +311,11 @@ public class GraphViewer<V extends VisualVertex, E extends VisualEdge<V>>
return new VertexToolTipInfo(vertex, event); return new VertexToolTipInfo(vertex, event);
} }
private void showTooltip(ToolTipInfo<?> info) {
JComponent tipComponent = info.getToolTipComponent();
if (tipComponent == null) {
return;
}
MouseEvent event = info.getMouseEvent();
showPopupWindow(event, tipComponent);
}
private void showPopupWindow(MouseEvent event, JComponent component) {
MenuSelectionManager menuManager = MenuSelectionManager.defaultManager();
if (menuManager.getSelectedPath().length != 0) {
return;
}
Window parentWindow = WindowUtilities.windowForComponent(this);
popupWindow = new PopupWindow(parentWindow, component);
popupWindow.addComponentListener(new ComponentAdapter() {
@Override
public void componentShown(ComponentEvent e) {
popupRegulator.popupShown();
}
@Override
public void componentHidden(ComponentEvent e) {
popupRegulator.popupHidden();
}
});
popupWindow.showPopup(event);
}
public VertexMouseInfo<V, E> createVertexMouseInfo(MouseEvent e, V v, public VertexMouseInfo<V, E> createVertexMouseInfo(MouseEvent e, V v,
Point2D vertexBasedClickPoint) { Point2D vertexBasedClickPoint) {
return new VertexMouseInfo<>(e, v, vertexBasedClickPoint, this); return new VertexMouseInfo<>(e, v, vertexBasedClickPoint, this);
} }
/*package*/ void setPopupDelay(int delayMs) {
popupRegulator.setPopupDelay(delayMs);
}
public void dispose() { public void dispose() {
viewUpdater.dispose(); viewUpdater.dispose();
@ -369,168 +325,50 @@ public class GraphViewer<V extends VisualVertex, E extends VisualEdge<V>>
removeAll(); removeAll();
} }
private GraphViewer<V, E> viewer() {
return GraphViewer.this;
}
//================================================================================================== //==================================================================================================
// Inner Classes // Inner Classes
//================================================================================================== //==================================================================================================
private class PopupRegulator { private class GraphViewerPopupSource implements PopupSource<V, E> {
private int popupDelay = 1000;
/**
* We need this timer because the default mechanism for triggering popups doesn't
* always work. We use this timer in conjunction with a mouse motion listener to
* get the results we want.
*/
private Timer popupTimer;
private MouseEvent popupMouseEvent;
/** the current target (vertex or edge) of a popup window */
private Object nextPopupTarget;
/**
* This value is not null when the user moves the cursor over a target for which a
* popup is already showing. We use this value to prevent showing a popup multiple times
* while over a single node.
*/
private Object lastShownPopupTarget;
/** The tooltip info used when showing the popup */
private ToolTipInfo<?> currentToolTipInfo;
PopupRegulator() {
popupTimer = new Timer(popupDelay, e -> {
if (isPopupShowing()) {
return; // don't show any new popups while the user is perusing
}
showPopupForMouseEvent(popupMouseEvent);
});
popupTimer.setRepeats(false);
addMouseMotionListener(new MouseMotionListener() {
@Override @Override
public void mouseDragged(MouseEvent e) { public ToolTipInfo<?> getToolTipInfo(MouseEvent event) {
hidePopupTooltips(); return viewer().getToolTipInfo(event);
popupTimer.stop();
popupMouseEvent = null; // clear any queued popups
} }
@Override @Override
public void mouseMoved(MouseEvent e) { public V getVertex(MouseEvent event) {
popupMouseEvent = e;
// this clears out the current last popup shown so that the user can
// move off and on a node to re-show the popup
savePopupTarget(e);
// make sure the popup gets triggered eventually
popupTimer.restart();
}
});
}
void setPopupDelay(int delayMs) {
popupTimer.stop();
popupTimer.setDelay(delayMs);
popupTimer.setInitialDelay(delayMs);
popupDelay = delayMs;
}
private void showPopupForMouseEvent(MouseEvent event) {
if (!showPopups) {
return;
}
if (event == null) {
return;
}
ToolTipInfo<?> toolTipInfo = getToolTipInfo(event);
JComponent toolTipComponent = toolTipInfo.getToolTipComponent();
boolean isCustomJavaTooltip = !(toolTipComponent instanceof JToolTip);
if (lastShownPopupTarget == nextPopupTarget && isCustomJavaTooltip) {
//
// Kinda Hacky:
// We don't show repeated popups for the same item (the user has to move away
// and then come back to re-show the popup). However, one caveat to this is that
// we do want to allow the user to see popups for the toolbar actions always. So,
// only return here if we have already shown a popup for the item *and* we are
// using a custom tooltip (which is used to show a vertex tooltip or an edge
// tooltip)
return;
}
currentToolTipInfo = toolTipInfo;
showTooltip(currentToolTipInfo);
}
void popupShown() {
lastShownPopupTarget = nextPopupTarget;
currentToolTipInfo.emphasize();
repaint();
}
void popupHidden() {
currentToolTipInfo.deEmphasize();
repaint();
}
private void savePopupTarget(MouseEvent event) {
nextPopupTarget = null;
V vertex = getVertexForEvent(event);
if (vertex != null) {
nextPopupTarget = vertex;
}
else {
E edge = getEdgeForEvent(event);
nextPopupTarget = edge;
}
if (nextPopupTarget == null) {
// We've moved off of a target. We will clear that last target so the user can
// mouse off of a vertex and back on in order to trigger a new popup
lastShownPopupTarget = null;
}
}
private V getVertexForEvent(MouseEvent event) {
Layout<V, E> viewerLayout = getGraphLayout(); Layout<V, E> viewerLayout = getGraphLayout();
Point p = event.getPoint(); Point p = event.getPoint();
return getPickSupport().getVertex(viewerLayout, p.getX(), p.getY()); return getPickSupport().getVertex(viewerLayout, p.getX(), p.getY());
} }
private E getEdgeForEvent(MouseEvent event) { @Override
public E getEdge(MouseEvent event) {
Layout<V, E> viewerLayout = getGraphLayout(); Layout<V, E> viewerLayout = getGraphLayout();
Point p = event.getPoint(); Point p = event.getPoint();
return getPickSupport().getEdge(viewerLayout, p.getX(), p.getY()); return getPickSupport().getEdge(viewerLayout, p.getX(), p.getY());
} }
@Override
public void addMouseMotionListener(MouseMotionListener l) {
viewer().addMouseMotionListener(l);
} }
/** Basic container object that knows how to generate tooltips */ @Override
private abstract class ToolTipInfo<T> { public void repaint() {
protected final MouseEvent event; viewer().repaint();
protected final T graphObject;
private JComponent tooltipComponent;
ToolTipInfo(MouseEvent event, T t) {
this.event = event;
this.graphObject = t;
tooltipComponent = createToolTipComponent(t);
} }
protected abstract JComponent createToolTipComponent(T t); @Override
public Window getPopupParent() {
protected abstract void emphasize(); return WindowUtilities.windowForComponent(viewer());
protected abstract void deEmphasize();
MouseEvent getMouseEvent() {
return event;
} }
JComponent getToolTipComponent() {
return tooltipComponent;
}
} }
private class VertexToolTipInfo extends ToolTipInfo<V> { private class VertexToolTipInfo extends ToolTipInfo<V> {
@ -540,19 +378,20 @@ public class GraphViewer<V extends VisualVertex, E extends VisualEdge<V>>
} }
@Override @Override
public JComponent createToolTipComponent(V vertex) { public JComponent createToolTipComponent() {
if (vertex == null) { if (graphObject == null) {
return null; return null;
} }
if (isScaledPastInteractionThreshold()) { if (isScaledPastInteractionThreshold()) {
return vertexTooltipProvider.getTooltip(vertex); return vertexTooltipProvider.getTooltip(graphObject);
} }
VertexMouseInfo<V, E> mouseInfo = VertexMouseInfo<V, E> mouseInfo =
GraphViewerUtils.convertMouseEventToVertexMouseEvent(GraphViewer.this, event); GraphViewerUtils.convertMouseEventToVertexMouseEvent(GraphViewer.this, event);
MouseEvent translatedMouseEvent = mouseInfo.getTranslatedMouseEvent(); MouseEvent translatedMouseEvent = mouseInfo.getTranslatedMouseEvent();
String toolTip = vertexTooltipProvider.getTooltipText(vertex, translatedMouseEvent); String toolTip =
vertexTooltipProvider.getTooltipText(graphObject, translatedMouseEvent);
if (toolTip == null) { if (toolTip == null) {
return null; return null;
} }
@ -591,15 +430,15 @@ public class GraphViewer<V extends VisualVertex, E extends VisualEdge<V>>
} }
@Override @Override
public JComponent createToolTipComponent(E edge) { public JComponent createToolTipComponent() {
if (edge == null) { if (graphObject == null) {
return null; return null;
} }
V start = edge.getStart(); V start = graphObject.getStart();
V end = edge.getEnd(); V end = graphObject.getEnd();
JComponent startComponent = vertexTooltipProvider.getTooltip(start, edge); JComponent startComponent = vertexTooltipProvider.getTooltip(start, graphObject);
if (startComponent == null) { if (startComponent == null) {
return null; return null;
} }
@ -611,7 +450,7 @@ public class GraphViewer<V extends VisualVertex, E extends VisualEdge<V>>
return component; return component;
} }
JComponent endComponent = vertexTooltipProvider.getTooltip(end, edge); JComponent endComponent = vertexTooltipProvider.getTooltip(end, graphObject);
if (endComponent == null) { if (endComponent == null) {
return null; return null;
} }

View file

@ -32,6 +32,9 @@ import ghidra.graph.viewer.*;
* <li>We clear state on mouseReleased() and mouseExited(), since we will get * <li>We clear state on mouseReleased() and mouseExited(), since we will get
* at least one of those calls</li> * at least one of those calls</li>
* </ul> * </ul>
*
* @param <V> the vertex type
* @param <E> the edge type
*/ */
//@formatter:off //@formatter:off
public abstract class VisualGraphAbstractGraphMousePlugin<V extends VisualVertex, public abstract class VisualGraphAbstractGraphMousePlugin<V extends VisualVertex,
@ -46,13 +49,18 @@ public abstract class VisualGraphAbstractGraphMousePlugin<V extends VisualVertex
protected E selectedEdge; protected E selectedEdge;
public VisualGraphAbstractGraphMousePlugin() { public VisualGraphAbstractGraphMousePlugin() {
this(InputEvent.BUTTON1_MASK); this(InputEvent.BUTTON1_DOWN_MASK);
} }
public VisualGraphAbstractGraphMousePlugin(int selectionModifiers) { public VisualGraphAbstractGraphMousePlugin(int selectionModifiers) {
super(selectionModifiers); super(selectionModifiers);
} }
@Override
public boolean checkModifiers(MouseEvent e) {
return e.getModifiersEx() == modifiers;
}
protected boolean checkForVertex(MouseEvent e) { protected boolean checkForVertex(MouseEvent e) {
if (!checkModifiers(e)) { if (!checkModifiers(e)) {
selectedVertex = null; selectedVertex = null;

View file

@ -24,7 +24,10 @@ import edu.uci.ics.jung.visualization.control.AnimatedPickingGraphMousePlugin;
import ghidra.graph.viewer.*; import ghidra.graph.viewer.*;
/** /**
* A mouse handler to center a vertex when the header is double-clicked. * A mouse handler to center a vertex when the header is double-clicked
*
* @param <V> the vertex type
* @param <E> the edge type
*/ */
public class VisualGraphAnimatedPickingGraphMousePlugin<V extends VisualVertex, E extends VisualEdge<V>> public class VisualGraphAnimatedPickingGraphMousePlugin<V extends VisualVertex, E extends VisualEdge<V>>
extends AnimatedPickingGraphMousePlugin<V, E> implements VisualGraphMousePlugin<V, E> { extends AnimatedPickingGraphMousePlugin<V, E> implements VisualGraphMousePlugin<V, E> {
@ -32,7 +35,7 @@ public class VisualGraphAnimatedPickingGraphMousePlugin<V extends VisualVertex,
private boolean isHandlingMouseEvents; private boolean isHandlingMouseEvents;
public VisualGraphAnimatedPickingGraphMousePlugin() { public VisualGraphAnimatedPickingGraphMousePlugin() {
super(InputEvent.BUTTON1_MASK); super(InputEvent.BUTTON1_DOWN_MASK);
this.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); this.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
} }

View file

@ -29,10 +29,17 @@ public class VisualGraphCursorRestoringGraphMousePlugin<V, E> extends AbstractGr
super(0); super(0);
} }
@Override
public boolean checkModifiers(MouseEvent e) {
return e.getModifiersEx() == modifiers;
}
@Override
public void mouseDragged(MouseEvent e) { public void mouseDragged(MouseEvent e) {
// don't care // don't care
} }
@Override
public void mouseMoved(MouseEvent e) { public void mouseMoved(MouseEvent e) {
installCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR), e); installCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR), e);
} }

View file

@ -38,9 +38,9 @@ public class VisualGraphEventForwardingGraphMousePlugin<V extends VisualVertex,
private boolean isHandlingEvent = false; private boolean isHandlingEvent = false;
// TODO for deprecated usage note, see the VisualGraphMousePlugin interface
public VisualGraphEventForwardingGraphMousePlugin() { public VisualGraphEventForwardingGraphMousePlugin() {
this(InputEvent.BUTTON1_MASK | InputEvent.BUTTON2_MASK | InputEvent.BUTTON3_MASK); this(InputEvent.BUTTON1_DOWN_MASK | InputEvent.BUTTON2_DOWN_MASK |
InputEvent.BUTTON3_DOWN_MASK);
} }
public VisualGraphEventForwardingGraphMousePlugin(int modifiers) { public VisualGraphEventForwardingGraphMousePlugin(int modifiers) {
@ -50,18 +50,18 @@ public class VisualGraphEventForwardingGraphMousePlugin<V extends VisualVertex,
@Override @Override
public boolean checkModifiers(MouseEvent e) { public boolean checkModifiers(MouseEvent e) {
int eventModifiers = e.getModifiers(); int eventModifiers = e.getModifiersEx();
eventModifiers = turnOffControlKey(eventModifiers); eventModifiers = turnOffControlKey(eventModifiers);
return ((eventModifiers & getModifiers()) == eventModifiers); return ((eventModifiers & getModifiers()) == eventModifiers);
} }
private int turnOffControlKey(int eventModifiers) { private int turnOffControlKey(int eventModifiers) {
return eventModifiers & (~DockingUtils.CONTROL_KEY_MODIFIER_MASK_DEPRECATED); return eventModifiers & (~DockingUtils.CONTROL_KEY_MODIFIER_MASK);
} }
private boolean isControlClick(MouseEvent e) { private boolean isControlClick(MouseEvent e) {
int allModifiers = e.getModifiers(); int allModifiers = e.getModifiersEx();
int osSpecificMask = DockingUtils.CONTROL_KEY_MODIFIER_MASK_DEPRECATED; int osSpecificMask = DockingUtils.CONTROL_KEY_MODIFIER_MASK;
return (allModifiers & osSpecificMask) == osSpecificMask; return (allModifiers & osSpecificMask) == osSpecificMask;
// can't use this until we fix the old modifiers usage // can't use this until we fix the old modifiers usage

View file

@ -61,6 +61,11 @@ public class VisualGraphHoverMousePlugin<V extends VisualVertex, E extends Visua
this.otherViewer = otherViewer; this.otherViewer = otherViewer;
} }
@Override
public boolean checkModifiers(MouseEvent e) {
return e.getModifiersEx() == modifiers;
}
@Override @Override
public void mouseMoved(MouseEvent e) { public void mouseMoved(MouseEvent e) {
lastMouseEvent = e; lastMouseEvent = e;

View file

@ -28,7 +28,6 @@ import edu.uci.ics.jung.visualization.transform.MutableTransformer;
import ghidra.graph.viewer.*; import ghidra.graph.viewer.*;
import ghidra.graph.viewer.renderer.*; import ghidra.graph.viewer.renderer.*;
/** /**
* A simple plugin that allows clients to be notified of mouse events before any of the other * A simple plugin that allows clients to be notified of mouse events before any of the other
* mouse plugins. * mouse plugins.
@ -52,14 +51,15 @@ public class VisualGraphMouseTrackingGraphMousePlugin<V extends VisualVertex,
private int mouseMovedCount; private int mouseMovedCount;
public VisualGraphMouseTrackingGraphMousePlugin(GraphViewer<V, E> viewer) { public VisualGraphMouseTrackingGraphMousePlugin(GraphViewer<V, E> viewer) {
super(InputEvent.BUTTON1_MASK | InputEvent.BUTTON2_MASK | InputEvent.BUTTON3_MASK); super(InputEvent.BUTTON1_DOWN_MASK | InputEvent.BUTTON2_DOWN_MASK |
InputEvent.BUTTON3_DOWN_MASK);
this.viewer = Objects.requireNonNull(viewer); this.viewer = Objects.requireNonNull(viewer);
viewer.addPostRenderPaintable(paintable); viewer.addPostRenderPaintable(paintable);
} }
@Override @Override
public boolean checkModifiers(MouseEvent e) { public boolean checkModifiers(MouseEvent e) {
int eventModifiers = e.getModifiers(); int eventModifiers = e.getModifiersEx();
eventModifiers = turnOffControlKey(eventModifiers); eventModifiers = turnOffControlKey(eventModifiers);
return ((eventModifiers & getModifiers()) == eventModifiers); return ((eventModifiers & getModifiers()) == eventModifiers);
} }

View file

@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -39,18 +38,17 @@ public class VisualGraphPickingGraphMousePlugin<V extends VisualVertex, E extend
// ALERT: -this class was created because mouseDragged() has a bug that generates a NPE // ALERT: -this class was created because mouseDragged() has a bug that generates a NPE
// -also, mousePressed() has a bug in that it does not check the modifiers when the method is entered // -also, mousePressed() has a bug in that it does not check the modifiers when the method is entered
// TODO for deprecated usage note, see the VisualGraphMousePlugin interface
public VisualGraphPickingGraphMousePlugin() { public VisualGraphPickingGraphMousePlugin() {
super(InputEvent.BUTTON1_MASK, super(InputEvent.BUTTON1_DOWN_MASK,
InputEvent.BUTTON1_MASK | DockingUtils.CONTROL_KEY_MODIFIER_MASK_DEPRECATED); InputEvent.BUTTON1_DOWN_MASK | DockingUtils.CONTROL_KEY_MODIFIER_MASK);
} }
@Override @Override
public boolean checkModifiers(MouseEvent e) { public boolean checkModifiers(MouseEvent e) {
if (e.getModifiers() == addToSelectionModifiers) { if (e.getModifiersEx() == addToSelectionModifiers) {
return true; return true;
} }
return super.checkModifiers(e); return e.getModifiersEx() == modifiers;
} }
@Override @Override
@ -80,7 +78,7 @@ public class VisualGraphPickingGraphMousePlugin<V extends VisualVertex, E extend
private void increaseDragRectangle(MouseEvent e) { private void increaseDragRectangle(MouseEvent e) {
Point2D out = e.getPoint(); Point2D out = e.getPoint();
int theModifiers = e.getModifiers(); int theModifiers = e.getModifiersEx();
if (theModifiers == addToSelectionModifiers || theModifiers == modifiers) { if (theModifiers == addToSelectionModifiers || theModifiers == modifiers) {
if (down != null) { if (down != null) {
rect.setFrameFromDiagonal(down, out); rect.setFrameFromDiagonal(down, out);

View file

@ -29,7 +29,7 @@ public abstract class VisualGraphSatelliteAbstractGraphMousePlugin<V extends Vis
extends VisualGraphAbstractGraphMousePlugin<V, E> { extends VisualGraphAbstractGraphMousePlugin<V, E> {
public VisualGraphSatelliteAbstractGraphMousePlugin() { public VisualGraphSatelliteAbstractGraphMousePlugin() {
this(InputEvent.BUTTON1_MASK); this(InputEvent.BUTTON1_DOWN_MASK);
} }
public VisualGraphSatelliteAbstractGraphMousePlugin(int selectionModifiers) { public VisualGraphSatelliteAbstractGraphMousePlugin(int selectionModifiers) {

View file

@ -1,25 +0,0 @@
/* ###
* 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.graph.viewer.event.mouse;
import ghidra.graph.viewer.VisualEdge;
import ghidra.graph.viewer.VisualVertex;
public class VisualGraphSatelliteAnimatedPickingGraphMousePlugin<V extends VisualVertex, E extends VisualEdge<V>>
extends VisualGraphAnimatedPickingGraphMousePlugin<V, E> {
// TODO - delete this class--it should not longer be needed
}

View file

@ -1,25 +0,0 @@
/* ###
* 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.graph.viewer.event.mouse;
import ghidra.graph.viewer.VisualEdge;
import ghidra.graph.viewer.VisualVertex;
public class VisualGraphSatelliteEdgeSelectionGraphMousePlugin<V extends VisualVertex, E extends VisualEdge<V>>
extends VisualGraphEdgeSelectionGraphMousePlugin<V, E> {
// TODO this class probably can be deleted now
}

View file

@ -25,7 +25,7 @@ public class VisualGraphSatelliteNavigationGraphMousePlugin<V extends VisualVert
extends VisualGraphSatelliteAbstractGraphMousePlugin<V, E> { extends VisualGraphSatelliteAbstractGraphMousePlugin<V, E> {
public VisualGraphSatelliteNavigationGraphMousePlugin() { public VisualGraphSatelliteNavigationGraphMousePlugin() {
super(InputEvent.BUTTON1_MASK); super(InputEvent.BUTTON1_DOWN_MASK);
} }
@Override @Override

View file

@ -46,7 +46,7 @@ public class VisualGraphSatelliteScalingGraphMousePlugin<V extends VisualVertex,
VisualGraphOptions options = viewer.getOptions(); VisualGraphOptions options = viewer.getOptions();
boolean scrollWheelPans = options.getScrollWheelPans(); boolean scrollWheelPans = options.getScrollWheelPans();
int scrollWheelModifierToggle = DockingUtils.CONTROL_KEY_MODIFIER_MASK; int scrollWheelModifierToggle = DockingUtils.CONTROL_KEY_MODIFIER_MASK;
int eventModifiers = e.getModifiers(); int eventModifiers = e.getModifiersEx();
if (scrollWheelPans) { if (scrollWheelPans) {
// scrolling will zoom if modified (unmodified in this case means to pan) // scrolling will zoom if modified (unmodified in this case means to pan)
return (scrollWheelModifierToggle & eventModifiers) == scrollWheelModifierToggle; return (scrollWheelModifierToggle & eventModifiers) == scrollWheelModifierToggle;

View file

@ -35,7 +35,7 @@ public class VisualGraphSatelliteTranslatingGraphMousePlugin<V extends VisualVer
// Note: for ideas on resizing instead of moving, see LensTranslatingGraphMousePlugin // Note: for ideas on resizing instead of moving, see LensTranslatingGraphMousePlugin
public VisualGraphSatelliteTranslatingGraphMousePlugin() { public VisualGraphSatelliteTranslatingGraphMousePlugin() {
super(InputEvent.BUTTON1_MASK); super(InputEvent.BUTTON1_DOWN_MASK);
this.cursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR); this.cursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR);
} }

View file

@ -25,6 +25,9 @@ import ghidra.graph.viewer.options.VisualGraphOptions;
/** /**
* Overridden implementation that allows us to change scaling behavior through options. This * Overridden implementation that allows us to change scaling behavior through options. This
* class works on the opposite modifier setup as FunctionGraphScrollWheelPanningPlugin. * class works on the opposite modifier setup as FunctionGraphScrollWheelPanningPlugin.
*
* @param <V> the vertex type
* @param <E> the edge type
*/ */
public class VisualGraphScalingGraphMousePlugin<V extends VisualVertex, E extends VisualEdge<V>> public class VisualGraphScalingGraphMousePlugin<V extends VisualVertex, E extends VisualEdge<V>>
extends ScalingGraphMousePlugin implements VisualGraphMousePlugin<V, E> { extends ScalingGraphMousePlugin implements VisualGraphMousePlugin<V, E> {
@ -51,8 +54,8 @@ public class VisualGraphScalingGraphMousePlugin<V extends VisualVertex, E extend
// TODO for deprecated usage note, see the VisualGraphMousePlugin interface // TODO for deprecated usage note, see the VisualGraphMousePlugin interface
VisualGraphOptions options = viewer.getOptions(); VisualGraphOptions options = viewer.getOptions();
boolean scrollWheelPans = options.getScrollWheelPans(); boolean scrollWheelPans = options.getScrollWheelPans();
int scrollWheelModifierToggle = DockingUtils.CONTROL_KEY_MODIFIER_MASK_DEPRECATED; int scrollWheelModifierToggle = DockingUtils.CONTROL_KEY_MODIFIER_MASK;
int eventModifiers = e.getModifiers(); int eventModifiers = e.getModifiersEx();
if (scrollWheelPans) { if (scrollWheelPans) {
// scrolling will zoom if modified (unmodified in this case means to pan) // scrolling will zoom if modified (unmodified in this case means to pan)
return (scrollWheelModifierToggle & eventModifiers) == scrollWheelModifierToggle; return (scrollWheelModifierToggle & eventModifiers) == scrollWheelModifierToggle;

View file

@ -16,8 +16,7 @@
package ghidra.graph.viewer.event.mouse; package ghidra.graph.viewer.event.mouse;
import java.awt.Point; import java.awt.Point;
import java.awt.event.MouseWheelEvent; import java.awt.event.*;
import java.awt.event.MouseWheelListener;
import docking.DockingUtils; import docking.DockingUtils;
import edu.uci.ics.jung.visualization.control.AbstractGraphMousePlugin; import edu.uci.ics.jung.visualization.control.AbstractGraphMousePlugin;
@ -31,9 +30,14 @@ public class VisualGraphScreenPositioningPlugin<V extends VisualVertex, E extend
super(0); super(0);
} }
@Override
public boolean checkModifiers(MouseEvent e) {
return e.getModifiersEx() == modifiers;
}
@Override @Override
public void mouseWheelMoved(MouseWheelEvent e) { public void mouseWheelMoved(MouseWheelEvent e) {
int eventModifiers = e.getModifiers(); int eventModifiers = e.getModifiersEx();
boolean controlKeyDown = (eventModifiers & DockingUtils.CONTROL_KEY_MODIFIER_MASK) != 0; boolean controlKeyDown = (eventModifiers & DockingUtils.CONTROL_KEY_MODIFIER_MASK) != 0;
if (!controlKeyDown) { if (!controlKeyDown) {
return; return;

View file

@ -16,8 +16,7 @@
package ghidra.graph.viewer.event.mouse; package ghidra.graph.viewer.event.mouse;
import java.awt.Point; import java.awt.Point;
import java.awt.event.MouseWheelEvent; import java.awt.event.*;
import java.awt.event.MouseWheelListener;
import docking.DockingUtils; import docking.DockingUtils;
import edu.uci.ics.jung.visualization.control.AbstractGraphMousePlugin; import edu.uci.ics.jung.visualization.control.AbstractGraphMousePlugin;
@ -35,6 +34,11 @@ public class VisualGraphScrollWheelPanningPlugin<V extends VisualVertex,
super(0); super(0);
} }
@Override
public boolean checkModifiers(MouseEvent e) {
return e.getModifiersEx() == modifiers;
}
@Override @Override
public void mouseWheelMoved(MouseWheelEvent e) { public void mouseWheelMoved(MouseWheelEvent e) {
if (!isScrollModifiers(e)) { if (!isScrollModifiers(e)) {
@ -91,7 +95,7 @@ public class VisualGraphScrollWheelPanningPlugin<V extends VisualVertex,
VisualGraphOptions options = viewer.getOptions(); VisualGraphOptions options = viewer.getOptions();
boolean scrollWheelPans = options.getScrollWheelPans(); boolean scrollWheelPans = options.getScrollWheelPans();
int scrollWheelModifierToggle = DockingUtils.CONTROL_KEY_MODIFIER_MASK; int scrollWheelModifierToggle = DockingUtils.CONTROL_KEY_MODIFIER_MASK;
int eventModifiers = e.getModifiers(); int eventModifiers = e.getModifiersEx();
if (scrollWheelPans) { if (scrollWheelPans) {
// scrolling will pan if *not* modified (modified in this case means to zoom) // scrolling will pan if *not* modified (modified in this case means to zoom)
return !((scrollWheelModifierToggle & eventModifiers) == scrollWheelModifierToggle); return !((scrollWheelModifierToggle & eventModifiers) == scrollWheelModifierToggle);

View file

@ -33,8 +33,8 @@ import ghidra.graph.viewer.*;
* the graph display in the x and y direction. The default MouseButtonOne modifier can be overridden * the graph display in the x and y direction. The default MouseButtonOne modifier can be overridden
* to cause a different mouse gesture to translate the display. * to cause a different mouse gesture to translate the display.
* *
* * @param <V> the vertex type
* * @param <E> the edge type
*/ */
public class VisualGraphTranslatingGraphMousePlugin<V extends VisualVertex, E extends VisualEdge<V>> public class VisualGraphTranslatingGraphMousePlugin<V extends VisualVertex, E extends VisualEdge<V>>
extends AbstractGraphMousePlugin extends AbstractGraphMousePlugin
@ -43,9 +43,8 @@ public class VisualGraphTranslatingGraphMousePlugin<V extends VisualVertex, E ex
private boolean panning; private boolean panning;
private boolean isHandlingEvent; private boolean isHandlingEvent;
// TODO for deprecated usage note, see the VisualGraphMousePlugin interface
public VisualGraphTranslatingGraphMousePlugin() { public VisualGraphTranslatingGraphMousePlugin() {
this(InputEvent.BUTTON1_MASK); this(InputEvent.BUTTON1_DOWN_MASK);
} }
public VisualGraphTranslatingGraphMousePlugin(int modifiers) { public VisualGraphTranslatingGraphMousePlugin(int modifiers) {
@ -53,6 +52,11 @@ public class VisualGraphTranslatingGraphMousePlugin<V extends VisualVertex, E ex
this.cursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR); this.cursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR);
} }
@Override
public boolean checkModifiers(MouseEvent e) {
return e.getModifiersEx() == modifiers;
}
@Override @Override
public void mousePressed(MouseEvent e) { public void mousePressed(MouseEvent e) {
GraphViewer<V, E> viewer = getGraphViewer(e); GraphViewer<V, E> viewer = getGraphViewer(e);
@ -158,7 +162,7 @@ public class VisualGraphTranslatingGraphMousePlugin<V extends VisualVertex, E ex
} }
private boolean checkModifiersForCursor(MouseEvent e) { private boolean checkModifiersForCursor(MouseEvent e) {
if (e.getModifiers() == 0) { if (e.getModifiersEx() == 0) {
return true; return true;
} }
return false; return false;

View file

@ -24,13 +24,15 @@ import ghidra.graph.viewer.*;
/** /**
* A handler to zoom nodes when double-clicked. If the vertex is zoomed out, then we will zoom * A handler to zoom nodes when double-clicked. If the vertex is zoomed out, then we will zoom
* in and center. If the vertex is zoomed to full size, then we will zoom out and center. * in and center. If the vertex is zoomed to full size, then we will zoom out and center.
*
* @param <V> the vertex type
* @param <E> the edge type
*/ */
public class VisualGraphZoomingPickingGraphMousePlugin<V extends VisualVertex, E extends VisualEdge<V>> public class VisualGraphZoomingPickingGraphMousePlugin<V extends VisualVertex, E extends VisualEdge<V>>
extends VisualGraphAbstractGraphMousePlugin<V, E> { extends VisualGraphAbstractGraphMousePlugin<V, E> {
// TODO for deprecated usage note, see the VisualGraphMousePlugin interface
public VisualGraphZoomingPickingGraphMousePlugin() { public VisualGraphZoomingPickingGraphMousePlugin() {
super(InputEvent.BUTTON1_MASK); super(InputEvent.BUTTON1_DOWN_MASK);
this.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); this.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
} }

View file

@ -0,0 +1,221 @@
/* ###
* 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.graph.viewer.popup;
import java.awt.Window;
import java.awt.event.*;
import javax.swing.*;
import docking.widgets.PopupWindow;
/**
* A class to control popups for graph clients, bypassing Java's default tool tip mechanism
*
* @param <V> the vertex type
* @param <E> the edge type
*/
public class PopupRegulator<V, E> {
private int popupDelay = 1000;
/**
* We need this timer because the default mechanism for triggering popups doesn't
* always work. We use this timer in conjunction with a mouse motion listener to
* get the results we want.
*/
private Timer popupTimer;
private MouseEvent popupMouseEvent;
/** the current target (vertex or edge) of a popup window */
private Object nextPopupTarget;
/**
* This value is not null when the user moves the cursor over a target for which a
* popup is already showing. We use this value to prevent showing a popup multiple times
* while over a single node.
*/
private Object lastShownPopupTarget;
/** The tooltip info used when showing the popup */
private ToolTipInfo<?> currentToolTipInfo;
private PopupSource<V, E> popupSource;
private PopupWindow popupWindow;
private boolean showPopups = true;
public PopupRegulator(PopupSource<V, E> popupSupplier) {
this.popupSource = popupSupplier;
popupTimer = new Timer(popupDelay, e -> {
if (isPopupShowing()) {
return; // don't show any new popups while the user is perusing
}
showPopupForMouseEvent(popupMouseEvent);
});
popupTimer.setRepeats(false);
popupSupplier.addMouseMotionListener(new MouseMotionListener() {
@Override
public void mouseDragged(MouseEvent e) {
hidePopupTooltips();
popupTimer.stop();
popupMouseEvent = null; // clear any queued popups
}
@Override
public void mouseMoved(MouseEvent e) {
popupMouseEvent = e;
// this clears out the current last popup shown so that the user can
// move off and on a node to re-show the popup
savePopupTarget(e);
// make sure the popup gets triggered eventually
popupTimer.restart();
}
});
}
/**
* Returns true if this class's popup is showing
* @return true if this class's popup is showing
*/
public boolean isPopupShowing() {
return popupWindow != null && popupWindow.isShowing();
}
/**
* Sets the time between mouse movements to wait before showing this class's popup
* @param delayMs the delay
*/
public void setPopupDelay(int delayMs) {
popupTimer.stop();
popupTimer.setDelay(delayMs);
popupTimer.setInitialDelay(delayMs);
popupDelay = delayMs;
}
/**
* Sets the enablement of this class's popup
* @param visible true to have popups enabled
*/
public void setPopupsVisible(boolean visible) {
this.showPopups = visible;
if (!showPopups) {
hidePopupTooltips();
}
}
private void showPopupForMouseEvent(MouseEvent event) {
if (!showPopups) {
return;
}
if (event == null) {
return;
}
ToolTipInfo<?> toolTipInfo = popupSource.getToolTipInfo(event);
JComponent toolTipComponent = toolTipInfo.getToolTipComponent();
boolean isCustomJavaTooltip = !(toolTipComponent instanceof JToolTip);
if (lastShownPopupTarget == nextPopupTarget && isCustomJavaTooltip) {
//
// Kinda Hacky:
// We don't show repeated popups for the same item (the user has to move away
// and then come back to re-show the popup). However, one caveat to this is that
// we do want to allow the user to see popups for the toolbar actions always. So,
// only return here if we have already shown a popup for the item *and* we are
// using a custom tooltip (which is used to show a vertex tooltip or an edge
// tooltip)
return;
}
currentToolTipInfo = toolTipInfo;
showTooltip(currentToolTipInfo);
}
private void popupShown() {
lastShownPopupTarget = nextPopupTarget;
currentToolTipInfo.emphasize();
popupSource.repaint();
}
private void popupHidden() {
currentToolTipInfo.deEmphasize();
popupSource.repaint();
}
private void savePopupTarget(MouseEvent event) {
nextPopupTarget = null;
V vertex = popupSource.getVertex(event);
if (vertex != null) {
nextPopupTarget = vertex;
}
else {
E edge = popupSource.getEdge(event);
nextPopupTarget = edge;
}
if (nextPopupTarget == null) {
// We've moved off of a target. We will clear that last target so the user can
// mouse off of a vertex and back on in order to trigger a new popup
lastShownPopupTarget = null;
}
}
private void hidePopupTooltips() {
if (popupWindow != null && popupWindow.isShowing()) {
popupWindow.hide();
// don't call dispose, or we don't get our componentHidden() callback
// popupWindow.dispose();
}
}
private void showTooltip(ToolTipInfo<?> info) {
JComponent tipComponent = info.getToolTipComponent();
if (tipComponent == null) {
return;
}
MouseEvent event = info.getMouseEvent();
showPopupWindow(event, tipComponent);
}
private void showPopupWindow(MouseEvent event, JComponent component) {
MenuSelectionManager menuManager = MenuSelectionManager.defaultManager();
if (menuManager.getSelectedPath().length != 0) {
return;
}
Window parentWindow = popupSource.getPopupParent();
popupWindow = new PopupWindow(parentWindow, component);
popupWindow.addComponentListener(new ComponentAdapter() {
@Override
public void componentShown(ComponentEvent e) {
popupShown();
}
@Override
public void componentHidden(ComponentEvent e) {
popupHidden();
}
});
popupWindow.showPopup(event);
}
}

View file

@ -0,0 +1,72 @@
/* ###
* 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.graph.viewer.popup;
import java.awt.Window;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
/**
* An interface that provides graph and component information to the {@link PopupRegulator}
*
* @param <V> the vertex type
* @param <E> the edge type
*/
public interface PopupSource<V, E> {
/**
* Returns the tool tip info object for the given mouse event. Implementations will use the
* event to determine whether a popup should be created for a vertex, edge, the graph or
* not at all.
*
* @param event the event
* @return the info; null for no popup
*/
public ToolTipInfo<?> getToolTipInfo(MouseEvent event);
/**
* Returns a vertex for the given event
* @param event the event
* @return the vertex or null
*/
public V getVertex(MouseEvent event);
/**
* Returns an edge for the given event
* @param event the event
* @return the edge or null
*/
public E getEdge(MouseEvent event);
/**
* Adds the given mouse motion listener to the graph component. This allows the popup
* regulator to decided when to show and hide popups.
*
* @param l the listener
*/
public void addMouseMotionListener(MouseMotionListener l);
/**
* Signals that the graph needs to repaint
*/
public void repaint();
/**
* Returns a suitable window parent for the popup window
* @return the window parent
*/
public Window getPopupParent();
}

View file

@ -0,0 +1,70 @@
/* ###
* 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.graph.viewer.popup;
import java.awt.event.MouseEvent;
import javax.swing.JComponent;
/**
* Basic container object that knows how to generate tooltips
*
* @param <T> the type of object for which to create a tooltip
*/
public abstract class ToolTipInfo<T> {
protected final MouseEvent event;
protected final T graphObject;
private JComponent tooltipComponent;
public ToolTipInfo(MouseEvent event, T t) {
this.event = event;
this.graphObject = t;
tooltipComponent = createToolTipComponent();
}
/**
* Creates a tool tip component
* @return the tool tip component
*/
protected abstract JComponent createToolTipComponent();
/**
* Signals for the implementation to emphasis the original graph object passed to this info
*/
protected abstract void emphasize();
/**
* Signals for the implementation to turn off emphasis
*/
protected abstract void deEmphasize();
/**
* Returns the mouse event from this tool tip info
* @return the mouse event from this tool tip info
*/
MouseEvent getMouseEvent() {
return event;
}
/**
* Returns the tool tip component created by this info
* @return the tool tip component created by this info
*/
JComponent getToolTipComponent() {
return tooltipComponent;
}
}

View file

@ -16,14 +16,19 @@
package ghidra.service.graph; package ghidra.service.graph;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.text.StringEscapeUtils;
/** /**
* Generic directed graph edge implementation * Generic directed graph edge implementation
*/ */
public class AttributedEdge extends Attributed { public class AttributedEdge extends Attributed {
private final String id; private final String id;
/** /**
* cache of the edge label parsed as html * Cache of the edge label parsed as html
*/ */
private String htmlString; private String htmlString;
@ -41,20 +46,27 @@ public class AttributedEdge extends Attributed {
} }
/** /**
* create (once) the html representation of the key/values for this edge * The html representation of the key/values for this edge
* @return html formatted label for the edge * @return html formatted label for the edge
*/ */
public String getHtmlString() { public String getHtmlString() {
if (htmlString == null) { if (htmlString != null) {
return htmlString;
}
Set<Entry<String, String>> entries = entrySet();
if (entries.isEmpty()) {
return ""; // empty so tooltip clients can handle empty data
}
StringBuilder buf = new StringBuilder("<html>"); StringBuilder buf = new StringBuilder("<html>");
for (Map.Entry<String, String> entry : entrySet()) { for (Map.Entry<String, String> entry : entries) {
buf.append(entry.getKey()); buf.append(entry.getKey());
buf.append(":"); buf.append(":");
buf.append(entry.getValue()); buf.append(StringEscapeUtils.escapeHtml4(entry.getValue()));
buf.append("<br>"); buf.append("<br>");
} }
htmlString = buf.toString(); htmlString = buf.toString();
}
return htmlString; return htmlString;
} }

View file

@ -15,9 +15,9 @@
*/ */
package ghidra.service.graph; package ghidra.service.graph;
import org.apache.commons.text.StringEscapeUtils;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/** /**
* Graph vertex with attributes * Graph vertex with attributes
@ -85,16 +85,24 @@ public class AttributedVertex extends Attributed {
* @return the html string * @return the html string
*/ */
public String getHtmlString() { public String getHtmlString() {
if (htmlString == null) {
if (htmlString != null) {
return htmlString;
}
Set<Entry<String, String>> entries = entrySet();
if (entries.isEmpty()) {
return ""; // empty so tooltip clients can handle empty data
}
StringBuilder buf = new StringBuilder("<html>"); StringBuilder buf = new StringBuilder("<html>");
for (Map.Entry<String, String> entry : entrySet()) { for (Map.Entry<String, String> entry : entries) {
buf.append(entry.getKey()); buf.append(entry.getKey());
buf.append(":"); buf.append(":");
buf.append(StringEscapeUtils.escapeHtml4(entry.getValue())); buf.append(entry.getValue());
buf.append("<br>"); buf.append("<br>");
} }
htmlString = buf.toString(); htmlString = buf.toString();
}
return htmlString; return htmlString;
} }

View file

@ -404,7 +404,7 @@ public abstract class PluginTool extends AbstractDockingTool {
"You cannot persist generic tools: " + getClass().getName()); "You cannot persist generic tools: " + getClass().getName());
} }
public void restoreWindowingDataFromXml(Element windowData) { public void restoreWindowingDataFromXml(Element element) {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
"You cannot persist generic tools: " + getClass().getName()); "You cannot persist generic tools: " + getClass().getName());
} }
@ -553,6 +553,7 @@ public abstract class PluginTool extends AbstractDockingTool {
} }
restoreOptionsFromXml(root); restoreOptionsFromXml(root);
winMgr.restorePreferencesFromXML(root);
setDefaultOptionValues(); setDefaultOptionValues();
boolean hasErrors = false; boolean hasErrors = false;
try { try {
@ -563,7 +564,7 @@ public abstract class PluginTool extends AbstractDockingTool {
Msg.showError(this, getToolFrame(), "Error Restoring Plugins", e.getMessage()); Msg.showError(this, getToolFrame(), "Error Restoring Plugins", e.getMessage());
} }
winMgr.restoreFromXML(root); winMgr.restoreWindowDataFromXml(root);
winMgr.setToolName(fullName); winMgr.setToolName(fullName);
return hasErrors; return hasErrors;
} }

View file

@ -125,8 +125,8 @@ public class GhidraTool extends PluginTool {
} }
@Override @Override
public void restoreWindowingDataFromXml(Element windowData) { public void restoreWindowingDataFromXml(Element rootElement) {
winMgr.restoreWindowDataFromXml(windowData); winMgr.restoreWindowDataFromXml(rootElement);
} }
@Override @Override

View file

@ -166,8 +166,8 @@ class WorkspaceImpl implements Workspace {
Iterator<?> iter = root.getChildren("RUNNING_TOOL").iterator(); Iterator<?> iter = root.getChildren("RUNNING_TOOL").iterator();
while (iter.hasNext()) { while (iter.hasNext()) {
Element elememnt = (Element) iter.next(); Element element = (Element) iter.next();
String toolName = elememnt.getAttributeValue(ToolTemplate.TOOL_NAME_XML_NAME); String toolName = element.getAttributeValue(ToolTemplate.TOOL_NAME_XML_NAME);
if (toolName == null) { if (toolName == null) {
continue; continue;
} }
@ -182,12 +182,9 @@ class WorkspaceImpl implements Workspace {
} }
boolean hadChanges = tool.hasConfigChanged(); boolean hadChanges = tool.hasConfigChanged();
Element windowData = elememnt.getChild("ROOT_NODE"); tool.restoreWindowingDataFromXml(element);
if (windowData != null) { // backward compatibility
tool.restoreWindowingDataFromXml(windowData);
}
Element toolDataElem = elememnt.getChild("DATA_STATE"); Element toolDataElem = element.getChild("DATA_STATE");
tool.restoreDataStateFromXml(toolDataElem); tool.restoreDataStateFromXml(toolDataElem);
if (hadChanges) { if (hadChanges) {
// restore the dirty state, which is cleared by the restoreDataState call // restore the dirty state, which is cleared by the restoreDataState call

View file

@ -52,10 +52,24 @@ generateGrammarSource {
include "ghidra/sleigh/grammar/SleighCompiler.g" include "ghidra/sleigh/grammar/SleighCompiler.g"
// README: See src/main/antlr/ghidra/sleigh/grammar/README.txt for an explanation of this. // README: See src/main/antlr/ghidra/sleigh/grammar/README.txt for an explanation of this.
doFirst {
// Ensure that SleighLexar.tokens is rebuilt by removing it if any of the
// contributing grammar files have changed. Antlr plugin does not know
// about the SleighLexar.tokens dependency on changes to BaseLexer.g,
// DisplayLexer.g or SemanticLexer.g
delete file("$buildDir/${genSrcDir}/ghidra/sleigh/grammar/SleighLexer.tokens")
}
doLast { doLast {
// SleighLexar.g is only needed to produce SleighLexar.tokens.
// Remove its generated java artifacts which may conflict
delete fileTree("$buildDir/${genSrcDir}/ghidra/sleigh/grammar") { delete fileTree("$buildDir/${genSrcDir}/ghidra/sleigh/grammar") {
include "SleighLexer*.java" include "SleighLexer*.java"
} }
// antlr src directories cause generated java files to output
// into the correct package directories, however we need to add
// the appropriate java package statement which is missing.
fileTree("$buildDir/${genSrcDir}/ghidra/sleigh/grammar") { fileTree("$buildDir/${genSrcDir}/ghidra/sleigh/grammar") {
include "*.java" include "*.java"
}.each { File src -> }.each { File src ->

View file

@ -119,6 +119,7 @@ tokens {
OP_SUBTABLE; OP_SUBTABLE;
OP_TABLE; OP_TABLE;
OP_TOKEN; OP_TOKEN;
OP_TOKEN_ENDIAN;
OP_TRUNCATION_SIZE; OP_TRUNCATION_SIZE;
OP_TYPE; OP_TYPE;
OP_UNIMPL; OP_UNIMPL;

View file

@ -183,7 +183,17 @@ tokendef
if (sym != null) { if (sym != null) {
redefinedError(sym, n, "token"); redefinedError(sym, n, "token");
} else { } else {
$tokendef::tokenSymbol = sc.defineToken(find(n), $n.value.getText(), $i.value.intValue()); $tokendef::tokenSymbol = sc.defineToken(find(n), $n.value.getText(), $i.value.intValue(), 0);
}
}
} fielddefs)
| ^(OP_TOKEN_ENDIAN n=specific_identifier["token definition"] i=integer s=endian {
if (n != null) {
SleighSymbol sym = sc.findSymbol($n.value.getText());
if (sym != null) {
redefinedError(sym, n, "token");
} else {
$tokendef::tokenSymbol = sc.defineToken(find(n), $n.value.getText(), $i.value.intValue(), $s.value ==0 ? -1 : 1);
} }
} }
} fielddefs) } fielddefs)

View file

@ -60,6 +60,7 @@ aligndef
tokendef tokendef
: ^(OP_TOKEN n=identifier i=integer { out("define token " + $n.value + "(" + $i.value + ")"); } fielddefs) : ^(OP_TOKEN n=identifier i=integer { out("define token " + $n.value + "(" + $i.value + ")"); } fielddefs)
| ^(OP_TOKEN_ENDIAN n=identifier i=integer s=endian { out("define token endian" + $n.value + "(" + $i.value + ")"); } fielddefs)
; ;
fielddefs fielddefs

View file

@ -74,6 +74,7 @@ aligndef
tokendef tokendef
: lc=KEY_DEFINE KEY_TOKEN identifier LPAREN integer rp=RPAREN fielddefs[$rp] -> ^(OP_TOKEN[$lc, "define token"] identifier integer fielddefs) : lc=KEY_DEFINE KEY_TOKEN identifier LPAREN integer rp=RPAREN fielddefs[$rp] -> ^(OP_TOKEN[$lc, "define token"] identifier integer fielddefs)
| lc=KEY_DEFINE KEY_TOKEN identifier LPAREN integer RPAREN rp=KEY_ENDIAN ASSIGN endian fielddefs[$rp] -> ^(OP_TOKEN_ENDIAN[$lc, "define token"] identifier integer endian fielddefs)
; ;
fielddefs[Token lc] fielddefs[Token lc]

View file

@ -888,12 +888,14 @@ public class SleighLanguage implements Language {
throw new SleighException(".sla file for " + getLanguageID() + " has the wrong format"); throw new SleighException(".sla file for " + getLanguageID() + " has the wrong format");
} }
boolean isBigEndian = SpecXmlUtils.decodeBoolean(el.getAttribute("bigendian")); boolean isBigEndian = SpecXmlUtils.decodeBoolean(el.getAttribute("bigendian"));
// check the instruction endianess, not the program data endianess if (isBigEndian ^ description.getEndian().isBigEndian()) {
if (isBigEndian ^ description.getInstructionEndian().isBigEndian()) { if (description.getInstructionEndian().isBigEndian() == description.getEndian()
.isBigEndian()) {
throw new SleighException( throw new SleighException(
".ldefs says " + getLanguageID() + " is " + description.getInstructionEndian() + ".ldefs says " + getLanguageID() + " is " + description.getEndian() +
" but .sla says " + el.getAttribute("bigendian")); " but .sla says " + el.getAttribute("bigendian"));
} }
}
uniqueBase = SpecXmlUtils.decodeLong(el.getAttribute("uniqbase")); uniqueBase = SpecXmlUtils.decodeLong(el.getAttribute("uniqbase"));
alignment = SpecXmlUtils.decodeInt(el.getAttribute("align")); alignment = SpecXmlUtils.decodeInt(el.getAttribute("align"));
uniqueAllocateMask = 0; // Default mask is 0 uniqueAllocateMask = 0; // Default mask is 0

View file

@ -310,11 +310,13 @@ public class Emulate {
else { else {
takeBranch = memstate.getValue(condVar) != 0; takeBranch = memstate.getValue(condVar) != 0;
} }
if (takeBranch) if (takeBranch) {
executeBranch(op); executeBranch(op);
else }
else {
fallthruOp(); fallthruOp();
} }
}
/// Since the full instruction is cached, we can do relative branches properly /// Since the full instruction is cached, we can do relative branches properly
/// \param op is the particular branch op being executed /// \param op is the particular branch op being executed
@ -324,11 +326,13 @@ public class Emulate {
long id = destaddr.getOffset(); long id = destaddr.getOffset();
id = id + current_op; id = id + current_op;
current_op = (int) id; current_op = (int) id;
if (current_op == pcode.length) if (current_op == pcode.length) {
fallthruOp(); fallthruOp();
else if ((current_op < 0) || (current_op >= pcode.length)) }
else if ((current_op < 0) || (current_op >= pcode.length)) {
throw new LowlevelError("Bad intra-instruction branch"); throw new LowlevelError("Bad intra-instruction branch");
} }
}
else { else {
setCurrentAddress(destaddr); setCurrentAddress(destaddr);
} }
@ -451,18 +455,18 @@ public class Emulate {
"Unsupported pcode op (opcode=" + op.getOpcode() + ", seq=" + op.getSeqnum() + ")"); "Unsupported pcode op (opcode=" + op.getOpcode() + ", seq=" + op.getSeqnum() + ")");
} }
if (behave instanceof UnaryOpBehavior) { if (behave instanceof UnaryOpBehavior) {
UnaryOpBehavior uniaryBehave = (UnaryOpBehavior) behave; UnaryOpBehavior unaryBehave = (UnaryOpBehavior) behave;
Varnode in1var = op.getInput(0); Varnode in1var = op.getInput(0);
Varnode outvar = op.getOutput(); Varnode outvar = op.getOutput();
if (in1var.getSize() > 8 || outvar.getSize() > 8) { if (in1var.getSize() > 8 || outvar.getSize() > 8) {
BigInteger in1 = memstate.getBigInteger(op.getInput(0), false); BigInteger in1 = memstate.getBigInteger(op.getInput(0), false);
BigInteger out = uniaryBehave.evaluateUnary(op.getOutput().getSize(), BigInteger out = unaryBehave.evaluateUnary(op.getOutput().getSize(),
op.getInput(0).getSize(), in1); op.getInput(0).getSize(), in1);
memstate.setValue(op.getOutput(), out); memstate.setValue(op.getOutput(), out);
} }
else { else {
long in1 = memstate.getValue(op.getInput(0)); long in1 = memstate.getValue(op.getInput(0));
long out = uniaryBehave.evaluateUnary(op.getOutput().getSize(), long out = unaryBehave.evaluateUnary(op.getOutput().getSize(),
op.getInput(0).getSize(), in1); op.getInput(0).getSize(), in1);
memstate.setValue(op.getOutput(), out); memstate.setValue(op.getOutput(), out);
} }

View file

@ -20,9 +20,28 @@ import generic.stl.MapSTL;
import ghidra.pcode.error.LowlevelError; import ghidra.pcode.error.LowlevelError;
import ghidra.program.model.address.AddressSpace; import ghidra.program.model.address.AddressSpace;
/**
* An subclass of {@link MemoryBank} intended for modeling the "unique" memory
* space. The space is byte-addressable and paging is not supported.
*/
public class UniqueMemoryBank extends MemoryBank { public class UniqueMemoryBank extends MemoryBank {
protected MapSTL<Long, byte[]> map = new ComparableMapSTL<Long, byte[]>(); /**A map from {@link Long} offsets to byte values would require many lookups.
* As an optimization, this map is defined from {@link Long} values to
* {@link WordInfo} objects, each of which represents an eight-byte word
* of memory. Each key in this map must be 0 mod 8.
*/
protected MapSTL<Long, WordInfo> map = new ComparableMapSTL<Long, WordInfo>();
private static final long ALIGNMENT_MASK = 0xfffffffffffffff8L;
//note that WordInfo use the bits in a byte to record whether
//or not a given byte has been written to, so you can't just
//change WORD_SIZE to another value and without also changing
//the implementation of WordInfo
private static final int WORD_SIZE = 8;
private byte[] buffer = new byte[WORD_SIZE];
public UniqueMemoryBank(AddressSpace spc, boolean isBigEndian) { public UniqueMemoryBank(AddressSpace spc, boolean isBigEndian) {
super(spc, isBigEndian, 0, null); super(spc, isBigEndian, 0, null);
@ -45,24 +64,93 @@ public class UniqueMemoryBank extends MemoryBank {
} }
@Override @Override
public int getChunk(long addrOffset, int size, byte[] res, boolean ignoreFault) { public int getChunk(long offset, int size, byte[] dest, boolean stopOnUninitialized) {
byte[] value = map.get(addrOffset); int bytesRead = 0;
if (value == null) { if (size == 0) {
throw new LowlevelError("Unique value read before written: 0x" + return bytesRead;
Long.toHexString(addrOffset));
} }
if (value.length != size) { try {
throw new LowlevelError("Unique value size mismatch: 0x" + Long.toHexString(addrOffset)); //align if necessary
int adjustment = (int) offset % WORD_SIZE;
if (adjustment != 0) {
WordInfo word = map.get(offset & ALIGNMENT_MASK);
if (word == null) {
throw new LowlevelError("Attempted to read uninitialized word in unique space");
}
for (int i = adjustment; i < WORD_SIZE && bytesRead < size; ++i) {
dest[bytesRead++] = word.getByte(i);
offset += 1;
}
}
//copy a word at a time
while (size - bytesRead > 0) {
WordInfo word = map.get(offset & ALIGNMENT_MASK);
if (word == null) {
throw new LowlevelError("Attempted to read uninitialized word in unique space");
}
offset += WORD_SIZE;
//whole word is initialized, copy it (or the appropriate
//initial segment) all at once
int bytesToRead = Math.min(WORD_SIZE, size - bytesRead);
if (word.isEntireWordInitialized()) {
word.getWord(buffer);
System.arraycopy(buffer, 0, dest, bytesRead,
Math.min(WORD_SIZE, size - bytesRead));
bytesRead += bytesToRead;
continue;
}
//not entirely initialized, copy one byte at a time until
//all requested bytes read (or word.getByte throws an exception)
int base = bytesRead;
for (int i = 0; i < bytesToRead; ++i) {
dest[base + i] = word.getByte(i);
bytesRead += 1;
}
}
return bytesRead;
}
catch (LowlevelError e) {
if (stopOnUninitialized) {
return bytesRead;
}
throw e;
} }
System.arraycopy(value, 0, res, 0, size);
return size;
} }
@Override @Override
public void setChunk(long offset, int size, byte[] val) { public void setChunk(long offset, int size, byte[] src) {
byte[] value = new byte[size]; if (size == 0 || src.length == 0) {
System.arraycopy(val, 0, value, 0, size); return;
map.put(offset, value); }
int currentPosition = 0;
//align if necessary
int adjustment = (int) offset % WORD_SIZE;
if (adjustment != 0) {
WordInfo word = map.get(offset & ALIGNMENT_MASK);
if (word == null) {
word = new WordInfo();
map.put(offset & ALIGNMENT_MASK, word);
}
for (int i = adjustment; i < WORD_SIZE; ++i) {
word.setByte(src[currentPosition], i);
offset += 1;
currentPosition += 1;
}
}
while (size > currentPosition) {
WordInfo word = map.get(offset & ALIGNMENT_MASK);
if (word == null) {
word = new WordInfo();
map.put(offset & ALIGNMENT_MASK, word);
}
int bytesToWrite = Math.min(WORD_SIZE, size - currentPosition);
for (int i = 0; i < bytesToWrite; i++) {
word.setByte(src[currentPosition + i], i);
}
offset += bytesToWrite;
currentPosition += bytesToWrite;
}
return;
} }
/** /**
@ -72,4 +160,95 @@ public class UniqueMemoryBank extends MemoryBank {
map.clear(); map.clear();
} }
/**
* A simple class representing a byte-addressable word of memory. Each
* byte can be either initialized to a byte value or uninitialized.
* It is an error to attempt to read an uninitialized byte.
*/
public static class WordInfo {
public byte initialized;
public long word;
/**
* Constructs a {@link WordInfo} object with all bytes uninitialized.
*/
public WordInfo() {
initialized = 0;
word = 0;
}
/**
* Initializes the byte at {@code index} and sets its value to
* {@code val}
* @param val new value
* @param index index
* @throws LowlevelError if the index is invalid
*/
public void setByte(byte val, int index) {
validateIndex(index);
word &= ~(0xffL << (WORD_SIZE * index));
long shifted = ((long) val) << (WORD_SIZE * index);
word |= shifted;
initialized |= (1 << index);
}
/**
* Returns the byte at the given index
* @param index index
* @return corresponding byte value
* @throws LowlevelError if the index is invalid or the requested byte
* is not initialized.
*/
public byte getByte(int index) {
validateIndex(index);
checkInitialized(index);
long selected = word & (0xffL << (WORD_SIZE * index));
long adjusted = selected >> (WORD_SIZE * index);
return (byte) adjusted;
}
/**
* Writes an entire word into {@code buffer}
* @param buffer buffer to write a single word to. Must have
* length 8.
* @throws LowlevelError if the entire word is not initialized
*/
public void getWord(byte[] buffer) {
if (initialized != ((byte) (0xff))) {
throw new LowlevelError("Attempted to read uninitialized word in unique space");
}
if (buffer.length != WORD_SIZE) {
throw new IllegalArgumentException("Buffer must have length 8");
}
for (int i = 0; i < WORD_SIZE; ++i) {
buffer[i] = (byte) ((word & (0xffL << (WORD_SIZE * i))) >> (WORD_SIZE * i));
}
}
/**
* Returns true precisely when the entire word is initialized.
* @return true if entire work initialized
*/
protected boolean isEntireWordInitialized() {
return initialized == (byte) 0xff;
}
//assumes 0 <= index <= 7
private void checkInitialized(int index) {
if ((initialized & (1 << (index))) == 0) {
throw new LowlevelError(
"Attempted to read uninitialized memory in the unique space.");
}
}
//ensure that the provided index is valid
private void validateIndex(int index) {
if (index < 0 || index > 7) {
throw new LowlevelError("Invalid index: " + Integer.toString(index));
}
return;
}
}
} }

View file

@ -529,8 +529,8 @@ public class SleighCompile extends SleighBase {
static int findCollision(Map<Long, Integer> local2Operand, ArrayList<Long> locals, static int findCollision(Map<Long, Integer> local2Operand, ArrayList<Long> locals,
int operand) { int operand) {
Integer boxOperand = Integer.valueOf(operand); Integer boxOperand = Integer.valueOf(operand);
for (int i = 0; i < locals.size(); ++i) { for (Long local : locals) {
Integer previous = local2Operand.putIfAbsent(locals.get(i), boxOperand); Integer previous = local2Operand.putIfAbsent(local, boxOperand);
if (previous != null) { if (previous != null) {
if (previous.intValue() != operand) { if (previous.intValue() != operand) {
return previous.intValue(); return previous.intValue();
@ -842,7 +842,7 @@ public class SleighCompile extends SleighBase {
} }
// Parser functions // Parser functions
public TokenSymbol defineToken(Location location, String name, long sz) { public TokenSymbol defineToken(Location location, String name, long sz, int endian) {
entry("defineToken", location, name, sz); entry("defineToken", location, name, sz);
int size = (int) sz; int size = (int) sz;
if ((size & 7) != 0) { if ((size & 7) != 0) {
@ -853,8 +853,15 @@ public class SleighCompile extends SleighBase {
else { else {
size = size / 8; size = size / 8;
} }
boolean isBig;
if (endian == 0) {
isBig = isBigEndian();
}
else {
isBig = (endian > 0);
}
ghidra.pcodeCPort.context.Token newtoken = ghidra.pcodeCPort.context.Token newtoken =
new ghidra.pcodeCPort.context.Token(name, size, isBigEndian(), tokentable.size()); new ghidra.pcodeCPort.context.Token(name, size, isBig, tokentable.size());
tokentable.push_back(newtoken); tokentable.push_back(newtoken);
TokenSymbol res = new TokenSymbol(location, newtoken); TokenSymbol res = new TokenSymbol(location, newtoken);
addSymbol(res); addSymbol(res);

View file

@ -83,7 +83,12 @@ public class ParameterDefinitionImpl implements ParameterDefinition {
throw new IllegalArgumentException(kind + throw new IllegalArgumentException(kind +
" type must be specified with fixed-length data type: " + dataType.getName()); " type must be specified with fixed-length data type: " + dataType.getName());
} }
if (dataType instanceof VoidDataType) { DataType baseType = dataType;
if(baseType instanceof TypedefDataType) {
baseType = ((TypedefDataType)baseType).getBaseDataType();
}
if (baseType instanceof VoidDataType) {
if (!isReturn) { if (!isReturn) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Parameter type may not specify the void datatype - empty parameter list should be used"); "Parameter type may not specify the void datatype - empty parameter list should be used");

View file

@ -0,0 +1,253 @@
/* ###
* 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.pcode.memstate;
import static org.junit.Assert.*;
import java.util.Arrays;
import org.junit.Before;
import org.junit.Test;
import generic.test.AbstractGenericTest;
import ghidra.pcode.error.LowlevelError;
import ghidra.pcode.memstate.UniqueMemoryBank.WordInfo;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.address.GenericAddressSpace;
public class UniqueMemoryBankTest extends AbstractGenericTest {
private AddressSpace uniqueSpace;
private UniqueMemoryBank uniqueBank;
private byte[] eightTestBytes = new byte[] { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 };
private byte[] eightZeroBytes = new byte[8];
private byte[] sixteenTestBytes = new byte[] { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9,
0xa, 0xb, 0xc, 0xd, 0xe, 0xf };
@Before
public void setUp() {
uniqueSpace = new GenericAddressSpace("unique", 64, AddressSpace.TYPE_UNIQUE, 0);
uniqueBank = new UniqueMemoryBank(uniqueSpace, false);
}
public UniqueMemoryBankTest() {
super();
}
@Test
public void WordInfoBasicTest() {
WordInfo info = new WordInfo();
assertFalse(info.isEntireWordInitialized());
info.setByte((byte) 0x0, 0);
assertFalse(info.isEntireWordInitialized());
info.setByte((byte) 0x1, 1);
assertFalse(info.isEntireWordInitialized());
info.setByte((byte) 0x2, 2);
assertFalse(info.isEntireWordInitialized());
info.setByte((byte) 0x3, 3);
assertFalse(info.isEntireWordInitialized());
info.setByte((byte) 0x4, 4);
assertFalse(info.isEntireWordInitialized());
info.setByte((byte) 0x5, 5);
assertFalse(info.isEntireWordInitialized());
info.setByte((byte) 0x6, 6);
assertFalse(info.isEntireWordInitialized());
info.setByte((byte) 0x7, 7);
assertTrue(info.isEntireWordInitialized());
for (int i = 0; i < 8; ++i) {
assertEquals((byte) i, info.getByte(i));
}
}
@Test(expected = LowlevelError.class)
public void testGetUnitializedByte() {
WordInfo info = new WordInfo();
info.setByte((byte) 0, 0);
info.setByte((byte) 1, 1);
info.setByte((byte) 3, 3);
info.setByte((byte) 4, 4);
info.setByte((byte) 5, 5);
info.setByte((byte) 6, 6);
info.setByte((byte) 7, 7);
@SuppressWarnings("unused")
byte val = info.getByte(2);
}
@Test
public void testSimpleRead() {
uniqueBank.setChunk(0x1000, 8, eightTestBytes);
byte[] dest = new byte[8];
int numBytes = uniqueBank.getChunk(0x1000, 8, dest, true);
assertEquals(8, numBytes);
assertTrue(Arrays.equals(dest, eightTestBytes));
}
@Test
public void testDifferentlySizedReads() {
uniqueBank.setChunk(0x1000, 8, eightTestBytes);
byte[] dest = new byte[4];
int numBytes = uniqueBank.getChunk(0x1000, 4, dest, true);
assertEquals(4, numBytes);
assertTrue(Arrays.equals(dest, new byte[] { 0x0, 0x1, 0x2, 0x3 }));
numBytes = uniqueBank.getChunk(0x1004, 4, dest, true);
assertEquals(4, numBytes);
assertTrue(Arrays.equals(dest, new byte[] { 0x4, 0x5, 0x6, 0x7 }));
}
@Test
public void testLargeReadWrite() {
uniqueBank.setChunk(0x1004, 16, sixteenTestBytes);
byte[] dest = new byte[16];
int numBytes = uniqueBank.getChunk(0x1004, 16, dest, true);
assertEquals(16, numBytes);
assertTrue(Arrays.equals(dest, sixteenTestBytes));
byte[] largeSrc = new byte[64];
for (int i = 0; i < 64; ++i) {
largeSrc[i] = (byte) (i + 1);
}
uniqueBank.setChunk(0x1007, 64, largeSrc);
dest = new byte[64];
numBytes = uniqueBank.getChunk(0x1007, 64, dest, true);
assertEquals(64, numBytes);
assertTrue(Arrays.equals(dest, largeSrc));
}
@Test
public void testReadAcrossUndefined() {
byte[] fourBytes = new byte[] { 0x11, 0x22, 0x33, 0x44 };
uniqueBank.setChunk(0x1007, 4, fourBytes);
uniqueBank.setChunk(0x100c, 4, fourBytes);
byte[] dest = new byte[9];
int numBytes = uniqueBank.getChunk(0x1007, 9, dest, true);
assertEquals(4, numBytes);
assertEquals(0x11, dest[0]);
assertEquals(0x22, dest[1]);
assertEquals(0x33, dest[2]);
assertEquals(0x44, dest[3]);
}
@Test
public void testNonAlignedReadWrite() {
byte[] fourBytes = new byte[] { 0x11, 0x22, 0x33, 0x44 };
uniqueBank.setChunk(0x1004, 4, fourBytes);
byte[] dest = new byte[4];
int numBytes = uniqueBank.getChunk(0x1004, 4, dest, true);
assertEquals(4, numBytes);
assertTrue(Arrays.equals(fourBytes, dest));
}
@Test
public void testOverlappingReadWrite() {
uniqueBank.setChunk(0x1000, 16, sixteenTestBytes);
uniqueBank.setChunk(0x1004, 8, eightZeroBytes);
byte[] dest = new byte[16];
int numBytes = uniqueBank.getChunk(0x1000, 16, dest, true);
assertEquals(16, numBytes);
for (int i = 0; i < 16; ++i) {
if (i > 3 && i < 12) {
assertEquals(0, dest[i]);
}
else {
assertEquals(i, dest[i]);
}
}
}
@Test
public void testOneByteRead() {
byte[] one = new byte[] { (byte) 0x7f };
uniqueBank.setChunk(0x1000, 1, one);
byte[] dest = new byte[16];
int numBytes = uniqueBank.getChunk(0x1000, 1, dest, false);
assertEquals(1, numBytes);
assertEquals(dest[0], (byte) 0x7f);
}
@Test
public void testClear() {
uniqueBank.setChunk(0x1000, 8, eightTestBytes);
byte[] dest = new byte[8];
uniqueBank.clear();
int numBytes = uniqueBank.getChunk(0x1000, 8, dest, true);
assertEquals(0, numBytes);
numBytes = uniqueBank.getChunk(0x1000, 7, dest, true);
assertEquals(0, numBytes);
numBytes = uniqueBank.getChunk(0x1000, 6, dest, true);
assertEquals(0, numBytes);
numBytes = uniqueBank.getChunk(0x1000, 5, dest, true);
assertEquals(0, numBytes);
numBytes = uniqueBank.getChunk(0x1000, 4, dest, true);
assertEquals(0, numBytes);
numBytes = uniqueBank.getChunk(0x1000, 3, dest, true);
assertEquals(0, numBytes);
numBytes = uniqueBank.getChunk(0x1000, 2, dest, true);
assertEquals(0, numBytes);
numBytes = uniqueBank.getChunk(0x1000, 1, dest, true);
assertEquals(0, numBytes);
numBytes = uniqueBank.getChunk(0x1000, 0, dest, true);
assertEquals(0, numBytes);
}
@Test
public void testSimpleOverwrite() {
uniqueBank.setChunk(0x1000, 8, eightTestBytes);
byte[] dest = new byte[8];
int numBytes = uniqueBank.getChunk(0x1000, 8, dest, true);
assertEquals(8, numBytes);
assertTrue(Arrays.equals(dest, eightTestBytes));
uniqueBank.setChunk(0x1000, 8, eightZeroBytes);
numBytes = uniqueBank.getChunk(0x1000, 8, dest, true);
assertEquals(8, numBytes);
assertTrue(Arrays.equals(dest, eightZeroBytes));
}
@Test(expected = LowlevelError.class)
public void testUnitializedReadStop() {
byte[] dest = new byte[16];
uniqueBank.getChunk(0x1000, 0x10, dest, false);
}
@Test
public void testUnitializedReadContinue() {
byte[] dest = new byte[16];
int bytesRead = uniqueBank.getChunk(0x1000, 0x10, dest, true);
assertEquals(0, bytesRead);
}
@SuppressWarnings("unused")
@Test(expected = UnsupportedOperationException.class)
public void testGetPageException() {
MemoryPage page = uniqueBank.getPage(0);
}
@Test(expected = UnsupportedOperationException.class)
public void testSetPageException() {
uniqueBank.setPage(0, new byte[0], 0, 4096, 0);
}
@Test(expected = UnsupportedOperationException.class)
public void testSetPageInitializedException() {
uniqueBank.setPageInitialized(0, true, 0, 4096, 0);
}
//possibly add:
//zero-byte read/write
//try to write more bytes than the array has
//try to read more bytes into the array than it has
}

View file

@ -253,7 +253,7 @@ public class DummyTool extends PluginTool {
} }
@Override @Override
public void restoreWindowingDataFromXml(Element windowData) { public void restoreWindowingDataFromXml(Element element) {
//do nothing //do nothing
} }

View file

@ -32,7 +32,11 @@
# and the destination is not the upper half of the register (ie, bit 30 q=0) # and the destination is not the upper half of the register (ie, bit 30 q=0)
# then the unused remaining upper bits must be set to 0. # then the unused remaining upper bits must be set to 0.
@if DATA_ENDIAN == "little"
define endian=little; define endian=little;
@else
define endian=big;
@endif
define alignment=4; define alignment=4;
# Unlike the above, these are preprocessor macros. Use them with e.g. $(TAG_GRANULE) in SLEIGH statements. # Unlike the above, these are preprocessor macros. Use them with e.g. $(TAG_GRANULE) in SLEIGH statements.
@ -1008,7 +1012,7 @@ define context contextreg
ShowMemTag = (24,24) noflow ShowMemTag = (24,24) noflow
; ;
define token instrAARCH64 (32) define token instrAARCH64 (32) endian = little
Rm = (16,20) Rm = (16,20)
Rn = (5,9) Rn = (5,9)
@ -2644,27 +2648,86 @@ vIndexHLM: val is b_2223=0 & b_2121 & b_1111 & b_2020 [ val = b_1111 << 2 | b_21
vIndexHL: val is b_2223=0b01 & b_21 & b_11 [ val = b_11 << 1 | b_21; ] { export *[const]:8 val; } vIndexHL: val is b_2223=0b01 & b_21 & b_11 [ val = b_11 << 1 | b_21; ] { export *[const]:8 val; }
vIndexHL: b_11 is b_2223=0b10 & b_11 { export *[const]:8 b_11; } vIndexHL: b_11 is b_2223=0b10 & b_11 { export *[const]:8 b_11; }
Re_VPR128.B.vIndex: Re_VPR128.B^"["^vIndex^"]" is Re_VPR128.B & vIndex { } @if DATA_ENDIAN == "little"
Re_VPR128.S.vIndex: Re_VPR128.S^"["^vIndex^"]" is Re_VPR128.S & vIndex { } Re_VPR128.B.sel: Re_VPR128, val is Re_VPR128 & b_2222=0 & b_2121 & b_1111 [ val = 0x5000 + 32*Re_VPR128 + b_1111 * 2 + b_2121; ] { export *[register]:1 val; }
Re_VPR128.D.vIndex: Re_VPR128.D^"["^vIndex^"]" is Re_VPR128.D & vIndex { } Re_VPR128.B.sel: Re_VPR128, val is Re_VPR128 & b_2222=1 & b_2121=0 & b_1111 [ val = 0x5000 + 32*Re_VPR128 + b_1111; ] { export *[register]:1 val; }
Re_VPR128.S.sel: Re_VPR128, val is Re_VPR128 & b_2222=0 & b_2121 & b_1111 [ val = 0x5000 + 32*Re_VPR128 + (b_1111 * 2 + b_2121) * 4; ] { export *[register]:4 val; }
Re_VPR128.S.sel: Re_VPR128, val is Re_VPR128 & b_2222=1 & b_2121=0 & b_1111 [ val = 0x5000 + 32*Re_VPR128 + b_1111 * 4; ] { export *[register]:4 val; }
Re_VPR128.D.sel: Re_VPR128, val is Re_VPR128 & b_2222=0 & b_2121 & b_1111 [ val = 0x5000 + 32*Re_VPR128 + (b_1111 * 2 + b_2121) * 8; ] { export *[register]:8 val; }
Re_VPR128.D.sel: Re_VPR128, val is Re_VPR128 & b_2222=1 & b_2121=0 & b_1111 [ val = 0x5000 + 32*Re_VPR128 + b_1111 * 8; ] { export *[register]:8 val; }
@else
Re_VPR128.B.sel: Re_VPR128, val is Re_VPR128 & b_2222=0 & b_2121 & b_1111 [ val = 0x501f + 32*Re_VPR128 - b_1111 * 2 - b_2121; ] { export *[register]:1 val; }
Re_VPR128.B.sel: Re_VPR128, val is Re_VPR128 & b_2222=1 & b_2121=0 & b_1111 [ val = 0x501f + 32*Re_VPR128 - b_1111; ] { export *[register]:1 val; }
Re_VPR128.S.sel: Re_VPR128, val is Re_VPR128 & b_2222=0 & b_2121 & b_1111 [ val = 0x501c + 32*Re_VPR128 - (b_1111 * 2 + b_2121) * 4; ] { export *[register]:4 val; }
Re_VPR128.S.sel: Re_VPR128, val is Re_VPR128 & b_2222=1 & b_2121=0 & b_1111 [ val = 0x501c + 32*Re_VPR128 - b_1111 * 4; ] { export *[register]:4 val; }
Re_VPR128.D.sel: Re_VPR128, val is Re_VPR128 & b_2222=0 & b_2121 & b_1111 [ val = 0x5018 + 32*Re_VPR128 - (b_1111 * 2 + b_2121) * 8; ] { export *[register]:8 val; }
Re_VPR128.D.sel: Re_VPR128, val is Re_VPR128 & b_2222=1 & b_2121=0 & b_1111 [ val = 0x5018 + 32*Re_VPR128 - b_1111 * 8; ] { export *[register]:8 val; }
@endif
Rd_VPR128.B.imm_neon_uimm4: Rd_VPR128.B^"["^imm_neon_uimm4^"]" is Rd_VPR128.B & imm_neon_uimm4 { export Rd_VPR128.B; } Re_VPR128.B.vIndex: Re_VPR128.B^"["^vIndex^"]" is Re_VPR128.B & vIndex & Re_VPR128.B.sel { export Re_VPR128.B.sel; }
Rd_VPR128.H.imm_neon_uimm3: Rd_VPR128.H^"["^imm_neon_uimm3^"]" is Rd_VPR128.H & imm_neon_uimm3 { export Rd_VPR128.H; } Re_VPR128.S.vIndex: Re_VPR128.S^"["^vIndex^"]" is Re_VPR128.S & vIndex & Re_VPR128.S.sel { export Re_VPR128.S.sel; }
Rd_VPR128.S.imm_neon_uimm2: Rd_VPR128.S^"["^imm_neon_uimm2^"]" is Rd_VPR128.S & imm_neon_uimm2 { export Rd_VPR128.S; } Re_VPR128.D.vIndex: Re_VPR128.D^"["^vIndex^"]" is Re_VPR128.D & vIndex & Re_VPR128.D.sel { export Re_VPR128.D.sel; }
Rd_VPR128.D.imm_neon_uimm1: Rd_VPR128.D^"["^imm_neon_uimm1^"]" is Rd_VPR128.D & imm_neon_uimm1 { export Rd_VPR128.D; }
Rn_VPR128.B.immN_neon_uimm4: Rn_VPR128.B^"["^immN_neon_uimm4^"]" is Rn_VPR128.B & immN_neon_uimm4 { export Rn_VPR128.B; } @if DATA_ENDIAN == "little"
Rn_VPR128.H.immN_neon_uimm3: Rn_VPR128.H^"["^immN_neon_uimm3^"]" is Rn_VPR128.H & immN_neon_uimm3 { export Rn_VPR128.H; } Rd_VPR128.B.sel: Rd_VPR128, val is Rd_VPR128 & imm_neon_uimm4 [ val = 0x5000 + 32*Rd_VPR128 + imm_neon_uimm4; ] { export *[register]:1 val; }
Rn_VPR128.S.immN_neon_uimm2: Rn_VPR128.S^"["^immN_neon_uimm2^"]" is Rn_VPR128.S & immN_neon_uimm2 { export Rn_VPR128.S; } Rd_VPR128.H.sel: Rd_VPR128, val is Rd_VPR128 & imm_neon_uimm3 [ val = 0x5000 + 32*Rd_VPR128 + 2*imm_neon_uimm3; ] { export *[register]:2 val; }
Rn_VPR128.D.immN_neon_uimm1: Rn_VPR128.D^"["^immN_neon_uimm1^"]" is Rn_VPR128.D & immN_neon_uimm1 { export Rn_VPR128.D; } Rd_VPR128.S.sel: Rd_VPR128, val is Rd_VPR128 & imm_neon_uimm2 [ val = 0x5000 + 32*Rd_VPR128 + 4*imm_neon_uimm2; ] { export *[register]:4 val; }
Rd_VPR128.D.sel: Rd_VPR128, val is Rd_VPR128 & imm_neon_uimm1 [ val = 0x5000 + 32*Rd_VPR128 + 8*imm_neon_uimm1; ] { export *[register]:8 val; }
@else
Rd_VPR128.B.sel: Rd_VPR128, val is Rd_VPR128 & imm_neon_uimm4 [ val = 0x501f + 32*Rd_VPR128 - imm_neon_uimm4; ] { export *[register]:1 val; }
Rd_VPR128.H.sel: Rd_VPR128, val is Rd_VPR128 & imm_neon_uimm3 [ val = 0x501e + 32*Rd_VPR128 - 2*imm_neon_uimm3; ] { export *[register]:2 val; }
Rd_VPR128.S.sel: Rd_VPR128, val is Rd_VPR128 & imm_neon_uimm2 [ val = 0x501c + 32*Rd_VPR128 - 4*imm_neon_uimm2; ] { export *[register]:4 val; }
Rd_VPR128.D.sel: Rd_VPR128, val is Rd_VPR128 & imm_neon_uimm1 [ val = 0x5018 + 32*Rd_VPR128 - 8*imm_neon_uimm1; ] { export *[register]:8 val; }
@endif
Rd_VPR128.B.imm_neon_uimm4: Rd_VPR128.B^"["^imm_neon_uimm4^"]" is Rd_VPR128.B & imm_neon_uimm4 & Rd_VPR128.B.sel { export Rd_VPR128.B.sel; }
Rd_VPR128.H.imm_neon_uimm3: Rd_VPR128.H^"["^imm_neon_uimm3^"]" is Rd_VPR128.H & imm_neon_uimm3 & Rd_VPR128.H.sel { export Rd_VPR128.H.sel; }
Rd_VPR128.S.imm_neon_uimm2: Rd_VPR128.S^"["^imm_neon_uimm2^"]" is Rd_VPR128.S & imm_neon_uimm2 & Rd_VPR128.S.sel { export Rd_VPR128.S.sel; }
Rd_VPR128.D.imm_neon_uimm1: Rd_VPR128.D^"["^imm_neon_uimm1^"]" is Rd_VPR128.D & imm_neon_uimm1 & Rd_VPR128.D.sel { export Rd_VPR128.D.sel; }
Rn_VPR128.B.imm_neon_uimm4: Rn_VPR128.B^"["^imm_neon_uimm4^"]" is Rn_VPR128.B & imm_neon_uimm4 { export Rn_VPR128.B; } @if DATA_ENDIAN == "little"
Rn_VPR128.H.imm_neon_uimm3: Rn_VPR128.H^"["^imm_neon_uimm3^"]" is Rn_VPR128.H & imm_neon_uimm3 { export Rn_VPR128.H; } Rn_VPR128.B.selN: Rn_VPR128, val is Rn_VPR128 & immN_neon_uimm4 [ val = 0x5000 + 32*Rn_VPR128 + immN_neon_uimm4; ] { export *[register]:1 val; }
Rn_VPR128.S.imm_neon_uimm2: Rn_VPR128.S^"["^imm_neon_uimm2^"]" is Rn_VPR128.S & imm_neon_uimm2 { export Rn_VPR128.S; } Rn_VPR128.H.selN: Rn_VPR128, val is Rn_VPR128 & immN_neon_uimm3 [ val = 0x5000 + 32*Rn_VPR128 + 2*immN_neon_uimm3; ] { export *[register]:2 val; }
Rn_VPR128.D.imm_neon_uimm1: Rn_VPR128.D^"["^imm_neon_uimm1^"]" is Rn_VPR128.D & imm_neon_uimm1 { export Rn_VPR128.D; } Rn_VPR128.S.selN: Rn_VPR128, val is Rn_VPR128 & immN_neon_uimm2 [ val = 0x5000 + 32*Rn_VPR128 + 4*immN_neon_uimm2; ] { export *[register]:4 val; }
Rn_VPR128.D.selN: Rn_VPR128, val is Rn_VPR128 & immN_neon_uimm1 [ val = 0x5000 + 32*Rn_VPR128 + 8*immN_neon_uimm1; ] { export *[register]:8 val; }
@else
Rn_VPR128.B.selN: Rn_VPR128, val is Rn_VPR128 & immN_neon_uimm4 [ val = 0x501f + 32*Rn_VPR128 - immN_neon_uimm4; ] { export *[register]:1 val; }
Rn_VPR128.H.selN: Rn_VPR128, val is Rn_VPR128 & immN_neon_uimm3 [ val = 0x501e + 32*Rn_VPR128 - 2*immN_neon_uimm3; ] { export *[register]:2 val; }
Rn_VPR128.S.selN: Rn_VPR128, val is Rn_VPR128 & immN_neon_uimm2 [ val = 0x501c + 32*Rn_VPR128 - 4*immN_neon_uimm2; ] { export *[register]:4 val; }
Rn_VPR128.D.selN: Rn_VPR128, val is Rn_VPR128 & immN_neon_uimm1 [ val = 0x5018 + 32*Rn_VPR128 - 8*immN_neon_uimm1; ] { export *[register]:8 val; }
@endif
Rn_VPR128.B.immN_neon_uimm4: Rn_VPR128.B^"["^immN_neon_uimm4^"]" is Rn_VPR128.B & immN_neon_uimm4 & Rn_VPR128.B.selN { export Rn_VPR128.B.selN; }
Rn_VPR128.H.immN_neon_uimm3: Rn_VPR128.H^"["^immN_neon_uimm3^"]" is Rn_VPR128.H & immN_neon_uimm3 & Rn_VPR128.H.selN { export Rn_VPR128.H.selN; }
Rn_VPR128.S.immN_neon_uimm2: Rn_VPR128.S^"["^immN_neon_uimm2^"]" is Rn_VPR128.S & immN_neon_uimm2 & Rn_VPR128.S.selN { export Rn_VPR128.S.selN; }
Rn_VPR128.D.immN_neon_uimm1: Rn_VPR128.D^"["^immN_neon_uimm1^"]" is Rn_VPR128.D & immN_neon_uimm1 & Rn_VPR128.D.selN { export Rn_VPR128.D.selN; }
@if DATA_ENDIAN == "little"
Rn_VPR128.B.sel: Rn_VPR128, val is Rn_VPR128 & imm_neon_uimm4 [ val = 0x5000 + 32*Rn_VPR128 + imm_neon_uimm4; ] { export *[register]:1 val; }
Rn_VPR128.H.sel: Rn_VPR128, val is Rn_VPR128 & imm_neon_uimm3 [ val = 0x5000 + 32*Rn_VPR128 + 2*imm_neon_uimm3; ] { export *[register]:2 val; }
Rn_VPR128.S.sel: Rn_VPR128, val is Rn_VPR128 & imm_neon_uimm2 [ val = 0x5000 + 32*Rn_VPR128 + 4*imm_neon_uimm2; ] { export *[register]:4 val; }
Rn_VPR128.D.sel: Rn_VPR128, val is Rn_VPR128 & imm_neon_uimm1 [ val = 0x5000 + 32*Rn_VPR128 + 8*imm_neon_uimm1; ] { export *[register]:8 val; }
@else
Rn_VPR128.B.sel: Rn_VPR128, val is Rn_VPR128 & imm_neon_uimm4 [ val = 0x501f + 32*Rn_VPR128 - imm_neon_uimm4; ] { export *[register]:1 val; }
Rn_VPR128.H.sel: Rn_VPR128, val is Rn_VPR128 & imm_neon_uimm3 [ val = 0x501e + 32*Rn_VPR128 - 2*imm_neon_uimm3; ] { export *[register]:2 val; }
Rn_VPR128.S.sel: Rn_VPR128, val is Rn_VPR128 & imm_neon_uimm2 [ val = 0x501c + 32*Rn_VPR128 - 4*imm_neon_uimm2; ] { export *[register]:4 val; }
Rn_VPR128.D.sel: Rn_VPR128, val is Rn_VPR128 & imm_neon_uimm1 [ val = 0x5018 + 32*Rn_VPR128 - 8*imm_neon_uimm1; ] { export *[register]:8 val; }
@endif
Rn_VPR128.B.imm_neon_uimm4: Rn_VPR128.B^"["^imm_neon_uimm4^"]" is Rn_VPR128.B & imm_neon_uimm4 & Rn_VPR128.B.sel { export Rn_VPR128.B.sel; }
Rn_VPR128.H.imm_neon_uimm3: Rn_VPR128.H^"["^imm_neon_uimm3^"]" is Rn_VPR128.H & imm_neon_uimm3 & Rn_VPR128.H.sel { export Rn_VPR128.H.sel; }
Rn_VPR128.S.imm_neon_uimm2: Rn_VPR128.S^"["^imm_neon_uimm2^"]" is Rn_VPR128.S & imm_neon_uimm2 & Rn_VPR128.S.sel { export Rn_VPR128.S.sel; }
Rn_VPR128.D.imm_neon_uimm1: Rn_VPR128.D^"["^imm_neon_uimm1^"]" is Rn_VPR128.D & imm_neon_uimm1 & Rn_VPR128.D.sel { export Rn_VPR128.D.sel; }
Re_VPR128.H.vIndexHL: Re_VPR128.H^"["^vIndexHL^"]" is Re_VPR128.H & vIndexHL { } Re_VPR128.H.vIndexHL: Re_VPR128.H^"["^vIndexHL^"]" is Re_VPR128.H & vIndexHL { }
Re_VPR128Lo.H.vIndexHLM: Re_VPR128Lo.H^"["^vIndexHLM^"]" is Re_VPR128Lo.H & vIndexHLM { }
@if DATA_ENDIAN == "little"
Re_VPR128Lo.H.sel: Re_VPR128, val is Re_VPR128 & b_2223=2 & b_2121 & b_1111 [ val = 0x5000 + 32*Re_VPR128 + (b_1111 * 2 + b_2121)*2; ] { export *[register]:2 val; }
Re_VPR128Lo.H.sel: Re_VPR128, val is Re_VPR128 & b_2223=1 & b_2121 & b_1111 & b_2020 [ val = 0x5000 + 32*Re_VPR128 + (b_1111*4 + b_2121*2 + b_2020)*2; ] { export *[register]:2 val; }
Re_VPR128Lo.H.sel: Re_VPR128, val is Re_VPR128 & b_2223=0 & b_2121 & b_1111 & b_2020 [ val = 0x5000 + 32*Re_VPR128 + (b_1111*4 + b_2121*2 + b_2020)*2; ] { export *[register]:2 val; }
@else
Re_VPR128Lo.H.sel: Re_VPR128, val is Re_VPR128 & b_2223=2 & b_2121 & b_1111 [ val = 0x501e + 32*Re_VPR128 - (b_1111 * 2 + b_2121)*2; ] { export *[register]:2 val; }
Re_VPR128Lo.H.sel: Re_VPR128, val is Re_VPR128 & b_2223=1 & b_2121 & b_1111 & b_2020 [ val = 0x501e + 32*Re_VPR128 - (b_1111*4 + b_2121*2 + b_2020)*2; ] { export *[register]:2 val; }
Re_VPR128Lo.H.sel: Re_VPR128, val is Re_VPR128 & b_2223=0 & b_2121 & b_1111 & b_2020 [ val = 0x501e + 32*Re_VPR128 - (b_1111*4 + b_2121*2 + b_2020)*2; ] { export *[register]:2 val; }
@endif
Re_VPR128Lo.H.vIndexHLM: Re_VPR128Lo.H^"["^vIndexHLM^"]" is Re_VPR128Lo.H & vIndexHLM & Re_VPR128Lo.H.sel { export Re_VPR128Lo.H.sel; }
FBitsOp: "#"^fbits is Scale [ fbits = 64 - Scale; ] { export *[const]:2 fbits; } FBitsOp: "#"^fbits is Scale [ fbits = 64 - Scale; ] { export *[const]:2 fbits; }
@ -3193,55 +3256,17 @@ PACIXSP_BTITARGETS: is ShowBTI=0 { }
# These pseudo ops are used in neon # These pseudo ops are used in neon
define pcodeop SIMD_COPY;
define pcodeop SIMD_FLOAT;
define pcodeop SIMD_FLOAT2FLOAT;
define pcodeop SIMD_FLOAT_ABS;
define pcodeop SIMD_FLOAT_ADD;
define pcodeop SIMD_FLOAT_DIV;
define pcodeop SIMD_FLOAT_MULT;
define pcodeop SIMD_FLOAT_NEG;
define pcodeop SIMD_FLOAT_SUB;
define pcodeop SIMD_INT;
define pcodeop SIMD_INT_2COMP;
define pcodeop SIMD_INT_ABS;
define pcodeop SIMD_INT_ADD;
define pcodeop SIMD_INT_AND;
define pcodeop SIMD_INT_LEFT;
define pcodeop SIMD_INT_LESS;
define pcodeop SIMD_INT_MULT;
define pcodeop SIMD_INT_NEGATE;
define pcodeop SIMD_INT_OR;
define pcodeop SIMD_INT_RIGHT;
define pcodeop SIMD_INT_SEXT;
define pcodeop SIMD_INT_SLESS;
define pcodeop SIMD_INT_SRIGHT;
define pcodeop SIMD_INT_SUB;
define pcodeop SIMD_INT_XOR;
define pcodeop SIMD_INT_ZEXT;
define pcodeop SIMD_PIECE; define pcodeop SIMD_PIECE;
define pcodeop SIMD_TRUNC;
define pcodeop NEON_abs;
define pcodeop NEON_add;
define pcodeop NEON_addhn;
define pcodeop NEON_addhn2;
define pcodeop NEON_addp;
define pcodeop NEON_addv; define pcodeop NEON_addv;
define pcodeop NEON_aesd; define pcodeop NEON_aesd;
define pcodeop NEON_aese; define pcodeop NEON_aese;
define pcodeop NEON_aesimc; define pcodeop NEON_aesimc;
define pcodeop NEON_aesmc; define pcodeop NEON_aesmc;
define pcodeop NEON_and;
define pcodeop NEON_bcax;
define pcodeop NEON_bfcvt;
define pcodeop NEON_bfcvtn;
define pcodeop NEON_bfcvtn2;
define pcodeop NEON_bfdot; define pcodeop NEON_bfdot;
define pcodeop NEON_bfmlalb; define pcodeop NEON_bfmlalb;
define pcodeop NEON_bfmlalt; define pcodeop NEON_bfmlalt;
define pcodeop NEON_bfmmla; define pcodeop NEON_bfmmla;
define pcodeop NEON_bic;
define pcodeop NEON_bif; define pcodeop NEON_bif;
define pcodeop NEON_bit; define pcodeop NEON_bit;
define pcodeop NEON_bsl; define pcodeop NEON_bsl;
@ -3256,40 +3281,18 @@ define pcodeop NEON_cmle;
define pcodeop NEON_cmlt; define pcodeop NEON_cmlt;
define pcodeop NEON_cmtst; define pcodeop NEON_cmtst;
define pcodeop NEON_cnt; define pcodeop NEON_cnt;
define pcodeop NEON_dup;
define pcodeop NEON_eor;
define pcodeop NEON_eor3;
define pcodeop NEON_ext; define pcodeop NEON_ext;
define pcodeop NEON_fabd;
define pcodeop NEON_fabs;
define pcodeop NEON_facge; define pcodeop NEON_facge;
define pcodeop NEON_facgt; define pcodeop NEON_facgt;
define pcodeop NEON_fadd;
define pcodeop NEON_faddp;
define pcodeop NEON_fcadd; define pcodeop NEON_fcadd;
define pcodeop NEON_fccmp;
define pcodeop NEON_fccmpe;
define pcodeop NEON_fcmeq; define pcodeop NEON_fcmeq;
define pcodeop NEON_fcmge; define pcodeop NEON_fcmge;
define pcodeop NEON_fcmgt; define pcodeop NEON_fcmgt;
define pcodeop NEON_fcmla; define pcodeop NEON_fcmla;
define pcodeop NEON_fcmle; define pcodeop NEON_fcmle;
define pcodeop NEON_fcmlt; define pcodeop NEON_fcmlt;
define pcodeop NEON_fcmp;
define pcodeop NEON_fcmpe;
define pcodeop NEON_fcsel;
define pcodeop NEON_fcvt;
define pcodeop NEON_fcvt_amnpz_su;
define pcodeop NEON_fcvtl;
define pcodeop NEON_fcvtl2;
define pcodeop NEON_fcvtn;
define pcodeop NEON_fcvtn2;
define pcodeop NEON_fcvtxn;
define pcodeop NEON_fcvtxn2;
define pcodeop NEON_fcvtzs; define pcodeop NEON_fcvtzs;
define pcodeop NEON_fcvtzu; define pcodeop NEON_fcvtzu;
define pcodeop NEON_fdiv;
define pcodeop NEON_fjcvtzs;
define pcodeop NEON_fmadd; define pcodeop NEON_fmadd;
define pcodeop NEON_fmax; define pcodeop NEON_fmax;
define pcodeop NEON_fmaxnm; define pcodeop NEON_fmaxnm;
@ -3303,50 +3306,22 @@ define pcodeop NEON_fminnmp;
define pcodeop NEON_fminnmv; define pcodeop NEON_fminnmv;
define pcodeop NEON_fminp; define pcodeop NEON_fminp;
define pcodeop NEON_fminv; define pcodeop NEON_fminv;
define pcodeop NEON_fmla;
define pcodeop NEON_fmlal;
define pcodeop NEON_fmlal2;
define pcodeop NEON_fmls;
define pcodeop NEON_fmlsl;
define pcodeop NEON_fmlsl2;
define pcodeop NEON_fmov; define pcodeop NEON_fmov;
define pcodeop NEON_fmsub; define pcodeop NEON_fmsub;
define pcodeop NEON_fmul;
define pcodeop NEON_fmulx; define pcodeop NEON_fmulx;
define pcodeop NEON_fneg;
define pcodeop NEON_fnmadd; define pcodeop NEON_fnmadd;
define pcodeop NEON_fnmsub; define pcodeop NEON_fnmsub;
define pcodeop NEON_fnmul;
define pcodeop NEON_frecpe; define pcodeop NEON_frecpe;
define pcodeop NEON_frecps; define pcodeop NEON_frecps;
define pcodeop NEON_frecpx; define pcodeop NEON_frecpx;
define pcodeop NEON_frint_aimnpxz;
define pcodeop NEON_frsqrte; define pcodeop NEON_frsqrte;
define pcodeop NEON_frsqrts; define pcodeop NEON_frsqrts;
define pcodeop NEON_fsqrt; define pcodeop NEON_fsqrt;
define pcodeop NEON_fsub;
define pcodeop NEON_ldnp1;
define pcodeop NEON_ldnp2;
define pcodeop NEON_ldp1;
define pcodeop NEON_ldp2;
define pcodeop NEON_ldr;
define pcodeop NEON_ldur;
define pcodeop NEON_mla;
define pcodeop NEON_mls;
define pcodeop NEON_mov;
define pcodeop NEON_movi;
define pcodeop NEON_mul;
define pcodeop NEON_mvn;
define pcodeop NEON_mvni;
define pcodeop NEON_neg; define pcodeop NEON_neg;
define pcodeop NEON_orn;
define pcodeop NEON_orr;
define pcodeop NEON_pmul; define pcodeop NEON_pmul;
define pcodeop NEON_pmull; define pcodeop NEON_pmull;
define pcodeop NEON_pmull2; define pcodeop NEON_pmull2;
define pcodeop NEON_raddhn; define pcodeop NEON_raddhn;
define pcodeop NEON_raddhn2;
define pcodeop NEON_rax1;
define pcodeop NEON_rbit; define pcodeop NEON_rbit;
define pcodeop NEON_rev16; define pcodeop NEON_rev16;
define pcodeop NEON_rev32; define pcodeop NEON_rev32;
@ -3356,22 +3331,11 @@ define pcodeop NEON_rshrn2;
define pcodeop NEON_rsubhn; define pcodeop NEON_rsubhn;
define pcodeop NEON_rsubhn2; define pcodeop NEON_rsubhn2;
define pcodeop NEON_saba; define pcodeop NEON_saba;
define pcodeop NEON_sabal;
define pcodeop NEON_sabal2;
define pcodeop NEON_sabd; define pcodeop NEON_sabd;
define pcodeop NEON_sabdl;
define pcodeop NEON_sabdl2;
define pcodeop NEON_sadalp;
define pcodeop NEON_saddl;
define pcodeop NEON_saddl2;
define pcodeop NEON_saddlp;
define pcodeop NEON_saddlv; define pcodeop NEON_saddlv;
define pcodeop NEON_saddw;
define pcodeop NEON_saddw2;
define pcodeop NEON_scvtf; define pcodeop NEON_scvtf;
define pcodeop NEON_sdot; define pcodeop NEON_sdot;
define pcodeop NEON_sha1c; define pcodeop NEON_sha1c;
define pcodeop NEON_sha1h;
define pcodeop NEON_sha1m; define pcodeop NEON_sha1m;
define pcodeop NEON_sha1p; define pcodeop NEON_sha1p;
define pcodeop NEON_sha1su0; define pcodeop NEON_sha1su0;
@ -3386,10 +3350,6 @@ define pcodeop NEON_sha512su0;
define pcodeop NEON_sha512su1; define pcodeop NEON_sha512su1;
define pcodeop NEON_shadd; define pcodeop NEON_shadd;
define pcodeop NEON_shl; define pcodeop NEON_shl;
define pcodeop NEON_shll;
define pcodeop NEON_shll2;
define pcodeop NEON_shrn;
define pcodeop NEON_shrn2;
define pcodeop NEON_shsub; define pcodeop NEON_shsub;
define pcodeop NEON_sli; define pcodeop NEON_sli;
define pcodeop NEON_sm3partw1; define pcodeop NEON_sm3partw1;
@ -3407,24 +3367,10 @@ define pcodeop NEON_smaxv;
define pcodeop NEON_smin; define pcodeop NEON_smin;
define pcodeop NEON_sminp; define pcodeop NEON_sminp;
define pcodeop NEON_sminv; define pcodeop NEON_sminv;
define pcodeop NEON_smlal;
define pcodeop NEON_smlal2;
define pcodeop NEON_smlsl;
define pcodeop NEON_smlsl2;
define pcodeop NEON_smov;
define pcodeop NEON_smmla; define pcodeop NEON_smmla;
define pcodeop NEON_smull;
define pcodeop NEON_smull2;
define pcodeop NEON_sqabs;
define pcodeop NEON_sqadd; define pcodeop NEON_sqadd;
define pcodeop NEON_sqdmlal;
define pcodeop NEON_sqdmlal2;
define pcodeop NEON_sqdmlsl;
define pcodeop NEON_sqdmlsl2;
define pcodeop NEON_sqdmulh; define pcodeop NEON_sqdmulh;
define pcodeop NEON_sqdmull; define pcodeop NEON_sqdmull;
define pcodeop NEON_sqdmull2;
define pcodeop NEON_sqneg;
define pcodeop NEON_sqrdml_as_h; define pcodeop NEON_sqrdml_as_h;
define pcodeop NEON_sqrdmulh; define pcodeop NEON_sqrdmulh;
define pcodeop NEON_sqrshl; define pcodeop NEON_sqrshl;
@ -3447,45 +3393,12 @@ define pcodeop NEON_srhadd;
define pcodeop NEON_sri; define pcodeop NEON_sri;
define pcodeop NEON_srshl; define pcodeop NEON_srshl;
define pcodeop NEON_srshr; define pcodeop NEON_srshr;
define pcodeop NEON_srsra;
define pcodeop NEON_sshl; define pcodeop NEON_sshl;
define pcodeop NEON_sshll;
define pcodeop NEON_sshll2;
define pcodeop NEON_sshr; define pcodeop NEON_sshr;
define pcodeop NEON_ssra;
define pcodeop NEON_ssubl;
define pcodeop NEON_ssubl2;
define pcodeop NEON_ssubw;
define pcodeop NEON_ssubw2;
define pcodeop NEON_stnp1;
define pcodeop NEON_stnp2;
define pcodeop NEON_stp1;
define pcodeop NEON_stp2;
define pcodeop NEON_str;
define pcodeop NEON_stur;
define pcodeop NEON_sub;
define pcodeop NEON_subhn;
define pcodeop NEON_subhn2;
define pcodeop NEON_sudot; define pcodeop NEON_sudot;
define pcodeop NEON_suqadd;
define pcodeop NEON_sxtl;
define pcodeop NEON_sxtl2;
define pcodeop NEON_tblx;
define pcodeop NEON_trn1;
define pcodeop NEON_trn2;
define pcodeop NEON_uaba; define pcodeop NEON_uaba;
define pcodeop NEON_uabal;
define pcodeop NEON_uabal2;
define pcodeop NEON_uabd; define pcodeop NEON_uabd;
define pcodeop NEON_uabdl;
define pcodeop NEON_uabdl2;
define pcodeop NEON_uadalp;
define pcodeop NEON_uaddl;
define pcodeop NEON_uaddl2;
define pcodeop NEON_uaddlp;
define pcodeop NEON_uaddlv; define pcodeop NEON_uaddlv;
define pcodeop NEON_uaddw;
define pcodeop NEON_uaddw2;
define pcodeop NEON_ucvtf; define pcodeop NEON_ucvtf;
define pcodeop NEON_udot; define pcodeop NEON_udot;
define pcodeop NEON_uhadd; define pcodeop NEON_uhadd;
@ -3496,14 +3409,8 @@ define pcodeop NEON_umaxv;
define pcodeop NEON_umin; define pcodeop NEON_umin;
define pcodeop NEON_uminp; define pcodeop NEON_uminp;
define pcodeop NEON_uminv; define pcodeop NEON_uminv;
define pcodeop NEON_umlal;
define pcodeop NEON_umlal2;
define pcodeop NEON_umlsl;
define pcodeop NEON_umlsl2;
define pcodeop NEON_ummla; define pcodeop NEON_ummla;
define pcodeop NEON_umov;
define pcodeop NEON_umull; define pcodeop NEON_umull;
define pcodeop NEON_umull2;
define pcodeop NEON_uqadd; define pcodeop NEON_uqadd;
define pcodeop NEON_uqrshl; define pcodeop NEON_uqrshl;
define pcodeop NEON_uqrshrn; define pcodeop NEON_uqrshrn;
@ -3519,28 +3426,10 @@ define pcodeop NEON_urhadd;
define pcodeop NEON_urshl; define pcodeop NEON_urshl;
define pcodeop NEON_urshr; define pcodeop NEON_urshr;
define pcodeop NEON_ursqrte; define pcodeop NEON_ursqrte;
define pcodeop NEON_ursra;
define pcodeop NEON_usdot; define pcodeop NEON_usdot;
define pcodeop NEON_ushl; define pcodeop NEON_ushl;
define pcodeop NEON_ushll;
define pcodeop NEON_ushll2;
define pcodeop NEON_ushr;
define pcodeop NEON_usmmla; define pcodeop NEON_usmmla;
define pcodeop NEON_usqadd; define pcodeop NEON_usqadd;
define pcodeop NEON_usra;
define pcodeop NEON_usubl;
define pcodeop NEON_usubl2;
define pcodeop NEON_usubw;
define pcodeop NEON_usubw2;
define pcodeop NEON_uxtl;
define pcodeop NEON_uxtl2;
define pcodeop NEON_uzp1;
define pcodeop NEON_uzp2;
define pcodeop NEON_xar;
define pcodeop NEON_xtn;
define pcodeop NEON_xtn2;
define pcodeop NEON_zip1;
define pcodeop NEON_zip2;
# These pseudo ops are automatically generated # These pseudo ops are automatically generated
@ -4013,112 +3902,59 @@ macro set_NZCV(value, condMask)
# Macro to access simd lanes # Macro to access simd lanes
macro simd_address_at(dest, reg, elem, esize, vsize)
{
@if DATA_ENDIAN == "little"
dest = &reg + elem * esize;
@else
dest = &reg + vsize - esize - elem * esize;
@endif
}
# Macros to zero the high bits of the Z or Q registers # Macros to zero the high bits of the Z or Q registers
# These are friendlier to the decompiler # These are friendlier to the decompiler
macro zext_zb(reg) macro zext_zb(reg)
{ {
@if DATA_ENDIAN == "little"
reg[8,56] = 0; reg[8,56] = 0;
reg[64,64] = 0; reg[64,64] = 0;
reg[128,64] = 0; reg[128,64] = 0;
reg[192,64] = 0; reg[192,64] = 0;
@else
reg[192,56] = 0;
reg[128,64] = 0;
reg[64,64] = 0;
reg[0,64] = 0;
@endif
} }
macro zext_zh(reg) macro zext_zh(reg)
{ {
@if DATA_ENDIAN == "little"
reg[16,48] = 0; reg[16,48] = 0;
reg[64,64] = 0; reg[64,64] = 0;
reg[128,64] = 0; reg[128,64] = 0;
reg[192,64] = 0; reg[192,64] = 0;
@else
reg[192,48] = 0;
reg[128,64] = 0;
reg[64,64] = 0;
reg[0,64] = 0;
@endif
} }
macro zext_zs(reg) macro zext_zs(reg)
{ {
@if DATA_ENDIAN == "little"
reg[32,32] = 0; reg[32,32] = 0;
reg[64,64] = 0; reg[64,64] = 0;
reg[128,64] = 0; reg[128,64] = 0;
reg[192,64] = 0; reg[192,64] = 0;
@else
reg[192,32] = 0;
reg[128,64] = 0;
reg[64,64] = 0;
reg[0,64] = 0;
@endif
} }
macro zext_zd(reg) macro zext_zd(reg)
{ {
@if DATA_ENDIAN == "little"
reg[64,64] = 0; reg[64,64] = 0;
reg[128,64] = 0; reg[128,64] = 0;
reg[192,64] = 0; reg[192,64] = 0;
@else
reg[0,64] = 0;
reg[64,64] = 0;
reg[128,64] = 0;
@endif
} }
macro zext_zq(reg) macro zext_zq(reg)
{ {
@if DATA_ENDIAN == "little"
reg[128,64] = 0; reg[128,64] = 0;
reg[192,64] = 0; reg[192,64] = 0;
@else
reg[0,64] = 0;
reg[64,64] = 0;
@endif
} }
macro zext_rb(reg) macro zext_rb(reg)
{ {
@if DATA_ENDIAN == "little"
reg[8,56] = 0; reg[8,56] = 0;
@else
reg[0,56] = 0;
@endif
} }
macro zext_rh(reg) macro zext_rh(reg)
{ {
@if DATA_ENDIAN == "little"
reg[16,48] = 0; reg[16,48] = 0;
@else
reg[0,48] = 0;
@endif
} }
macro zext_rs(reg) macro zext_rs(reg)
{ {
@if DATA_ENDIAN == "little"
reg[32,32] = 0; reg[32,32] = 0;
@else
reg[0,32] = 0;
@endif
} }
# SECTION instructions # SECTION instructions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more