# How Ghidra Handles ELF Relocation Processing: A Deep Dive into the Modular Pipeline

> Explore how Ghidra's modular pipeline handles ELF relocation processing. Discover how it applies architecture-specific fix-ups to program memory for deep binary analysis.

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

---

**Ghidra processes ELF relocations through a layered pipeline where `ElfProgramBuilder` orchestrates table iteration, `ElfRelocationHandlerFactory` discovers processor-specific handlers via classpath scanning, and concrete handlers like `X86_64_ElfRelocationHandler` apply architecture-specific fix-ups to program memory.**

The NSA’s Ghidra reverse engineering framework implements a robust, extensible system for applying ELF relocation fix-ups during binary import. This article examines how the `ElfProgramBuilder` coordinates with the `ElfRelocationHandlerFactory` and `ElfRelocationContext` classes to resolve symbols and patch memory according to the ELF specification.

## The ELF Relocation Processing Pipeline

Ghidra’s ELF loader follows a layered, plug-in architecture that isolates the generic relocation workflow from processor-specific logic. The pipeline progresses through four distinct phases: table discovery, handler selection, context initialization, and entry-level fix-up.

### Workflow Orchestration in ElfProgramBuilder

The entry point for relocation processing resides in [`ElfProgramBuilder.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/ElfProgramBuilder.java). At lines 862-889, the loader gathers all relocation tables from the ELF header and checks whether the user has enabled the *“Perform Relocations”* option. If enabled, the builder iterates over each relocation table and delegates processing to specialized methods.

For each table, `processRelocationTable` (lines 946-964) computes the base address, accounting for whether the ELF is relocatable (`ET_REL`). It then calls `processRelocationTableEntries` to iterate over individual relocation entries. This separation ensures that address calculations occur before any processor-specific logic executes.

### Handler Discovery via ElfRelocationHandlerFactory

Before processing entries, Ghidra must locate the appropriate processor-specific handler. The `ElfRelocationHandlerFactory` (lines 31-36) uses the `ClassSearcher` utility to scan the classpath for classes whose names end with `ElfRelocationHandler`.

The factory selects a handler by invoking `canRelocate(ElfHeader)`, which compares the handler’s supported machine type against the ELF’s `e_machine` field. This discovery mechanism allows Ghidra to support new architectures simply by adding handler classes to the classpath, without modifying the core loader.

### Context Creation and Symbol Resolution

Once a handler is selected, `ElfRelocationContext.getRelocationContext` (lines 188-196) constructs a context object that bundles three critical components: the selected handler, an `ElfLoadHelper` instance, and a symbol-to-address map. This context serves as the execution environment for all subsequent relocation operations.

The context provides utilities such as `getRelocationAddress`, which converts relative offsets within a relocation table into absolute `Address` objects within the Ghidra program space. It also maintains references to the ELF header and memory buffers required for reading addends and writing patched values.

### Entry-Level Processing

For each relocation entry, the context’s `processRelocation` method (lines 95-112) performs the final orchestration. It calculates the absolute relocation address using `getRelocationAddress`, resolves the associated symbol via the symbol map, and invokes the processor-specific handler’s `relocate` method.

This method signature includes the relocation type, target address, symbol address, and addend, providing handlers with all necessary data to compute the final value according to the ELF specification.

## Processor-Specific Handler Implementation

While the pipeline handles generic ELF structure, the actual byte-level fix-ups occur within architecture-specific handlers. All handlers extend `AbstractElfRelocationHandler`, which provides common utilities and delegates to concrete implementations.

### The AbstractElfRelocationHandler Base Class

[`AbstractElfRelocationHandler.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/AbstractElfRelocationHandler.java) (lines 52-61) defines the generic `relocate` entry point. This method parses the relocation type into an enum value, validates the operation, and invokes the abstract `relocate(C, ElfRelocation, T, …)` method implemented by subclasses.

The base class also provides `markAsWarning` and `markAsError` helpers, which bookmark failed relocations in the program database for later review by analysts.

### Concrete Handler Example: x86-64

