Scroll to navigation

R_DEBUG(3) Library Functions Manual R_DEBUG(3)

NAME

r_debugradare2 debugger library

SYNOPSIS

#include <r_debug.h>

DESCRIPTION

The r_debug library provides debugging capabilities for radare2, supporting process attachment, stepping, breakpoints, register manipulation, memory inspection, and various debugging operations across different platforms and architectures.

The core structure is RDebug, which manages debugger state, plugins, breakpoints, registers, and process information.

INITIALIZATION

RDebug * (int hard)

Creates and returns a new debugger context. Use this as the first step when writing code that drives the debugger programmatically. The optional `hard` parameter enables hardware-specific debugging features (when available) such as hardware breakpoints. Typical usage is to create the context with `r_debug_new ()`, configure the `RDebug` fields (for example setting the core pointer or event hooks), then call `r_debug_start ()` or `r_debug_attach ()` to begin debugging.

Always free the returned object with `r_debug_free ()` once debugging is finished to avoid leaking resources.

void (RDebug *dbg)

Frees all resources associated with the debugger context and its substructures. After calling this function the `RDebug *` must not be used. Typical pattern:

RDebug *dbg = r_debug_new (0);
// configure dbg->coreb.core, dbg->anal, etc.
// attach or start target
// debug loop
r_debug_free (dbg);

PROCESS CONTROL

bool (RDebug *dbg, int pid)

Attach to an existing process and prepare the internal plugin and IO callbacks for controlling the target. After a successful attach you usually call `r_debug_reg_sync (dbg, R_REG_TYPE_GPR, false)` to populate register state and `r_debug_bp_update (dbg)` if breakpoints need to be resolved against symbol expressions. When attaching to remote backends the plugin selected (the `dbg->current->plugin`) determines the behaviour of `r_debug_attach ()` and subsequent I/O.

bool (RDebug *dbg, int pid)

Detach the debugger from the target process. Use this when you want to leave the debuggee running under its own control. After detaching you may still call `r_debug_info ()` to read cached information, but most control functions will no longer be available.

bool (RDebug *dbg, const char *cmd)

Start and spawn a new process for debugging with the given command. This sets up the debugging plugin's process creation flow (for example using `fork/exec` on native backends or a remote protocol for remote backends). After `r_debug_start ()` the usual flow is to call `r_debug_wait ()` to observe the initial stop event, then set breakpoints and continue.

EXECUTION CONTROL

int (RDebug *dbg, int steps)

Perform one or more single instruction steps on the selected thread. Use `r_debug_step ()` for instruction-level stepping. For higher-level stepping that steps over function calls use `r_debug_step_over ()`. If you need to execute until a given address use `r_debug_continue_until ()`.

When stepping after hitting a software breakpoint, the library handles the "recoil" process (clearing the breakpoint, stepping once, restoring the breakpoint). Callers do not normally need to implement recoiling themselves but should be aware that `r_debug_step ()` may return a value less than requested when a breakpoint was involved.

int (RDebug *dbg, int steps)

Step over function calls for `steps` instructions. This is useful when implementing source-level stepping or when the caller wants to skip stepping into library code.

int (RDebug *dbg)

Resume execution until the debuggee stops due to a breakpoint, signal or other event. Before calling `r_debug_continue ()` ensure that software breakpoints are properly restored by calling `r_debug_bp_update ()` if you modified breakpoint expressions, and sync registers if you modified them locally. For multi-threaded targets prefer using the `continue_all_threads` flag on `RDebug` when supported by the backend.

bool (RDebug *dbg, ut64 addr)

Resume execution and stop when the program counter reaches `addr`. This is convenient for implementing temporary range stepping: add a temporary breakpoint with `r_debug_bp_add ()` and then call `r_debug_continue ()`, or use `r_debug_continue_until ()` to avoid explicit breakpoint management when supported by the plugin.

WAITING AND REASONS

RDebugReasonType (RDebug *dbg, RBreakpointItem **bp)

Block until the debuggee stops and return the reason (an `RDebugReasonType`). `r_debug_wait ()` is the canonical way to implement a debugging loop: call it after `r_debug_continue ()` or `r_debug_step ()` and inspect the returned reason via `r_debug_stop_reason ()` or by testing the return value. If `bp` is non-NULL and the stop reason involves a breakpoint, `*bp` will be set to the `RBreakpointItem *` that was hit.

