NationalSecurityAgency / ghidra

Ghidra is a software reverse engineering (SRE) framework
https://www.nsa.gov/ghidra
Apache License 2.0
51.07k stars 5.82k forks source link

Cannot invoke "ghidra.framework.main.FrontEndService.addProjectListener(ghidra.framework.model.ProjectListener)" because "frontEnd" is null #6937

Closed sbrptdev2 closed 1 week ago

sbrptdev2 commented 2 weeks ago

Describe the bug

I'm encountering this exception when closing the Ghidra process via exit menu, while developing an extension with Eclipse. There is no indication to the actual source of the problem that I can see, and searching for occurrences of this exception yields nothing.

java.lang.NullPointerException: Cannot invoke "ghidra.framework.main.FrontEndService.addProjectListener(ghidra.framework.model.ProjectListener)" because "frontEnd" is null
java.lang.RuntimeException: java.lang.NullPointerException: Cannot invoke "ghidra.framework.main.FrontEndService.addProjectListener(ghidra.framework.model.ProjectListener)" because "frontEnd" is null
    at ghidra.framework.plugintool.Plugin.cleanup(Plugin.java:288)
    at ghidra.framework.plugintool.PluginManager.dispose(PluginManager.java:105)
    at ghidra.framework.plugintool.PluginTool.dispose(PluginTool.java:515)
    at ghidra.framework.main.FrontEndTool.dispose(FrontEndTool.java:170)
    at ghidra.framework.plugintool.PluginTool.close(PluginTool.java:1212)
    at ghidra.framework.main.AppInfo.exitGhidra(AppInfo.java:49)
    at ghidra.framework.plugintool.PluginTool$1.actionPerformed(PluginTool.java:1010)
    at docking.DockingActionProxy.actionPerformed(DockingActionProxy.java:47)
    at docking.DockingActionPerformer.lambda$perform$0(DockingActionPerformer.java:73)
    at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:318)
    at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:773)
    at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:720)
    at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:714)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:400)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:87)
    at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:742)
    at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
    at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
    at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
Caused by: java.lang.NullPointerException: Cannot invoke "ghidra.framework.main.FrontEndService.addProjectListener(ghidra.framework.model.ProjectListener)" because "frontEnd" is null
    at ghidra.app.plugin.debug.DomainFolderChangesDisplayPlugin.dispose(DomainFolderChangesDisplayPlugin.java:63)
    at ghidra.framework.plugintool.Plugin.cleanup(Plugin.java:277)
    ... 21 more

---------------------------------------------------
Build Date: 2024-Sep-16
Ghidra Version: 11.3
Java Home: /usr/lib/jvm/java-21-openjdk-amd64
JVM Version: Ubuntu 21.0.4
OS: Linux amd64

Expected behavior Clean exit.

dragonmacher commented 2 weeks ago

Taking a peek at this trace, this code seems to have a bug in it (but this bug does not explain this stack trace):

    @Override
    protected void dispose() {
        FrontEndService frontEnd = tool.getService(FrontEndService.class);
        frontEnd.addProjectListener(this);

        Project activeProject = tool.getProjectManager().getActiveProject();
        if (activeProject != null) {
            projectClosed(activeProject);
        }
    }

I believe the call to addProjectListener() should be removeProjectListener()

The code above does not expect the frontEnd to be null at the time of disposal. I'm guessing that there is some other exception happening that is causing the front end to be cleared inside tool so that the call the tool.getService() is failing.

sbrptdev2 commented 2 weeks ago

This is happening with and without loading the extension being developed (just tested again).

