# How Ghidra Debugger Implements Trace RMI to Connect to GDB, LLDB, and WinDbg

> Learn how Ghidra Debugger uses Trace RMI via Python agents to connect GDB, LLDB, and WinDbg to Ghidra's unified trace model. Explore remote debugging capabilities.

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

---

**Ghidra’s Debugger uses Trace RMI, a lightweight Remote Method Invocation protocol over TCP, to connect to external debuggers like GDB, LLDB, and WinDbg through Python agents that bridge native debugger APIs with Ghidra’s unified trace model.**

The National Security Agency’s Ghidra provides a modern debugging framework that unifies analysis and dynamic inspection through Trace RMI. This protocol enables the Ghidra Debugger to communicate with external debugging engines across different platforms and architectures. Understanding how Trace RMI works is essential for extending Ghidra’s debugging capabilities or integrating custom debugger backends.

## What Is Trace RMI in Ghidra?

**Trace RMI** is a binary protocol built on **protobuf** that transports a Ghidra *trace*—the unified view of a target’s memory, registers, threads, and breakpoints—over a TCP channel. Unlike traditional debugger interfaces that require tight integration with native APIs, Trace RMI decouples the debugging UI from the debugging engine, allowing Ghidra to control external processes running GDB, LLDB, or WinDbg (via `dbgeng.dll`).

The protocol defines messages for memory reads and writes, register updates, breakpoint management, and thread lifecycle events. These messages are exchanged between Ghidra’s in-process **TraceRmiService** and the external **TraceRmiClient** implemented in Python.

## Core Architecture: Three Layers of Trace RMI

Ghidra’s Trace RMI implementation consists of three loosely-coupled layers that handle service provision, launcher discovery, and external debugger integration.

### Trace RMI Service Layer

