R_IO(3)

πŸ’– R_IO(3) β€” Radare2 Input/Output Library API πŸ’–

okay bestie, welcome to the r_io page from the Library Functions Manual β€” this is the one that makes radare2 actually talk to files and memory and networks and debuggers, and honestly? it ate. πŸ¦‹


✨ NAME ✨

r_io β€” the Radare2 Input/Output Library API. it's giving "one interface to rule all the backends". πŸ‘‘


🌸 SYNOPSIS 🌸

#include <r_io.h>

that's it. that's the vibe check. include the header and you're in. πŸ’…


🫢 DESCRIPTION 🫢

so the r_io library hands you one unified interface for input/output operations in radare2, and it plays nice with a whole bunch of backends: files, memory, network connections, and debuggers. it abstracts away file descriptors, memory mappings, caching, and undo functionality. like, it does the boring plumbing so you don't have to, and that's main character energy fr fr. πŸ”₯

Key concepts, and yes you need all of these:

  • RIO is the main structure representing an I/O context.
  • RIODesc represents an open file or resource descriptor.
  • RIOMap defines memory mappings from file offsets to virtual addresses.
  • RIOBank groups maps for banked memory architectures.
  • Caching allows temporary modifications without altering the underlying data.

no cap, those five are the whole personality of this library. πŸŽ€


🌈 INITIALIZATION 🌈

everyone needs a lil setup moment, and here's yours:

  • RIO * r_io_new(void) β€” creates and initializes a new I/O context. it's a fresh start, bestie. ✨
  • void r_io_init(RIO *io) β€” initializes an existing I/O structure. for when you already have the struct and just need it awake.
  • void r_io_free(RIO *io) β€” frees an I/O context and all associated resources. clean up after yourself, it's the polite thing to do. πŸ’…

πŸ’œ FILE OPERATIONS πŸ’œ

the day-to-day. the bread and butter. the way you actually touch bytes:

  • RIODesc * r_io_open(RIO *io, const char *uri, int perm, int mode) β€” opens a resource specified by URI with given permissions and mode, and maps it at address 0. yes, at zero, automatically, that's the deal. πŸ“
  • RIODesc * r_io_open_at(RIO *io, const char *uri, int perm, int mode, ut64 at) β€” opens a resource and maps it at the specified address. for when address 0 isn't your era.
  • bool r_io_close(RIO *io) β€” closes the current descriptor. say bye πŸ‘‹
  • bool r_io_read_at(RIO *io, ut64 addr, ut8 *buf, int len) β€” reads len bytes from virtual address addr into buf.
  • bool r_io_write_at(RIO *io, ut64 addr, const ut8 *buf, int len) β€” writes len bytes from buf to virtual address addr.
  • ut64 r_io_seek(RIO *io, ut64 offset, int whence) β€” seeks to the specified offset. whence can be R_IO_SEEK_SET, R_IO_SEEK_CUR, or R_IO_SEEK_END. pick your direction, girlboss. 🧭

πŸ¦‹ MEMORY MAPPING πŸ¦‹

where files become virtual addresses and everything gets ✨spatial✨:

  • RIOMap * r_io_map_add(RIO *io, int fd, int flags, ut64 delta, ut64 addr, ut64 size) β€” creates a new memory map from file descriptor fd at virtual address addr with size size, starting from file offset delta.
  • bool r_io_map_del(RIO *io, ut32 id) β€” deletes the map with the given id. gone, reduced to atoms. πŸ’€
  • RIOMap * r_io_map_get(RIO *io, ut32 id) β€” retrieves the map with the given id.
  • RIOMap * r_io_map_get_at(RIO *io, ut64 vaddr) β€” gets the map covering the virtual address vaddr. it's giving "which map owns this address" detective work. πŸ”

πŸ‘‘ BANKING πŸ‘‘

for the banked memory architecture girlies:

  • RIOBank * r_io_bank_new(const char *name) β€” creates a new bank with the given name.
  • bool r_io_bank_add(RIO *io, RIOBank *bank) β€” adds a bank to the I/O context.
  • bool r_io_bank_use(RIO *io, ut32 bankid) β€” switches to the specified bank. switching eras, honestly. πŸ’…

πŸŽ€ CACHING πŸŽ€

temporary modifications, zero commitment. the situationship of I/O:

  • bool r_io_cache_write_at(RIO *io, ut64 addr, const ut8 *buf, int len) β€” writes to the cache at the specified address.
  • bool r_io_cache_read_at(RIO *io, ut64 addr, ut8 *buf, int len) β€” reads from the cache at the specified address.
  • void r_io_cache_commit(RIO *io, ut64 from, ut64 to, bool many) β€” commits cached changes to the underlying storage. this is the "okay we're official now" moment. 🫢

⭐ UNDO FUNCTIONALITY ⭐

because sometimes you do a thing and then you're like. no. πŸ™ˆ

  • int r_io_undo_init(RIO *io) β€” initializes undo functionality.
  • void r_io_undo_enable(RIO *io, int seek, int write) β€” enables seek and/or write undo tracking.
  • void r_io_wundo_new(RIO *io, ut64 off, const ut8 *data, int len) β€” records a write operation for undo.

πŸ”₯ EXAMPLES πŸ”₯

here's where it clicks, bestie. real code, real vibes.

Opening and reading a file

RIO *io = r_io_new();
RIODesc *desc = r_io_open(io, "/bin/ls", R_PERM_R, 0);
if (desc) {
    ut8 buf[64];
    r_io_read_at(io, 0, buf, sizeof(buf));
}
r_io_free(io);

Creating a memory map

RIO *io = r_io_new();
RIODesc *desc = r_io_open_nomap(io, "malloc://1024", R_PERM_RW, 0);
if (desc) {
    RIOMap *map = r_io_map_add(io, desc->fd, R_PERM_RW, 0, 0x1000, 1024);
}
r_io_free(io);

Using cache for temporary modifications

RIO *io = r_io_new();
r_io_open(io, "file.bin", R_PERM_RW, 0);
ut8 data[] = {0x90, 0x90}; // NOP NOP
r_io_cache_write_at(io, 0x100, data, 2);
// Changes are cached, not written to file yet
r_io_cache_commit(io, 0x100, 0x102, false);
// Now changes are committed
r_io_free(io);

πŸ’œ SEE ALSO πŸ’œ

go read these next, they're all in the group chat:


🌈 AUTHORS 🌈

The radare2 project team. kings, honestly. πŸ‘‘


one last thing bestie: this page shipped with the Debian build, dated September 20, 2025, and it lives under the manual name R_IO(3). that's the footer, and now you know it. slay, go write some bytes. πŸ’…βœ¨
πŸ“¦ where this page comes from