R_ARCH(3)

πŸ’– R_ARCH β€” the architecture abstraction library for radare2 πŸ’–

okay bestie, pull up a chair, this is the r_arch manual page (section 3, library functions manual) and it is lowkey a whole personality. grab a snack. πŸ“


✨ NAME ✨

r_arch β€” Architecture abstraction library for radare2


🌸 SYNOPSIS 🌸

The primary functions and types provided by the r_arch API are used to select architecture plugins, configure decoding/encoding behaviour and create per-plugin sessions for advanced callbacks (for example to fetch register profiles or run ESIL hooks). Use r_arch_config_new to create a configuration and r_arch_use to activate an architecture plugin.

#include <r_arch.h>

πŸ¦‹ DESCRIPTION πŸ¦‹

r_arch implements a thin, stable interface that core consumers (such as libr/core and libr/anal) use to perform disassembly, assembly and to access plugin-specific capabilities. The API exposes helpers to query plugin limits (e.g. maximum opcode size), run the generic decode/encode paths and to wrap plugin sessions when direct access to plugin callbacks is required.

Key concepts and why they matter in practice:

  • RArch β€” holds available arch plugins and an active session; most clients keep a single RArch per RCore or RAnal instance so the arch state (current plugin, esil, reg profile) is shared. πŸ’…
  • RArchConfig β€” describes desired architecture parameters (name, bits, endianness, syntax). Set this before calling r_arch_use so the right plugin variant is selected.
  • RAnalOp β€” the container for decoded instruction information. Decoders fill op.size, op.mnemonic, op.esil and other fields depending on the decode mask. Consumers such as the ESIL executor rely on op.esil to obtain instruction semantics.
  • RArchSession β€” per-plugin state allowing callers to directly invoke plugin callbacks (for example, plugin->regs to obtain a register profile). Use a session when you need functionality not exposed by the generic decode/encode wrappers.

πŸŽ€ INITIALIZATION πŸŽ€

Before decoding or encoding, create an RArch instance and configure the plugin you want to use. The configuration controls bits, syntax and other plugin-specific options.

RArch * r_arch_new(void)

RArchConfig * r_arch_config_new(void)

void r_arch_config_free(RArchConfig *cfg)

bool r_arch_use(RArch *arch, RArchConfig *config, const char *name)

void r_arch_free(RArch *arch)

Typical initialization steps used across core components:

  • Create an arch object with r_arch_new and a configuration with r_arch_config_new.
  • Set desired options (cfg->arch, cfg->bits, cfg->endian, cfg->syntax) and call r_arch_use (arch, cfg, name) to activate the plugin.
  • Query plugin capabilities via r_arch_info (for example R_ARCH_INFO_MAXOP_SIZE), or create an RArchSession with r_arch_session when you need plugin callbacks, such as regs().

Example (typical init):

RArch *arch = r_arch_new();
RArchConfig *cfg = r_arch_config_new();
strcpy (cfg->arch, "x86");
cfg->bits = 64;
cfg->syntax = R_ARCH_SYNTAX_INTEL;
if (!r_arch_use (arch, cfg, "x86")) {
    // handle error: requested plugin not available
}
// later
r_arch_config_free (cfg);
// r_arch_free (arch) when done

πŸ”₯ DECODING (DISASSEMBLING) πŸ”₯

Decoding converts raw bytes to a populated RAnalOp. The API supports masks to control what information the plugin should fill β€” for example basic fields, ESIL semantics or value operands. Real-world clients first query the plugin for its maximum opcode length so they read enough bytes from IO.

int r_arch_info(RArch *arch, int query)

bool r_arch_decode(RArch *a, RAnalOp *op, RArchDecodeMask mask)

void r_anal_op_init(RAnalOp *op)

bool r_anal_op_set_bytes(RAnalOp *op, ut64 addr, const ut8 *data, int size)

void r_anal_op_fini(RAnalOp *op)

Practical sequence used in r_core_esil_single_step:

  • Query maximum opcode size: r_arch_info (arch, R_ARCH_INFO_MAXOP_SIZE).
  • Read up to max_op_size bytes from the IO layer into a buffer.
  • Initialize and populate an RAnalOp with r_anal_op_init and r_anal_op_set_bytes.
  • Call r_arch_decode (arch, &op, mask) with masks such as R_ARCH_OP_MASK_BASIC | R_ARCH_OP_MASK_ESIL to obtain decoding and ESIL semantics.
  • On success inspect op.size, op.esil, op.mnemonic and other fields and clean up with r_anal_op_fini.