The **TraceRmiService** provides the core RMI server and client API used by the UI and scripts. The primary implementation resides in `ghidra.app.services.TraceRmiService` (internal), while the public-facing interface is **`TraceRmiLauncherService`**, implemented by **[`TraceRmiLauncherServicePlugin.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/TraceRmiLauncherServicePlugin.java)**.

This plugin manages the lifecycle of RMI connections, creates `Trace` objects when connections are established, and notifies UI components of state changes. It also provides the entry point for scripts to query available launchers and initiate debugging sessions programmatically.

### Launch-Offer Framework

The **Launch-Offer Framework** discovers and describes how to start remote debuggers. Each *offer* knows its parameters, UI prompts, and how to invoke the external agent. The core interfaces are **`TraceRmiLaunchOffer`** and **`TraceRmiLaunchOpinion`**, with concrete implementations parsing launcher scripts to generate offers.

Key opinion implementations include:

- **[`UnixShellScriptTraceRmiLaunchOpinion.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/UnixShellScriptTraceRmiLaunchOpinion.java)** – Parses shell scripts like [`local-gdb.sh`](https://github.com/NationalSecurityAgency/ghidra/blob/main/local-gdb.sh) and [`remote-gdb.sh`](https://github.com/NationalSecurityAgency/ghidra/blob/main/remote-gdb.sh) on Unix systems.
- **[`PowerShellScriptTraceRmiLaunchOpinion.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/PowerShellScriptTraceRmiLaunchOpinion.java)** – Handles Windows PowerShell and batch scripts such as `dbgeng.bat`.
- **[`LldbDebuggerPlatformOpinion.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/LldbDebuggerPlatformOpinion.java)** – Provides LLDB-specific argument handling and environment setup.
- **[`JavaTraceRmiLaunchOpinion.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/JavaTraceRmiLaunchOpinion.java)** – Supports launching Java programs via JDI (JPDA) connectors.

Each launcher script contains **metadata comments** (e.g., `#@title`, `#@desc`, `#@env`, `#@args`) that the opinion parses to build the offer. These tags drive the UI prompts and default values presented to the user.

### External Debugger Agents

The **External Debugger Agents** are small Python packages that wrap GDB, LLDB, or `dbgeng` (WinDbg) and speak the Trace RMI protocol back to Ghidra. These agents run as separate processes, enabling them to use the full capabilities of the native debugger without JVM constraints.

The three main agent packages are:

- **`Debugger-agent-gdb`** – Wraps GDB using the Machine Interface (MI) mode.
- **`Debugger-agent-lldb`** – Interfaces with LLDB’s Python API.
- **`Debugger-agent-dbgeng`** – Uses `pybag` to wrap `dbgeng.dll` for Windows kernel and user-mode debugging.

Each agent ships with launcher scripts (e.g., [`local-gdb.sh`](https://github.com/NationalSecurityAgency/ghidra/blob/main/local-gdb.sh), [`local-lldb.sh`](https://github.com/NationalSecurityAgency/ghidra/blob/main/local-lldb.sh), `dbgeng.bat`) and a Python package containing the protobuf definitions (`trace_rmi.proto`) and client runtime (`Debugger-rmi-trace/pypkg`). The agents use **pybag**—a JNA-based wrapper—to bridge between Python and the native debugger APIs.

## How Ghidra Connects to GDB, LLDB, and WinDbg

The connection process follows a consistent pattern across all supported debuggers, with variations only in the launcher script and Python agent specifics.

### GDB Integration via Debugger-agent-gdb

When connecting to GDB, Ghidra uses **`UnixShellScriptTraceRmiLaunchOpinion`** to parse [`local-gdb.sh`](https://github.com/NationalSecurityAgency/ghidra/blob/main/local-gdb.sh). This script launches GDB in MI mode and injects the [`ghidra_debugger.py`](https://github.com/NationalSecurityAgency/ghidra/blob/main/ghidra_debugger.py) client from `Debugger-agent-gdb/pypkg`.

The Python client establishes a TCP connection back to Ghidra’s `TraceRmiService`, then translates GDB/MI events into Trace RMI protobuf messages. Memory maps, register values, and thread states flow from GDB → Python → Ghidra, while user commands travel the reverse path.

### LLDB Integration via Debugger-agent-lldb

For LLDB, **`LldbDebuggerPlatformOpinion`** provides platform-specific argument handling. The launcher script [`local-lldb.sh`](https://github.com/NationalSecurityAgency/ghidra/blob/main/local-lldb.sh) (generated at build time) starts LLDB with the Python agent loaded.

The LLDB agent uses LLDB’s native Python API to register callbacks for breakpoints, thread events, and memory access. These callbacks serialize data using the same `trace_rmi.proto` definitions, ensuring consistency with the GDB agent. The agent handles LLDB-specific quirks, such as process launching versus attaching, by mapping LLDB operations to generic Trace RMI commands.

### WinDbg Integration via Debugger-agent-dbgeng

Windows debugging uses **`PowerShellScriptTraceRmiLaunchOpinion`** to parse `dbgeng.bat`. This launcher initializes the `Debugger-agent-dbgeng` Python package, which uses **pybag** to load `dbgeng.dll` and create a debugger engine instance.

The dbgeng agent supports both user-mode and kernel-mode debugging, translating WinDbg’s event model (e.g., `IDebugEventCallbacks`) into Trace RMI messages. Because `dbgeng.dll` provides a COM-based API, the pybag layer handles the JNA interop, while the Python agent manages the Trace RMI connection to Ghidra.

## Launching Debuggers Programmatically

Ghidra exposes the Trace RMI functionality through the **`FlatDebuggerRmiAPI`**, allowing scripts to discover and launch debuggers without manual UI interaction.

### Listing Available Trace RMI Launchers

```java
import ghidra.debug.flatapi.FlatDebuggerRmiAPI;
import ghidra.debug.api.tracermi.TraceRmiLaunchOffer;
import java.util.Collection;

FlatDebuggerRmiAPI dbg = new FlatDebuggerRmiAPI() {};
Collection<TraceRmiLaunchOffer> offers = dbg.getLaunchOffers();

println("Available Trace RMI launchers:");
for (TraceRmiLaunchOffer o : offers) {
    println("- " + o.getConfigName() + " (" + o.getTitle() + ")");
}

```

This script calls `getLaunchOffers()` in [`FlatDebuggerRmiAPI.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/FlatDebuggerRmiAPI.java), which delegates to `TraceRmiLauncherService.getOffers` and ultimately queries all `TraceRmiLaunchOpinion` implementations via `ClassSearcher`.

### Programmatically Launching a Remote LLDB Session

```java
import ghidra.debug.flatapi.FlatDebuggerRmiAPI;
import ghidra.debug.api.tracermi.TraceRmiLaunchOffer;
import ghidra.util.task.TaskMonitor;
import java.util.Map;

FlatDebuggerRmiAPI dbg = new FlatDebuggerRmiAPI() {};

TraceRmiLaunchOffer lldbOffer = dbg.getLaunchOffers()
    .stream()
    .filter(o -> o.getConfigName().contains("lldb"))
    .findFirst()
    .orElseThrow(() -> new IllegalStateException("No LLDB launcher found"));

// Override the remote host argument
Map<String, String> overrides = Map.of("RmiAddress", "192.168.1.10:9999");
dbg.launch(lldbOffer, overrides, TaskMonitor.DUMMY);

```

The `launch` method passes overrides to the `LaunchConfigurator`, which merges them with saved defaults before invoking the offer’s `launchProgram` implementation.

### Accessing the Active Trace from a Script

```java
import ghidra.debug.api.Trace;
import ghidra.app.services.TraceRmiLauncherService;

TraceRmiLauncherService svc = state.getTool().getService(TraceRmiLauncherService.class);
Trace trace = svc.getCurrentTrace();

println("Trace ID: " + trace.getUniqueID());

```

This retrieves the `Trace` object created by the active RMI session, allowing scripts to inspect memory, registers, and thread state programmatically.

## End-to-End Connection Flow

When a user initiates a debugging session, the following sequence occurs:

1. **User** selects *Launch → Configure → gdb* from the Debug menu.
2. `TraceRmiLauncherServicePlugin` queries `UnixShellScriptTraceRmiLaunchOpinion` to build a `TraceRmiLaunchOffer` from [`local-gdb.sh`](https://github.com/NationalSecurityAgency/ghidra/blob/main/local-gdb.sh) metadata.
3. The **offer** displays configurable parameters; the user confirms or overrides arguments.
4. `offer.launchProgram` spawns [`local-gdb.sh`](https://github.com/NationalSecurityAgency/ghidra/blob/main/local-gdb.sh), which runs GDB in MI mode and starts the [`ghidra_debugger.py`](https://github.com/NationalSecurityAgency/ghidra/blob/main/ghidra_debugger.py) client from `Debugger-agent-gdb/pypkg`.
5. The **Python client** connects to Ghidra’s in-process `TraceRmiService` over TCP.
6. Ghidra creates a new `Trace` object, populates it with the target’s initial state, and updates all debugger UI panels.
7. Subsequent UI commands (step, continue, breakpoint) serialize via protobuf, send to the Python client, which forwards them to GDB through the **pybag** API.

This identical pattern applies to LLDB (`Debugger-agent-lldb`) and WinDbg (`Debugger-agent-dbgeng`), with only the launcher script and Python agent varying between platforms.

## Summary

- **Trace RMI** is Ghidra’s protobuf-based protocol for transporting debugger state over TCP, enabling remote debugging without native JVM bindings.
- The architecture separates concerns into three layers: the **TraceRmiService** (Java), the **Launch-Offer Framework** (discovery and UI), and **External Agents** (Python wrappers).
- **TraceRmiLauncherServicePlugin.java** orchestrates the discovery of launch offers through `TraceRmiLaunchOpinion` implementations that parse metadata from shell scripts.
- External agents like `Debugger-agent-gdb`, `Debugger-agent-lldb`, and `Debugger-agent-dbgeng` use **pybag** and **protobuf** to translate native debugger events into Trace RMI messages.
- Developers can programmatically control debugging sessions using [`FlatDebuggerRmiAPI.java`](https://github.com/NationalSecurityAgency/ghidra/blob/main/FlatDebuggerRmiAPI.java) to list offers, launch targets, and access trace data.

## Frequently Asked Questions

### What is Trace RMI in Ghidra?

Trace RMI is a lightweight Remote Method Invocation protocol that uses TCP and protobuf to transport debugging events between Ghidra and external debugger processes. It allows Ghidra to maintain a unified **Trace** model of the target’s state while delegating actual debugging operations to platform-specific agents like GDB, LLDB, or WinDbg.

### How does Ghidra communicate with GDB?

Ghidra communicates with GDB through the `Debugger-agent-gdb` Python package. When a user launches a GDB session, `TraceRmiLauncherServicePlugin` executes [`local-gdb.sh`](https://github.com/NationalSecurityAgency/ghidra/blob/main/local-gdb.sh), which starts GDB in MI mode and loads [`ghidra_debugger.py`](https://github.com/NationalSecurityAgency/ghidra/blob/main/ghidra_debugger.py). This Python client connects back to Ghidra’s `TraceRmiService` over TCP, translating GDB/MI events into Trace RMI protobuf messages and forwarding user commands to GDB via the **pybag** library.

### Can I use Trace RMI to connect to custom debuggers?

Yes. You can implement custom Trace RMI agents by creating a Python client that speaks the protobuf protocol defined in `Debugger-rmi-trace/pypkg/protobuf/trace_rmi.proto`. Additionally, you must implement a `TraceRmiLaunchOpinion` (Java) to discover your launcher scripts and provide `TraceRmiLaunchOffer` instances to the UI. This allows your custom debugger to appear in Ghidra’s launch menus alongside GDB and LLDB.

### Where are the launcher scripts located in the Ghidra repository?

Launcher scripts are distributed within their respective agent packages. GDB launchers such as [`local-gdb.sh`](https://github.com/NationalSecurityAgency/ghidra/blob/main/local-gdb.sh) and [`remote-gdb.sh`](https://github.com/NationalSecurityAgency/ghidra/blob/main/remote-gdb.sh) reside in `Debugger-agent-gdb/data/debugger-launchers/`. LLDB launchers like [`local-lldb.sh`](https://github.com/NationalSecurityAgency/ghidra/blob/main/local-lldb.sh) are generated in `Debugger-agent-lldb/pypkg/`. WinDbg launchers including `dbgeng.bat` are located in `Debugger-agent-dbgeng/data/debugger-launchers/`. Each script contains metadata comments (`#@title`, `#@args`, etc.) parsed by the `TraceRmiLaunchOpinion` implementations to build the launch configuration UI.