mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-13 13:39:07 +08:00
GP-729 Decode RUNTIME_INFO and UNWIND_INFO structures in the PE .pdata section
This commit is contained in:
@@ -29,7 +29,8 @@ If applicable, please attach any files that caused problems or log files generat
|
||||
**Environment (please complete the following information):**
|
||||
- OS: [e.g. macOS 10.14.2]
|
||||
- Java Version: [e.g. 11.0]
|
||||
- Ghidra Version: [e.g. 9.0]
|
||||
- Ghidra Version: [e.g. 9.1.2]
|
||||
- Ghidra Origin: [e.g. official ghidra-sre.org distro, third party distro, locally built]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
||||
@@ -6,14 +6,61 @@
|
||||
</HEAD>
|
||||
|
||||
<BODY>
|
||||
<H1 align="center">Ghidra 9.2.3 Change History (March 2021)</H1>
|
||||
<blockquote><p><u>Improvements</u></p>
|
||||
<ul>
|
||||
<li><I>Analysis</I>. Added check for vftable entries in <code>.NEP</code> section and relaxed the requirement that the code must have a return. (GP-649)</li>
|
||||
<li><I>Analysis</I>. Corrected flaw in RTTI analyzer determination of size of vftables. (GP-688)</li>
|
||||
<li><I>Basic Infrastructure</I>. Updated TLS protocol preference to use the most preferred/recent version available to both sides of an SSL connection (e.g., TLSv1.3) instead of forcing use of TLSv1.2. (GP-622)</li>
|
||||
<li><I>Build</I>. Corrected build issues which had prevented users from building Ghidra on an Apple M1 (OS X, AARCH64 architecture). (GP-600, Issue #2653)</li>
|
||||
<li><I>Demangler</I>. Increased Gnu Demangler parsing performance by changing some regular expressions. (GP-705)</li>
|
||||
<li><I>Eclipse Integration</I>. Updated SleighEditor to support new endian tag on <B>define token</B> definitions. (GP-721)</li>
|
||||
<li><I>GUI</I>. Updated the Choose Data Type dialog to apply data types in the same manner as dragging types from the Data Types window. This provides users more control when choosing how to overwrite existing types. (GP-521)</li>
|
||||
<li><I>Importer:ELF</I>. Added support for ELF relocation <code>R_X86_64_IRELATIVE</code>. (GP-651, Issue #1189)</li>
|
||||
<li><I>Importer:ELF</I>. Sped up loading of ELF files with large symbol tables. (GP-697)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
<blockquote><p><u>Bugs</u></p>
|
||||
<ul>
|
||||
<li><I>Analysis</I>. The RTTI analyzer now runs prior to Reference analysis so that references into vftables are not turned into code or data before the vftables are created. (GP-517)</li>
|
||||
<li><I>API</I>. <code>Funtion.getCalledFunctions(TaskMonitor)</code> and <code>Function.getCallingFunctions(TaskMonitor)</code> now support passing <code>null</code> for the task monitor parameter, which previously would have thrown an exception. (GP-589, Issue #2643)</li>
|
||||
<li><I>Data Types</I>. Corrected segmented 32-bit pointer datatype address generation for 16:16 x86 far pointers. (GP-534, Issue #2548)</li>
|
||||
<li><I>Decompiler</I>. Fixed Decompiler issue where, when a function name extends beyond the line limit, an end-of-line comment could wrap around to additional lines without including additional <code>//</code> comment indicators. (GP-473)</li>
|
||||
<li><I>Decompiler</I>. Corrected an exception that could occur when attempting to edit function signature from the Decompiler. (GP-597, Issue #2601)</li>
|
||||
<li><I>Eclipse Integration</I>. When installing the SleighEditor into Eclipse, the plugin will now show up under the Ghidra category. Previously the <B>Group Items by Category</B> option had to be turned off before the SleighEditor would appear as a visible entry. (GP-564)</li>
|
||||
<li><I>Eclipse Integration</I>. Fixed an issue with Eclipse PyDev breakpoints not catching. (GP-668, Issue #2713)</li>
|
||||
<li><I>Eclipse Integration</I>. Fixed an Eclipse GhidraDev exception that occurred when creating a new Ghidra scripting project if a <B>~/ghidra_scripts</B> directory did not exist. (GP-669)</li>
|
||||
<li><I>Emulator</I>. Replaced Java floating point emulation to fix multiple rounding issues. (GP-357, Issue #2414)</li>
|
||||
<li><I>Graphing</I>. Fixed issue with graph filters not updating satellite view when changing edge filters. (GP-557)</li>
|
||||
<li><I>Graphing</I>. Fixed Function Graph keybindings that did not work when docked in the main Code Browser window. (GP-586, Issue #2641)</li>
|
||||
<li><I>GUI</I>. Fixed NPE due to using <B>Go To</B> action when there was no open program in the Listing. (GP-66)</li>
|
||||
<li><I>GUI</I>. Fixed bug in Reference Code Viewer options that caused an exception. (GP-620, Issue #2672)</li>
|
||||
<li><I>Importer</I>. Fixed exception caused when importing previously exported XML data where the bookmark override option was turned off. (GP-667)</li>
|
||||
<li><I>Importer:ELF</I>. Fixed a NullPointerException caused by importing an ELF with an uninitialized <code>.got</code> section. (GP-360, Issue #2416)</li>
|
||||
<li><I>Importer:ELF</I>. Added Support for ELF <code>R_ARM_MOVW_ABS_NC</code> and <code>R_ARM_MOVT_ABS ELF</code> Relocations for ARM. (GP-555, Issue #2510)</li>
|
||||
<li><I>Importer:ELF</I>. Corrected ELF processing of <code>.init_array</code> and <code>.fini_array</code> which was incorrectly overadjusting entries for an image base change. (GP-699)</li>
|
||||
<li><I>Importer:Mach-O</I>. Corrected Mach-O fat-binary library import issue and resolved error related to unnamed Mach-O segment. (GP-652, Issue #2702)</li>
|
||||
<li><I>Importer:Mach-O</I>. Fixed an issue with DYLD Load Command data structures being created in the wrong locations. (GP-689, Issue #2624)</li>
|
||||
<li><I>Importer:Mach-O</I>. Fixed an exception that occurred when importing Mach-O files that define zero <code>LC_BUILD_VERSION</code> tool entries. (GP-702, Issue #2192)</li>
|
||||
<li><I>PDB</I>. Fixed createPdbXmlFiles.bat to permit spaces in the path name of Ghidra installation folder and the batch argument name. (GP-575, Issue #2167)</li>
|
||||
<li><I>PDB</I>. Fix PDB Universal analyzer to set the run-once flag when finished. (GP-724)</li>
|
||||
<li><I>Processors</I>. Added missing <code>RFE</code> instruction in MIPS up to version R3000. (GP-33, Issue #1766)</li>
|
||||
<li><I>Processors</I>. ARM instruction <code>VMUL</code> now decodes correctly. (GP-627, Issue #2677)</li>
|
||||
<li><I>Processors</I>. Added missing <code>CFINV</code> instruction to AARCH64 processor specification and added definitions for locals in neon instructions. (GP-655, Issue #2710)</li>
|
||||
<li><I>Scripting</I>. Fixed analyzeHeadless <code><B>-scriptPath</B></code> option that didn't work for Python and other non-Java scripts located in non-default directories. (GP-528, Issue #2561)</li>
|
||||
<li><I>Scripting</I>. Fixed concurrency issue with management of scripting bundle paths. (GP-576)</li>
|
||||
<li><I>Scripting</I>. Corrected handling for Ghidra Script files which are symlinks that were broken in Ghidra 9.2. (GP-650, Issue #2698)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
|
||||
<H1 align="center">Ghidra 9.2.2 Change History (December 2020)</H1>
|
||||
<blockquote><p><u>Bugs</u></p>
|
||||
<ul>
|
||||
<li><I>Graphing</I>. Fixed issue with Graph filters not working and satellite view sometimes not matching graph. (GP-526)</li>
|
||||
<li><I>Importer:MachO</I>. MachO DYLD cache incorrect offset use has been fixed. (GP-550, Issue ##2560)</li>
|
||||
<li><I>Listing</I>. Fixed issue where Edit Label action (L key) did not work on primary function symbols. (GP-537)</li>
|
||||
<li><I>Importer:Mach-O</I>. Mach-O DYLD cache incorrect offset use has been fixed. (GP-550, Issue #2560)</li>
|
||||
<li><I>Listing</I>. Fixed issue where <B>Edit Label</B> action (L key) did not work on primary function symbols. (GP-537)</li>
|
||||
<li><I>Multi-User</I>. Corrected Ghidra Server build issue for version 9.2.1 which had an improperly generated <B>classpath.frag</B> file. Issue caused server to fail startup with a ClassNotFoundException. (GP-542)</li>
|
||||
<li><I>Processors</I>. The V850 JMP instruction has been corrected not to use the PC in the address calculation (GP-548, Issue #2570)</li>
|
||||
<li><I>Processors</I>. The V850 <code>JMP</code> instruction has been corrected not to use the PC in the address calculation. (GP-548, Issue #2570)</li>
|
||||
<li><I>Processors</I>. Removed erroneous VST4 variant, most likely from a copy/paste error. This fixes the ARM Thumb BL instruction disassembly with a negative offset. (GP-549, Issue #2559)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
@@ -24,6 +71,7 @@
|
||||
<li><I>Analysis</I>. Updated RTTI analyzer to find <code>type_info</code> vftable when it cannot be found with its mangled name. This will enable many more Windows programs to have their RTTI structures created that were unable to be parsed in previous Ghidra versions. (GP-141)</li>
|
||||
<li><I>API</I>. Relaxed memory block naming restrictions and restored ability to have spaces in memory block names. However, if a memory block is flagged as an overlay, the associated overlay space name may be modified to ensure validity and uniqueness. The DuplicateNameException has been removed from all memory block API methods since this was entirely an overlay space concern. Memory block GUI has also been changed eliminate the duplicate block name restriction. (GP-420, Issue #2465)</li>
|
||||
<li><I>Build</I>. Eliminated the need for installation of <B>bison</B> and <B>flex</B> when performing source-based <B>gradle</B> build of Ghidra or the Decompiler module. The generated files are now included with source files and maintained in source control. A separate <code><B>gradle Decompiler:generateParsers</B></code> task, which still requires <B>bison</B> and <B>flex</B>, must be used, explicitly, when changes are made to lex/yacc source files. (GP-467)</li>
|
||||
<li><I>Graphing</I>. Fixed issue with exporting graphs to DOT format due to invalid vertex IDs. (GP-280)</li>
|
||||
<li><I>Graphing</I>. Improved graphing where it did not navigate when clicking on external function nodes. Now it will navigate to the <B>fake</B> function location in the program, which is the location of the pointer to the external function. (GP-493)</li>
|
||||
<li><I>Listing:Symbols</I>. Removed restriction for naming labels that resemble default label names. (GT-3185, Issue #1057)</li>
|
||||
<li><I>PDB</I>. Crafted PDB type ID records <code>0x1608</code> and <code>0x1609</code> with presumed <B>class</B> and <B>struct</B> types and follow-on application of these types. Also fixed up some fall-back data type logic and improved some warning messages to reflect the <B>cause</B> of the conditions. (GP-474, Issue #2523)</li>
|
||||
@@ -37,7 +85,7 @@
|
||||
<li><I>Decompiler</I>. Fixed issue with the Auto Create/Fill Structure command that caused it to silently miss some pointer accesses. (GP-344)</li>
|
||||
<li><I>Decompiler</I>. Jump table recovery now takes into account encoded bits, like ARM/THUMB mode transition, that may be present in address tables. (GP-387, Issue #2420)</li>
|
||||
<li><I>Decompiler</I>. Fixed a bug in the Decompiler <B>renaming</B> action when applied to function references. (GP-477, Issue #2415)</li>
|
||||
<li><I>Decompiler</I>. Corrected 8-byte return value storage specification in compiler-spec affecting longlong and double return values. Endianess ordering of r0/r1 was incorrect. (GP-512, Issue #2547)</li>
|
||||
<li><I>Decompiler</I>. Corrected 8-byte return value storage specification in compiler-spec affecting <code>longlong</code> and <code>double</code> return values. Endianess ordering of <code>r0</code>/<code>r1</code> was incorrect. (GP-512, Issue #2547)</li>
|
||||
<li><I>Graphing</I>. Fixed the Function Graph's <B>drag-to-select-nodes</B> feature. (GP-430)</li>
|
||||
<li><I>Graphing</I>. Fixed issue where the graph in the satellite view is sometimes truncated. (GP-469)</li>
|
||||
<li><I>Graphing</I>. Fixed a stack trace issue caused by reusing a graph display window to show a graph that is larger than is allowed. (GP-492)</li>
|
||||
@@ -126,7 +174,7 @@
|
||||
<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:ELF</I>. Added support for ELF RELR relocations, such as those produced for Android. (GP-348)</li>
|
||||
<li><I>Importer:MachO</I>. DYLD Loader can now load x86_64 DYLD from macOS. (GT-3611, Issue #1566)</li>
|
||||
<li><I>Importer:Mach-O</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</I>. In the PCode field of the Listing, accesses of varnodes in the <code>unique</code> space are now always shown with the size of the access. Fixed bug which would cause the PCode emulator to reject valid pcode in rare instances. (GP-196)</li>
|
||||
@@ -282,8 +330,8 @@
|
||||
<li><I>Disassembly</I>. Corrected potential infinite loop with disassembler caused by branch to self with invalid delay slot instruction. (GT-3511, Issue #1486)</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>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>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>Importer:Mach-O</I>. A Mach-O 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>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>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>
|
||||
@@ -294,7 +342,7 @@
|
||||
<H1 align="center">Ghidra 9.1.1 Change History (December 2019)</H1>
|
||||
<blockquote><p><u>Improvements</u></p>
|
||||
<ul>
|
||||
<li><I>Importer:MachO</I>. Improved import/load time of DYLD shared cache files. (GT-3261)</li>
|
||||
<li><I>Importer:Mach-O</I>. Improved import/load time of DYLD shared cache files. (GT-3261)</li>
|
||||
<li><I>Program API</I>. Cached the addresses that correspond to executable memory to improve analysis performance. (GT-3260)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
@@ -331,7 +379,7 @@
|
||||
<li><I>Eclipse Integration</I>. Added new GhidraSleighEditor Eclipse plugin in the installation directory under Extensions/Eclipse. (GT-113)</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>Importer:MachO</I>. Added new importer/loader for DYLD-shared cache files. (GT-2343)</li>
|
||||
<li><I>Importer:Mach-O</I>. Added new importer/loader for DYLD-shared cache files. (GT-2343)</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>
|
||||
@@ -488,7 +536,7 @@
|
||||
<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>. Fixed a problem with register highlighting that could occur on certain register/sub-register combinations. (GT-3071, Issue #810)</li>
|
||||
<li><I>Multi-User</I>. Corrected terminate checkout from viewed checkout list which was always terminating first row range based upon number of selected rows and not the actual selected rows. (GT-2903)</li>
|
||||
<li><I>Multi-user</I>. Corrected ability for user to cancel checkin/checkout to Ghidra Server. (GT-3208)</li>
|
||||
<li><I>Multi-User</I>. Corrected ability for user to cancel checkin/checkout to Ghidra Server. (GT-3208)</li>
|
||||
<li><I>Multi-User:Ghidra Server</I>. Added proper Ghidra Server interface binding with new <code><B>-i</B></code> option. Corrected <code><B>-ip</B></code> option to strictly convey remote access hostname to clients. The updated server will only accept connections from Ghidra 9.1 and later clients due to the registry port now employing TLS. (GT-2685, Issue #101, #645)</li>
|
||||
<li><I>Multi-User:Ghidra Server</I>. Fixed argument-passing bug in svrAdmin script. (GT-3082, Issue #907)</li>
|
||||
<li><I>Multi-User:Merge</I>. Corrected merge problem affecting modified Function Definition datatypes which could result in a NullPointerException. (GT-2922)</li>
|
||||
@@ -518,7 +566,7 @@
|
||||
<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>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 path traversal vulnerability that could occur when restoring a malicious project archive. (GT-3001, Issue #789)</li>
|
||||
<li><I>Project Manager</I>. Fixed a path-traversal vulnerability that could occur when restoring a malicious project archive. (GT-3001, Issue #789)</li>
|
||||
<li><I>Scripting</I>. <code>GhidraScript.askDomainFile()</code> now correctly throws a CancelledException when the cancel button is clicked. (GT-2841)</li>
|
||||
<li><I>Scripting</I>. Removed deprecated scripting methods older than 5 releases. (GT-2949)</li>
|
||||
<li><I>Security</I>. Removed use of nonsecure XMLEncoder/XMLDecoder from Ghidra code base. (GT-3198, Issue #1090)</li>
|
||||
@@ -531,128 +579,123 @@
|
||||
<H1 align="center">Ghidra 9.0.4 Change History (May 2019)</H1>
|
||||
<blockquote><p><u>Bugs</u></p>
|
||||
<ul>
|
||||
<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>Multi-User:Ghidra Server</I>. Corrected severe script error in svrAdmin.bat introduced with 9.0.3 build. (GT-2874)</li>
|
||||
<li><I>GUI</I>. Restored the default 'p' key binding for creating pointers within the listing display. (GT-2854)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
|
||||
<H1 align="center">Ghidra 9.0.3 Change History (April 2019)</H1>
|
||||
<blockquote><p><u>New Features</u></p>
|
||||
<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. (GT-2114)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
|
||||
<blockquote><p><u>Improvements</u></p>
|
||||
<ul>
|
||||
<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>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>Decompiler</I>. Improved modeling of CFG on Windows 10. (GT-2755, Issue #340)</li>
|
||||
<li><I>Patcher</I>. Renamed patch directory to <install dir>/Ghidra/patch and added README.txt that explains how the patch directory is used. (GT-2734)</li>
|
||||
<li><I>Search</I>. Updated the Decompiler Data Type Finder to find references inside of nested array access in a line of Decompiler C output. (GT-2756, Issue #416)</li>
|
||||
<li><I>Sleigh</I>. Improved error reporting for SLEIGH compiler. (GT-2820, Issue #364)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
|
||||
<blockquote><p><u>Bugs</u></p>
|
||||
<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>. 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>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>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>Analysis</I>. Code that checks for thunks no longer throws an exception if the PC is not set for the processor. (GT-2730)</li>
|
||||
<li><I>Analysis</I>. Made a fix to enable Apply button when changing tool options. (GT-2801, 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. (GT-2736)</li>
|
||||
<li><I>Decompiler</I>. Fixed dynamic variables and equates in 16-bit x86 programs. (GT-2745, Issue #336)</li>
|
||||
<li><I>Decompiler:Java</I>. Fixed DEX decompilation regression issue. (Issue #350, GT-2743)</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. (GT-2721, Issues #283, #383)</li>
|
||||
<li><I>GUI</I>. Improved documentation on how to deal with HiDPI monitor issues in Linux. In the <I><ghidra_installation></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>Multi-User:Ghidra Server</I>. Restored ability to execute svrAdmin script in development mode. </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>Processors</I>. The 68000 BCD arithmetic instructions now have pcode semantics that allow disassembly to continue. (Issue #227)</li>
|
||||
<li><I>Search</I>. Fixed NullPointerException in Decompiler Data Type Reference Finder. (Issue #407)</li>
|
||||
<li><I>Importer</I>. Fixed an exception that occurred when batch importing APK files. (GT-2767, Issue #426)</li>
|
||||
<li><I>Multi-User:Ghidra Server</I>. Restored ability to execute svrAdmin script in development mode. (GT-2740) </li>
|
||||
<li><I>Processors</I>. The 6502 Zero page indexed addressing has been corrected to only access the Zero page. (GT-2759, Issue #201)</li>
|
||||
<li><I>Processors</I>. The M68000 BCD arithmetic instructions now have pcode semantics that allow disassembly to continue. (GT-2807, Issue #227)</li>
|
||||
<li><I>Search</I>. Fixed NullPointerException in Decompiler Data Type Reference Finder. (GT-2754. Issue #407)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
|
||||
<H1 align="center">Ghidra 9.0.2 Change History (April 2019)</H1>
|
||||
<blockquote><p><u>Bugs</u></p>
|
||||
<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>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 "Free Varnode" exception in RuleConditionalMove. (Issue #294) </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>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>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>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>Scripting</I>. MultiInstructionMemReference script has been corrected to consider input and output registers when placing a reference on an instruction.</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. (GT-2723, Issue #228)</li>
|
||||
<li><I>Decompiler</I>. Fixed Decompiler handling of Function Definition data types. (GT-2704, Issue #247)</li>
|
||||
<li><I>Decompiler</I>. Fixed rendering bug in the Decompiler when the "Find" dialog is closed. (GT-2716, Issue #282)</li>
|
||||
<li><I>Decompiler</I>. Fixed "Free Varnode" exception in RuleConditionalMove. (GT-2726, Issue #294)</li>
|
||||
<li><I>Diff</I>. Fixed exceptions that can occur in the Diff View for programs with overlays. (GT-2706)</li>
|
||||
<li><I>Documentation</I>. Corrected the spelling of "listener" throughout the source code. (GT-2702, Issue #235)</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. (GT-2703, Issue #260)</li>
|
||||
<li><I>GUI</I>. Fixed exception that occurs after disabling MyProgramChangesDisplayPlugin. (GT-2712)</li>
|
||||
<li><I>GUI</I>. Updated the "Open Program" dialog to disallow file drop operations. (GT-2705, Issue #252)</li>
|
||||
<li><I>Multi-User:Ghidra Server</I>. Corrected bug introduced into ghidraSvr.bat which could prevent Ghidra Server startup. (GT-2717, Issue #279)</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. (GT-2722, Issue #280)</li>
|
||||
<li><I>Scripting</I>. MultiInstructionMemReference script has been corrected to consider input and output registers when placing a reference on an instruction. (GT-2723)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
|
||||
<blockquote><p><u>Security</u></p>
|
||||
<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. (GT-2725, Issue #286)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
|
||||
<H1 align="center">Ghidra 9.0.1 Change History (March 2019)</H1>
|
||||
<blockquote><p><u>New Features</u></p>
|
||||
<ul>
|
||||
<li><I>Scripting</I>. Created a script to show all equates within the current selection. (Issue #111)</li>
|
||||
<li><I>Scripting</I>. Created ShowEquatesInSelectionScript to show all equates within the current selection. (GT-2651, Issue #111)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
|
||||
<blockquote><p><u>Improvements</u></p>
|
||||
<ul>
|
||||
<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>GUI</I>. Turned on font anti-aliasing by default for Linux. (Issue #212)</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
|
||||
when multiple symbol tables exist within the ELF binary. (Issue #52)</li>
|
||||
<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>
|
||||
<li><I>Processors</I>. Added ARM/Thumb SRS instruction decodes for undefined modes. (Issue #216)</li>
|
||||
<li><I>Basic Infrastructure</I>. Updated commons-compress library to version 1.18. (GT-2657, Issue #171)</li>
|
||||
<li><I>Eclipse Integration</I>. Ghidra now connects to the Eclipse GhidraDev plugin on 127.0.0.1 rather than localhost. (GT-2691)</li>
|
||||
<li><I>GUI</I>. Turned on font anti-aliasing by default for Linux. (GT-2674, Issue #212)</li>
|
||||
<li><I>GUI</I>. Fixed Options Dialog slow scrolling speed. (GT-2679, Issue #27)</li>
|
||||
<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. (GT-2646, Issue #52)</li>
|
||||
<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. (GT-2637)</li>
|
||||
<li><I>Processors</I>. Added ARM/Thumb SRS instruction decodes for undefined modes. (GT-2676, Issue #216)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
|
||||
<blockquote><p><u>Bugs</u></p>
|
||||
<ul>
|
||||
<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>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.
|
||||
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>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>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>. 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 stack trace in the Data Type Manager's tooltip generation. (Issue #133)</li>
|
||||
<li><I>GUI</I>. User key binding settings for the Recently Used and Define Pointer actions no longer lost after re-launching tool. (Issue #152)</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:PE</I>. The PeLoader now correctly parses the GuardCFFunctionTable when entries are more than 4 bytes each. (Issue #220)</li>
|
||||
<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
|
||||
considered for a future release.</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>API</I>. Fixed equals method on Varnode class. (GT-2648, Issue #97)</li>
|
||||
<li><I>API</I>. Fixed a bug in MaskImpl.complementMask(). (GT-2694, Issue #187)</li>
|
||||
<li><I>Basic Infrastructure</I>. Fixed special character handling in idaxml.py. (GT-2669, 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. This fixes certain unexpected exceptions. (GT-2680, Issue #209)</li>
|
||||
<li><I>Diff</I>. Fixed exception occasionally encountered when starting a Diff session. (GT-2672, Issue #211)</li>
|
||||
<li><I>Documentation</I>. Fixed javadoc search box redirecting to broken links. (GT-2655, Issue #129)</li>
|
||||
<li><I>Function Graph</I>. Fixed Function Graph exception when generating tooltip. (GT-2650, Issue #65)</li>
|
||||
<li><I>GUI</I>. Updated window placement to keep windows on screen. (GT-1516, Issue #41)</li>
|
||||
<li><I>GUI</I>. Add/Edit References dialog now restricts users to creating refs in valid memory address spaces. (GT-2638)</li>
|
||||
<li><I>GUI</I>. Fixed exception when exiting Ghidra while a table is being edited. (GT-2642, Issue #51)</li>
|
||||
<li><I>GUI</I>. Fixed some touchpad scrolling issues. (GT-2647, Issue #2)</li>
|
||||
<li><I>GUI</I>. Fixed stack trace in the Data Type Manager's tooltip generation. (GT-2656, Issue #133)</li>
|
||||
<li><I>GUI</I>. User key binding settings for the Recently Used and Define Pointer actions no longer lost after re-launching tool. (GT-2659, Issue #152)</li>
|
||||
<li><I>GUI</I>. Toolbar buttons now respond to fast clicking. (GT-2689)</li>
|
||||
<li><I>Importer:Mach-O</I>. The Mach-O loader can now find import libraries found in Universal Binary files. (GT-2663, Issue #136)</li>
|
||||
<li><I>Importer:PE</I>. The PeLoader now correctly parses the GuardCFFunctionTable when table entries are more than 4 bytes each. (GT-2671, Issue #220)</li>
|
||||
<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 considered for a future release. (GT-2653)</li>
|
||||
<li><I>PDB</I>. Corrected NPE error when processing PDB files. (GT-2673, Issues #138, #188)</li>
|
||||
<li><I>Processors</I>. Added missing PowerPC VLE conditional branch instructions: e_bdnz and e_bdz. (GT-2652, 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. (GT-2660, Issues #22, #53, #158, #157)</li>
|
||||
<li><I>Processors</I>. The M68000 MOVE instruction now correctly sets the CF and VF flags. (GT-2661, Issue #163)</li>
|
||||
<li><I>Processors</I>. Added four missing MOVEM instruction variants to the M68000 processor. (GT-2675, Issue #219)</li>
|
||||
<li><I>Processors</I>. An incorrect usage of X instead of Y in indexed mode for the 6502 has been corrected. (GT-2677, Issue #201)</li>
|
||||
<li><I>Processors</I>. PPC VLE now disassembles base PPC instructions that are valid in VLE mode. (GT-2681, Issue #127)</li>
|
||||
<li><I>Processors</I>. Added support for ARM Thumb half BL instruction on processor variants prior to v6. (GT-2684, Issue #39)</li>
|
||||
<li><I>Scripting</I>. Fixed a bug in ImportSymbolsScript.py that prevented it from running. (GT-2668, Issue #170)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
|
||||
<blockquote><p><u>Security</u></p>
|
||||
<ul>
|
||||
<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>Project Manager</I>. Fixed an XXE vulnerability affecting projects and many other saved components. (Issue #71)</li>
|
||||
<li><I>Basic Infrastructure</I>. Running Ghidra in debug mode no longer opens remotely accessible ports by default. (GT-2641, Issue #6)</li>
|
||||
<li><I>GUI</I>. The Defined Strings plugin no longer renders HTML in its table. (GT-2686, Issue #45)</li>
|
||||
<li><I>Project Manager</I>. Fixed an XXE vulnerability affecting projects and many other saved components. (GT-2643, Issue #71)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
abort
|
||||
CxxThrowException
|
||||
CxxThrowException@8
|
||||
CxxFrameHandler3
|
||||
crtExitProcess
|
||||
ExitProcess
|
||||
ExitThread
|
||||
exit
|
||||
FreeLibraryAndExitThread
|
||||
invalid_parameter_noinfo_noreturn
|
||||
invoke_watson
|
||||
longjmp
|
||||
quick_exit
|
||||
RpcRaiseException
|
||||
terminate
|
||||
|
||||
+28
-16
@@ -17,6 +17,7 @@ package ghidra.app.plugin.core.navigation;
|
||||
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.*;
|
||||
import docking.tool.ToolConstants;
|
||||
import ghidra.GhidraOptions;
|
||||
@@ -29,7 +30,7 @@ import ghidra.app.util.HelpTopics;
|
||||
import ghidra.app.util.navigation.GoToAddressLabelDialog;
|
||||
import ghidra.framework.options.*;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.util.*;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.bean.opteditor.OptionsVetoException;
|
||||
|
||||
@@ -91,11 +92,21 @@ public class GoToAddressLabelPlugin extends Plugin implements OptionsChangeListe
|
||||
public void actionPerformed(NavigatableActionContext context) {
|
||||
goToDialog.show(context.getNavigatable(), context.getAddress(), tool);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEnabledForContext(NavigatableActionContext context) {
|
||||
return context.getProgram() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAddToPopup(ActionContext context) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
action.setHelpLocation(new HelpLocation(HelpTopics.NAVIGATION, action.getName()));
|
||||
action.setMenuBarData(new MenuData(
|
||||
new String[] { ToolConstants.MENU_NAVIGATION, "Go To..." }, null, "GoTo",
|
||||
MenuData.NO_MNEMONIC, "2")); // second item in the menu
|
||||
action.setMenuBarData(
|
||||
new MenuData(new String[] { ToolConstants.MENU_NAVIGATION, "Go To..." }, null, "GoTo",
|
||||
MenuData.NO_MNEMONIC, "2")); // second item in the menu
|
||||
|
||||
action.setKeyBindingData(new KeyBindingData(KeyEvent.VK_G, 0));
|
||||
|
||||
@@ -144,10 +155,11 @@ public class GoToAddressLabelPlugin extends Plugin implements OptionsChangeListe
|
||||
* @param newValue new value of the option
|
||||
*/
|
||||
@Override
|
||||
public void optionsChanged(ToolOptions options, String opName, Object oldValue, Object newValue) {
|
||||
public void optionsChanged(ToolOptions options, String opName, Object oldValue,
|
||||
Object newValue) {
|
||||
if (opName.equals(GhidraOptions.OPTION_MAX_GO_TO_ENTRIES)) {
|
||||
maximumGotoEntries =
|
||||
options.getInt(GhidraOptions.OPTION_MAX_GO_TO_ENTRIES, DEFAULT_MAX_GOTO_ENTRIES);
|
||||
options.getInt(GhidraOptions.OPTION_MAX_GO_TO_ENTRIES, DEFAULT_MAX_GOTO_ENTRIES);
|
||||
if (maximumGotoEntries <= 0) {
|
||||
throw new OptionsVetoException("Search limit must be greater than 0");
|
||||
}
|
||||
@@ -155,7 +167,7 @@ public class GoToAddressLabelPlugin extends Plugin implements OptionsChangeListe
|
||||
}
|
||||
else if (opName.equals(GhidraOptions.OPTION_NUMERIC_FORMATTING)) {
|
||||
cStyleInput =
|
||||
options.getBoolean(GhidraOptions.OPTION_NUMERIC_FORMATTING, DEFAULT_C_STYLE);
|
||||
options.getBoolean(GhidraOptions.OPTION_NUMERIC_FORMATTING, DEFAULT_C_STYLE);
|
||||
goToDialog.setCStyleInput(cStyleInput);
|
||||
}
|
||||
else if (opName.equals(GO_TO_MEMORY)) {
|
||||
@@ -182,20 +194,20 @@ public class GoToAddressLabelPlugin extends Plugin implements OptionsChangeListe
|
||||
ToolOptions opt = tool.getOptions(ToolConstants.TOOL_OPTIONS);
|
||||
// descriptions
|
||||
opt.registerOption(GhidraOptions.OPTION_NUMERIC_FORMATTING, DEFAULT_C_STYLE, null,
|
||||
"Interpret value entered in the Go To dialog as either hex, "
|
||||
+ "octal, or binary number.");
|
||||
"Interpret value entered in the Go To dialog as either hex, " +
|
||||
"octal, or binary number.");
|
||||
opt.registerOption(GhidraOptions.OPTION_MAX_GO_TO_ENTRIES, DEFAULT_MAX_GOTO_ENTRIES, null,
|
||||
"Max number of entries remembered in the go to list.");
|
||||
"Max number of entries remembered in the go to list.");
|
||||
opt.registerOption(GO_TO_MEMORY, DEFAULT_MEMORY, null,
|
||||
"Remember the last successful go to input in the "
|
||||
+ "Go To dialog. If this option is enabled, then the "
|
||||
+ "Go To dialog will leave the last "
|
||||
+ "successful go to input in the combo box of the Go "
|
||||
+ "To dialog and will select the " + "value for easy paste replacement.");
|
||||
"Remember the last successful go to input in the " +
|
||||
"Go To dialog. If this option is enabled, then the " +
|
||||
"Go To dialog will leave the last " +
|
||||
"successful go to input in the combo box of the Go " +
|
||||
"To dialog and will select the " + "value for easy paste replacement.");
|
||||
|
||||
// options
|
||||
maximumGotoEntries =
|
||||
opt.getInt(GhidraOptions.OPTION_MAX_GO_TO_ENTRIES, DEFAULT_MAX_GOTO_ENTRIES);
|
||||
opt.getInt(GhidraOptions.OPTION_MAX_GO_TO_ENTRIES, DEFAULT_MAX_GOTO_ENTRIES);
|
||||
|
||||
cStyleInput = opt.getBoolean(GhidraOptions.OPTION_NUMERIC_FORMATTING, DEFAULT_C_STYLE);
|
||||
goToDialog.setCStyleInput(cStyleInput);
|
||||
|
||||
+298
-255
File diff suppressed because it is too large
Load Diff
+486
@@ -0,0 +1,486 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.bin.format.pe;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
/**
|
||||
* typedef struct _IMAGE_RUNTIME_FUNCTION_ENTRY {
|
||||
* DWORD BeginAddress;
|
||||
* DWORD EndAddress;
|
||||
* union {
|
||||
* DWORD UnwindInfoAddress;
|
||||
* DWORD UnwindData;
|
||||
* } DUMMYUNIONNAME;
|
||||
* } RUNTIME_FUNCTION, *PRUNTIME_FUNCTION, _IMAGE_RUNTIME_FUNCTION_ENTRY, *_PIMAGE_RUNTIME_FUNCTION_ENTRY;
|
||||
*
|
||||
* #define UNW_FLAG_NHANDLER 0x0
|
||||
* #define UNW_FLAG_EHANDLER 0x1
|
||||
* #define UNW_FLAG_UHANDLER 0x2
|
||||
* #define UNW_FLAG_CHAININFO 0x4
|
||||
*
|
||||
* typedef struct _UNWIND_INFO {
|
||||
* UCHAR Version : 3;
|
||||
* UCHAR Flags : 5;
|
||||
* UCHAR SizeOfProlog;
|
||||
* UCHAR CountOfUnwindCodes;
|
||||
* UCHAR FrameRegister : 4;
|
||||
* UCHAR FrameOffset : 4;
|
||||
* UNWIND_CODE UnwindCode[1];
|
||||
*
|
||||
* //
|
||||
* // The unwind codes are followed by an optional DWORD aligned field that
|
||||
* // contains the exception handler address or the address of chained unwind
|
||||
* // information. If an exception handler address is specified, then it is
|
||||
* // followed by the language specified exception handler data.
|
||||
* //
|
||||
* // union {
|
||||
* // ULONG ExceptionHandler;
|
||||
* // ULONG FunctionEntry;
|
||||
* // };
|
||||
* //
|
||||
* // ULONG ExceptionData[];
|
||||
* //
|
||||
* } UNWIND_INFO, *PUNWIND_INFO;
|
||||
*/
|
||||
public class ImageRuntimeFunctionEntries {
|
||||
private final static int UNWIND_INFO_VERSION_BITMASK = 0x07;
|
||||
private final static int UNWIND_INFO_FLAGS_SHIFT = 0x03;
|
||||
private final static int UNWIND_INFO_FRAME_REGISTER_MASK = 0x0F;
|
||||
private final static int UNWIND_INFO_FRAME_OFFSET_SHIFT = 0x04;
|
||||
private final static int UNWIND_INFO_OPCODE_MASK = 0x0F;
|
||||
private final static int UNWIND_INFO_OPCODE_INFO_SHIFT = 0x04;
|
||||
private final static int UNWIND_INFO_SIZE = 0x0C;
|
||||
|
||||
List<_IMAGE_RUNTIME_FUNCTION_ENTRY> functionEntries = new ArrayList<>();
|
||||
|
||||
static ImageRuntimeFunctionEntries createImageRuntimeFunctionEntries(
|
||||
FactoryBundledWithBinaryReader reader, long index, NTHeader ntHeader)
|
||||
throws IOException {
|
||||
ImageRuntimeFunctionEntries imageRuntimeFunctionEntriesSection =
|
||||
(ImageRuntimeFunctionEntries) reader.getFactory()
|
||||
.create(ImageRuntimeFunctionEntries.class);
|
||||
imageRuntimeFunctionEntriesSection.initImageRuntimeFunctionEntries(reader, index, ntHeader);
|
||||
return imageRuntimeFunctionEntriesSection;
|
||||
}
|
||||
|
||||
/**
|
||||
* DO NOT USE THIS CONSTRUCTOR, USE create*(GenericFactory ...) FACTORY METHODS INSTEAD.
|
||||
*/
|
||||
public ImageRuntimeFunctionEntries() {
|
||||
}
|
||||
|
||||
private void initImageRuntimeFunctionEntries(FactoryBundledWithBinaryReader reader, long index,
|
||||
NTHeader ntHeader) throws IOException {
|
||||
|
||||
int entryCount = 0;
|
||||
|
||||
// Find the exception handler data section. This is an unbounded array of
|
||||
// RUNTIME_INFO structures one after another and there's no count field
|
||||
// to tell us how many there are, so get the maximum number there could be
|
||||
// based on the size of the section.
|
||||
FileHeader fh = ntHeader.getFileHeader();
|
||||
for (SectionHeader section : fh.getSectionHeaders()) {
|
||||
if (section.getName().contentEquals(".pdata")) {
|
||||
entryCount = section.getSizeOfRawData() / UNWIND_INFO_SIZE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (entryCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
long origIndex = reader.getPointerIndex();
|
||||
|
||||
reader.setPointerIndex(index);
|
||||
|
||||
for (int i = 0; i < entryCount; i++) {
|
||||
_IMAGE_RUNTIME_FUNCTION_ENTRY entry = new _IMAGE_RUNTIME_FUNCTION_ENTRY();
|
||||
entry.beginAddress = reader.readNextUnsignedInt();
|
||||
entry.endAddress = reader.readNextUnsignedInt();
|
||||
entry.unwindInfoAddressOrData = reader.readNextUnsignedInt();
|
||||
|
||||
// When the size of the section is bigger than the number of structures
|
||||
// the structure data fields will all be null, signaling the end of the
|
||||
// array of structures. Break out here.
|
||||
if (entry.beginAddress == 0 && entry.endAddress == 0 &&
|
||||
entry.unwindInfoAddressOrData == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Read and process the UNWIND_INFO structures the RUNTIME_INFO
|
||||
// structures point to
|
||||
entry.unwindInfo = readUnwindInfo(reader, entry.unwindInfoAddressOrData, ntHeader);
|
||||
|
||||
functionEntries.add(entry);
|
||||
}
|
||||
|
||||
reader.setPointerIndex(origIndex);
|
||||
}
|
||||
|
||||
private UNWIND_INFO readUnwindInfo(FactoryBundledWithBinaryReader reader, long offset,
|
||||
NTHeader ntHeader) throws IOException {
|
||||
long origIndex = reader.getPointerIndex();
|
||||
|
||||
long pointer = ntHeader.rvaToPointer(offset);
|
||||
UNWIND_INFO unwindInfo = new UNWIND_INFO(pointer);
|
||||
|
||||
if (pointer < 0) {
|
||||
return unwindInfo;
|
||||
}
|
||||
|
||||
reader.setPointerIndex(pointer);
|
||||
byte splitByte = reader.readNextByte();
|
||||
unwindInfo.version = (byte) (splitByte & UNWIND_INFO_VERSION_BITMASK);
|
||||
unwindInfo.flags = (byte) (splitByte >> UNWIND_INFO_FLAGS_SHIFT);
|
||||
|
||||
unwindInfo.sizeOfProlog = reader.readNextByte();
|
||||
unwindInfo.countOfUnwindCodes = reader.readNextByte();
|
||||
|
||||
splitByte = reader.readNextByte();
|
||||
unwindInfo.frameRegister = (byte) (splitByte & UNWIND_INFO_FRAME_REGISTER_MASK);
|
||||
unwindInfo.frameOffset = (byte) (splitByte >> UNWIND_INFO_FRAME_OFFSET_SHIFT);
|
||||
|
||||
unwindInfo.unwindCodes = new UNWIND_CODE[unwindInfo.countOfUnwindCodes];
|
||||
for (int i = 0; i < unwindInfo.countOfUnwindCodes; i++) {
|
||||
UNWIND_CODE code = new UNWIND_CODE();
|
||||
code.offsetInProlog = reader.readNextByte();
|
||||
|
||||
int opCodeData = reader.readNextUnsignedByte();
|
||||
code.opCode = UNWIND_CODE_OPCODE.fromInt((opCodeData & UNWIND_INFO_OPCODE_MASK));
|
||||
code.opInfoRegister =
|
||||
UNWIND_CODE_OPINFO_REGISTER.fromInt(opCodeData >> UNWIND_INFO_OPCODE_INFO_SHIFT);
|
||||
|
||||
unwindInfo.unwindCodes[i] = code;
|
||||
}
|
||||
|
||||
// You can have an exception handler and/or an unwind handler, or you
|
||||
// can have chained exception handling info only.
|
||||
if (unwindInfo.hasExceptionHandler() || unwindInfo.hasUnwindHandler()) {
|
||||
if (unwindInfo.hasExceptionHandler()) {
|
||||
unwindInfo.exceptionHandlerFunction = reader.readNextInt();
|
||||
}
|
||||
if (unwindInfo.hasUnwindHandler()) {
|
||||
unwindInfo.unwindHandlerFunction = reader.readNextInt();
|
||||
}
|
||||
}
|
||||
else if (unwindInfo.hasChainedUnwindInfo()) {
|
||||
unwindInfo.unwindHandlerChainInfo = new _IMAGE_RUNTIME_FUNCTION_ENTRY();
|
||||
unwindInfo.unwindHandlerChainInfo.beginAddress = reader.readNextInt();
|
||||
unwindInfo.unwindHandlerChainInfo.endAddress = reader.readNextInt();
|
||||
unwindInfo.unwindHandlerChainInfo.unwindInfoAddressOrData = reader.readNextInt();
|
||||
|
||||
// Follow the chain to the referenced UNWIND_INFO structure until we
|
||||
// get to the end
|
||||
unwindInfo.unwindHandlerChainInfo.unwindInfo = readUnwindInfo(reader,
|
||||
unwindInfo.unwindHandlerChainInfo.unwindInfoAddressOrData, ntHeader);
|
||||
}
|
||||
|
||||
reader.setPointerIndex(origIndex);
|
||||
|
||||
return unwindInfo;
|
||||
}
|
||||
|
||||
public List<_IMAGE_RUNTIME_FUNCTION_ENTRY> getRuntimeFunctionEntries() {
|
||||
return functionEntries;
|
||||
}
|
||||
|
||||
public class _IMAGE_RUNTIME_FUNCTION_ENTRY {
|
||||
public long beginAddress;
|
||||
public long endAddress;
|
||||
public long unwindInfoAddressOrData;
|
||||
public UNWIND_INFO unwindInfo;
|
||||
}
|
||||
|
||||
public enum UNWIND_CODE_OPCODE {
|
||||
UWOP_PUSH_NONVOL(0x00),
|
||||
UWOP_ALLOC_LARGE(0x01),
|
||||
UWOP_ALLOC_SMALL(0x02),
|
||||
UWOP_SET_FPREG(0x03),
|
||||
UWOP_SAVE_NONVOL(0x04),
|
||||
UWOP_SAVE_NONVOL_FAR(0x05),
|
||||
UWOP_SAVE_XMM(0x06),
|
||||
UWOP_SAVE_XMM_FAR(0x07),
|
||||
UWOP_SAVE_XMM128(0x08),
|
||||
UWOP_SAVE_XMM128_FAR(0x09),
|
||||
UWOP_PUSH_MACHFRAME(0x0A);
|
||||
|
||||
private final int id;
|
||||
|
||||
UNWIND_CODE_OPCODE(int value) {
|
||||
id = value;
|
||||
}
|
||||
|
||||
public int id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public static UNWIND_CODE_OPCODE fromInt(int id) {
|
||||
UNWIND_CODE_OPCODE[] values = UNWIND_CODE_OPCODE.values();
|
||||
for (UNWIND_CODE_OPCODE value : values) {
|
||||
if (value.id == id) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public enum UNWIND_CODE_OPINFO_REGISTER {
|
||||
UNWIND_OPINFO_REGISTER_RAX(0x00),
|
||||
UNWIND_OPINFO_REGISTER_RCX(0x01),
|
||||
UNWIND_OPINFO_REGISTER_RDX(0x02),
|
||||
UNWIND_OPINFO_REGISTER_RBX(0x03),
|
||||
UNWIND_OPINFO_REGISTER_RSP(0x04),
|
||||
UNWIND_OPINFO_REGISTER_RBP(0x05),
|
||||
UNWIND_OPINFO_REGISTER_RSI(0x06),
|
||||
UNWIND_OPINFO_REGISTER_RDI(0x07),
|
||||
UNWIND_OPINFO_REGISTER_R8(0x08),
|
||||
UNWIND_OPINFO_REGISTER_R9(0x09),
|
||||
UNWIND_OPINFO_REGISTER_R10(0x0A),
|
||||
UNWIND_OPINFO_REGISTER_R11(0x0B),
|
||||
UNWIND_OPINFO_REGISTER_R12(0x0C),
|
||||
UNWIND_OPINFO_REGISTER_R13(0x0D),
|
||||
UNWIND_OPINFO_REGISTER_R14(0x0E),
|
||||
UNWIND_OPINFO_REGISTER_R15(0x0F);
|
||||
|
||||
private final int id;
|
||||
|
||||
UNWIND_CODE_OPINFO_REGISTER(int value) {
|
||||
id = value;
|
||||
}
|
||||
|
||||
public int id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public static UNWIND_CODE_OPINFO_REGISTER fromInt(int id) {
|
||||
UNWIND_CODE_OPINFO_REGISTER[] values = UNWIND_CODE_OPINFO_REGISTER.values();
|
||||
for (UNWIND_CODE_OPINFO_REGISTER value : values) {
|
||||
if (value.id == id) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class UNWIND_CODE {
|
||||
public byte offsetInProlog;
|
||||
public UNWIND_CODE_OPCODE opCode;
|
||||
public UNWIND_CODE_OPINFO_REGISTER opInfoRegister;
|
||||
}
|
||||
|
||||
public class UNWIND_INFO implements StructConverter {
|
||||
private static final String NAME = "UNWIND_INFO";
|
||||
|
||||
private final static int UNW_FLAG_NHANDLER = 0x0;
|
||||
private final static int UNW_FLAG_EHANDLER = 0x1;
|
||||
private final static int UNW_FLAG_UHANDLER = 0x2;
|
||||
private final static int UNW_FLAG_CHAININFO = 0x4;
|
||||
|
||||
private final static int UNWIND_VERSION_FIELD_LENGTH = 0x03;
|
||||
private final static int UNWIND_FLAGS_FIELD_LENGTH = 0x05;
|
||||
private final static int UNWIND_FRAME_REGISTER_LENGTH = 0x04;
|
||||
private final static int UNWIND_OP_FIELD_LENGTH = 0x04;
|
||||
|
||||
byte version;
|
||||
byte flags;
|
||||
byte sizeOfProlog;
|
||||
byte countOfUnwindCodes;
|
||||
byte frameRegister;
|
||||
byte frameOffset;
|
||||
UNWIND_CODE[] unwindCodes;
|
||||
int exceptionHandlerFunction;
|
||||
int unwindHandlerFunction;
|
||||
_IMAGE_RUNTIME_FUNCTION_ENTRY unwindHandlerChainInfo;
|
||||
|
||||
long startOffset;
|
||||
|
||||
public UNWIND_INFO(long offset) {
|
||||
startOffset = offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
StructureDataType struct = new StructureDataType(NAME + "_" + startOffset, 0);
|
||||
try {
|
||||
StructureDataType vf = new StructureDataType("VersionFlags", 0);
|
||||
vf.insertBitField(0, 1, 0, BYTE, UNWIND_VERSION_FIELD_LENGTH, "Version", null);
|
||||
vf.insertBitField(0, 1, UNWIND_VERSION_FIELD_LENGTH, defineFlagsField(),
|
||||
UNWIND_FLAGS_FIELD_LENGTH, "Flags", null);
|
||||
|
||||
struct.add(vf, "Version + Flags", null);
|
||||
}
|
||||
catch (InvalidDataTypeException e) {
|
||||
struct.add(BYTE, "Version + Flags", null);
|
||||
}
|
||||
|
||||
struct.add(BYTE, "SizeOfProlog", null);
|
||||
struct.add(BYTE, "CountOfUnwindCodes", null);
|
||||
|
||||
try {
|
||||
StructureDataType fr = new StructureDataType("FrameRegisterAndOffset", 0);
|
||||
fr.insertBitField(0, 1, 0, BYTE, UNWIND_FRAME_REGISTER_LENGTH, "FrameRegister",
|
||||
null);
|
||||
fr.insertBitField(0, 1, UNWIND_FRAME_REGISTER_LENGTH, BYTE,
|
||||
UNWIND_FRAME_REGISTER_LENGTH, "FrameOffset", null);
|
||||
struct.add(fr, "FrameRegister + FrameOffset", null);
|
||||
}
|
||||
catch (InvalidDataTypeException e) {
|
||||
struct.add(BYTE, "FrameRegister + FrameOffset", null);
|
||||
}
|
||||
|
||||
for (int i = 0; i < countOfUnwindCodes; i++) {
|
||||
StructureDataType unwindCode = new StructureDataType("UnwindCode", 0);
|
||||
unwindCode.add(BYTE, "OffsetInProlog", null);
|
||||
|
||||
StructureDataType unwindCodeInfo = new StructureDataType("UnwindCodeInfo", 0);
|
||||
try {
|
||||
if (unwindCodes[i].opCode != null) {
|
||||
unwindCodeInfo.insertBitField(0, 1, 0, defineUnwindOpCodeField(),
|
||||
UNWIND_OP_FIELD_LENGTH, "UnwindOpCode", null);
|
||||
}
|
||||
else {
|
||||
unwindCodeInfo.insertBitField(0, 1, 0, BYTE, UNWIND_OP_FIELD_LENGTH,
|
||||
"UnwindOpCode", null);
|
||||
}
|
||||
|
||||
if (unwindCodes[i].opInfoRegister != null) {
|
||||
unwindCodeInfo.insertBitField(0, 1, UNWIND_OP_FIELD_LENGTH,
|
||||
defineUnwindCodeRegisterField(), UNWIND_OP_FIELD_LENGTH, "OpInfo",
|
||||
null);
|
||||
}
|
||||
else {
|
||||
unwindCodeInfo.insertBitField(0, 1, UNWIND_OP_FIELD_LENGTH, BYTE,
|
||||
UNWIND_OP_FIELD_LENGTH, "OpInfo", null);
|
||||
}
|
||||
}
|
||||
catch (InvalidDataTypeException e) {
|
||||
}
|
||||
unwindCode.add(unwindCodeInfo, "UnwindCodeInfo", null);
|
||||
|
||||
struct.add(unwindCode, "UnwindCode", null);
|
||||
}
|
||||
|
||||
if (hasExceptionHandler() || hasUnwindHandler()) {
|
||||
if (hasExceptionHandler()) {
|
||||
struct.add(IBO32, "ExceptionHandler", null);
|
||||
}
|
||||
if (hasUnwindHandler()) {
|
||||
struct.add(IBO32, "UnwindHandler", null);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (hasChainedUnwindInfo()) {
|
||||
struct.add(IBO32, "FunctionStartAddress", null);
|
||||
struct.add(IBO32, "FunctionEndAddress", null);
|
||||
struct.add(IBO32, "FunctionUnwindInfoAddress", null);
|
||||
}
|
||||
}
|
||||
|
||||
return struct;
|
||||
}
|
||||
|
||||
public boolean hasExceptionHandler() {
|
||||
return (flags & UNW_FLAG_EHANDLER) == UNW_FLAG_EHANDLER;
|
||||
}
|
||||
|
||||
public boolean hasUnwindHandler() {
|
||||
return (flags & UNW_FLAG_UHANDLER) == UNW_FLAG_UHANDLER;
|
||||
}
|
||||
|
||||
public boolean hasChainedUnwindInfo() {
|
||||
return (flags & UNW_FLAG_CHAININFO) == UNW_FLAG_CHAININFO;
|
||||
}
|
||||
|
||||
private EnumDataType defineFlagsField() {
|
||||
EnumDataType flagsField = new EnumDataType("Flags", 5);
|
||||
flagsField.add("UNW_FLAG_NHANDLER", UNW_FLAG_NHANDLER);
|
||||
flagsField.add("UNW_FLAG_EHANDLER", UNW_FLAG_EHANDLER);
|
||||
flagsField.add("UNW_FLAG_UHANDLER", UNW_FLAG_UHANDLER);
|
||||
flagsField.add("UNW_FLAG_CHAININFO", UNW_FLAG_CHAININFO);
|
||||
|
||||
return flagsField;
|
||||
}
|
||||
|
||||
private EnumDataType defineUnwindOpCodeField() {
|
||||
EnumDataType unwindOpCodeField = new EnumDataType("UNWIND_CODE_OPCODE", 4);
|
||||
unwindOpCodeField.add("UWOP_PUSH_NONVOL", UNWIND_CODE_OPCODE.UWOP_PUSH_NONVOL.id);
|
||||
unwindOpCodeField.add("UWOP_ALLOC_LARGE", UNWIND_CODE_OPCODE.UWOP_ALLOC_LARGE.id);
|
||||
unwindOpCodeField.add("UWOP_ALLOC_SMALL", UNWIND_CODE_OPCODE.UWOP_ALLOC_SMALL.id);
|
||||
unwindOpCodeField.add("UWOP_SET_FPREG", UNWIND_CODE_OPCODE.UWOP_SET_FPREG.id);
|
||||
unwindOpCodeField.add("UWOP_SAVE_NONVOL", UNWIND_CODE_OPCODE.UWOP_SAVE_NONVOL.id);
|
||||
unwindOpCodeField.add("UWOP_SAVE_NONVOL_FAR",
|
||||
UNWIND_CODE_OPCODE.UWOP_SAVE_NONVOL_FAR.id);
|
||||
unwindOpCodeField.add("UWOP_SAVE_XMM", UNWIND_CODE_OPCODE.UWOP_SAVE_XMM.id);
|
||||
unwindOpCodeField.add("UWOP_SAVE_XMM_FAR", UNWIND_CODE_OPCODE.UWOP_SAVE_XMM_FAR.id);
|
||||
unwindOpCodeField.add("UWOP_SAVE_XMM128", UNWIND_CODE_OPCODE.UWOP_SAVE_XMM128.id);
|
||||
unwindOpCodeField.add("UWOP_SAVE_XMM128_FAR",
|
||||
UNWIND_CODE_OPCODE.UWOP_SAVE_XMM128_FAR.id);
|
||||
unwindOpCodeField.add("UWOP_PUSH_MACHFRAME", UNWIND_CODE_OPCODE.UWOP_PUSH_MACHFRAME.id);
|
||||
|
||||
return unwindOpCodeField;
|
||||
}
|
||||
|
||||
private EnumDataType defineUnwindCodeRegisterField() {
|
||||
EnumDataType unwindCodeRegisterField =
|
||||
new EnumDataType("UNWIND_CODE_OPINFO_REGISTER", 4);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_RAX",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_RAX.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_RCX",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_RCX.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_RDX",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_RDX.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_RBX",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_RBX.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_RSP",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_RSP.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_RBP",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_RBP.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_RSI",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_RSI.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_RDI",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_RDI.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_R8",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_R8.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_R9",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_R9.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_R10",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_R10.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_R11",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_R11.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_R12",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_R12.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_R13",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_R13.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_R14",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_R14.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_R15",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_R15.id);
|
||||
|
||||
return unwindCodeRegisterField;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ import ghidra.util.task.TaskMonitorAdapter;
|
||||
|
||||
/**
|
||||
* A class to represent the <b><code>IMAGE_NT_HEADERS32</code></b> and
|
||||
* IMAGE_NT_HEADERS64 structs as defined in
|
||||
* IMAGE_NT_HEADERS64 structs as defined in
|
||||
* <code>winnt.h</code>.
|
||||
* <pre>
|
||||
* typedef struct _IMAGE_NT_HEADERS {
|
||||
@@ -39,8 +39,8 @@ import ghidra.util.task.TaskMonitorAdapter;
|
||||
* IMAGE_OPTIONAL_HEADER32 OptionalHeader;
|
||||
* };
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class NTHeader implements StructConverter, OffsetValidator {
|
||||
/**
|
||||
@@ -82,8 +82,8 @@ public class NTHeader implements StructConverter, OffsetValidator {
|
||||
public NTHeader() {
|
||||
}
|
||||
|
||||
private void initNTHeader(FactoryBundledWithBinaryReader reader, int index, SectionLayout layout,
|
||||
boolean advancedProcess, boolean parseCliHeaders)
|
||||
private void initNTHeader(FactoryBundledWithBinaryReader reader, int index,
|
||||
SectionLayout layout, boolean advancedProcess, boolean parseCliHeaders)
|
||||
throws InvalidNTHeaderException, IOException {
|
||||
this.reader = reader;
|
||||
this.index = index;
|
||||
@@ -172,9 +172,9 @@ public class NTHeader implements StructConverter, OffsetValidator {
|
||||
//low alignment mode?
|
||||
//
|
||||
if (optionalHeader != null) {
|
||||
if (optionalHeader.getFileAlignment() == optionalHeader.getSectionAlignment()
|
||||
&& optionalHeader.getSectionAlignment() < 800
|
||||
&& optionalHeader.getFileAlignment() > 1) {
|
||||
if (optionalHeader.getFileAlignment() == optionalHeader.getSectionAlignment() &&
|
||||
optionalHeader.getSectionAlignment() < 800 &&
|
||||
optionalHeader.getFileAlignment() > 1) {
|
||||
return rva;
|
||||
}
|
||||
}
|
||||
@@ -270,6 +270,7 @@ public class NTHeader implements StructConverter, OffsetValidator {
|
||||
|
||||
fileHeader.processSections(optionalHeader);
|
||||
fileHeader.processSymbols();
|
||||
fileHeader.processImageRuntimeFunctionEntries();
|
||||
|
||||
if (advancedProcess) {
|
||||
optionalHeader.processDataDirectories(TaskMonitorAdapter.DUMMY_MONITOR);
|
||||
@@ -278,7 +279,7 @@ public class NTHeader implements StructConverter, OffsetValidator {
|
||||
|
||||
void writeHeader(RandomAccessFile raf, DataConverter dc) throws IOException {
|
||||
|
||||
raf.seek( index );
|
||||
raf.seek(index);
|
||||
|
||||
raf.write(dc.getBytes(signature));
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.bin.format.mz.DOSHeader;
|
||||
import ghidra.app.util.bin.format.pe.*;
|
||||
import ghidra.app.util.bin.format.pe.ImageCor20Header.ImageCor20Flags;
|
||||
import ghidra.app.util.bin.format.pe.ImageRuntimeFunctionEntries._IMAGE_RUNTIME_FUNCTION_ENTRY;
|
||||
import ghidra.app.util.bin.format.pe.PortableExecutable.SectionLayout;
|
||||
import ghidra.app.util.bin.format.pe.debug.DebugCOFFSymbol;
|
||||
import ghidra.app.util.bin.format.pe.debug.DebugDirectoryParser;
|
||||
@@ -148,6 +149,7 @@ public class PeLoader extends AbstractPeDebugLoader {
|
||||
processProperties(optionalHeader, program, monitor);
|
||||
processComments(program.getListing(), monitor);
|
||||
processSymbols(fileHeader, sectionToAddress, program, monitor, log);
|
||||
processImageRuntimeFunctionEntries(fileHeader, program, monitor, log);
|
||||
|
||||
processEntryPoints(ntHeader, program, monitor);
|
||||
String compiler = CompilerOpinion.getOpinion(pe, provider).toString();
|
||||
@@ -248,24 +250,74 @@ public class PeLoader extends AbstractPeDebugLoader {
|
||||
setComment(CodeUnit.EOL_COMMENT, start, section.getName());
|
||||
start = start.add(dt.getLength());
|
||||
}
|
||||
|
||||
// for (int i = 0; i < datadirs.length; ++i) {
|
||||
// if (datadirs[i] == null || datadirs[i].getSize() == 0) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// if (datadirs[i].hasParsedCorrectly()) {
|
||||
// start = datadirs[i].getMarkupAddress(program, true);
|
||||
// dt = datadirs[i].toDataType();
|
||||
// DataUtilities.createData(program, start, dt, true, DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
catch (Exception e1) {
|
||||
Msg.error(this, "Error laying down header structures " + e1);
|
||||
}
|
||||
}
|
||||
|
||||
private void processImageRuntimeFunctionEntries(FileHeader fileHeader, Program program,
|
||||
TaskMonitor monitor, MessageLog log) {
|
||||
|
||||
// Check to see that we have exception data to process
|
||||
SectionHeader irfeHeader = null;
|
||||
for (SectionHeader header : fileHeader.getSectionHeaders()) {
|
||||
if (header.getName().contains(".pdata")) {
|
||||
irfeHeader = header;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (irfeHeader == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Address start = program.getImageBase().add(irfeHeader.getVirtualAddress());
|
||||
|
||||
List<_IMAGE_RUNTIME_FUNCTION_ENTRY> irfes = fileHeader.getImageRuntimeFunctionEntries();
|
||||
if (irfes == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
StructureDataType dt = new StructureDataType(".PDATA", 0);
|
||||
dt.setCategoryPath(new CategoryPath("/PE"));
|
||||
|
||||
// Lay an array of RUNTIME_INFO structure out over the data
|
||||
StructureDataType irfeStruct = new StructureDataType("_IMAGE_RUNTIME_FUNCTION_ENTRY", 0);
|
||||
irfeStruct.add(ghidra.app.util.bin.StructConverter.IBO32, "BeginAddress", null);
|
||||
irfeStruct.add(ghidra.app.util.bin.StructConverter.IBO32, "EndAddress", null);
|
||||
irfeStruct.add(ghidra.app.util.bin.StructConverter.IBO32, "UnwindInfoAddressOrData", null);
|
||||
|
||||
ArrayDataType irfeArray =
|
||||
new ArrayDataType(irfeStruct, irfes.size(), irfeStruct.getLength());
|
||||
|
||||
try {
|
||||
DataUtilities.createData(program, start, irfeArray, irfeArray.getLength(), true,
|
||||
DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
|
||||
}
|
||||
catch (CodeUnitInsertionException e) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Each RUNTIME_INFO contains an address to an UNWIND_INFO structure
|
||||
// which also needs to be laid out. When they contain chaining data
|
||||
// they're recursive but the toDataType() function handles that.
|
||||
for (_IMAGE_RUNTIME_FUNCTION_ENTRY entry : irfes) {
|
||||
if (entry.unwindInfoAddressOrData > 0) {
|
||||
try {
|
||||
dt = (StructureDataType) entry.unwindInfo.toDataType();
|
||||
start = program.getImageBase().add(entry.unwindInfoAddressOrData);
|
||||
|
||||
DataUtilities.createData(program, start, dt, dt.getLength(), true,
|
||||
DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
|
||||
}
|
||||
catch (CodeUnitInsertionException | DuplicateNameException | IOException e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processSymbols(FileHeader fileHeader, Map<SectionHeader, Address> sectionToAddress,
|
||||
Program program, TaskMonitor monitor, MessageLog log) {
|
||||
List<DebugCOFFSymbol> symbols = fileHeader.getSymbols();
|
||||
@@ -493,7 +545,7 @@ public class PeLoader extends AbstractPeDebugLoader {
|
||||
break;
|
||||
}
|
||||
|
||||
// Get address of current position in the import address table
|
||||
// Get address of current position in the import address table
|
||||
Address iatAddr = iatBaseAddr.add(offset);
|
||||
Data iatData = listing.getDataAt(iatAddr);
|
||||
if (iatData == null || !(iatData.getValue() instanceof Address)) {
|
||||
@@ -506,8 +558,7 @@ public class PeLoader extends AbstractPeDebugLoader {
|
||||
importInfo.getName(), null, SourceType.IMPORTED, 0, RefType.DATA);
|
||||
}
|
||||
catch (DuplicateNameException | InvalidInputException e) {
|
||||
log.appendMsg(
|
||||
"Failed to create Delay Load external function at: " + iatAddr);
|
||||
log.appendMsg("Failed to create Delay Load external function at: " + iatAddr);
|
||||
}
|
||||
|
||||
// Create delay load proxy function
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ import ghidra.util.timer.GTimer;
|
||||
*
|
||||
* Refactor fileInfo -> needs dialog to show properties
|
||||
* Refactor GFile.getInfo() to return Map<> instead of String.
|
||||
* Persistant filesystem - when reopen tool, filesystems should auto-reopen.
|
||||
* Persistent filesystem - when reopen tool, filesystems should auto-reopen.
|
||||
* Unify GhidraFileChooser with GFileSystem.
|
||||
* Add "Mounted Filesystems" button to show currently opened GFilesystems?
|
||||
* Dockable filesystem browser in FrontEnd.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -170,7 +169,7 @@ public class VarnodeOperation extends Varnode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPersistant() {
|
||||
public boolean isPersistent() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+27
-1
@@ -205,7 +205,33 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||
waitForSwing();
|
||||
|
||||
waitForTree();
|
||||
runSwing(() -> jTree.stopEditing());
|
||||
|
||||
// verify that the tree opens a new node with the default
|
||||
// category name is "New Category"
|
||||
assertEquals(childCount + 1, miscNode.getChildCount());
|
||||
GTreeNode node = miscNode.getChild("New Category");
|
||||
assertNotNull(node);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateCategory_WhileFiltered() throws Exception {
|
||||
// select a category
|
||||
GTreeNode miscNode = programNode.getChild("MISC");
|
||||
assertNotNull(miscNode);
|
||||
expandNode(miscNode);
|
||||
|
||||
int childCount = miscNode.getChildCount();
|
||||
selectNode(miscNode);
|
||||
|
||||
filterTree(miscNode.getName());
|
||||
|
||||
DockingActionIf action = getAction(plugin, "New Category");
|
||||
assertTrue(action.isEnabledForContext(treeContext));
|
||||
|
||||
// select "New Category" action
|
||||
DataTypeTestUtils.performAction(action, tree, false);
|
||||
|
||||
waitForDialogComponent("Cannot Edit Tree Node");
|
||||
|
||||
// verify that the tree opens a new node with the default
|
||||
// category name is "New Category"
|
||||
|
||||
@@ -31,6 +31,7 @@ import ghidra.program.model.pcode.*;
|
||||
import ghidra.service.graph.*;
|
||||
import ghidra.util.Msg;
|
||||
import java.util.*;
|
||||
import static ghidra.service.graph.GraphDisplay.*;
|
||||
|
||||
public class GraphAST extends GhidraScript {
|
||||
protected static final String COLOR_ATTRIBUTE = "Color";
|
||||
@@ -66,11 +67,12 @@ public class GraphAST extends GhidraScript {
|
||||
buildGraph();
|
||||
|
||||
Map<String, String> properties = new HashMap<>();
|
||||
properties.put("selectedVertexColor", "0xFF1493");
|
||||
properties.put("selectedEdgeColor", "0xFF1493");
|
||||
properties.put("initialLayoutAlgorithm", "Hierarchical MinCross Coffman Graham");
|
||||
properties.put("displayVerticesAsIcons", "false");
|
||||
properties.put("vertexLabelPosition", "S");
|
||||
properties.put(SELECTED_VERTEX_COLOR, "0xFF1493");
|
||||
properties.put(SELECTED_EDGE_COLOR, "0xFF1493");
|
||||
properties.put(INITIAL_LAYOUT_ALGORITHM, "Hierarchical MinCross Coffman Graham");
|
||||
properties.put(DISPLAY_VERTICES_AS_ICONS, "false");
|
||||
properties.put(VERTEX_LABEL_POSITION, "S");
|
||||
properties.put(ENABLE_EDGE_SELECTION, "true");
|
||||
GraphDisplay graphDisplay =
|
||||
graphDisplayBroker.getDefaultGraphDisplay(false, properties, monitor);
|
||||
// graphDisplay.defineVertexAttribute(CODE_ATTRIBUTE); //
|
||||
@@ -137,7 +139,7 @@ public class GraphAST extends GhidraScript {
|
||||
else if (vn.isUnique()) {
|
||||
colorattrib = "Black";
|
||||
}
|
||||
else if (vn.isPersistant()) {
|
||||
else if (vn.isPersistent()) {
|
||||
colorattrib = "DarkOrange";
|
||||
}
|
||||
else if (vn.isAddrTied()) {
|
||||
|
||||
@@ -926,7 +926,7 @@ This is a truncation operator that understands the endianess of the
|
||||
data. Input1 indicates the number of least significant bytes of input0
|
||||
to be thrown away. Output is then filled with any remaining bytes of
|
||||
input0 <emphasis>up to the size of output</emphasis>. If the size of
|
||||
output is smaller than the size of input0 plus the constant input1,
|
||||
output is smaller than the size of input0 minus the constant input1,
|
||||
then the additional most significant bytes of input0 will also be
|
||||
truncated.
|
||||
</para>
|
||||
|
||||
+6
-3
@@ -34,6 +34,8 @@ import ghidra.util.exception.GraphException;
|
||||
import ghidra.util.task.Task;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
import static ghidra.service.graph.GraphDisplay.*;
|
||||
|
||||
public class ASTGraphTask extends Task {
|
||||
enum GraphType {
|
||||
CONTROL_FLOW_GRAPH("AST Control Flow"), DATA_FLOW_GRAPH("AST Data Flow");
|
||||
@@ -104,9 +106,10 @@ public class ASTGraphTask extends Task {
|
||||
createControlFlowGraph(graph, monitor);
|
||||
}
|
||||
Map<String, String> properties = new HashMap<>();
|
||||
properties.put("selectedVertexColor", "0xFF1493");
|
||||
properties.put("selectedEdgeColor", "0xFF1493");
|
||||
properties.put("initialLayoutAlgorithm", "Hierarchical MinCross Coffman Graham");
|
||||
properties.put(SELECTED_VERTEX_COLOR, "0xFF1493");
|
||||
properties.put(SELECTED_EDGE_COLOR, "0xFF1493");
|
||||
properties.put(INITIAL_LAYOUT_ALGORITHM, "Hierarchical MinCross Coffman Graham");
|
||||
properties.put(ENABLE_EDGE_SELECTION, "true");
|
||||
GraphDisplay display = graphService.getDefaultGraphDisplay(!newGraph, properties, monitor);
|
||||
ASTGraphDisplayListener displayListener =
|
||||
new ASTGraphDisplayListener(tool, display, hfunction, graphType);
|
||||
|
||||
+5
-1
@@ -15,9 +15,10 @@
|
||||
*/
|
||||
package ghidra.file.formats.sevenzip;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
import ghidra.formats.gfilesystem.*;
|
||||
@@ -261,6 +262,9 @@ public class SevenZipFileSystem implements GFileSystem {
|
||||
case UNKNOWN_OPERATION_RESULT: {
|
||||
throw new IOException("Unexpected: 7-Zip returned unknown operation result");
|
||||
}
|
||||
case WRONG_PASSWORD: {
|
||||
throw new IOException("7-Zip wrong password");
|
||||
}
|
||||
case OK:
|
||||
default: {
|
||||
// it's all ok!
|
||||
|
||||
+6
-9
@@ -239,16 +239,13 @@ public class GnuDemanglerParser {
|
||||
* Pattern: text for|to text
|
||||
*
|
||||
* Parts:
|
||||
* -required text (capture group 2)
|
||||
* --note: this uses '++', a possessive quantifier, to help keep the
|
||||
* backtracking to a minimum
|
||||
* -a space
|
||||
* -'for' or 'to' (capture group 3)
|
||||
* -a space
|
||||
* -required text (capture group 2) -+
|
||||
* -'for' or 'to' (capture group 3) | (capture group 1)
|
||||
* -a space -+
|
||||
* -optional text (capture group 4)
|
||||
*
|
||||
* Note: capture group 1 is the combination of groups 2 and 3
|
||||
*
|
||||
*
|
||||
* Note: capture group 1 is the combination of groups 2 and 3 with trailing space
|
||||
*
|
||||
* Examples:
|
||||
* construction vtable for
|
||||
* vtable for
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
EXCLUDE FROM GHIDRA JAR: true
|
||||
|
||||
MODULE FILE LICENSE: lib/jungrapht-visualization-1.1.jar BSD
|
||||
MODULE FILE LICENSE: lib/jungrapht-layout-1.1.jar BSD
|
||||
MODULE FILE LICENSE: lib/jungrapht-visualization-1.2.jar BSD
|
||||
MODULE FILE LICENSE: lib/jungrapht-layout-1.2.jar BSD
|
||||
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/jheaps-0.13.jar Apache License 2.0
|
||||
|
||||
@@ -12,9 +12,9 @@ dependencies {
|
||||
compile project(":Base")
|
||||
|
||||
// jungrapht - exclude slf4j which produces a conflict with other uses with Ghidra
|
||||
compile ("com.github.tomnelson:jungrapht-visualization:1.1") { exclude group: "org.slf4j", module: "slf4j-api" }
|
||||
compile ("com.github.tomnelson:jungrapht-layout:1.1") { exclude group: "org.slf4j", module: "slf4j-api" }
|
||||
|
||||
compile ("com.github.tomnelson:jungrapht-visualization:1.2") { exclude group: "org.slf4j", module: "slf4j-api" }
|
||||
compile ("com.github.tomnelson:jungrapht-layout:1.2") { exclude group: "org.slf4j", module: "slf4j-api" }
|
||||
|
||||
compile "org.jgrapht:jgrapht-core:1.5.0"
|
||||
|
||||
// not using jgrapht-io code that depends on antlr, so exclude antlr
|
||||
|
||||
+70
-26
@@ -103,28 +103,33 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||
|
||||
private static final String ACTION_OWNER = "GraphServices";
|
||||
|
||||
/*
|
||||
A handful of properties that can be set via the constructor
|
||||
*/
|
||||
private static final String SELECTED_VERTEX_COLOR = "selectedVertexColor";
|
||||
private static final String SELECTED_EDGE_COLOR = "selectedEdgeColor";
|
||||
private static final String INITIAL_LAYOUT_ALGORITHM = "initialLayoutAlgorithm";
|
||||
private static final String DISPLAY_VERTICES_AS_ICONS = "displayVerticesAsIcons";
|
||||
private static final String VERTEX_LABEL_POSITION = "vertexLabelPosition";
|
||||
|
||||
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 Map<String, String> displayProperties = new HashMap<>();
|
||||
private Map<String, String> displayProperties;
|
||||
private Set<DockingActionIf> addedActions = new LinkedHashSet<>();
|
||||
private GraphDisplayListener listener = new DummyGraphDisplayListener();
|
||||
private String title;
|
||||
|
||||
private AttributedGraph graph;
|
||||
|
||||
private static String DEFAULT_EDGE_TYPE_PRIORITY_LIST =
|
||||
"Fall-Through,"+
|
||||
"Conditional-Return,"+
|
||||
"Unconditional-Jump,"+
|
||||
"Conditional-Jump,"+
|
||||
"Unconditional-Call,"+
|
||||
"Conditional-Call,"+
|
||||
"Terminator,"+
|
||||
"Computed,"+
|
||||
"Indirection,"+
|
||||
"Entry";
|
||||
|
||||
private static String DEFAULT_FAVORED_EDGES = "Fall-Through";
|
||||
|
||||
/**
|
||||
* a unique id for this {@link GraphDisplay}
|
||||
*/
|
||||
@@ -219,7 +224,9 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||
viewer.getComponent().add(satelliteViewer.getComponent());
|
||||
}
|
||||
layoutTransitionManager =
|
||||
new LayoutTransitionManager(viewer, this::isRoot);
|
||||
new LayoutTransitionManager(viewer, this::isRoot,
|
||||
getEdgeTypePriorityList(),
|
||||
getFavoredEdgePredicate());
|
||||
|
||||
viewer.getComponent().addComponentListener(new ComponentAdapter() {
|
||||
@Override
|
||||
@@ -252,6 +259,19 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||
return Colors.getHexColor(property);
|
||||
}
|
||||
|
||||
private List<String> getEdgeTypePriorityList() {
|
||||
return Arrays.asList(displayProperties
|
||||
.getOrDefault(EDGE_TYPE_PRIORITY_LIST, DEFAULT_EDGE_TYPE_PRIORITY_LIST)
|
||||
.split(","));
|
||||
}
|
||||
|
||||
private Predicate<AttributedEdge> getFavoredEdgePredicate() {
|
||||
String[] favoredEdges = displayProperties.getOrDefault(FAVORED_EDGES, DEFAULT_FAVORED_EDGES)
|
||||
.split(",");
|
||||
return attributedEdge -> Arrays.stream(favoredEdges)
|
||||
.anyMatch(s -> s.equals(attributedEdge.getAttribute("EdgeType")));
|
||||
}
|
||||
|
||||
JComponent getComponent() {
|
||||
JComponent component = viewer.getComponent();
|
||||
component.setFocusable(true);
|
||||
@@ -486,7 +506,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||
"Extends the current selection by including the target/source vertices " +
|
||||
"of all edges whose source/target is selected")
|
||||
.keyBinding("ctrl C")
|
||||
.enabledWhen(c -> !isAllSelected(getSourceVerticesFromSelected()) &&
|
||||
.enabledWhen(c -> !isAllSelected(getSourceVerticesFromSelected()) ||
|
||||
!isAllSelected(getTargetVerticesFromSelected()))
|
||||
.onAction(c -> growSelection(getAllComponentVerticesFromSelected()))
|
||||
.buildAndInstallLocal(componentProvider);
|
||||
@@ -610,6 +630,23 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||
|
||||
private void growSelection(Set<AttributedVertex> vertices) {
|
||||
viewer.getSelectedVertexState().select(vertices);
|
||||
selectEdgesConnecting(vertices);
|
||||
}
|
||||
|
||||
// select all the edges that connect the supplied vertices
|
||||
private void selectEdgesConnecting(Collection<AttributedVertex> vertices) {
|
||||
viewer.getSelectedEdgeState().select(
|
||||
graph.edgeSet()
|
||||
.stream()
|
||||
.filter(
|
||||
e -> {
|
||||
AttributedVertex source = graph.getEdgeSource(e);
|
||||
AttributedVertex target = graph.getEdgeTarget(e);
|
||||
return vertices.contains(source)
|
||||
&& vertices.contains(target);
|
||||
})
|
||||
.collect(Collectors.toSet()));
|
||||
|
||||
}
|
||||
|
||||
private boolean isAllSelected(Set<AttributedVertex> vertices) {
|
||||
@@ -617,8 +654,8 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||
}
|
||||
|
||||
private Set<AttributedVertex> getSourceVerticesFromSelected() {
|
||||
Set<AttributedVertex> sources = new HashSet<>();
|
||||
Set<AttributedVertex> selectedVertices = getSelectedVertices();
|
||||
Set<AttributedVertex> sources = new HashSet<>(selectedVertices);
|
||||
for (AttributedVertex v : selectedVertices) {
|
||||
Set<AttributedEdge> edges = graph.incomingEdgesOf(v);
|
||||
edges.forEach(e -> sources.add(graph.getEdgeSource(e)));
|
||||
@@ -635,8 +672,8 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||
}
|
||||
|
||||
private Set<AttributedVertex> getTargetVerticesFromSelected() {
|
||||
Set<AttributedVertex> targets = new HashSet<>();
|
||||
Set<AttributedVertex> selectedVertices = getSelectedVertices();
|
||||
Set<AttributedVertex> targets = new HashSet<>(selectedVertices);
|
||||
for (AttributedVertex v : selectedVertices) {
|
||||
Set<AttributedEdge> edges = graph.outgoingEdgesOf(v);
|
||||
edges.forEach(e -> targets.add(graph.getEdgeTarget(e)));
|
||||
@@ -674,9 +711,20 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||
return upstream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gather all source and target vertices until there are no more available.
|
||||
* @return all the vertices in the component(s) of the selected vertices
|
||||
*/
|
||||
public Set<AttributedVertex> getAllComponentVerticesFromSelected() {
|
||||
Set<AttributedVertex> componentVertices = getAllDownstreamVerticesFromSelected();
|
||||
componentVertices.addAll(getAllUpstreamVerticesFromSelected());
|
||||
Set<AttributedVertex> componentVertices = new HashSet<>(viewer.getSelectedVertices());
|
||||
Set<AttributedVertex> downstream = getAllDownstreamVerticesFromSelected();
|
||||
Set<AttributedVertex> upstream = getAllUpstreamVerticesFromSelected();
|
||||
while (!downstream.isEmpty() || !upstream.isEmpty()) {
|
||||
componentVertices.addAll(downstream);
|
||||
componentVertices.addAll(upstream);
|
||||
downstream = getAllDownstreamVerticesFromSelected();
|
||||
upstream = getAllUpstreamVerticesFromSelected();
|
||||
}
|
||||
return componentVertices;
|
||||
}
|
||||
|
||||
@@ -955,13 +1003,14 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||
|
||||
/**
|
||||
* 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 incoming "favored" edge
|
||||
* or if all edges of a vertex are 'loop' edges
|
||||
* @param vertex the vertex to test if it is a root
|
||||
* @return true if the vertex is a root
|
||||
*/
|
||||
private boolean isRoot(AttributedVertex vertex) {
|
||||
Set<AttributedEdge> incomingEdgesOf = graph.incomingEdgesOf(vertex);
|
||||
return incomingEdgesOf.isEmpty();
|
||||
return incomingEdgesOf.isEmpty() ||
|
||||
graph.incomingEdgesOf(vertex).equals(graph.outgoingEdgesOf(vertex));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1230,12 +1279,6 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||
|
||||
setVertexPreferences(vv);
|
||||
|
||||
// the selectedEdgeState will be controlled by the vertices that are selected.
|
||||
// if both endpoints of an edge are selected, select that edge.
|
||||
vv.setSelectedEdgeState(
|
||||
new VertexEndpointsSelectedEdgeSelectedState<>(vv.getVisualizationModel()::getGraph,
|
||||
vv.getSelectedVertexState()));
|
||||
|
||||
// selected edges will be drawn with a wider stroke
|
||||
renderContext.setEdgeStrokeFunction(
|
||||
e -> isSelected(e) ? new BasicStroke(20.f)
|
||||
@@ -1259,7 +1302,6 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||
|
||||
// cause the lightweight (optimized) renderer to use the vertex shapes instead
|
||||
// of using default shapes.
|
||||
|
||||
if (vertexRenderer instanceof LightweightVertexRenderer) {
|
||||
Function<AttributedVertex, Shape> vertexShapeFunction =
|
||||
renderContext.getVertexShapeFunction();
|
||||
@@ -1282,7 +1324,9 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||
vv.getComponent().removeMouseListener(mouseListener);
|
||||
}
|
||||
|
||||
graphMouse = new JgtGraphMouse(this);
|
||||
graphMouse = new JgtGraphMouse(this,
|
||||
Boolean.parseBoolean(displayProperties.getOrDefault(ENABLE_EDGE_SELECTION,
|
||||
"false")));
|
||||
vv.setGraphMouse(graphMouse);
|
||||
|
||||
return vv;
|
||||
|
||||
+55
-6
@@ -15,7 +15,9 @@
|
||||
*/
|
||||
package ghidra.graph.visualization;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.jungrapht.visualization.layout.algorithms.*;
|
||||
import org.jungrapht.visualization.layout.algorithms.repulsion.BarnesHutFRRepulsion;
|
||||
@@ -36,22 +38,38 @@ class LayoutFunction
|
||||
|
||||
static final String KAMADA_KAWAI = "Force Balanced";
|
||||
static final String FRUCTERMAN_REINGOLD = "Force Directed";
|
||||
static final String CIRCLE_MINCROSS = "Circle";
|
||||
static final String CIRCLE = "Circle";
|
||||
static final String TIDIER_TREE = "Compact Hierarchical";
|
||||
static final String TIDIER_RADIAL_TREE = "Compact Radial";
|
||||
static final String MIN_CROSS_TOP_DOWN = "Hierarchical MinCross Top Down";
|
||||
static final String MIN_CROSS_LONGEST_PATH = "Hierarchical MinCross Longest Path";
|
||||
static final String MIN_CROSS_NETWORK_SIMPLEX = "Hierarchical MinCross Network Simplex";
|
||||
static final String MIN_CROSS_COFFMAN_GRAHAM = "Hierarchical MinCross Coffman Graham";
|
||||
static final String EXP_MIN_CROSS_TOP_DOWN = "Experimental Hierarchical MinCross Top Down";
|
||||
static final String EXP_MIN_CROSS_LONGEST_PATH = "Experimental Hierarchical MinCross Longest Path";
|
||||
static final String EXP_MIN_CROSS_NETWORK_SIMPLEX = "Experimental Hierarchical MinCross Network Simplex";
|
||||
static final String EXP_MIN_CROSS_COFFMAN_GRAHAM = "Experimental Hierarchical MinCross Coffman Graham";
|
||||
static final String TREE = "Hierarchical";
|
||||
static final String RADIAL = "Radial";
|
||||
static final String BALLOON = "Balloon";
|
||||
static final String GEM = "Gem (Graph Embedder)";
|
||||
|
||||
Predicate<AttributedEdge> favoredEdgePredicate;
|
||||
Comparator<AttributedEdge> edgeTypeComparator;
|
||||
|
||||
LayoutFunction(Comparator<AttributedEdge> edgeTypeComparator, Predicate<AttributedEdge> favoredEdgePredicate) {
|
||||
this.edgeTypeComparator = edgeTypeComparator;
|
||||
this.favoredEdgePredicate = favoredEdgePredicate;
|
||||
}
|
||||
|
||||
public String[] getNames() {
|
||||
return new String[] { TIDIER_TREE, TREE,
|
||||
TIDIER_RADIAL_TREE, MIN_CROSS_TOP_DOWN, MIN_CROSS_LONGEST_PATH,
|
||||
MIN_CROSS_NETWORK_SIMPLEX, MIN_CROSS_COFFMAN_GRAHAM, CIRCLE_MINCROSS,
|
||||
MIN_CROSS_NETWORK_SIMPLEX, MIN_CROSS_COFFMAN_GRAHAM, CIRCLE,
|
||||
EXP_MIN_CROSS_TOP_DOWN,
|
||||
EXP_MIN_CROSS_LONGEST_PATH,
|
||||
EXP_MIN_CROSS_NETWORK_SIMPLEX,
|
||||
EXP_MIN_CROSS_COFFMAN_GRAHAM,
|
||||
KAMADA_KAWAI, FRUCTERMAN_REINGOLD, RADIAL, BALLOON, GEM
|
||||
};
|
||||
}
|
||||
@@ -67,27 +85,56 @@ class LayoutFunction
|
||||
case FRUCTERMAN_REINGOLD:
|
||||
return FRLayoutAlgorithm.<AttributedVertex> builder()
|
||||
.repulsionContractBuilder(BarnesHutFRRepulsion.builder());
|
||||
case CIRCLE_MINCROSS:
|
||||
case CIRCLE:
|
||||
return CircleLayoutAlgorithm.<AttributedVertex> builder()
|
||||
.reduceEdgeCrossing(true);
|
||||
.reduceEdgeCrossing(false);
|
||||
case TIDIER_RADIAL_TREE:
|
||||
return TidierRadialTreeLayoutAlgorithm
|
||||
.<AttributedVertex, AttributedEdge> edgeAwareBuilder();
|
||||
.<AttributedVertex, AttributedEdge> edgeAwareBuilder()
|
||||
.edgeComparator(edgeTypeComparator);
|
||||
case MIN_CROSS_TOP_DOWN:
|
||||
return EiglspergerLayoutAlgorithm
|
||||
.<AttributedVertex, AttributedEdge> edgeAwareBuilder()
|
||||
.edgeComparator(edgeTypeComparator)
|
||||
.layering(Layering.TOP_DOWN);
|
||||
case MIN_CROSS_LONGEST_PATH:
|
||||
return EiglspergerLayoutAlgorithm
|
||||
.<AttributedVertex, AttributedEdge> edgeAwareBuilder()
|
||||
.edgeComparator(edgeTypeComparator)
|
||||
.layering(Layering.LONGEST_PATH);
|
||||
case MIN_CROSS_NETWORK_SIMPLEX:
|
||||
return EiglspergerLayoutAlgorithm
|
||||
.<AttributedVertex, AttributedEdge> edgeAwareBuilder()
|
||||
.edgeComparator(edgeTypeComparator)
|
||||
.layering(Layering.NETWORK_SIMPLEX);
|
||||
case MIN_CROSS_COFFMAN_GRAHAM:
|
||||
return EiglspergerLayoutAlgorithm
|
||||
.<AttributedVertex, AttributedEdge> edgeAwareBuilder()
|
||||
.edgeComparator(edgeTypeComparator)
|
||||
.layering(Layering.COFFMAN_GRAHAM);
|
||||
case EXP_MIN_CROSS_TOP_DOWN:
|
||||
return EiglspergerLayoutAlgorithm
|
||||
.<AttributedVertex, AttributedEdge> edgeAwareBuilder()
|
||||
.edgeComparator(edgeTypeComparator)
|
||||
.favoredEdgePredicate(favoredEdgePredicate)
|
||||
.layering(Layering.TOP_DOWN);
|
||||
case EXP_MIN_CROSS_LONGEST_PATH:
|
||||
return EiglspergerLayoutAlgorithm
|
||||
.<AttributedVertex, AttributedEdge> edgeAwareBuilder()
|
||||
.edgeComparator(edgeTypeComparator)
|
||||
.favoredEdgePredicate(favoredEdgePredicate)
|
||||
.layering(Layering.LONGEST_PATH);
|
||||
case EXP_MIN_CROSS_NETWORK_SIMPLEX:
|
||||
return EiglspergerLayoutAlgorithm
|
||||
.<AttributedVertex, AttributedEdge> edgeAwareBuilder()
|
||||
.edgeComparator(edgeTypeComparator)
|
||||
.favoredEdgePredicate(favoredEdgePredicate)
|
||||
.layering(Layering.NETWORK_SIMPLEX);
|
||||
case EXP_MIN_CROSS_COFFMAN_GRAHAM:
|
||||
return EiglspergerLayoutAlgorithm
|
||||
.<AttributedVertex, AttributedEdge> edgeAwareBuilder()
|
||||
.edgeComparator(edgeTypeComparator)
|
||||
.favoredEdgePredicate(favoredEdgePredicate)
|
||||
.layering(Layering.COFFMAN_GRAHAM);
|
||||
case RADIAL:
|
||||
return RadialTreeLayoutAlgorithm
|
||||
@@ -103,7 +150,9 @@ class LayoutFunction
|
||||
case TIDIER_TREE:
|
||||
default:
|
||||
return TidierTreeLayoutAlgorithm
|
||||
.<AttributedVertex, AttributedEdge> edgeAwareBuilder();
|
||||
.<AttributedVertex, AttributedEdge> edgeAwareBuilder()
|
||||
.edgeComparator(edgeTypeComparator);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+25
-37
@@ -38,7 +38,7 @@ import ghidra.service.graph.AttributedVertex;
|
||||
*/
|
||||
class LayoutTransitionManager {
|
||||
|
||||
LayoutFunction layoutFunction = new LayoutFunction();
|
||||
LayoutFunction layoutFunction;
|
||||
/**
|
||||
* the {@link VisualizationServer} used to display graphs using the requested {@link LayoutAlgorithm}
|
||||
*/
|
||||
@@ -49,24 +49,6 @@ class LayoutTransitionManager {
|
||||
*/
|
||||
Predicate<AttributedVertex> rootPredicate;
|
||||
|
||||
public static final List<String> EDGE_PRIORITY_LIST =
|
||||
List.of(
|
||||
"Fall-Through",
|
||||
"Conditional-Return",
|
||||
"Unconditional-Jump",
|
||||
"Conditional-Jump",
|
||||
"Unconditional-Call",
|
||||
"Conditional-Call",
|
||||
"Terminator",
|
||||
"Computed",
|
||||
"Indirection",
|
||||
"Entry");
|
||||
/**
|
||||
* a {@link Comparator} to sort edges during layout graph traversal
|
||||
* The default uses the {@code EDGE_PRIORTITY_LIST }
|
||||
*/
|
||||
Comparator<AttributedEdge> edgeComparator = new EdgeComparator(EDGE_PRIORITY_LIST);
|
||||
|
||||
/**
|
||||
* a {@link Function} to provide {@link Rectangle} (and thus bounds} for vertices
|
||||
*/
|
||||
@@ -81,47 +63,54 @@ class LayoutTransitionManager {
|
||||
|
||||
LayoutPaintable.RadialRings<AttributedVertex> radialLayoutRings;
|
||||
|
||||
|
||||
/**
|
||||
* Create an instance with passed parameters
|
||||
* @param visualizationServer displays the graph
|
||||
* @param rootPredicate selects root vertices
|
||||
* @param edgeTypePriorityList a {@code List} of EdgeType names in priority order
|
||||
* @param favoredEdgePredicate q {@code Predicate} that will cause certain EdgeTypes to be favored during layout
|
||||
*/
|
||||
public LayoutTransitionManager(
|
||||
VisualizationServer<AttributedVertex, AttributedEdge> visualizationServer,
|
||||
Predicate<AttributedVertex> rootPredicate) {
|
||||
Predicate<AttributedVertex> rootPredicate,
|
||||
List<String> edgeTypePriorityList,
|
||||
Predicate<AttributedEdge> favoredEdgePredicate) {
|
||||
this.visualizationServer = visualizationServer;
|
||||
this.rootPredicate = rootPredicate;
|
||||
|
||||
this.renderContext = visualizationServer.getRenderContext();
|
||||
this.vertexBoundsFunction = visualizationServer.getRenderContext().getVertexBoundsFunction();
|
||||
}
|
||||
|
||||
public void setEdgeComparator(Comparator<AttributedEdge> edgeComparator) {
|
||||
this.edgeComparator = edgeComparator;
|
||||
this.layoutFunction = new LayoutFunction(new EdgeComparator(edgeTypePriorityList),
|
||||
favoredEdgePredicate);
|
||||
}
|
||||
|
||||
/**
|
||||
* set the layout in order to configure the requested {@link LayoutAlgorithm}
|
||||
* @param layoutName the name of the layout algorithm to use
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setLayout(String layoutName) {
|
||||
LayoutAlgorithm.Builder<AttributedVertex, ?, ?> builder = layoutFunction.apply(layoutName);
|
||||
LayoutAlgorithm<AttributedVertex> layoutAlgorithm = builder.build();
|
||||
// layout algorithm considers the size of vertices
|
||||
if (layoutAlgorithm instanceof VertexBoundsFunctionConsumer) {
|
||||
((VertexBoundsFunctionConsumer<AttributedVertex>) layoutAlgorithm)
|
||||
.setVertexBoundsFunction(vertexBoundsFunction);
|
||||
}
|
||||
// mincross layouts are 'layered'. put some bounds on the number of
|
||||
// iterations of the level cross function based on the size of the graph
|
||||
// very large graphs do not improve enough to out-weigh the cost of
|
||||
// repeated iterations
|
||||
if (layoutAlgorithm instanceof Layered) {
|
||||
((Layered<AttributedVertex, AttributedEdge>) layoutAlgorithm)
|
||||
.setMaxLevelCrossFunction(g ->
|
||||
Math.max(1, Math.min(10, 500 / g.vertexSet().size())));
|
||||
}
|
||||
// tree layouts need a way to determine which vertices are roots
|
||||
// especially when the graph is not a DAG
|
||||
if (layoutAlgorithm instanceof TreeLayout) {
|
||||
((TreeLayout<AttributedVertex>) layoutAlgorithm).setRootPredicate(rootPredicate);
|
||||
}
|
||||
// remove any previously added layout paintables
|
||||
// and apply paintables to these 2 algorithms
|
||||
removePaintable(radialLayoutRings);
|
||||
removePaintable(balloonLayoutRings);
|
||||
if (layoutAlgorithm instanceof BalloonLayoutAlgorithm) {
|
||||
@@ -138,9 +127,7 @@ class LayoutTransitionManager {
|
||||
visualizationServer.addPreRenderPaintable(radialLayoutRings);
|
||||
}
|
||||
|
||||
if (layoutAlgorithm instanceof EdgeSorting) {
|
||||
((EdgeSorting<AttributedEdge>) layoutAlgorithm).setEdgeComparator(edgeComparator);
|
||||
}
|
||||
// apply the layout algorithm
|
||||
LayoutAlgorithmTransition.apply(visualizationServer,
|
||||
layoutAlgorithm);
|
||||
}
|
||||
@@ -151,7 +138,10 @@ class LayoutTransitionManager {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
/**
|
||||
* Supplies the {@code LayoutAlgorithm} to be used for the initial @{code Graph} visualization
|
||||
* @return
|
||||
*/
|
||||
public LayoutAlgorithm<AttributedVertex> getInitialLayoutAlgorithm() {
|
||||
LayoutAlgorithm<AttributedVertex> initialLayoutAlgorithm =
|
||||
layoutFunction.apply(TIDIER_TREE).build();
|
||||
@@ -159,12 +149,6 @@ class LayoutTransitionManager {
|
||||
if (initialLayoutAlgorithm instanceof TreeLayout) {
|
||||
((TreeLayout<AttributedVertex>) initialLayoutAlgorithm)
|
||||
.setRootPredicate(rootPredicate);
|
||||
((TreeLayout<AttributedVertex>) initialLayoutAlgorithm)
|
||||
.setVertexBoundsFunction(vertexBoundsFunction);
|
||||
}
|
||||
if (initialLayoutAlgorithm instanceof EdgeSorting) {
|
||||
((EdgeSorting<AttributedEdge>) initialLayoutAlgorithm)
|
||||
.setEdgeComparator(edgeComparator);
|
||||
}
|
||||
if (initialLayoutAlgorithm instanceof VertexBoundsFunctionConsumer) {
|
||||
((VertexBoundsFunctionConsumer<AttributedVertex>) initialLayoutAlgorithm)
|
||||
@@ -173,6 +157,10 @@ class LayoutTransitionManager {
|
||||
return initialLayoutAlgorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Supplies a {@code String[]} array of the supported layout names
|
||||
* @return
|
||||
*/
|
||||
public String[] getLayoutNames() {
|
||||
return layoutFunction.getNames();
|
||||
}
|
||||
|
||||
+5
-3
@@ -29,12 +29,14 @@ import ghidra.service.graph.AttributedVertex;
|
||||
public class JgtGraphMouse extends DefaultGraphMouse<AttributedVertex, AttributedEdge> {
|
||||
|
||||
private DefaultGraphDisplay graphDisplay;
|
||||
private boolean allowEdgeSelection;
|
||||
|
||||
// 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 JgtGraphMouse(DefaultGraphDisplay graphDisplay) {
|
||||
public JgtGraphMouse(DefaultGraphDisplay graphDisplay, boolean allowEdgeSelection) {
|
||||
super(DefaultGraphMouse.builder());
|
||||
this.graphDisplay = graphDisplay;
|
||||
this.allowEdgeSelection = allowEdgeSelection;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,13 +56,13 @@ public class JgtGraphMouse extends DefaultGraphMouse<AttributedVertex, Attribute
|
||||
// JUNGRAPHT CHANGE 1,2
|
||||
//
|
||||
// Note: this code can go away when we can turn off the picking square
|
||||
add(new JgtSelectingGraphMousePlugin());
|
||||
add(allowEdgeSelection ? new SelectingGraphMousePlugin() : new VertexSelectingGraphMousePlugin<>());
|
||||
// add(new SelectingGraphMousePlugin<>());
|
||||
|
||||
add(new RegionSelectingGraphMousePlugin<>());
|
||||
|
||||
// the grab/pan feature
|
||||
add(new TranslatingGraphMousePlugin(InputEvent.BUTTON1_DOWN_MASK));
|
||||
add(TranslatingGraphMousePlugin.builder().translatingMask(InputEvent.BUTTON1_DOWN_MASK).build());
|
||||
|
||||
// scaling
|
||||
add(new ScalingGraphMousePlugin());
|
||||
|
||||
+6
-4
@@ -39,19 +39,21 @@ public class JgtSatelliteGraphMouse
|
||||
//
|
||||
// JUNGRAPHT CHANGE 3
|
||||
//
|
||||
// disable single selection in satellite view by setting masks to 0
|
||||
SelectingGraphMousePlugin<AttributedVertex, AttributedEdge> mySelectingPlugin =
|
||||
new JgtSelectingGraphMousePlugin(singleSelectionMask, addSingleSelectionMask);
|
||||
new JgtSelectingGraphMousePlugin(0, 0);
|
||||
mySelectingPlugin.setLocked(true);
|
||||
selectingPlugin = mySelectingPlugin;
|
||||
|
||||
regionSelectingPlugin =
|
||||
RegionSelectingGraphMousePlugin.builder()
|
||||
.regionSelectionMask(regionSelectionMask)
|
||||
.addRegionSelectionMask(addRegionSelectionMask)
|
||||
.toggleRegionSelectionMask(toggleRegionSelectionMask)
|
||||
.regionSelectionCompleteMask(regionSelectionCompleteMask)
|
||||
.addRegionSelectionCompleteMask(addRegionSelectionCompleteMask)
|
||||
.toggleRegionSelectionCompleteMask(toggleRegionSelectionCompleteMask)
|
||||
.build();
|
||||
translatingPlugin = new SatelliteTranslatingGraphMousePlugin(translatingMask);
|
||||
translatingPlugin = SatelliteTranslatingGraphMousePlugin.builder()
|
||||
.translatingMask(translatingMask).build();
|
||||
add(selectingPlugin);
|
||||
add(regionSelectingPlugin);
|
||||
add(translatingPlugin);
|
||||
|
||||
+4
-6
@@ -54,8 +54,10 @@ public class JgtSelectingGraphMousePlugin
|
||||
this.pickFootprintPaintable = dummyPickFootprintPaintable;
|
||||
}
|
||||
|
||||
public JgtSelectingGraphMousePlugin(int singleSelectionMask, int addSingleSelectionMask) {
|
||||
super(singleSelectionMask, addSingleSelectionMask);
|
||||
public JgtSelectingGraphMousePlugin(int singleSelectionMask, int toggleSingleSelectionMask) {
|
||||
super(SelectingGraphMousePlugin.<AttributedVertex, AttributedEdge>builder()
|
||||
.singleSelectionMask(singleSelectionMask)
|
||||
.toggleSingleSelectionMask(toggleSingleSelectionMask));
|
||||
|
||||
//
|
||||
// JUNGRAPHT CHANGE 1
|
||||
@@ -89,7 +91,6 @@ public class JgtSelectingGraphMousePlugin
|
||||
selectedVertexState.clear();
|
||||
}
|
||||
selectedVertexState.select(vertex);
|
||||
deselectedVertex = null;
|
||||
}
|
||||
else {
|
||||
// If this vertex is still around in mouseReleased, it will be deselected
|
||||
@@ -99,9 +100,6 @@ public class JgtSelectingGraphMousePlugin
|
||||
//
|
||||
// JUNGRAPHT CHANGE 2 HERE
|
||||
//
|
||||
if (addToSelection) {
|
||||
deselectedVertex = vertex;
|
||||
}
|
||||
}
|
||||
e.consume();
|
||||
return true;
|
||||
|
||||
@@ -13,8 +13,8 @@ jungrapht.satelliteBackgroundTransparent=false
|
||||
jungrapht.satelliteLensColor= 0xFAFAFA
|
||||
|
||||
jungrapht.pickedEdgeColor=0xFF0000
|
||||
jungrapht.edgeArrowLength=30
|
||||
jungrapht.edgeArrowWidth=20
|
||||
jungrapht.edgeArrowLength=50
|
||||
jungrapht.edgeArrowWidth=40
|
||||
|
||||
# default spacing for tree layouts
|
||||
jungrapht.treeLayoutHorizontalSpacing=2
|
||||
@@ -34,7 +34,7 @@ jungrapht.lensStrokeWidth=10.0
|
||||
# when scale is < .1, switch to lightweight rendering
|
||||
jungrapht.lightweightScaleThreshold=.1
|
||||
# under 50 vertices will use heavyweight rendering all the time
|
||||
jungrapht.lightweightCountThreshold=50
|
||||
jungrapht.lightweightCountThreshold=80
|
||||
|
||||
# default pixels spacings for vertices
|
||||
jungrapht.mincross.horizontalOffset=10
|
||||
@@ -69,12 +69,12 @@ jungrapht.edgeSpatialSupport=RTREE
|
||||
# the mask for single vertex/edge selection
|
||||
jungrapht.singleSelectionMask=MB1
|
||||
# the mask to augment the selection with a single vertex/edge
|
||||
jungrapht.addSingleSelectionMask=MB1_MENU
|
||||
jungrapht.toggleSingleSelectionMask=MB1_MENU
|
||||
# the mask to select vertices within a region
|
||||
jungrapht.regionSelectionMask=MB1_MENU
|
||||
# the mask to augment the selection with vertices in a region
|
||||
jungrapht.addRegionSelectionMask=MB1_SHIFT_MENU
|
||||
jungrapht.toggleRegionSelectionMask=MB1_SHIFT_MENU
|
||||
# the mask to indicate that the selection region is complete/closed and selection may commence
|
||||
jungrapht.regionSelectionCompleteMask=MENU
|
||||
# the mask to indicate that the selection region for augmentation is complete/closed and selection may commence
|
||||
jungrapht.addRegionSelectionCompleteMask=SHIFT_MENU
|
||||
jungrapht.toggleRegionSelectionCompleteMask=SHIFT_MENU
|
||||
|
||||
+3
-2
@@ -15,9 +15,10 @@
|
||||
*/
|
||||
package ghidra.app.util.pdb.pdbapplicator;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import ghidra.app.cmd.label.SetLabelPrimaryCmd;
|
||||
import ghidra.app.util.NamespaceUtils;
|
||||
import ghidra.app.util.SymbolPath;
|
||||
@@ -204,7 +205,7 @@ public class PdbApplicator {
|
||||
throw new PdbException("Invalid Restriction");
|
||||
}
|
||||
|
||||
if (program == null) {
|
||||
if (program != null) {
|
||||
Options options = program.getOptions(Program.PROGRAM_INFO);
|
||||
options.setBoolean(PdbParserConstants.PDB_LOADED, true);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package docking;
|
||||
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -134,7 +133,7 @@ public class ActionToGuiMapper {
|
||||
return menuGroupMap;
|
||||
}
|
||||
|
||||
public void showPopupMenu(ComponentPlaceholder componentInfo, MouseEvent e) {
|
||||
popupActionManager.popupMenu(componentInfo, e);
|
||||
public void showPopupMenu(ComponentPlaceholder componentInfo, PopupMenuContext popupContext) {
|
||||
popupActionManager.popupMenu(componentInfo, popupContext);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,17 +67,17 @@ public class DockableComponent extends JPanel implements ContainerListener {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
componentSelected((Component) e.getSource());
|
||||
processPopupMouseEvent(e);
|
||||
showContextMenu(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
processPopupMouseEvent(e);
|
||||
showContextMenu(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
processPopupMouseEvent(e);
|
||||
showContextMenu(e);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -146,24 +146,27 @@ public class DockableComponent extends JPanel implements ContainerListener {
|
||||
return focusedComponent;
|
||||
}
|
||||
|
||||
private void processPopupMouseEvent(final MouseEvent e) {
|
||||
void showContextMenu(PopupMenuContext popupContext) {
|
||||
actionMgr.showPopupMenu(placeholder, popupContext);
|
||||
}
|
||||
|
||||
private void showContextMenu(MouseEvent e) {
|
||||
Component component = e.getComponent();
|
||||
if (component == null) {
|
||||
return;
|
||||
return; // not sure this can happen
|
||||
}
|
||||
|
||||
// get the bounds to see if the clicked point is over the component
|
||||
Rectangle bounds = component.getBounds(); // get bounds to get width and height
|
||||
|
||||
Rectangle bounds = component.getBounds();
|
||||
if (component instanceof JComponent) {
|
||||
((JComponent) component).computeVisibleRect(bounds);
|
||||
}
|
||||
|
||||
Point point = e.getPoint();
|
||||
boolean withinBounds = bounds.contains(point);
|
||||
|
||||
if (e.isPopupTrigger() && withinBounds) {
|
||||
actionMgr.showPopupMenu(placeholder, e);
|
||||
PopupMenuContext popupContext = new PopupMenuContext(e);
|
||||
actionMgr.showPopupMenu(placeholder, popupContext);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -476,17 +479,11 @@ public class DockableComponent extends JPanel implements ContainerListener {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.awt.event.ContainerListener#componentAdded(java.awt.event.ContainerEvent)
|
||||
*/
|
||||
@Override
|
||||
public void componentAdded(ContainerEvent e) {
|
||||
initializeComponents(e.getChild());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.awt.event.ContainerListener#componentRemoved(java.awt.event.ContainerEvent)
|
||||
*/
|
||||
@Override
|
||||
public void componentRemoved(ContainerEvent e) {
|
||||
deinitializeComponents(e.getChild());
|
||||
|
||||
@@ -18,6 +18,8 @@ package docking;
|
||||
import java.awt.Component;
|
||||
import java.awt.Window;
|
||||
|
||||
import org.apache.commons.text.WordUtils;
|
||||
|
||||
import docking.widgets.OkDialog;
|
||||
import docking.widgets.OptionDialog;
|
||||
import ghidra.util.*;
|
||||
@@ -26,11 +28,11 @@ import ghidra.util.exception.MultipleCauses;
|
||||
public class DockingErrorDisplay implements ErrorDisplay {
|
||||
|
||||
/**
|
||||
* Error dialog used to append exceptions.
|
||||
*
|
||||
* Error dialog used to append exceptions.
|
||||
*
|
||||
* <p>While this dialog is showing all new exceptions will be added to the dialog. When
|
||||
* this dialog is closed, this reference will be cleared.
|
||||
*
|
||||
* this dialog is closed, this reference will be cleared.
|
||||
*
|
||||
* <p>Note: all use of this variable <b>must be on the Swing thread</b> to avoid thread
|
||||
* visibility issues.
|
||||
*/
|
||||
@@ -62,23 +64,34 @@ public class DockingErrorDisplay implements ErrorDisplay {
|
||||
Component parent, String title, Object message, Throwable throwable) {
|
||||
|
||||
int dialogType = OptionDialog.PLAIN_MESSAGE;
|
||||
|
||||
String messageString = message != null ? message.toString() : null;
|
||||
String rawMessage = HTMLUtilities.fromHTML(messageString);
|
||||
if (messageString != null) {
|
||||
// prevent excessive message degenerate cases
|
||||
int maxChars = 1000;
|
||||
String safeMessage = StringUtilities.trimMiddle(messageString, maxChars);
|
||||
|
||||
// wrap any poorly formatted text that gets displayed in the label; 80-100 chars is
|
||||
// a reasonable line length based on historical print margins
|
||||
messageString = WordUtils.wrap(safeMessage, 100, null, true);
|
||||
}
|
||||
|
||||
String unformattedMessage = HTMLUtilities.fromHTML(messageString);
|
||||
switch (messageType) {
|
||||
case INFO:
|
||||
dialogType = OptionDialog.INFORMATION_MESSAGE;
|
||||
consoleDisplay.displayInfoMessage(errorLogger, originator, parent, title,
|
||||
rawMessage);
|
||||
unformattedMessage);
|
||||
break;
|
||||
case WARNING:
|
||||
case ALERT:
|
||||
dialogType = OptionDialog.WARNING_MESSAGE;
|
||||
consoleDisplay.displayWarningMessage(errorLogger, originator, parent, title,
|
||||
rawMessage, throwable);
|
||||
unformattedMessage, throwable);
|
||||
break;
|
||||
case ERROR:
|
||||
consoleDisplay.displayErrorMessage(errorLogger, originator, parent, title,
|
||||
rawMessage, throwable);
|
||||
unformattedMessage, throwable);
|
||||
dialogType = OptionDialog.ERROR_MESSAGE;
|
||||
break;
|
||||
}
|
||||
@@ -93,8 +106,8 @@ public class DockingErrorDisplay implements ErrorDisplay {
|
||||
return component;
|
||||
}
|
||||
|
||||
private void showDialog(final String title, final Throwable throwable,
|
||||
final int dialogType, final String messageString, final Component parent) {
|
||||
private void showDialog(final String title, final Throwable throwable, final int dialogType,
|
||||
final String messageString, final Component parent) {
|
||||
|
||||
Swing.runIfSwingOrRunLater(() -> {
|
||||
|
||||
@@ -108,8 +121,8 @@ public class DockingErrorDisplay implements ErrorDisplay {
|
||||
});
|
||||
}
|
||||
|
||||
private void showDialogOnSwing(String title, Throwable throwable,
|
||||
int dialogType, String messageString, Component parent) {
|
||||
private void showDialogOnSwing(String title, Throwable throwable, int dialogType,
|
||||
String messageString, Component parent) {
|
||||
|
||||
if (activeDialog != null) {
|
||||
activeDialog.addException(messageString, throwable);
|
||||
|
||||
@@ -1765,8 +1765,11 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||
}
|
||||
|
||||
if (bestParent == null) {
|
||||
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||
bestParent = kfm.getActiveWindow();
|
||||
bestParent = getJavaActiveWindow();
|
||||
}
|
||||
|
||||
if (bestParent != null && !bestParent.isShowing()) {
|
||||
bestParent = null; // don't let non-showing windows be parents
|
||||
}
|
||||
|
||||
return bestParent;
|
||||
@@ -1775,8 +1778,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||
private static Window getBestNonModalParent(DialogComponentProvider newProvider,
|
||||
Window bestParent) {
|
||||
|
||||
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||
Window activeWindow = kfm.getActiveWindow();
|
||||
Window activeWindow = getJavaActiveWindow();
|
||||
if (!(activeWindow instanceof DockingDialog)) {
|
||||
return bestParent;
|
||||
}
|
||||
@@ -1937,13 +1939,19 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||
|
||||
private static Window getJavaActiveWindow() {
|
||||
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||
return kfm.getActiveWindow();
|
||||
Window activeWindow = kfm.getActiveWindow();
|
||||
if (activeWindow == null) {
|
||||
return null;
|
||||
}
|
||||
if (!activeWindow.isShowing()) {
|
||||
return null; // don't let non-showing windows be considered active
|
||||
}
|
||||
return activeWindow;
|
||||
}
|
||||
|
||||
private static Window getActiveNonTransientWindow() {
|
||||
|
||||
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||
Window bestWindow = kfm.getActiveWindow();
|
||||
Window bestWindow = getJavaActiveWindow();
|
||||
if (bestWindow instanceof DockingDialog) {
|
||||
// We do not want Task Dialogs becoming parents, as they will get closed when the
|
||||
// task is finished, closing any other child dialogs, which means that dialogs such
|
||||
@@ -2172,6 +2180,34 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||
objectUnderMouse = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a popup menu over the given component. If this given component is not part of the
|
||||
* docking windows hierarchy, then no action is taken.
|
||||
*
|
||||
* @param component the component
|
||||
*/
|
||||
public static void showContextMenu(Component component) {
|
||||
|
||||
DockingWindowManager dwm = getInstance(component);
|
||||
if (dwm == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
DockableComponent dockableComponent = dwm.getDockableComponent(component);
|
||||
if (dockableComponent == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Rectangle bounds = dockableComponent.getBounds();
|
||||
|
||||
bounds.x = 0;
|
||||
bounds.y = 0;
|
||||
int x = (int) bounds.getCenterX();
|
||||
int y = (int) bounds.getCenterY();
|
||||
PopupMenuContext popupContext = new PopupMenuContext(dockableComponent, new Point(x, y));
|
||||
dockableComponent.showContextMenu(popupContext);
|
||||
}
|
||||
|
||||
public void contextChanged(ComponentProvider provider) {
|
||||
|
||||
if (provider == null) {
|
||||
|
||||
@@ -43,7 +43,7 @@ import ghidra.util.table.column.GColumnRenderer;
|
||||
import utilities.util.reflection.ReflectionUtilities;
|
||||
|
||||
/**
|
||||
* A dialog that takes error text and displays it with an option details button. If there is
|
||||
* A dialog that takes error text and displays it with an option details button. If there is
|
||||
* an {@link ErrorReporter}, then a button is provided to report the error.
|
||||
*/
|
||||
public class ErrLogDialog extends AbstractErrDialog {
|
||||
@@ -149,7 +149,8 @@ public class ErrLogDialog extends AbstractErrDialog {
|
||||
introPanel.add(
|
||||
new GIconLabel(UIManager.getIcon("OptionPane.errorIcon"), SwingConstants.RIGHT),
|
||||
BorderLayout.WEST);
|
||||
introPanel.add(new GHtmlLabel(HTMLUtilities.toHTML(message)) {
|
||||
String html = HTMLUtilities.toHTML(message);
|
||||
introPanel.add(new GHtmlLabel(html) {
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
// rendering HTML the label can expand larger than the screen; keep it reasonable
|
||||
@@ -387,6 +388,16 @@ public class ErrLogDialog extends AbstractErrDialog {
|
||||
textDetails.scrollToBottom();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
Dimension size = super.getPreferredSize();
|
||||
|
||||
// Cap preferred width to something reasonable; most displays have more than 1000 width.
|
||||
// Users can still resize as desired
|
||||
size.width = Math.min(size.width, 1000);
|
||||
return size;
|
||||
}
|
||||
|
||||
void setError(ErrorEntry e) {
|
||||
error = e;
|
||||
setExceptionMessage(e.getDetailsText());
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package docking;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Point;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
@@ -67,28 +68,27 @@ public class PopupActionManager implements PropertyChangeListener {
|
||||
}
|
||||
}
|
||||
|
||||
void popupMenu(ComponentPlaceholder info, MouseEvent e) {
|
||||
void popupMenu(ComponentPlaceholder placeholder, PopupMenuContext popupContext) {
|
||||
|
||||
if (e.isConsumed()) {
|
||||
return;
|
||||
}
|
||||
ComponentProvider popupProvider = info.getProvider();
|
||||
ActionContext actionContext = popupProvider.getActionContext(e);
|
||||
MouseEvent event = popupContext.getEvent();
|
||||
ComponentProvider popupProvider = placeholder.getProvider();
|
||||
ActionContext actionContext = popupProvider.getActionContext(event);
|
||||
if (actionContext == null) {
|
||||
actionContext = new ActionContext();
|
||||
}
|
||||
|
||||
actionContext.setSourceObject(e.getSource());
|
||||
actionContext.setMouseEvent(e);
|
||||
actionContext.setSourceObject(popupContext.getSource());
|
||||
actionContext.setMouseEvent(event);
|
||||
|
||||
Iterator<DockingActionIf> localActions = info.getActions();
|
||||
Iterator<DockingActionIf> localActions = placeholder.getActions();
|
||||
JPopupMenu popupMenu = createPopupMenu(localActions, actionContext);
|
||||
if (popupMenu == null) {
|
||||
return; // no matching actions
|
||||
}
|
||||
|
||||
Component c = (Component) e.getSource();
|
||||
popupMenu.show(c, e.getX(), e.getY());
|
||||
Component c = popupContext.getComponent();
|
||||
Point p = popupContext.getPoint();
|
||||
popupMenu.show(c, p.x, p.y);
|
||||
}
|
||||
|
||||
protected JPopupMenu createPopupMenu(Iterator<DockingActionIf> localActions,
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/* ###
|
||||
* 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 docking;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Point;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A class that holds information used to show a popup menu
|
||||
*/
|
||||
public class PopupMenuContext {
|
||||
|
||||
private Component component;
|
||||
private MouseEvent event;
|
||||
private Point point;
|
||||
|
||||
PopupMenuContext(MouseEvent event) {
|
||||
this.event = event;
|
||||
this.component = Objects.requireNonNull(event.getComponent());
|
||||
this.point = event.getPoint();
|
||||
}
|
||||
|
||||
PopupMenuContext(Component component, Point point) {
|
||||
this.component = Objects.requireNonNull(component);
|
||||
this.point = point;
|
||||
}
|
||||
|
||||
public MouseEvent getEvent() {
|
||||
return event;
|
||||
}
|
||||
|
||||
public Component getComponent() {
|
||||
return component;
|
||||
}
|
||||
|
||||
public Point getPoint() {
|
||||
return new Point(point);
|
||||
}
|
||||
|
||||
public Object getSource() {
|
||||
if (event != null) {
|
||||
return event.getSource();
|
||||
}
|
||||
return component;
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,7 @@ package docking.action;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.Action;
|
||||
import javax.swing.KeyStroke;
|
||||
@@ -61,6 +60,7 @@ public class KeyBindingsManager implements PropertyChangeListener {
|
||||
|
||||
public void addReservedAction(DockingActionIf action) {
|
||||
KeyStroke keyBinding = action.getKeyBinding();
|
||||
Objects.requireNonNull(keyBinding);
|
||||
addReservedKeyBinding(action, keyBinding);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/* ###
|
||||
* 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 docking.action;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.DockingWindowManager;
|
||||
|
||||
/**
|
||||
* An action to trigger a context menu over the focus owner. This allows context menus to be
|
||||
* triggered from the keyboard.
|
||||
*/
|
||||
public class ShowContextMenuAction extends DockingAction {
|
||||
|
||||
public ShowContextMenuAction(KeyStroke keyStroke) {
|
||||
super("Show Context Menu", DockingWindowManager.DOCKING_WINDOWS_OWNER);
|
||||
setKeyBindingData(new KeyBindingData(keyStroke));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
||||
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||
Window window = kfm.getActiveWindow();
|
||||
if (window == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// use the focused component to determine what should get the context menu
|
||||
Component focusOwner = kfm.getFocusOwner();
|
||||
if (focusOwner != null) {
|
||||
DockingWindowManager.showContextMenu(focusOwner);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -91,6 +91,10 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
||||
keyBindingsManager.addReservedAction(new HelpAction(false, ReservedKeyBindings.HELP_KEY2));
|
||||
keyBindingsManager.addReservedAction(
|
||||
new HelpAction(true, ReservedKeyBindings.HELP_INFO_KEY));
|
||||
keyBindingsManager.addReservedAction(
|
||||
new ShowContextMenuAction(ReservedKeyBindings.CONTEXT_MENU_KEY1));
|
||||
keyBindingsManager.addReservedAction(
|
||||
new ShowContextMenuAction(ReservedKeyBindings.CONTEXT_MENU_KEY2));
|
||||
|
||||
// these are diagnostic
|
||||
if (SystemUtilities.isInDevelopmentMode()) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,9 +25,8 @@ import javax.swing.plaf.MenuItemUI;
|
||||
|
||||
public class DockingCheckboxMenuItemUI extends DockingMenuItemUI {
|
||||
public static ComponentUI createUI(JComponent c) {
|
||||
LookAndFeel underlying = UIManager.getLookAndFeel();
|
||||
DockingCheckboxMenuItemUI result = new DockingCheckboxMenuItemUI();
|
||||
result.ui = (MenuItemUI) underlying.getDefaults().getUI(c);
|
||||
result.ui = (MenuItemUI) UIManager.getDefaults().getUI(c);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,9 +58,8 @@ public class DockingMenuItemUI extends MenuItemUI {
|
||||
protected MenuItemUI ui;
|
||||
|
||||
public static ComponentUI createUI(JComponent c) {
|
||||
LookAndFeel underlying = UIManager.getLookAndFeel();
|
||||
DockingMenuItemUI result = new DockingMenuItemUI();
|
||||
result.ui = (MenuItemUI) underlying.getDefaults().getUI(c);
|
||||
result.ui = (MenuItemUI) UIManager.getDefaults().getUI(c);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,9 +21,8 @@ import javax.swing.plaf.MenuItemUI;
|
||||
|
||||
public class DockingMenuUI extends DockingMenuItemUI {
|
||||
public static ComponentUI createUI(JComponent c) {
|
||||
LookAndFeel underlying = UIManager.getLookAndFeel();
|
||||
DockingMenuUI result = new DockingMenuUI();
|
||||
result.ui = (MenuItemUI) underlying.getDefaults().getUI(c);
|
||||
result.ui = (MenuItemUI) UIManager.getDefaults().getUI(c);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2017,7 +2017,7 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
|
||||
// ThreadedTableModelUpdateMgr<ROW_OBJECT>
|
||||
Object updateManager = getInstanceField("updateManager", model);
|
||||
SwingUpdateManager sum =
|
||||
(SwingUpdateManager) getInstanceField("updateManager", updateManager);
|
||||
(SwingUpdateManager) getInstanceField("addRemoveUpdater", updateManager);
|
||||
Worker worker = (Worker) getInstanceField("worker", model);
|
||||
String workerState = worker == null ? "<no worker>" : Boolean.toString(worker.isBusy());
|
||||
return "Table model busy state - Swing Update Manager? " + sum.isBusy() + "; worker?" +
|
||||
|
||||
+7
-8
@@ -60,16 +60,15 @@ public class GTreeStartEditingTask extends GTreeTask {
|
||||
|
||||
private void edit() {
|
||||
|
||||
if (tree.isFiltered()) {
|
||||
Msg.showWarn(getClass(), tree, "Cannot Edit Tree Node",
|
||||
"Can't edit tree node \"" + childName + "\" while tree is filtered.");
|
||||
return;
|
||||
}
|
||||
|
||||
GTreeNode editNode = parent.getChild(childName);
|
||||
if (editNode == null) {
|
||||
if (tree.isFiltered()) {
|
||||
Msg.showWarn(getClass(), tree, "Cannot Edit Tree Node",
|
||||
"Can't edit tree node \"" + childName + "\" while tree is filtered.");
|
||||
}
|
||||
else {
|
||||
Msg.debug(this,
|
||||
"Can't find node \"" + childName + "\" to edit.");
|
||||
}
|
||||
Msg.debug(this, "Can't find node \"" + childName + "\" to edit.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+2
-3
@@ -204,7 +204,7 @@ public class DockingWindowsLookAndFeelUtils {
|
||||
case NIMBUS_LOOK_AND_FEEL:
|
||||
// fix scroll bar grabber disappearing. See https://bugs.openjdk.java.net/browse/JDK-8134828
|
||||
// This fix looks like it should not cause harm even if the bug is fixed on the jdk side.
|
||||
UIDefaults defaults = lookAndFeel.getDefaults();
|
||||
UIDefaults defaults = UIManager.getDefaults();
|
||||
defaults.put("ScrollBar.minimumThumbSize", new Dimension(30, 30));
|
||||
|
||||
// (see NimbusDefaults for key values that can be changed here)
|
||||
@@ -277,8 +277,7 @@ public class DockingWindowsLookAndFeelUtils {
|
||||
|
||||
/** Allows you to globally set the font size (don't use this method!) */
|
||||
private static void setGlobalFontSizeOverride(int fontSize) {
|
||||
LookAndFeel lookAndFeel = UIManager.getLookAndFeel();
|
||||
UIDefaults defaults = lookAndFeel.getDefaults();
|
||||
UIDefaults defaults = UIManager.getDefaults();
|
||||
|
||||
Set<Entry<Object, Object>> set = defaults.entrySet();
|
||||
Iterator<Entry<Object, Object>> iterator = set.iterator();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -34,6 +33,11 @@ public class ReservedKeyBindings {
|
||||
public static final KeyStroke HELP_INFO_KEY =
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_F1, DockingUtils.CONTROL_KEY_MODIFIER_MASK);
|
||||
|
||||
public static final KeyStroke CONTEXT_MENU_KEY1 =
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_F10, InputEvent.SHIFT_DOWN_MASK);
|
||||
public static final KeyStroke CONTEXT_MENU_KEY2 =
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_CONTEXT_MENU, 0);
|
||||
|
||||
public static final KeyStroke FOCUS_INFO_KEY =
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_F2, DockingUtils.CONTROL_KEY_MODIFIER_MASK |
|
||||
InputEvent.ALT_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK);
|
||||
@@ -50,7 +54,8 @@ public class ReservedKeyBindings {
|
||||
code == KeyEvent.VK_CAPS_LOCK || code == KeyEvent.VK_TAB ||
|
||||
HELP_KEY1.equals(keyStroke) || HELP_KEY2.equals(keyStroke) ||
|
||||
HELP_INFO_KEY.equals(keyStroke) || UPDATE_KEY_BINDINGS_KEY.equals(keyStroke) ||
|
||||
FOCUS_INFO_KEY.equals(keyStroke) || FOCUS_CYCLE_INFO_KEY.equals(keyStroke)) {
|
||||
FOCUS_INFO_KEY.equals(keyStroke) || FOCUS_CYCLE_INFO_KEY.equals(keyStroke) ||
|
||||
CONTEXT_MENU_KEY1.equals(keyStroke) || CONTEXT_MENU_KEY2.equals(keyStroke)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -92,9 +92,9 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
*
|
||||
* @param includeTextField if true, the dialog can display a status progressMessage with progress details
|
||||
* @param includeCancelButton if true, a cancel button will be displayed
|
||||
* @param includeCancelButton if true, a cancel button will be displayed
|
||||
*/
|
||||
public TaskMonitorComponent(boolean includeTextField, boolean includeCancelButton) {
|
||||
updateProgressPanelRunnable = () -> updateProgressPanel();
|
||||
@@ -106,7 +106,7 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
|
||||
shouldCancelRunnable = () -> {
|
||||
int currentTaskID = taskID.get();
|
||||
|
||||
boolean userSaysYes = OptionDialog.showYesNoDialog(TaskMonitorComponent.this, "Cancel?",
|
||||
boolean userSaysYes = OptionDialog.showYesNoDialog(null, "Cancel?",
|
||||
"Do you really want to cancel " + getTaskName() + "?") == OptionDialog.OPTION_ONE;
|
||||
|
||||
if (userSaysYes && currentTaskID == taskID.get()) {
|
||||
@@ -206,7 +206,7 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
|
||||
// a chance to do so. In other words, the background thread will end up
|
||||
// blocking instead of working, which defeats our attempts to never show
|
||||
// a task dialog for fast background tasks.
|
||||
//
|
||||
//
|
||||
isIndeterminate.set(indeterminate);
|
||||
Swing.runIfSwingOrRunLater(() -> {
|
||||
boolean newValue = isIndeterminate.get();
|
||||
@@ -269,7 +269,7 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
|
||||
/**
|
||||
* Returns true if {@link #setIndeterminate(boolean)} with a value of <code>true</code> has
|
||||
* been called.
|
||||
*
|
||||
*
|
||||
* @return true if {@link #setIndeterminate(boolean)} with a value of <code>true</code> has
|
||||
* been called.
|
||||
*/
|
||||
@@ -280,7 +280,7 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
|
||||
|
||||
/**
|
||||
* Set whether the progress bar should be visible
|
||||
*
|
||||
*
|
||||
* @param show true if the progress bar should be visible
|
||||
*/
|
||||
public synchronized void showProgress(boolean show) {
|
||||
@@ -293,7 +293,7 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
|
||||
/**
|
||||
* Set the name of the task; the name shows up in the tool tip for
|
||||
* the cancel button.
|
||||
*
|
||||
*
|
||||
* @param name the name of the task
|
||||
*/
|
||||
public void setTaskName(String name) {
|
||||
@@ -303,7 +303,7 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
|
||||
|
||||
/**
|
||||
* Set the visibility of the cancel button
|
||||
*
|
||||
*
|
||||
* @param visible if true, show the cancel button; false otherwise
|
||||
*/
|
||||
public void setCancelButtonVisibility(boolean visible) {
|
||||
@@ -325,7 +325,7 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
|
||||
|
||||
/**
|
||||
* Sets the visibility of the progress icon
|
||||
*
|
||||
*
|
||||
* @param visible if true, display the progress icon
|
||||
*/
|
||||
public void showProgressIcon(boolean visible) {
|
||||
|
||||
+34
-73
@@ -30,71 +30,43 @@ import docking.*;
|
||||
import docking.test.AbstractDockingTest;
|
||||
import docking.widgets.PasswordDialog;
|
||||
import generic.test.category.NightlyCategory;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
// The splash screen is sensitive to windows being activated/deactivated, so don't run
|
||||
// when other test windows may be open
|
||||
@Category(NightlyCategory.class)
|
||||
public class SplashScreenTest extends AbstractDockingTest {
|
||||
|
||||
private AboutDialog aboutDialog;
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
Msg.debug(this, "tearDown() - open windows before closing");
|
||||
printOpenWindows();
|
||||
|
||||
runSwing(() -> SplashScreen.disposeSplashScreen());
|
||||
|
||||
closeAllWindows();
|
||||
printOpenWindows();
|
||||
|
||||
Msg.debug(this, "tearDown() - open windows after closing");
|
||||
disposeAllWindows();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShowInfoWindow() throws Exception {
|
||||
// no parent
|
||||
showModalInfoWindow(null);
|
||||
|
||||
ensureInfoWindowVisible();
|
||||
hideInfoWindow();
|
||||
|
||||
// not visible parent
|
||||
JFrame parentFrame = new JFrame("InfoWindowTest.testShowInfoWindow Frame");
|
||||
parentFrame.setBounds(-100, -100, 0, 0);
|
||||
showModalInfoWindow(parentFrame);
|
||||
|
||||
ensureInfoWindowVisible();
|
||||
hideInfoWindow();
|
||||
|
||||
// visible parent
|
||||
parentFrame.setVisible(true);
|
||||
showModalInfoWindow(parentFrame);
|
||||
|
||||
ensureInfoWindowVisible();
|
||||
hideInfoWindow();
|
||||
private void disposeAllWindows() {
|
||||
for (Window window : getAllWindows()) {
|
||||
runSwing(window::dispose);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShowAndHideSplashScreen() {
|
||||
showSplashScreen(true);
|
||||
ensureSpashScreenVisible(true);
|
||||
assertSpashScreenVisible(true);
|
||||
|
||||
showSplashScreen(false);
|
||||
ensureSpashScreenVisible(false);
|
||||
assertSpashScreenVisible(false);
|
||||
|
||||
showSplashScreen(true);
|
||||
ensureSpashScreenVisible(true);
|
||||
assertSpashScreenVisible(true);
|
||||
|
||||
showSplashScreen(false);
|
||||
ensureSpashScreenVisible(false);
|
||||
assertSpashScreenVisible(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateSplashScreenStatus() {
|
||||
showSplashScreen(true);
|
||||
ensureSpashScreenVisible(true);
|
||||
assertSpashScreenVisible(true);
|
||||
|
||||
JLabel statusLabel = (JLabel) getInstanceField("statusLabel", SplashScreen.class);
|
||||
|
||||
@@ -120,24 +92,24 @@ public class SplashScreenTest extends AbstractDockingTest {
|
||||
public void testSplashScreenPasswordModality_SharedParent() throws Exception {
|
||||
|
||||
showSplashScreen(true);
|
||||
ensureSpashScreenVisible(true);
|
||||
assertSpashScreenVisible(true);
|
||||
|
||||
// show a modal dialog with no parent (this will use the Splash Screen's parent)
|
||||
showModalPasswordDialog(null);
|
||||
|
||||
// When the splash screen and the dialog share a parent, then the dialog should NOT
|
||||
// cause the splash screen to go away
|
||||
ensureSpashScreenVisible(true);
|
||||
assertSpashScreenVisible(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplashScreenPasswordModality_UnsharedParent() throws Exception {
|
||||
// show the splash screen
|
||||
showSplashScreen(true);
|
||||
ensureSpashScreenVisible(true);
|
||||
assertSpashScreenVisible(true);
|
||||
|
||||
DockingFrame frame = new DockingFrame("Modal Parent Frame");
|
||||
frame.setVisible(true);
|
||||
show(frame);
|
||||
showModalPasswordDialog(frame);
|
||||
|
||||
ensureSplashScreenWillClose();
|
||||
@@ -147,6 +119,10 @@ public class SplashScreenTest extends AbstractDockingTest {
|
||||
// Private Methods
|
||||
//==================================================================================================
|
||||
|
||||
private void show(JFrame frame) {
|
||||
runSwing(() -> frame.setVisible(true));
|
||||
}
|
||||
|
||||
private void ensureSplashScreenWillClose() {
|
||||
waitForCondition(() -> {
|
||||
SplashScreen splash = getSplash();
|
||||
@@ -155,42 +131,44 @@ public class SplashScreenTest extends AbstractDockingTest {
|
||||
}
|
||||
|
||||
private DockingDialog showModalPasswordDialog(Frame parentFrame) throws Exception {
|
||||
|
||||
String dialogTitle = "InfoWindowTest.testSplashScreenPasswordModality() Dialog";
|
||||
DialogComponentProvider passwordDialog = runSwing(() -> new PasswordDialog(dialogTitle,
|
||||
"Server Type", "Server Name", "Prompt", null, null));
|
||||
|
||||
if (parentFrame == null) {
|
||||
// null means to share the parent
|
||||
Object splashParent = getInstanceField("hiddenFrame", SplashScreen.class);
|
||||
parentFrame = (Frame) splashParent;
|
||||
}
|
||||
|
||||
Frame finalParent = parentFrame;
|
||||
executeOnSwingWithoutBlocking(
|
||||
() -> DockingWindowManager.showDialog(parentFrame, passwordDialog));
|
||||
() -> {
|
||||
DockingDialog dialog =
|
||||
DockingDialog.createDialog(finalParent, passwordDialog, finalParent);
|
||||
dialog.setVisible(true);
|
||||
});
|
||||
|
||||
JDialog dialog = waitForJDialog(dialogTitle);
|
||||
assertNotNull(dialog);
|
||||
|
||||
Window dialogWindow = SwingUtilities.windowForComponent(dialog);
|
||||
Msg.debug(this, "Created modal dialog with parent: " + getTitleForWindow(dialogWindow) +
|
||||
" - id: " + System.identityHashCode(dialogWindow));
|
||||
|
||||
return (DockingDialog) dialog;
|
||||
}
|
||||
|
||||
// handles showing the modal info window, which must be done from a thread outside of the
|
||||
// test thread
|
||||
private void showModalInfoWindow(final JFrame parentFrame) {
|
||||
// create a thread to show the modal dialog so that the current thread doesn't block
|
||||
aboutDialog = runSwing(() -> new AboutDialog());
|
||||
executeOnSwingWithoutBlocking(() -> DockingWindowManager.showDialog(null, aboutDialog));
|
||||
}
|
||||
|
||||
private void showSplashScreen(final boolean makeVisible) {
|
||||
|
||||
if (makeVisible) {
|
||||
SplashScreen splash = runSwing(() -> SplashScreen.showSplashScreen());
|
||||
assertNotNull("Failed showing splash screen", splash);
|
||||
waitForSwing();
|
||||
return;
|
||||
}
|
||||
SplashScreen.disposeSplashScreen();
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
private void ensureSpashScreenVisible(boolean visible) {
|
||||
private void assertSpashScreenVisible(boolean visible) {
|
||||
// get the 'splashWindow' and make sure that it is not null and that it is visible
|
||||
SplashScreen splashScreen = getSplash();
|
||||
|
||||
@@ -206,23 +184,6 @@ public class SplashScreenTest extends AbstractDockingTest {
|
||||
|
||||
// timing issue debug
|
||||
waitForCondition(() -> splashScreen.isVisible());
|
||||
|
||||
if (!splashScreen.isVisible()) {
|
||||
|
||||
// this can happen if other OS windows trigger the splash window to be hidden
|
||||
printOpenWindows();
|
||||
fail("The splash screen is not visible when expected to be so - " + splashScreen);
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureInfoWindowVisible() {
|
||||
// get the 'infoDialog' and make sure that it is not null and that it is visible
|
||||
assertTrue("The info dialog is not visible after it was supposed to " + "have been shown.",
|
||||
aboutDialog.isVisible());
|
||||
}
|
||||
|
||||
private void hideInfoWindow() throws Exception {
|
||||
runSwing(() -> aboutDialog.close());
|
||||
}
|
||||
|
||||
private SplashScreen getSplash() {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package ghidra.graph.viewer.popup;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.*;
|
||||
|
||||
@@ -129,6 +130,13 @@ public class PopupRegulator<V, E> {
|
||||
return;
|
||||
}
|
||||
|
||||
Component c = event.getComponent();
|
||||
if (!c.isShowing()) {
|
||||
// This method is called from a a timer. It is possible that the graph has been
|
||||
// closed by the time this method is called.
|
||||
return;
|
||||
}
|
||||
|
||||
ToolTipInfo<?> toolTipInfo = popupSource.getToolTipInfo(event);
|
||||
JComponent toolTipComponent = toolTipInfo.getToolTipComponent();
|
||||
boolean isCustomJavaTooltip = !(toolTipComponent instanceof JToolTip);
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@ public interface PcodeFactory {
|
||||
public HighSymbol getSymbol(long symbolId);
|
||||
public Varnode setInput(Varnode vn,boolean val);
|
||||
public void setAddrTied(Varnode vn,boolean val);
|
||||
public void setPersistant(Varnode vn,boolean val);
|
||||
public void setPersistent(Varnode vn, boolean val);
|
||||
public void setUnaffected(Varnode vn,boolean val);
|
||||
public void setMergeGroup(Varnode vn,short val);
|
||||
public void setDataType(Varnode vn,DataType type);
|
||||
|
||||
+2
-2
@@ -392,9 +392,9 @@ public class PcodeSyntaxTree implements PcodeFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPersistant(Varnode vn, boolean val) {
|
||||
public void setPersistent(Varnode vn, boolean val) {
|
||||
VarnodeAST vnast = (VarnodeAST) vn;
|
||||
vnast.setPersistant(val);
|
||||
vnast.setPersistent(val);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+3
-3
@@ -255,9 +255,9 @@ public class Varnode {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return is persistant
|
||||
* @return is persistent
|
||||
*/
|
||||
public boolean isPersistant() {
|
||||
public boolean isPersistent() {
|
||||
return false; // Not a valid query with a free varnode
|
||||
}
|
||||
|
||||
@@ -476,7 +476,7 @@ public class Varnode {
|
||||
}
|
||||
attrstring = el.getAttribute("persists");
|
||||
if ((attrstring != null) && (SpecXmlUtils.decodeBoolean(attrstring))) {
|
||||
factory.setPersistant(vn, true);
|
||||
factory.setPersistent(vn, true);
|
||||
}
|
||||
attrstring = el.getAttribute("addrtied");
|
||||
if ((attrstring != null) && (SpecXmlUtils.decodeBoolean(attrstring))) {
|
||||
|
||||
+6
-6
@@ -37,7 +37,7 @@ public class VarnodeAST extends Varnode {
|
||||
|
||||
private boolean bInput;
|
||||
private boolean bAddrTied;
|
||||
private boolean bPersistant;
|
||||
private boolean bPersistent;
|
||||
private boolean bUnaffected;
|
||||
private boolean bFree;
|
||||
private int uniqId; // Unique Id for distinguishing otherwise identical varnodes
|
||||
@@ -50,7 +50,7 @@ public class VarnodeAST extends Varnode {
|
||||
super(a, sz);
|
||||
bInput = false;
|
||||
bAddrTied = false;
|
||||
bPersistant = false;
|
||||
bPersistent = false;
|
||||
bUnaffected = false;
|
||||
bFree = true;
|
||||
uniqId = id;
|
||||
@@ -70,8 +70,8 @@ public class VarnodeAST extends Varnode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPersistant() {
|
||||
return bPersistant;
|
||||
public boolean isPersistent() {
|
||||
return bPersistent;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -132,8 +132,8 @@ public class VarnodeAST extends Varnode {
|
||||
def = null;
|
||||
}
|
||||
|
||||
public void setPersistant(boolean val) {
|
||||
bPersistant = val;
|
||||
public void setPersistent(boolean val) {
|
||||
bPersistent = val;
|
||||
}
|
||||
|
||||
public void setUnaffected(boolean val) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user