# Ghidra Decompiler API and Internal Architecture: A Complete Technical Guide

> Explore the Ghidra Decompiler API and internal architecture. Understand its client-server Java C++ communication for code decompilation into C or P-code.

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

---

**The Ghidra Decompiler operates as a client-server system where a Java API communicates with a native C++ process over a binary pipe, exposing functionality through `DecompInterface` and `Decompiler` classes to produce high-level C code or P-code representations.**

The decompiler in NSA's Ghidra reverse engineering framework is not a monolithic library but a carefully architected client-server system. Understanding the Ghidra Decompiler API requires knowledge of how the Java frontend orchestrates the native backend process. This guide examines the internal communication protocol, lifecycle management, and practical programming interfaces defined in the `ghidra/app/decompiler` package of the NationalSecurityAgency/ghidra repository.

## Client-Server Architecture Overview

Ghidra's decompiler uses a **client-server architecture** that isolates the analysis engine in a separate native process. This design prevents crashes in the decompiler logic from affecting the main Ghidra UI while enabling robust recovery mechanisms.

### The Native C++ Backend

The backend is a standalone executable written in C++ that performs the actual decompilation analysis. It communicates via standard input/output streams using a binary pipe protocol. When the Java side sends a command like `"decompileAt"`, the native process returns XML-encoded results containing function structures or C code markup.

### Java Frontend Wrapper

The Java side provides object-oriented abstractions over this pipe communication. The primary entry points are `DecompInterface` for low-level control and `Decompiler` for high-level convenience. These classes handle process lifecycle, option serialization, and XML parsing automatically.

## Core API Components

Four main classes constitute the public API surface for decompiler interactions:

- **`DecompInterface`** – Manages the native process lifecycle and command encoding
- **`Decompiler`** – Caches `DecompInterface` instances per program and provides simplified `decompile()` methods
- **`DecompileResults`** – Container for decompilation artifacts including `HighFunction` and C code markup
- **`DecompileOptions`** – Encapsulates configurable parameters like simplification style and payload limits

## Process Lifecycle and Communication

Understanding the internal communication flow is essential for debugging decompiler failures or extending the API.

### Program Registration

Decompilation begins with `DecompInterface.openProgram(Program prog)`, defined in [`DecompInterface.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/DecompInterface.java). This method validates P-code support for the program's language, instantiates a `DecompileCallback`, and invokes `initializeProcess()`. The initialization sequence sends the language specification, compiler specification, and core type information to the backend before applying cached options.

### Command Execution Protocol

Each decompilation request follows a strict protocol:

1. **Build request** – Create an `EncodeDecodeSet` containing a `PatchPackedEncode` encoder and `PackedDecode` decoder
2. **Transmit address** – Write the function entry address to the binary pipe
3. **Issue command** – Send directives like `"decompileAt"` or `"generateSignatures"`
4. **Parse response** – `DecompileResults.decodeStream(Decoder)` processes XML elements such as `<function>` (for `HighFunction`), `<doc>` (for `ClangTokenGroup`), or `<parammeasures>` (for signature data)

If the native process crashes, `DecompInterface` automatically restarts it, re-registers the current program, and reapplies cached options without user intervention.

### XML Encoding and Decoding

Data crossing the Java/C++ boundary uses a packed XML format. The `EncodeDecodeSet` class manages `PatchPackedEncode` for outgoing commands and `PackedDecode` for responses. For overlay address spaces, specialized variants `PackedEncodeOverlay` and `PackedDecodeOverlay` handle address translation, ensuring memory references resolve correctly in the disassembly view.

## Practical API Usage

The following patterns demonstrate how to programmatically control the decompiler from Ghidra scripts or plugins.

### Basic Decompilation to C

The standard approach uses the `Decompiler` facade with explicit options:

```java
import ghidra.app.decompiler.*;
import ghidra.program.model.listing.*;
import ghidra.util.task.TaskMonitor;

// Obtain current program context
Program program = currentProgram;
Function function = getFunctionContaining(currentAddress);

// Configure decompilation options
DecompileOptions options = new DecompileOptions();
options.setSimplify(true);
options.setMaxPayloadMBytes(10);

// Initialize with 30-second timeout
Decompiler decompiler = new Decompiler(options, 30 * 1000);

// Execute decompilation
DecompileResults results = decompiler.decompile(program, function, null, TaskMonitor.DUMMY);

if (results.decompileCompleted()) {
    DecompiledFunction decompiled = results.getDecompiledFunction();
    String cCode = decompiled.getC();
    println(cCode);
} else {
    printerr("Decompilation failed: " + results.getErrorMessage());
}

```

This example uses `Decompiler.decompile(Program, Function, File, TaskMonitor)`, which returns a `DecompileResults` object containing the C source via `getDecompiledFunction()`.

### Accessing High-Level P-code

For analysis tools requiring the intermediate representation rather than C source, extract the `HighFunction`:

```java
DecompileResults res = decompiler.decompile(program, function, null, monitor);
HighFunction high = res.getHighFunction();

if (high != null) {
    for (PcodeOpAST op : high.getPcodeOps()) {
        println("Opcode: " + op.getMnemonic());
    }
}

```

The `HighFunction` class, defined in [`HighFunction.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/HighFunction.java), provides access to the decompiler's abstract syntax tree and P-code operations through `getPcodeOps()`.