Typical usage pattern:

r_debug_continue (dbg);
RBreakpointItem *bp = NULL;
RDebugReasonType why = r_debug_wait (dbg, &bp);
if (why == R_DEBUG_REASON_BREAKPOINT && bp) {
    // inspect registers and memory here
}

RDebugReasonType (RDebug *dbg)

Return the last stop reason stored in `dbg->reason`. This is useful when you cannot block on `r_debug_wait ()` (for example when integrating with an event loop) and need to poll the current state.

const char * (int type)

Return a human-readable string for the given reason code. Useful for logging or user-facing messages when reporting why a debuggee stopped.

REGISTER MANAGEMENT

bool (RDebug *dbg, int type, int write)

Synchronize registers between the local `RReg` view and the debuggee. Call with `write == false` to read registers from the debuggee into the local `RReg` and with `write == true` to push local register values into the debuggee. Always call `r_debug_reg_sync (dbg, R_REG_TYPE_GPR, false)` after attaching to ensure that `r_debug_reg_get ()` returns meaningful values.

ut64 (RDebug *dbg, const char *name)

Return the value of the register named `name` using the local `RReg` cache. This value is only as up-to-date as the last `r_debug_reg_sync ()`.

bool (RDebug *dbg, const char *name, ut64 num)

Set a register locally. After calling `r_debug_reg_set ()` call `r_debug_reg_sync (dbg, R_REG_TYPE_GPR, true)` to write the change to the debuggee. Common pattern: read PC with `r_debug_reg_get ()`, update a register, write back with `r_debug_reg_sync ()`, then `r_debug_continue ()`.

bool (RDebug *dbg, int type, int size, PJ *pj, int rad, const char *use_color)

Produce a formatted list of registers suitable for printing. Useful for implementing `info registers` style commands in front-ends.

BREAKPOINTS

RBreakpointItem * (RDebug *dbg, ut64 addr, int hw, bool watch, int rw, char *module, st64 m_delta)

Add a breakpoint at `addr`. Use `hw` to request a hardware breakpoint when supported by the backend, `watch` to indicate a watchpoint and `rw` to specify read/write conditions for watchpoints. `module` and `m_delta` are used for module-relative expressions. After adding breakpoints call `r_debug_bp_update ()` to resolve expression-based breakpoints and make sure the plugin has the correct breakpoint list loaded.

The library distinguishes software and hardware breakpoints: software breakpoints are typically implemented by writing a trap instruction to the target process and therefore must be temporarily removed when the debugger needs to execute target code (for example when implementing recoil). Hardware breakpoints do not require patching memory but are usually limited in number.

void (RDebug *dbg)

Resolve breakpoint expressions and update the internal breakpoint addresses. Call this after adding breakpoints by name/expression. The implementation calls `r_bp_get_at ()` and `r_bp_restore ()` internally to manage software breakpoints; front-ends should not duplicate that logic.

MEMORY MANAGEMENT

RList * (void)

Create an empty list used to store memory map entries (the same format used by `r_debug_map_get ()`). Use this to enumerate memory regions when implementing memory listing or to build a synthetic map for analysis.

RDebugMap * (RDebug *dbg, ut64 addr)

Return the `RDebugMap` corresponding to the memory region that contains `addr`. The returned map is owned by the `RDebug` internals; callers should not free it directly. Use `r_debug_map_protect ()` to change memory protections for writable patching or to make a region executable for `r_debug_execute ()`.

bool (RDebug *dbg, ut64 addr, int size, int perms)

Change memory protection for a region starting at `addr`. This is useful when you want to write code into the target and execute it (for example for trampolines). Many backends require cooperation with the plugin, and the call will fail if the underlying OS does not permit the kind of change requested.

PROCESS INFORMATION

RDebugInfo * (RDebug *dbg, const char *arg)

Query miscellaneous information about the debugged process, such as its current working directory, command line and executable path. Backends populate this structure from platform-specific sources; it is convenient for front-ends that need to show process metadata.

void (RDebugInfo *rdi)

Free the `RDebugInfo` structure returned by `r_debug_info ()`.

THREADS AND PROCESSES

RList * (RDebug *dbg, int pid)

Return a list of process/thread identifiers that the backend knows about. Use this to implement thread list UIs. Each list element is typically an `RDebugPid` or similar structure depending on the plugin.

