# How to Add Support for New Binary File Formats in Ghidra: A Complete Developer Guide

> Learn to add support for new binary file formats in Ghidra. This guide details extending AbstractProgramLoader and registering custom loaders via Java SPI.

- Repository: [National Security Agency/ghidra](https://github.com/NationalSecurityAgency/ghidra)
- Tags: how-to-guide
- Published: 2026-03-04

---

**To add support for a new binary file format in Ghidra, extend `AbstractProgramLoader` from the `ghidra.app.util.opinion` package, implement the required API methods, and register your class via Java's Service Provider Interface in `META-INF/services/ghidra.app.util.opinion.Loader`.**

Ghidra, the NSA's open-source reverse engineering framework, processes unknown binaries through a pluggable **Loader** architecture. When you need to analyze proprietary or custom firmware formats, implementing a custom loader allows Ghidra to parse headers, map memory segments, and set entry points automatically during the import process.

## Understanding Ghidra's Loader Architecture

### The Loader Interface and AbstractProgramLoader

All binary import functionality in Ghidra centers on the `Loader` interface defined in [`Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/Loader.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/Loader.java). This interface declares the contract between the framework and format-specific parsers, specifying methods for file type detection, option handling, and program creation.

Most developers extend `AbstractProgramLoader` (located in [`Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractProgramLoader.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractProgramLoader.java)) rather than implementing `Loader` directly. This base class provides common utilities for **file-type detection**, **option handling**, and **logging**, reducing boilerplate code when targeting new formats.

### How Loaders Are Discovered at Runtime

Ghidra uses the Java **Service Provider Interface (SPI)** mechanism to discover loaders dynamically at startup. The framework scans the classpath for a resource file at `META-INF/services/ghidra.app.util.opinion.Loader`, which contains the fully-qualified class names of all available loaders. Each line in this file points to a concrete loader implementation that Ghidra instantiates and registers in the *Import File* dialog without requiring manual configuration.

## Step-by-Step Implementation Guide

### 1. Create a Loader Class Extending AbstractProgramLoader

Create a new Java class in your module that extends `AbstractProgramLoader`. You must override three critical methods:

- `getName()` – Returns the display name shown in Ghidra's import dialog
- `getSupportedLoadSpecs(ByteProvider provider)` – Analyzes the file to determine if your loader can handle it
- `load(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program program, TaskMonitor monitor)` – Performs the actual parsing and memory mapping