### Generating Function Signatures

The decompiler can produce binary signatures for function matching via `generateSignatures()`:

```java
// Configure signature generation settings
decompiler.setSignatureSettings(SignatureResult.SIG_SETTING_DEFAULT);

SignatureResult sig = decompiler.generateSignatures(
    function,
    true,    // keep call list
    10,      // 10-second timeout
    monitor
);

if (sig != null) {
    byte[] rawSignature = sig.getBytes();
    println("Generated signature of " + rawSignature.length + " bytes");
}

```

This functionality, implemented in the native backend and exposed through `DecompInterface.generateSignatures()`, returns a `SignatureResult` containing a feature vector suitable for binary diffing.

## Summary

- **Ghidra's decompiler uses a client-server architecture** where Java code communicates with a native C++ process over a binary pipe, enabling crash isolation and automatic recovery.
- **`DecompInterface`** provides low-level control over process lifecycle, option management, and command serialization, while **`Decompiler`** offers a convenient cached façade for scripts.
- **Communication uses packed XML** transmitted via `EncodeDecodeSet`, with specialized handling for overlay address spaces through `PackedEncodeOverlay` and `PackedDecodeOverlay`.
- **Results are encapsulated in `DecompileResults`**, which provides access to C source code through `getDecompiledFunction()`, P-code graphs via `getHighFunction()`, and markup tokens through `getCCodeMarkup()`.
- **All API methods are thread-safe** through synchronized blocks, allowing concurrent decompilation requests from multiple analysis threads.

## Frequently Asked Questions

### How does the Ghidra Decompiler API handle process crashes?

The `DecompInterface` class monitors the native process health during communication. If the C++ backend terminates unexpectedly, the Java wrapper automatically invokes `initializeProcess()` to restart the executable, re-registers the current program via `openProgram()`, and reapplies all cached options including simplification style and toggles. This recovery happens transparently to the API consumer, ensuring that subsequent calls to `decompile()` succeed without manual intervention.

### What is the difference between `DecompInterface` and `Decompiler` classes?

`DecompInterface`, located in [`DecompInterface.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/DecompInterface.java), is the low-level wrapper that directly manages the native process lifecycle, binary pipe encoding via `EncodeDecodeSet`, and command protocols. `Decompiler`, found in [`Decompiler.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/Decompiler.java), is a higher-level façade that caches `DecompInterface` instances per program to avoid repeated process initialization overhead. Most scripts should use `Decompiler` for its simplified `decompile(Program, Function, File, TaskMonitor)` method, while plugins requiring granular control over timeouts or raw XML decoding should interact with `DecompInterface` directly.

### Can I retrieve the decompiled C code as plain text instead of XML markup?

Yes. While `DecompileResults.getCCodeMarkup()` returns a `ClangTokenGroup` containing XML-annotated tokens suitable for syntax highlighting, you can obtain plain C strings by calling `getDecompiledFunction()` on the results object. This returns a `DecompiledFunction` instance where `getC()` provides the raw source code without markup tags. This approach is used when exporting decompiled functions to external files or performing string analysis on the recovered source.

### Is the Ghidra Decompiler API thread-safe for parallel analysis?

Yes. According to the source implementation in [`DecompInterface.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/DecompInterface.java), all public methods that interact with the native process are protected by `synchronized` blocks. This allows multiple threads to share a single `Decompiler` instance or `DecompInterface` and issue concurrent `decompile()` requests safely. The underlying binary pipe communication is serialized internally, ensuring that XML responses are correctly matched to their respective request threads without data corruption.