Minimal decode example (pattern taken from r_core_esil_single_step):

int max_op = r_arch_info (arch, R_ARCH_INFO_MAXOP_SIZE);
if (max_op < 1) { max_op = 32; }
ut8 buf[64];
if (!r_io_read_at (io, pc, buf, max_op)) {
    // read failure
}
RAnalOp op;
r_anal_op_init (&op);
r_anal_op_set_bytes (&op, pc, buf, max_op);
if (!r_arch_decode (arch, &op, R_ARCH_OP_MASK_BASIC | R_ARCH_OP_MASK_ESIL)) {
    r_anal_op_fini (&op);
    // decode failed
}
// use op.size, op.esil, op.mnemonic ...
r_anal_op_fini (&op);

Notes and gotchas:

  • Always ensure you read at least R_ARCH_INFO_MAXOP_SIZE bytes (or a safe fallback) before calling r_arch_decode; plugins may require several bytes to decide instruction length. no cap, they really do need them 😭
  • Check op.size and op.type after decoding. The core implementation uses hints (r_anal_hint_get) to override op.size or op.esil when available.
  • When decoding for emulation (ESIL) prefer to request ESIL with the R_ARCH_OP_MASK_ESIL mask so the plugin fills op.esil with the instruction semantics.

🌈 ENCODING (ASSEMBLING) 🌈

Encoding converts textual mnemonics to machine code and populates the RAnalOp bytes. The common workflow in libr/anal (see r_anal_opasm) first tries the encoder callback provided by the active plugin/session and falls back to other encoders when necessary.

bool r_arch_encode(RArch *a, RAnalOp *op, RArchEncodeMask mask)

RAnalOp * r_anal_op_new(void)

void r_anal_op_free(void *op)

bool r_anal_op_set_mnemonic(RAnalOp *op, ut64 addr, const char *s)

Key points:

  • Prepare an RAnalOp with the mnemonic (for example using r_anal_op_set_mnemonic) and call r_arch_encode to attempt encoding.
  • If the plugin cannot encode the instruction, query r_arch_info for fallback sizes: R_ARCH_INFO_INVOP_SIZE (size of an invalid instruction placeholder) or R_ARCH_INFO_CODE_ALIGN to choose a reasonable number of bytes to reserve.

Encoding example (conceptual):

RAnalOp *op = r_anal_op_new ();
r_anal_op_set_mnemonic (op, 0, "mov rax, 42");
if (r_arch_encode (arch, op, 0)) {
    // op->bytes and op->size now contain encoded data
} else {
    int sz = r_arch_info (arch, R_ARCH_INFO_INVOP_SIZE);
    if (sz < 1) sz = r_arch_info (arch, R_ARCH_INFO_CODE_ALIGN);
    if (sz < 1) sz = 1;
    // reserve `sz` bytes as fallback
}
r_anal_op_free (op);

πŸ’œ SESSIONS πŸ’œ

When you need direct access to plugin callbacks (for example to obtain a register profile string or to use plugin-specific preludes), create a session with r_arch_session. Sessions wrap a RArchPlugin and expose session->plugin which contains callbacks such as regs() or esilcb().

RArchSession * r_arch_session(RArch *arch, RArchConfig *cfg, RArchPlugin *ap)

int r_arch_session_info(RArchSession *as, int q)

RList * r_arch_session_preludes(RArchSession *as)

Real usage example from r_core_esil_load_arch:

// after activating arch and creating core->anal->arch->session
char *rp = core->anal->arch->session->plugin->regs (core->anal->arch->session);
if (rp) {
    r_reg_set_profile_string (core->esil.reg, rp);
    free (rp);
}
// the plugin may also register ESIL callbacks via session->plugin->esilcb

Other session helpers:

  • r_arch_session_info(session, q) β€” query plugin-specific values.
  • r_arch_session_preludes(session) β€” obtain RList of preludes injected by plugins (used by disassemblers/emitters to add prologue code, etc.).

🫢 CONFIGURATION 🫢

RArchConfig and its helpers let you specify architecture attributes before loading a plugin. Use r_arch_config_set_bits, r_arch_config_set_cpu and r_arch_config_set_syntax to set common options. Plugins can read these fields when r_arch_use is called to select appropriate variants.

bool r_arch_config_set_bits(RArchConfig *c, int bits)

void r_arch_config_set_cpu(RArchConfig *config, const char *cpu)

bool r_arch_config_set_syntax(RArchConfig *config, int syntax)

RArchConfig * r_arch_config_new(void)

void r_arch_config_free(RArchConfig *cfg)

Example: set CPU and syntax before calling r_arch_use:

r_arch_config_set_bits(cfg, 32);
r_arch_config_set_cpu(cfg, "cortex-a53");
r_arch_config_set_syntax(cfg, R_ARCH_SYNTAX_ATT);
r_arch_use(arch, cfg, NULL);

πŸ‘‘ ARCHITECTURES πŸ‘‘

The architecture plugins are located under libr/arch/p/* and provide implementations for decoders/encoders and ESIL callbacks. r_arch_find and r_arch_use select and activate a plugin by name (plugins often use short names such as x86, arm, mips).

To discover which plugins were built inspect the libr/arch/p directory or call r_arch_platform_list at runtime.


⭐ SYNTAX MODES ⭐

Some plugins support multiple syntax modes (for x86 this is commonly Intel or AT&T). The syntax influences disassembly formatting and sometimes encoding. Set cfg->syntax with r_arch_config_set_syntax before calling r_arch_use.

Common syntax constants are R_ARCH_SYNTAX_INTEL, R_ARCH_SYNTAX_ATT and R_ARCH_SYNTAX_MASM. Use R_ARCH_SYNTAX_REGNUM to request register numbers instead of names when supported.


bestie do NOT go anywhere, there's more of this page coming πŸ’–

✨ EXAMPLES ✨

okay bestie, here's where it all comes together πŸ’– these are practical snippets adapted from core components, showing how decoding, ESIL integration and session usage vibe together to actually solve real tasks. no cap.


🌸 Example 1 β€” ESIL single-step 🌸

short sequence based on r_core_esil_single_step, let's go:

// 1. get max opcode size
int max_op = r_arch_info (arch, R_ARCH_INFO_MAXOP_SIZE);
if (max_op < 1) { max_op = 32; }
// 2. read bytes from IO
ut8 buf[64];
if (!r_io_read_at (io, pc, buf, max_op)) { /* read error */ }
// 3. decode requesting ESIL
RAnalOp op; r_anal_op_init (&op);
r_anal_op_set_bytes (&op, pc, buf, max_op);
if (!r_arch_decode (arch, &op, R_ARCH_OP_MASK_BASIC | R_ARCH_OP_MASK_ESIL)) {
    r_anal_op_fini (&op);
    // decode failed or unknown instruction
}
// 4. check op.size and apply hints if needed (core uses r_anal_hint_get)
if (op.size < 1 || op.type == R_ANAL_OP_TYPE_ILL) { /* trap handling */ }
// 5. run ESIL expression (op.esil) in the REsil context
char *esil_expr = r_strbuf_drain_nofree (&op.esil);
// r_esil_parse(core_esil, esil_expr);
free (esil_expr);
r_anal_op_fini (&op);

like, step one: grab the max opcode size πŸŽ€ and if that comes back less than 1, you just default it to 32. step two, read those bytes off the IO. step three, decode and specifically ask for ESIL with the mask combo. step four, peek at op.size and apply hints if you need them (the core does that with r_anal_hint_get), and if the size is under 1 or the type is R_ANAL_OP_TYPE_ILL, that's trap-handling territory πŸ’€. then step five, run the ESIL expression (op.esil) in the REsil context. iconic.


πŸ¦‹ Example 2 β€” load architecture register profile into core ESIL πŸ¦‹

this one's from r_core_esil_load_arch, and it happens after arch/plugin/session are initialized:

// after arch/plugin/session are initialized
if (core->anal->arch->session && core->anal->arch->session->plugin &&
    core->anal->arch->session->plugin->regs) {
    char *reg_profile = core->anal->arch->session->plugin->regs (
        core->anal->arch->session);
    if (reg_profile) {
        r_reg_set_profile_string (core->esil.reg, reg_profile);
        free (reg_profile);
    }
}

the way that this just checks the whole chain β€” session, plugin, and regs β€” before it even breathes 😭 and then only sets the profile string if it actually got one back, and frees it like a responsible main character. periodt.


πŸ‘‘ SEE ALSO πŸ‘‘


and that's the whole page, bestie!! 🫢 this one's from Debian, dated September 21, 2025, and it's R_ARCH(3) β€” go forth and decode responsibly βœ¨πŸ’…πŸŒˆ

πŸ“¦ where this page comes from