bool (RDebug *dbg, int pid, int tid)

Select a specific process and thread (if supported) for subsequent operations. After selecting a thread, calls like `r_debug_step ()` or `r_debug_reg_sync ()` operate on the selected thread. Remember to call `r_debug_reg_sync ()` after switching threads to refresh register state.

SIGNALS

int (RDebug *dbg, int num)

Send the signal `num` to the debuggee. This can be used to emulate user actions such as `SIGINT` or to forward signals from a front-end.

int (RDebug *dbg, const char *signame)

Resolve a textual signal name like "SIGINT" to its numeric value.

const char * (RDebug *dbg, int signum)

Return the canonical name for `signum` (for example, `SIGSEGV`). Useful for logging and user messages.

TRACING

RDebugTrace * (void)

Create and initialize a trace context used by the tracing facilities in the debugger. The trace APIs provide a way to record program execution history (PCs, memory snapshots) for later analysis.

void (RDebugTrace *dbg)

Free a trace context created with `r_debug_trace_new ()`.

RDebugTracepointItem * (RDebug *dbg, ut64 addr, int size)

Add a tracepoint at `addr`. When the tracepoint is hit the tracer may record state according to the trace configuration.

PLUGINS

bool (RDebug *dbg, RDebugPlugin *plugin)

Register a debugger plugin implementation with the `RDebug` instance. Plugins implement architecture/OS-specific behaviour such as process control, register IO and memory access. The built-in backends (native, gdb, etc.) are provided as plugins; front-ends can add custom plugins to support additional transports.

bool (RDebug *dbg, RDebugPlugin *plugin)

Unregister a previously added plugin. Removing the current plugin while debugging is not supported and will typically fail.

EXECUTION

bool (RDebug *dbg, const ut8 *buf, int len, ut64 *ret, bool restore, bool ignore_stack)

Execute the machine code in `buf` of length `len` inside the debugged process and optionally return a value via `ret`. If `restore` is true the original memory contents and registers are restored after execution. The `ignore_stack` flag lets the plugin adjust stack-related safety checks when injecting code. This function is useful to evaluate small snippets in the target process for dynamic analysis or for implementing functions that need to run in the target context.

DEBUG REASONS

The library defines various reasons why a debuggee might stop:

Hit a breakpoint
Received a signal
Completed a step
Segmentation fault

EXAMPLES

The examples below show common real-world sequences found in radare2's front-ends and plugins. They assume a native backend but the same patterns apply to remote backends via their plugin implementations.

Minimal attach-and-continue loop:

RDebug *dbg = r_debug_new (0);
// attach and populate registers
if (!r_debug_attach (dbg, pid)) {
    // handle error
}
if (!r_debug_reg_sync (dbg, R_REG_TYPE_GPR, false)) {
    // cannot read registers
}
// set a breakpoint at a symbol or address
RBreakpointItem *bp = r_debug_bp_add (dbg, 0x400000, 0, false, 0, NULL, 0);
r_debug_bp_update (dbg);
// resume and wait for events
r_debug_continue (dbg);
RBreakpointItem *hit = NULL;
RDebugReasonType why = r_debug_wait (dbg, &hit);
if (why == R_DEBUG_REASON_BREAKPOINT && hit) {
    ut64 pc = r_debug_reg_get (dbg, "PC");
    // inspect/modify registers or memory
}

Step-and-inspect example (useful for implementing a step debugger):

RDebug *dbg = r_debug_new (0);
// start or attach
r_debug_step (dbg, 1);
if (r_debug_wait (dbg, NULL) == R_DEBUG_REASON_STEP) {
    // make sure registers are synced
    r_debug_reg_sync (dbg, R_REG_TYPE_GPR, false);
    ut64 sp = r_debug_reg_get (dbg, "SP");
    // read memory via dbg->iob.read_at or use r_debug_map_get to find protections
}

Executing injected snippets safely:

// prepare the small machine code to run (architecture dependent)
const ut8 code[] = { 0x90, 0x90 }; // nop; nop
ut64 ret = 0;
if (r_debug_execute (dbg, code, sizeof (code), &ret, true, false)) {
    // result available in ret
}

SEE ALSO

r_bp(3), r_reg(3), r_anal(3)

September 20, 2025 Debian