The [`X86_64_ElfRelocationHandler.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/X86_64_ElfRelocationHandler.java) (lines 70-115) demonstrates a complete implementation for the x86-64 architecture. This handler implements a switch statement over `X86_64_ElfRelocationType` values, handling standard relocations including `R_X86_64_RELATIVE`, `R_X86_64_64`, and `R_X86_64_PC32`.

For each type, the handler reads the addend either from the relocation record (for RELA tables) or directly from the target memory address (for REL tables). It applies image-base adjustments for pre-linked binaries and writes the computed value back to the program memory using the `ElfLoadHelper`. Unsupported relocation types trigger warning bookmarks via the inherited helper methods.

## Error Handling and Resource Management

Throughout the pipeline, Ghidra maintains strict error handling and resource cleanup protocols. When a handler encounters an unsupported relocation or invalid symbol reference, it records the failure as a bookmark in the program listing, allowing reverse engineers to identify problematic fix-ups after import.

After processing all relocation tables, the `ElfRelocationContext.dispose` method (lines 317-335) executes cleanup operations. This includes releasing temporary memory fragments created for external blocks and clearing cached symbol maps to prevent memory leaks during bulk import operations.

## Practical Code Examples

The following examples demonstrate how to interact with Ghidra’s ELF relocation system programmatically.

### Loading an ELF with Automatic Relocations

When using the standard import workflow, relocations process automatically if enabled in the loader options:

```java
import ghidra.app.util.opinion.ElfProgramBuilder;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.listing.Program;
import ghidra.util.task.TaskMonitor;
import java.io.File;

File elfFile = new File("/path/to/binary");
MessageLog log = new MessageLog();
TaskMonitor monitor = TaskMonitor.DUMMY;

// Relocations are applied during load if "Perform Relocations" is enabled
Program program = ElfProgramBuilder.loadElf(elfFile, log, monitor);

```

### Manual Relocation Processing

For advanced use cases, you can manually invoke the relocation machinery after initial load:

```java
import ghidra.app.util.bin.format.elf.*;
import ghidra.app.util.bin.format.elf.relocation.*;
import ghidra.program.model.address.Address;

// Initialize load helper and context
ElfLoadHelper loadHelper = new ElfLoadHelper(program);
ElfRelocationContext<?> ctx = ElfRelocationContext.getRelocationContext(
    loadHelper, symbolMap);

// Process a specific relocation entry
ElfRelocation reloc = /* entry from ElfRelocationTable */;
Address relocAddr = ctx.getRelocationAddress(baseAddr, reloc.getOffset());
ctx.processRelocation(reloc, relocAddr);

```

### Implementing a Custom Handler

To add support for a new architecture, extend `AbstractElfRelocationHandler`:

```java
import ghidra.app.util.bin.format.elf.relocation.*;

public class RISCV_ElfRelocationHandler 
        extends AbstractElfRelocationHandler<RISCV_ElfRelocationType,
                                             RISCV_ElfRelocationContext> {

    public RISCV_ElfRelocationHandler() {
        super(RISCV_ElfRelocationType.class);
    }

    @Override
    public boolean canRelocate(ElfHeader elf) {
        return elf.e_machine() == RISCVConstants.EM_RISCV;
    }

    @Override
    protected RelocationResult relocate(RISCV_ElfRelocationContext ctx,
                                        ElfRelocation reloc,
                                        RISCV_ElfRelocationType type,
                                        Address addr,
                                        ElfSymbol sym,
                                        Address symAddr,
                                        long symValue,
                                        String symName) {
        // Implement RISC-V specific relocation logic
        return RelocationResult.SUCCESS;
    }
}

```

The `ElfRelocationHandlerFactory` automatically discovers this class at runtime provided the class name ends with `ElfRelocationHandler`.

## Summary

- **Modular Architecture**: Ghidra separates generic ELF parsing in `ElfProgramBuilder` from processor-specific logic via the handler factory pattern.
- **Dynamic Handler Discovery**: The `ElfRelocationHandlerFactory` uses classpath scanning to locate handlers matching the ELF’s `e_machine` value, enabling extensibility without core modifications.
- **Context-Driven Execution**: `ElfRelocationContext` encapsulates symbol resolution, address calculation, and handler delegation for each relocation table.
- **Architecture-Specific Fix-Ups**: Concrete handlers like `X86_64_ElfRelocationHandler` implement the ELF relocation spec for their respective ISAs, handling addends, PC-relative calculations, and image-base adjustments.
- **Robust Error Handling**: The system bookmarks unsupported relocations and cleans up resources via `ElfRelocationContext.dispose` to ensure stable bulk imports.

## Frequently Asked Questions

### How does Ghidra select which relocation handler to use?

Ghidra uses the `ElfRelocationHandlerFactory` to scan the classpath for classes ending with `ElfRelocationHandler`. For each candidate, it invokes `canRelocate(ElfHeader)`, which compares the handler’s supported machine type against the ELF’s `e_machine` field. The first handler returning `true` is instantiated and used for that binary.

### Can I disable ELF relocation processing during import?

Yes. When loading an ELF file through `ElfProgramBuilder`, the *“Perform Relocations”* importer option controls whether the loader processes relocation tables at lines 862-889. Disabling this option skips the entire relocation pipeline, leaving memory values at their file-offset values without fix-ups.

### How do I add support for a new processor architecture?

Create a class extending `AbstractElfRelocationHandler<T, C>` where `T` is your relocation type enum and `C` is your context class. Implement `canRelocate` to match your architecture’s `e_machine` constant and implement the `relocate` method to apply fix-ups. Name the class with the `ElfRelocationHandler` suffix so `ElfRelocationHandlerFactory` discovers it automatically.

### What happens when Ghidra encounters an unsupported relocation type?

When a handler encounters an unsupported type, it invokes `markAsWarning` or `markAsError` (inherited from `AbstractElfRelocationHandler`), which creates a bookmark in the program listing. The relocation is skipped, import continues, and the user can review bookmarked addresses in the Bookmark Manager to identify unresolved fix-ups.