Ghidra Extension Points and ServiceProvider API: Complete Implementation Guide
Ghidra extension points are marker interfaces discovered at runtime through suffix-based manifest files, while the ServiceProvider API enables loose coupling between plugins by allowing dynamic lookup of shared services.
The NationalSecurityAgency/ghidra repository implements a sophisticated plugin architecture that centers on two core abstractions: extension points for automatic class discovery and service providers for dependency injection. Understanding how ghidra.util.classfinder.ExtensionPoint and ghidra.framework.plugintool.ServiceProvider interact allows developers to build modular extensions that integrate seamlessly with Ghidra's reverse engineering framework.
Understanding Ghidra Extension Points
Extension points form the backbone of Ghidra's modularity, allowing third-party code to be discovered and loaded without explicit registration in a central registry.
The ExtensionPoint Marker Interface
At the core of this system is ghidra.util.classfinder.ExtensionPoint, a marker interface located in Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ExtensionPoint.java. Any class wishing to participate in automatic discovery must implement this interface, but implementation alone is insufficient for loading.
The critical requirement is the suffix rule: Ghidra's ClassSearcher only loads classes whose simple names end with suffixes declared in data/ExtensionPoint.manifest files. For example, classes ending in "Initializer", "Provider", or "Factory" are loaded only if those suffixes appear in a manifest file within the module's data/ directory.
The Discovery Mechanism
Discovery occurs at startup through ClassSearcher (Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassSearcher.java), which performs three operations:
- Gather suffixes from every module's
data/ExtensionPoint.manifestfile (e.g.,Ghidra/Framework/Generic/data/ExtensionPoint.manifest) - Scan the classpath for classes ending with those suffixes
- Instantiate valid classes via their no-arg constructor and sort them by priority
Common extension point interfaces in the codebase include:
ghidra.service.graph.GraphDisplayProvider– Supplies graph viewer implementations (Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/service/graph/GraphDisplayProvider.java)ghidra.pcode.exec.PcodeUseropLibraryFactory– Provides custom P-code user-ops for the emulator (Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/PcodeUseropLibraryFactory.java)ghidra.program.util.LanguageTranslator– Translates between language specifications (Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/LanguageTranslator.java)
Controlling Loading with Annotations
The @ExtensionPointProperties annotation (Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ExtensionPointProperties.java) provides two key controls:
- Priority: Higher integer values load first (useful when order matters)
- Exclusion: Set
exclude=trueto prevent automatic discovery while keeping the interface implementation
import ghidra.util.classfinder.ExtensionPointProperties;
@ExtensionPointProperties(priority = 100)
public class HighPriorityProvider implements SomeExtensionPoint {
// This loads before providers with lower priority values
}
Implementing Extension Points in Your Plugin
To create a discoverable extension, you must define the interface, declare the suffix in a manifest, and implement the contract.
Step 1: Define the Extension Point Interface
Create an interface extending ExtensionPoint:
package com.example.ghidra.extensions;
import ghidra.util.classfinder.ExtensionPoint;
public interface AnalysisReporter extends ExtensionPoint {
void reportFinding(String address, String details);
}
Step 2: Register the Suffix
Create data/ExtensionPoint.manifest in your extension's root directory containing:
Reporter
Step 3: Implement and Deploy
Implement the interface with a class whose name ends in the registered suffix:
package com.example.ghidra.extensions.impl;
import com.example.ghidra.extensions.AnalysisReporter;
public class ConsoleAnalysisReporter implements AnalysisReporter {
@Override
public void reportFinding(String address, String details) {
System.out.printf("[Analysis] %s: %s%n", address, details);
}
}
When Ghidra starts, ClassSearcher automatically instantiates ConsoleAnalysisReporter because its name ends with "Reporter" and it implements ExtensionPoint.
Using the ServiceProvider API
While extension points enable discovery, the ServiceProvider API enables runtime collaboration between plugins through dependency injection.
Core ServiceProvider Interfaces
The service provider infrastructure resides in Ghidra/Framework/Utility/src/main/java/ghidra/framework/plugintool/ and consists of three primary components:
ServiceProvider– The contract defininggetService(Class<T>)for service retrievalServiceProviderStub– A minimal implementation returningnullfor all services, useful in unit testsServiceProviderDecorator– Wraps existing providers to add or override services without modifying the original
Accessing Services in Plugins
Plugins receive a ServiceProvider through the PluginTool instance. In Ghidra/Framework/Utility/src/main/java/ghidra/framework/plugintool/Plugin.java, which implements ExtensionPoint, the standard pattern retrieves services in the constructor:
import ghidra.framework.plugintool.Plugin;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.app.services.GraphService;
public class MyAnalysisPlugin extends Plugin {
private final GraphService graphService;
public MyAnalysisPlugin(PluginTool tool) {
super(tool);
ServiceProvider services = tool.getServiceProvider();
this.graphService = services.getService(GraphService.class);
}
}
Decorating and Extending Service Providers
To expose your own services to other plugins, use ServiceProviderDecorator (ServiceProviderDecorator.java):
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.framework.plugintool.ServiceProviderDecorator;
ServiceProvider original = tool.getServiceProvider();
ServiceProvider enhanced = ServiceProviderDecorator.decorate(original)
.addService(MyCustomService.class, new MyCustomServiceImpl())
.getProvider();
tool.setServiceProvider(enhanced);
This pattern allows plugins to layer capabilities without requiring other plugins to import your implementation classes directly.
Complete Integration Example
The following example demonstrates defining an extension point, implementing it, and consuming it through the ServiceProvider API:
// Extension point definition
package example;
import ghidra.util.classfinder.ExtensionPoint;
public interface LogFormatter extends ExtensionPoint {
String format(String message);
}
// Implementation (discovered automatically if "Formatter" is in ExtensionPoint.manifest)
package example.impl;
import example.LogFormatter;
public class TimestampLogFormatter implements LogFormatter {
@Override
public String format(String message) {
return String.format("[%tF %<tT] %s", System.currentTimeMillis(), message);
}
}
// Consumer using ServiceProvider to access the extension
package example.client;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.util.classfinder.ClassSearcher;
import example.LogFormatter;
import java.util.List;
public class LogClient {
private final ServiceProvider provider;
public LogClient(ServiceProvider provider) {
this.provider = provider;
}
public void logAllServices() {
// Direct discovery via ClassSearcher
List<LogFormatter> formatters = ClassSearcher.getInstances(LogFormatter.class);
for (LogFormatter formatter : formatters) {
System.out.println(formatter.format("System initialized"));
}
}
}
Note that while ClassSearcher provides direct access to extension point implementations, exposing them through ServiceProvider enables other plugins to retrieve them via the standard service lookup mechanism.
Summary
- Extension points require implementing
ghidra.util.classfinder.ExtensionPointand following the suffix naming convention defined indata/ExtensionPoint.manifestfiles for automatic discovery byClassSearcher. - Discovery order can be controlled via the
@ExtensionPointPropertiesannotation using priority values or exclusion flags. - ServiceProvider (
ghidra.framework.plugintool.ServiceProvider) decouples plugins by allowing runtime lookup of services rather than compile-time dependencies. - ServiceProviderDecorator enables extending service capabilities without modifying existing provider implementations, supporting layered plugin architectures.
- PluginTool serves as the primary source of
ServiceProviderinstances for plugins, retrieved viagetServiceProvider()in the plugin constructor.
Frequently Asked Questions
What is the difference between an ExtensionPoint and a ServiceProvider?
An ExtensionPoint is a marker interface for classes that Ghidra discovers and instantiates automatically at startup based on naming suffixes in manifest files. A ServiceProvider is a runtime lookup mechanism that allows plugins to retrieve shared objects (services) dynamically using getService(Class<T>). Extension points define what can be extended; ServiceProvider defines how plugins share functionality.
How does Ghidra automatically discover my extension point implementation?
Ghidra's ClassSearcher (Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassSearcher.java) scans the classpath at startup, reading every module's data/ExtensionPoint.manifest file to collect valid suffixes. It then instantiates any class whose simple name ends with a registered suffix and which implements the ExtensionPoint marker interface. Simply implementing the interface is insufficient; the suffix must be declared in a manifest file.
Can I prevent my extension point from being loaded automatically?
Yes. Add the @ExtensionPointProperties annotation with exclude=true to your class declaration. Located in Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ExtensionPointProperties.java, this annotation tells ClassSearcher to skip the class during discovery, allowing you to instantiate it manually or control its lifecycle programmatically.
How do I access services from a class that is not a Plugin?
If your class does not extend Plugin but needs access to services, obtain a ServiceProvider instance from an existing plugin or tool context, or use ServiceProviderStub for testing scenarios. For standalone utilities, you can also use ClassSearcher.getInstances(ServiceClass.class) to discover implementations directly, though this bypasses the service lifecycle management that PluginTool provides.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →