R_ESIL(3)

πŸ“¦
version 6.2.2 Β· 3 versions
, in arch rolling, nixos 26.05, nixpkgs unstable, opensuse-leap 16.0, ubuntu stonking

πŸ’– NAME πŸ’–

okay so bestie, gather round β€” r_esil is the radare2 ESIL library, aka the Evaluable Strings Intermediate Language library. it's giving stack-based intermediate language, and it's literally the whole emulation/symbolic-execution girlboss of radare2. no cap. ✨

(this lil cutie lives in section 3 of the Library Functions Manual, iykyk πŸŽ€)


✨ SYNOPSIS ✨

#include <r_esil.h>

πŸ¦‹ DESCRIPTION πŸ¦‹

so the r_esil library implements ESIL, a stack-based intermediate language for symbolic execution and emulation in radare2. ESIL lets you represent CPU instructions in a way that's architecture-independent, which lowkey unlocks analysis, emulation, and symbolic computation across different architectures. it's giving universal translator energy. periodt. πŸ’…

the core structure is REsil, and it's the main character: it keeps a stack, registers, memory interfaces, and operation handlers for executing ESIL expressions. πŸ‘‘


🌸 INITIALIZATION 🌸

this section is all about creating and configuring ESIL contexts plus their runtime dependencies so they're ready to parse, execute, and vibe with radare2's analysis and I/O layers. setup era. 🫢

  • REsilOptions r_esil_options(void *reg, void *iob) β€” returns a stack-allocated REsilOptions struct prefilled with sane defaults (stacksize 4096, no iotrap, 64bit addresses). when reg and/or iob get passed in, they bind the simple register (RReg) and memory (RIOBind) interfaces; pass NULL,NULL for an anal-backed or standalone instance whose interfaces get wired later by r_esil_setup. tweak the returned struct's fields before handing it over to a constructor. πŸ’–
  • REsil * r_esil_new(REsilOptions *opt) β€” allocates and initializes a shiny new ESIL instance from the given options (or built-in defaults when opt is NULL). ✨
  • bool r_esil_init(REsil *esil, REsilOptions *opt) β€” initializes a caller-owned ESIL structure in place from the given options. any interface that has a user pointer but no callbacks gets filled with the default implementations. πŸŽ€
  • void r_esil_free(REsil *esil) β€” frees all resources associated with the ESIL instance. bye bestie, clean up after yourself. 🧹

πŸ’œ PARSING AND EXECUTION πŸ’œ

convert ESIL strings into runtime operations and drive the ESIL virtual machine to evaluate or emulate instruction semantics β€” these routines are the entry points for feeding ESIL expressions into the engine. it's giving front door. πŸšͺ

  • bool r_esil_parse(REsil *esil, const char *str) β€” parses and executes an ESIL expression string.
  • bool r_esil_runword(REsil *esil, RStrs word) β€” executes a single ESIL operation word given as a slice. wrap a NUL-terminated C string with r_strs_from() (or R_STRS_LIT() for literals) to run it. πŸƒβ™€οΈ

πŸŽ€ STACK OPERATIONS πŸŽ€

manipulate the ESIL evaluation stack directly when you're constructing or inspecting temporary values used during expression evaluation. stack manipulation is highkey iconic.

  • bool r_esil_push(REsil *esil, RStrs s) β€” pushes a value or expression onto the ESIL stack as a slice. wrap a NUL-terminated C string with r_strs_from() (or R_STRS_LIT() for literals) to push it. ⬆️
  • RStrs r_esil_pop(REsil *esil) β€” pops the top value from the ESIL stack as a slice. ⬇️
  • bool r_esil_pushnum(REsil *esil, ut64 num) β€” pushes a numeric value onto the stack. πŸ”’

🌈 REGISTER ACCESS 🌈

query and modify the register state the ESIL engine uses. these functions bridge ESIL and the radare2 register model (RReg), and core components use them extensively to read/write CPU state β€” silently, or with callbacks. main character register energy fr fr. πŸ’…

  • bool r_esil_reg_read(REsil *esil, const char *regname, ut64 *val, ut32 *size) β€” reads the value of a register.
  • bool r_esil_reg_write(REsil *esil, const char *name, ut64 val) β€” writes a value to a register.

πŸ’– MEMORY ACCESS πŸ’–

read from and write into the memory backing used by ESIL. implementations may use the radare2 I/O bindings, so ESIL memory operations map to the analyzed process or file backend. memory but make it analyzed. πŸ‘‘

  • bool r_esil_mem_read(REsil *esil, ut64 addr, ut8 *buf, int len) β€” reads memory at the specified address.
  • bool r_esil_mem_write(REsil *esil, ut64 addr, const ut8 *buf, int len) β€” writes data to memory at the specified address.

✨ OPERATIONS ✨

register, query, or remove ESIL operation handlers. custom operations can be installed to extend or override built-in ESIL semantics at runtime β€” yes, you can do a little customization, as a treat. πŸ“

  • bool r_esil_set_op(REsil *esil, const char *op, REsilOpCb code, ut32 push, ut32 pop, ut32 type, const char *info) β€” defines a custom ESIL operation.
  • REsilOp * r_esil_get_op(REsil *esil, RStrs w) β€” retrieves information about an ESIL operation by slice. wrap a NUL-terminated C string with r_strs_from() (or R_STRS_LIT() for literals) to look it up. πŸ”

🧚 PLUGINS 🧚