In [`Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/PeLoader.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/PeLoader.java), you can see how the PE/COFF loader implements these methods to parse Windows executables, providing a production-ready reference for your implementation.

### 2. Define Supported File Extensions with LoadSpec

The `getSupportedLoadSpecs()` method returns a list of `LoadSpec` objects that describe which language/compiler specifications your format supports. Use the `LoadSpecBuilder` or constructor to specify:

- The **language ID** (e.g., `"x86:LE:32:default"` for 32-bit little-endian x86)
- Whether the loader can handle the specific file
- Optional file extension matchers

When you add extensions via `loaderInfo.addExtension(".foo")` or return a `LoadSpec` with a `FileExtension` matcher, Ghidra automatically filters the *Import File* dialog to show your loader for matching files.

### 3. Implement the load() Method

The `load()` method is where you transform the raw bytes into a Ghidra `Program` database. Typical operations include:

- Creating memory blocks using `program.getMemory().createInitializedBlock()`
- Mapping file sections to specific addresses
- Setting the image base with `program.setImageBase()`
- Defining entry points and symbols via `program.getSymbolTable().createLabel()`

You receive a `ByteProvider` for raw file access, a `LoadSpec` containing the selected language, and a `TaskMonitor` for reporting progress to the user.

### 4. Register Your Loader via SPI

To make Ghidra aware of your loader, create or edit the file at `src/main/resources/META-INF/services/ghidra.app.util.opinion.Loader` in your module. Add a single line containing your loader's fully-qualified class name:

```text
ghidra.myloaders.FooLoader

```

The build system packages this service file into your extension JAR. At startup, Ghidra's classloader reads this file, instantiates your loader, and adds it to the available importers list.

### 5. Package as a Ghidra Module

Structure your loader as a standard Ghidra module:

1. Create a Gradle module under `Ghidra/Features` for core contributions or `Ghidra/Extensions` for third-party add-ons
2. Add your module to `Ghidra/gradle.properties` to include it in the build
3. Place resources (icons, help docs) in `src/main/resources`
4. Ensure your `module.manifest` declares any dependencies on `Base` or `Generic` frameworks

## Complete Code Example: FooLoader

The following implementation demonstrates a minimal loader for a fictional `.foo` binary format. This class extends `AbstractProgramLoader`, checks for the `.foo` extension, and maps the entire file into memory at address `0x1000`.

```java
package ghidra.myloaders;

import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;

import ghidra.app.util.Option;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.opinion.AbstractProgramLoader;
import ghidra.app.util.opinion.LoadSpec;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;

/**
 * Minimal loader for the fictional ".foo" binary format.
 */
public class FooLoader extends AbstractProgramLoader {

    private static final String FILE_EXTENSION = "foo";

    @Override
    public String getName() {
        return "Foo Binary Loader";
    }

    @Override
    public List<LoadSpec> getSupportedLoadSpecs(ByteProvider provider) throws IOException {
        // Accept any file that ends with .foo
        if (provider.getAbsolutePath().toLowerCase().endsWith("." + FILE_EXTENSION)) {
            // Use the default language for this format (e.g., x86:LE:32:default)
            return Collections.singletonList(
                new LoadSpec(this, 0, 
                    new ghidra.program.model.lang.LanguageCompilerSpecPair("x86:LE:32:default"), 
                    true)
            );
        }
        return Collections.emptyList();
    }

    @Override
    protected void load(ByteProvider provider, LoadSpec loadSpec,
                        List<Option> options, Program program, TaskMonitor monitor)
            throws IOException, CancelledException {

        // Example: map the whole file into the program's memory at 0x1000
        long length = provider.length();
        MemoryBlock block = program.getMemory()
                .createInitializedBlock("FOO", addr(program, 0x1000), length, (byte)0, monitor, false);
        
        try (InputStream in = provider.getInputStream(0)) {
            block.putBytes(0, in.readAllBytes());
        }

        // Set the entry point (if the format defines one)
        program.getSymbolTable().createLabel(addr(program, 0x1000), "entry", SourceType.USER_DEFINED);
        program.setImageBase(addr(program, 0x1000), true);
    }

    private Address addr(Program program, long offset) {
        return program.getAddressFactory().getDefaultAddressSpace().getAddress(offset);
    }
}

```

Remember to add `ghidra.myloaders.FooLoader` to your `META-INF/services/ghidra.app.util.opinion.Loader` file to activate the loader.

## Testing Your Custom Loader

Validate your implementation using Ghidra's test framework. Write JUnit tests that invoke `ProgramLoader.builder()` to load sample files and assert that the resulting `Program` contains the expected memory layout, entry points, and symbol tables. Reference [`Ghidra/Features/Base/src/test/java/ghidra/app/util/opinion/PeLoaderTest.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/Ghidra/Features/Base/src/test/java/ghidra/app/util/opinion/PeLoaderTest.java) for established patterns on verifying loader correctness, including how to mock `ByteProvider` instances and inspect imported program structures.

## Summary

- **Extend `AbstractProgramLoader`** from `ghidra.app.util.opinion` to inherit common loading utilities
- **Implement `getName()`, `getSupportedLoadSpecs()`, and `load()`** to define your format's detection logic and parsing behavior
- **Use `LoadSpec`** to declare which processor languages and compiler specifications your binary format supports
- **Register via SPI** by adding your class name to `META-INF/services/ghidra.app.util.opinion.Loader` for automatic discovery
- **Reference [`PeLoader.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/PeLoader.java)** in the Ghidra source tree as a production-quality implementation example

## Frequently Asked Questions

### What is the difference between Loader and AbstractProgramLoader in Ghidra?

`Loader` is the core interface in `ghidra.app.util.opinion` that defines the contract for all binary importers, while `AbstractProgramLoader` is a base class that implements common functionality like file-type detection and option handling. According to the NSA Ghidra source code, you should extend `AbstractProgramLoader` rather than implementing `Loader` directly to avoid rewriting boilerplate code for standard import operations.

### How does Ghidra discover new loaders at runtime?

Ghidra discovers loaders through the Java **Service Provider Interface (SPI)** mechanism. At startup, the framework scans the classpath for `META-INF/services/ghidra.app.util.opinion.Loader` files, instantiates each listed class, and adds them to the import dialog. This registration happens automatically without requiring changes to Ghidra's core configuration files.

### Can I add loader options for user configuration during import?

Yes, override `getDefaultOptions()` to return a list of `Option` objects defining parameters like base address or endianness, and implement `processOptions()` to validate and apply user selections. These options appear in the import dialog when your loader is selected, allowing analysts to customize the loading behavior for specific binary instances.

### Where should I place my custom loader in the Ghidra source tree?

Create a new Gradle module under `Ghidra/Extensions` for third-party loaders or `Ghidra/Features` for contributions to the core distribution. Place your Java source in `src/main/java` and SPI registration files in `src/main/resources/META-INF/services`. Add the module name to `Ghidra/gradle.properties` to ensure it builds with the rest of the project.