It might be a separate issue but there is also no way to verify why a given plugin might not be loaded (or I don't see how to enable verbose output for the ClassSearch -which I guess is responsible for it-). I was about to test this one more time but the extension I am working on simply ceased to load. It compiles just fine and no out of the norm changes have been applied to its ProgramPlugin class.

dragonmacher commented 2 weeks ago

You can enable logging for the ClassSearcher and Extension related packages to get more details on what is happening during the load process. These packages have embedded trace() messages that you may find helpful.

Changing the log levels varies by development setup. Are you running from a pull of the repo, using the source code inside of Eclipse? Or are you building Ghidra from source code and then using that to write your own code?

sbrptdev2 commented 2 weeks ago

Yes, using Eclipse, replicated the instructions in "Building" and "Advanced Development".

Currently developing the extension that way, until there is a need to make a PR for integration. Haven't done any modifications to the Ghidra sources as-is.

sbrptdev2 commented 2 weeks ago

Testing now with the above PR #6939 modifying the extension's run configuration like so:

-Dlog4j.configurationFile=file:BASEPATH/ghidra/Ghidra/Framework/Generic/src/main/resources/generic.log4jdev.xml

Unfortunately, still at the same spot:

TRACE Using restricted extension class loader? false   (ClassSearcher.java:109) 
INFO  Searching for classes...   (ClassSearcher.java:409) 
TRACE Searching jar file: REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Framework/Utility/lib/Utility.jar   (ClassSearcher.java:425) 
TRACE Scanning module root directories: [REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/6502, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/68000, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/8048, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/8051, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/8085, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/AARCH64, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/ARM, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Debug/AnnotationValidator, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/Atmel, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/BPF, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/BSim, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/BSimFeatureVisualizer, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/Base, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/BytePatterns, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/ByteViewer, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/CP1600, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/CR16, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/CodeCompare, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/DATA, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Framework/DB, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/../GPL/DMG, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/Dalvik, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/DebugUtils, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Debug/Debugger, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Debug/Debugger-agent-dbgeng, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Debug/Debugger-agent-dbgmodel, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Debug/Debugger-agent-dbgmodel-traceloader, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Debug/Debugger-agent-frida, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Debug/Debugger-agent-gdb, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Debug/Debugger-agent-lldb, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Debug/Debugger-api, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Debug/Debugger-gadp, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Debug/Debugger-isf, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Debug/Debugger-jpda, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Debug/Debugger-rmi-trace, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Debug/Debugger-swig-lldb, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/Decompiler, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/DecompilerDependent, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/../GPL/DemanglerGnu, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Framework/Docking, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Framework/Emulation, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/FileFormats, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Framework/FileSystem, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Debug/Framework-AsyncComm, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Debug/Framework-Debugging, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Debug/Framework-TraceModeling, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/FunctionGraph, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/FunctionGraphDecompilerExtension, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/FunctionID, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Framework/Generic, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/GhidraGo, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/GhidraServer, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/GnuDemangler, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Framework/Graph, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/GraphFunctionCalls, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/GraphServices, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Framework/Gui, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/HCS08, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/HCS12, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Framework/Help, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/JVM, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/Jython, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/Loongarch, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/M16C, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/M8C, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/MC6800, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/MCS96, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/MIPS, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/MicrosoftCodeAnalyzer, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/MicrosoftDemangler, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/MicrosoftDmang, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/PA-RISC, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/PDB, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/PIC, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/PowerPC, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/ProgramDiff, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/ProgramGraph, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Framework/Project, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Debug/ProposedUtils, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Framework/Pty, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Configurations/Public_Release, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/PyGhidra, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/RISCV, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/Recognizers, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/Sarif, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Framework/SoftwareModeling, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/SourceCodeLookup, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/Sparc, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/SuperH, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/SuperH4, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/SwiftDemangler, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/SystemEmulation, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/TI_MSP430, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Debug/TaintAnalysis, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Framework/Utility, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/V850, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/VersionTracking, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/VersionTrackingBSim, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Features/WildcardAssembler, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/Xtensa, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/Z80, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/eBPF, REDACTED/extensions/ghidra-yara/ghidra_yara, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/tricore, REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/Processors/x86]   (ClassSearcher.java:558) 
TRACE Using extension point pattern: .*(TypeMapper|FieldFactory|VMInstruction|Constraint|FieldMouseHandler|ChecksumAlgorithm|FormatModel|ResultHandler|Analyzer|Exporter|DisassemblyInject|ContentHandler|ElfExtension|DebuggerMappingOpinion|PluginPackage|FSBFileHandler|FileSystem|TraceRmiLaunchOpinion|ProjectDataColumn|PcodeStateInitializer|Stringable|DebuggerProgramLaunchOpinion|RelocationFixupHandler|CodeComparisonPanTRACE Using extension point pattern: .*(TypeMapper|FieldFactory|VMInstruction|Constraint|FieldMouseHandler|ChecksumAlgorithm|FormatModel|ResultHandler|Analyzer|Exporter|DisassemblyInject|ContentHandler|ElfExtension|DebuggerMappingOpinion|PluginPackage|FSBFileHandler|FileSystem|TraceRmiLaunchOpinion|ProjectDataColumn|PcodeStateInitializer|Stringable|DebuggerProgramLaunchOpinion|RelocationFixupHandler|CodeComparisonPanel|DebuggerRegisterColumnFactory|DebuggerClientFactory|StringHandler|AutoReadMemorySpec|TableColumn|ProgramCorrelator|ProgramCorrelatorFactory|Recognizer|ElfInfoProducer|ToolAssociation|Initializer|LanguageTranslator|AutoMapSpec|InstructionSkipper|DebuggerBot|GraphExporter|Demangler|DebuggerPcodeEmulatorFactory|CoffRelocationHandler|FileSystemModel|BinaryAnalysisCommand|DWARFFunctionFixup|Loader|TableRowMapper|AddressCorrelatorFactory|RunHandler|GhidraProtocolHandler|GraphDisplayProvider|LocationTrackingSpecFactory|DataType|DataTypeReferenceFinder|AnnotationHandler|ScriptProvider|Decryptor|DebuggerPlatformOpinion|AddressCorrelator|OverviewColorService|DebuggerModelFactory|LayoutProvider|MachoRelocationHandler|ColumnConstraintProvider|Theme|ElfRelocationHandler|Validator|Plugin|RelocationHandler)$   (ClassSearcher.java:611) 
el|DebuggerRegisterColumnFactory|DebuggerClientFactory|StringHandler|AutoReadMemorySpec|TableColumn|ProgramCorrelator|ProgramCorrelatorFactory|Recognizer|ElfInfoProducer|ToolAssociation|Initializer|LanguageTranslator|AutoMapSpec|InstructionSkipper|DebuggerBot|GraphExporter|Demangler|DebuggerPcodeEmulatorFactory|CoffRelocationHandler|FileSystemModel|BinaryAnalysisCommand|DWARFFunctionFixup|Loader|TableRowMapper|AddressCorrelatorFactory|RunHandler|GhidraProtocolHandler|GraphDisplayProvider|LocationTrackingSpecFactory|DataType|DataTypeReferenceFinder|AnnotationHandler|ScriptProvider|Decryptor|DebuggerPlatformOpinion|AddressCorrelator|OverviewColorService|DebuggerModelFactory|LayoutProvider|MachoRelocationHandler|ColumnConstraintProvider|Theme|ElfRelocationHandler|Validator|Plugin|RelocationHandler)$   (ClassSearcher.java:611) 
TRACE Searching classpath directory: REDACTED/extensions/ghidra-yara/ghidra_yara/bin/main   (ClassSearcher.java:429) 
TRACE Searching classpath directory: REDACTED/ghidra/build/dist/ghidra_11.3_DEV/Ghidra/patch   (ClassSearcher.java:429) 
TRACE Ignoring jar file: REDACTED/extensions/ghidra-yara/ghidra_yara/lib/libyara-4.5.2-SNAPSHOT-linux64.jar   (ClassSearcher.java:421) 
TRACE Ignoring jar file: REDACTED/extensions/ghidra-yara/ghidra_yara/lib/libyara-4.5.2-SNAPSHOT.jar   (ClassSearcher.java:421) 
TRACE Ignoring jar file: REDACTED/extensions/ghidra-yara/ghidra_yara/lib/original-libyara-4.5.2-SNAPSHOT.jar   (ClassSearcher.java:421) 

The error still manifests when closing Ghidra, and also the Plugin class does not load (but the Analyzer does). The extension point pattern should match.

Also after gradle cleanEclipse eclipse (and doing a workspace-wide refresh and build) the AbstractAnalyzer class (from Ghidra) appears missing to Eclipse. I do think we need more documentation for all the possible quirks and problems with the development environment. You obviously have become accustomed to these over the years and curated a significant amount of knowhow in dealing with them, but to outsiders these toolchain "heisenbugs" can be mistifying.

Until I can figure out why the extension is no longer being "found" and loaded in its entirety, I can't re-test the exception. It still occurs without loading it, so I might test the modification you suggested. I do agree that this should have manifested for other people before, something else is amiss.

The search continues...

dragonmacher commented 2 weeks ago

I do think we need more documentation for all the possible quirks and problems with the development environment. You obviously have become accustomed to these over the years and curated a significant amount of knowhow in dealing with them, but to outsiders these toolchain "heisenbugs" can be mistifying.

So true. (Imagine how hard it is to keep all of this straight when you remember all the old ways of configuring and building that we have had over the decades.) There was a point where this dev process was simple. Things used to be easy when all of the code lived in one Eclipse project, with a simple Ant build system. As with software design, flexibility comes at a cost of understandability. We are very flexible now. So much that it feels near impossible to keep the entirety of the system in your head. Situations like yours are especially difficult for me, since I don't develop extensions regularly. I agree that we should have much better documentation for the happy paths of scripting development, as well as some basic troubleshooting steps.

Not sure if you are running into this or not, but for me, Eclipse has become quite flaky over the years. I pretty regularly get odd compile errors that eventually just go away. Regarding your Plugin not being loaded, it seems like your path is correct, based on the traces. I can't tell if you have all tracing on inside of the ghidra.util.classfinder package. There are more fine-grained messages that will show the actually directories being processed. Some things that may help (excuse me if they are already obvious to you; it sounds like you have done some of this):

sbrptdev2 commented 2 weeks ago

Quick response:

gradle cleanEclipse eclipse will ensure that your project and classpath files are created, are up to date with the directory contents, and can then be imported into Eclipse.

This was done always whenever touching the source (ex. pulls).

If you already have imported the projects, and then use the gradle command again, you must refresh all of the projects inside of Eclipse by selecting your projects and pressing F5 or using the right-click menu and pressing 'Refresh'. Eclipse is slow to pick up changes to the generated project files, so refreshing helps.

Noticed this, as nonsensical as it might be considering it's 2024 and there are a million ways (inotify and all) to watch filesystem activity to trigger a refresh.

You should be able to verify that your plugin class has been compiled and lives inside of your project's bin directory by using the Project Explorer window in Eclipse.

They are present but have not been rebuilt in a few days. Odd.

$ tree bin
bin
├── main
│   ├── ghidra_yara
│   │   ├── GhidraYaraAnalyzer.class
│   │   ├── GhidraYaraComponent$1.class
│   │   ├── GhidraYaraComponent.class
│   │   ├── GhidraYaraPlugin.class
│   │   ├── GhidraYaraRule.class
│   │   ├── GhidraYaraRuleTableModel.class
│   │   ├── IterableHelper.class
│   │   ├── ScanMethod.class
│   │   ├── Utils.class
│   │   ├── YaraRuleTableProvider$YaraRuleTableContextMenuAction.class
│   │   └── YaraRuleTableProvider.class
│   ├── help
│   │   ├── TOC_Source.xml
│   │   └── topics
│   │       └── ghidra_yara
│   │           └── help.html
│   └── images
│       └── README.txt
├── scripts
│   └── README.txt
└── test
    └── README.test.txt

Removing them entirely, and refreshing does not repopulate the project.

Nothing shows up in Configure as expected, as it isn't loaded.

image

If your plugin class name ends with Plugin, then it should get loaded as long as it is in the classpath (which the tracing should verify). If needed, you can debug this using ClassSearcher.loadExtensionPoint(), with conditional breakpoints for your class or even by altering the code and adding your own prints for when your class is encountered.

Yes, GhidraYaraPlugin. We can give you access to the private repository (it will be released when it is ready for public consumption in any case) if you want to test yourself.

After cleaning up bin:

$ tree 
.
├── bin
├── build.gradle
├── data
│   ├── README.txt
│   └── rules
│       ├── findcrypt3.yar
│       ├── packer.yar
│       ├── peid.yar
│       ├── README.md
│       └── signsrch
│           └── signsrch_le_be.yar
├── extension.properties
├── ghidra_scripts
│   └── README.txt
├── lib
│   ├── libyara-4.5.2-SNAPSHOT.jar
│   ├── libyara-4.5.2-SNAPSHOT-linux64.jar
│   ├── original-libyara-4.5.2-SNAPSHOT.jar
│   └── README.txt
├── LICENSE
├── Module.manifest
├── os
│   ├── linux_x86_64
│   │   └── README.txt
│   ├── mac_x86_64
│   │   └── README.txt
│   └── win_x86_64
│       └── README.txt
└── src
    ├── main
    │   ├── help
    │   │   └── help
    │   │       ├── TOC_Source.xml
    │   │       └── topics
    │   │           └── ghidra_yara
    │   │               └── help.html
    │   ├── java
    │   │   └── ghidra_yara
    │   │       ├── GhidraYaraAnalyzer.java
    │   │       ├── GhidraYaraComponent.java
    │   │       ├── GhidraYaraPlugin.java
    │   │       ├── GhidraYaraRule.java
    │   │       ├── GhidraYaraRuleTableModel.java
    │   │       ├── IterableHelper.java
    │   │       ├── ScanMethod.java
    │   │       ├── Utils.java
    │   │       └── YaraRuleTableProvider.java
    │   └── resources
    │       └── images
    │           └── README.txt
    └── test
        └── java
            └── README.test.txt

Building out of band with gradle:

ghidra_yara (main)
$ gradle

> Task :buildExtension

Created ghidra_11.3_DEV_20240921_ghidra_yara.zip in /.../ghidra/extensions/ghidra-yara/ghidra_yara/dist

BUILD SUCCESSFUL in 4s
7 actionable tasks: 7 executed
sbrptdev2 commented 2 weeks ago

Keeping things sane for now, I have not added the Maven package dependency from our Github package repository (but that is entirely public and there is CI to autobuild things for linux64). Instead, the jars are copied directly. I know they are the correct ones.

Edit:

image

And: image

After "Link with Ghidra", which is not documented anywhere but seemed like a good candidate to try.

dragonmacher commented 2 weeks ago

It seems like the issue is that Eclipse is not building your source for your project. This could be that the project's source inclusions are not setup correctly. Or, it could just be some flakiness of Eclipse, sadly. The project structure looks good to me. You can try deleting and re-importing the project (without deleting the contents) to see if that will force Eclipse to recompile.

Regarding the issues of running Ghidra, @ryanmkurtz is more up to speed with that.

sbrptdev2 commented 2 weeks ago

Indeed. Got it:

image

So, turns out the linking with Ghidra is needed (no surprise there, as you are essentially telling gradle internally where to find the GHIDRA_INSTALL_DIR... but this is odd, as it should be immediately provided by Run As (Ghidra).

The error related to Jython is also strange, as I have the correct PyDev 9.3 packages. Even more odd is that Eclipse happily finalizes the configuration despite the error, so closing the dialog and refreshing, and then re-running 'Run As' worked happily. Eclipse is truly an enterprise class environment. All the frustration, none of the basic decency of telling you why it takes a piss when it does. Alas....

Now we are back to a working point where I can check your suggestion regarding the frontEnd exception.

dragonmacher commented 2 weeks ago

Eclipse is truly an enterprise class environment. All the frustration, none of the basic decency of telling you why it takes a piss when it does. Alas....

Agreed. I do think Eclipse's best days are 15+ years ago.

sbrptdev2 commented 2 weeks ago

@dragonmacher So, preliminary debugging:

frontEnd is indeed null, replacing the call for removeProjectListener yields the same situation however:

java.lang.NullPointerException: Cannot invoke "ghidra.framework.main.FrontEndService.removeProjectListener(ghidra.framework.model.ProjectListener)" because "frontEnd" is null

I'm still trying to figure out where Eclipse hides the stack trace/call tree in the debugger. It must be the famed Java println debugging system being forced on me.

dragonmacher commented 2 weeks ago

I'm assuming you want to open the Debug view, which is where the threads are shown, along with breakpoint controls (which area also in the main window toolbar).

Honestly, print statements are my usual go to, since we are heavily multithreaded.

As mentioned before, I think there is some other initialization failure happening, which is polluting Ghidra's tear down process.

sbrptdev2 commented 2 weeks ago

Some notes:

From PluginManager, where the plugin list is iterated and the cleanup method gets called:

    void dispose() {
        for (Iterator<Plugin> it = pluginList.iterator(); it.hasNext();) {
            Plugin plugin = it.next();
            plugin.cleanup();
            it.remove();
        }
    }

Up to the point where DomainFolderChangesDisplayPlugin.dispose() gets called nothing seems out of the norm.

Looking at services from the FrontEndPlugin the list is pretty empty, only [0] ServiceInterfaceImplementationPair (id=495) remains at the time the dispose() method is called and frontEnd is returned as null from getService().

I don't know if this is the expected state, and I'm not sure if adding a check for null returns on getService() makes sense (as you say, this might be an issue elsewhere and the throwable handling in this later callstack is doing its job).

sbrptdev2 commented 2 weeks ago

Nervemind, FrontendPlugin is getting disposed of before the offending plugin:

image

Is this the correct behavior? I have no knowledge of the internals of Ghidra's handling inter-plugin dependency. In any case, it makes sense that any services provided by FrontendPlugin will not be there anymore past the lifetime of 'FrontendPlugin' itself.

Another edit: image

Indeed, disposed=true for this$0 FrontEndPlugin (id=137), and its services list is mostly empty with only [0] ServiceInterfaceImplementationPair (id=558) left in place. The 9 other services are disposed/null.

sbrptdev2 commented 2 weeks ago

The commit (https://github.com/subreption/ghidra/commit/50aa3dfef20e3101aad22d90a511eeb09fe2b545) in our fork seems to fix the manifestation of this issue at least for the DomainFolderChangesDisplayPlugin. Just tested on my local build. No apparent side-effects.

dragonmacher commented 2 weeks ago

It's quite possible that this is a latent issue that we have simply not encountered. I don't recall, but I would expect that dependents would be disposed before their dependencies. Even that could lead to arbitrary behavior if the dependency tree is complicated. The odd thing to me in this case is that I would expect the Front End to be disposed last of all, since that is the main entry into the system. But, it's possible that we do not have any special code to make this the case.

I will investigate more to see how this is all wired now.

sbrptdev2 commented 2 weeks ago

Agreed, appreciated. Back to the extension work here, will keep an eye on this issue however.