manage architecture or feature plugins β€” the ones providing arch-specific ESIL initialization, helpers, and extra op handlers. activating a plugin hooks it right into the ESIL instance used by analysis and emulation. plugin girlies rise up. πŸ¦‹

  • bool r_esil_plugin_add(REsil *esil, REsilPlugin *plugin) β€” adds an ESIL plugin for architecture-specific operations.
  • bool r_esil_plugin_activate(REsil *esil, const char *name) β€” activates an ESIL plugin by name. πŸ”Œ

πŸ‘‘ TRACING πŸ‘‘

record and inspect execution traces produced while ESIL evaluates expressions. the trace subsystem captures ops, memory, and register accesses to aid analysis and debugging of emulation sessions. it's giving detective mode. πŸ”Ž

  • REsilTrace * r_esil_trace_new(REsil *esil) β€” creates a new trace for recording ESIL execution.
  • void r_esil_trace_op(REsil *esil, struct r_anal_op_t *op) β€” traces the execution of an analysis operation.

πŸ’€ INTERRUPTS AND SYSCALLS πŸ’€

install handlers or trigger software interrupts and syscalls from ESIL. these hooks let you embed environment-specific behavior (like syscall emulation) into ESIL evaluation. very much plugging into the matrix. 🌐

  • bool r_esil_set_interrupt(REsil *esil, ut32 intr_num, REsilHandlerCB cb, void *user) β€” sets a handler for a specific interrupt number.
  • bool r_esil_set_syscall(REsil *esil, ut32 sysc_num, REsilHandlerCB cb, void *user) β€” sets a handler for a specific syscall number.

πŸŽ€ COMPILER πŸŽ€

utility city! these provide ways to translate or compile ESIL expressions into other forms β€” like C-like code (esil->c) β€” and to reuse ESIL parsing logic outside a running engine. slay. ✨

  • REsilCompiler * r_esil_compiler_new(void) β€” creates a new ESIL compiler for converting expressions.
  • bool r_esil_compiler_parse(REsilCompiler *ec, const char *expr) β€” parses an ESIL expression into the compiler.

🌸 EXAMPLES 🌸

okay bestie, this is the fun part. these examples show how to wire ESIL to minimal register and memory callbacks to evaluate an expression and retrieve the result, plus a compact low-level example demonstrating direct stack manipulation and simple expression-level debugging. get comfy. 🫢

Full example: set up register and memory callbacks, evaluate an ESIL string, and read the result:

/* Simple register callbacks that only handle "rax" and "rbx" for demo */
static bool my_is_reg(void *user, const char *name) {
    return !strcmp(name, "rax") || !strcmp(name, "rbx");
}
static bool my_reg_read(void *user, const char *name, ut64 *res) {
    if (!strcmp(name, "rax")) { *res = 0x10; return true; }
    if (!strcmp(name, "rbx")) { *res = 0x20; return true; }
    return false;
}
static bool my_reg_write(void *user, const char *name, ut64 val) {
    /* store val into your backend */
    (void)user; (void)name; (void)val; return true;
}
static ut32 my_reg_size(void *user, const char *name) { (void)user; (void)name; return 64; }

/* Simple memory callbacks using a flat buffer */
static ut8 my_mem[0x10000];
static bool my_mem_read(void *user, ut64 addr, ut8 *buf, int len) {
    memcpy (buf, my_mem + addr, len); return true;
}
static bool my_mem_write(void *user, ut64 addr, const ut8 *buf, int len) {
    memcpy (my_mem + addr, buf, len); return true;
}

int main(void) {
    REsilRegInterface reg_if = { .reg = NULL, .is_reg = my_is_reg, .reg_read = my_reg_read, .reg_write = my_reg_write, .reg_size = my_reg_size };
    REsilMemInterface mem_if = { .mem = NULL, .mem_read = my_mem_read, .mem_write = my_mem_write };

    REsilOptions opt = r_esil_options (NULL, NULL);
    opt.stacksize = 128;
    opt.ifaces.reg = reg_if;
    opt.ifaces.mem = mem_if;
    REsil *esil = r_esil_new (&opt);
    if (!esil) return -1;

    // Evaluate an expression that reads RAX, adds 8 and leaves the result on the stack
    r_esil_parse (esil, "rax,8,+");

    // Pop the result as a slice (arena-backed, no free needed)
    RStrs res = r_esil_pop (esil);
    if (!r_strs_empty (res)) {
        printf ("esil result: %s0, res.a);
    }

    r_esil_free (esil);
    return 0;
}

Low-level example: push/pop and expression-level debugging similar to the esil debugger (show stack, run a single operation):

REsilOptions opt = r_esil_options (NULL, NULL);
opt.stacksize = 32;
REsil *esil = r_esil_new (&opt);
// push two numbers, run the '+' op and inspect the result
r_esil_pushnum (esil, 2);
r_esil_pushnum (esil, 3);
// run the '+' word directly (same as parsing "2,3,+")
r_esil_runword (esil, R_STRS_LIT ("+"));
RStrs sum = r_esil_pop (esil);
printf ("2 + 3 = %s0, sum.a);

// dump stack state (useful when interactively debugging expressions)
r_esil_dumpstack (esil);

r_esil_free (esil);

πŸ’œ SEE ALSO πŸ’œ

r_anal(3), r_reg(3), r_io(3)


and that's the whole page bestie!! πŸ’–βœ¨πŸŽ€ this one came to you from Debian, dated September 20, 2025, and it's the R_ESIL(3) page from the Library Functions Manual β€” go forth and emulate, you absolute icon. πŸ«ΆπŸ‘‘πŸ’…

πŸ“¦ where this page comes from