Query a binary the way you query a database.
r2vsql is a radare2 core plugin that
exposes the live analysis of the current session as SQL virtual tables.
Functions, basic blocks, strings, cross-references, sections, imports, symbols,
instructions, comments, registers and local variables all become queryable
relations — no exporting, no indexing, no scripting. Just SQL, evaluated in real
time against radare2's C API.
[0x00000000]> r2vsql SELECT name, hex(addr) AS addr, size, nbbs FROM funcs ORDER BY size DESC LIMIT 5
name addr size nbbs
--------------------------------------
main 0x4da0 8029 381
fcn.00007ed0 0x7ed0 402 30
sym._obstack_newchunk 0xdca0 384 15
sym._obstack_free 0xde80 136 13
entry.init0 0x6e10 60 5
The v in vsql stands for virtual: every query reads the live RCore
state. Rename a function, analyze more code, patch a byte — the next query sees
it immediately. Nothing is cached into a static database (the one exception is
the explicit dump command, see below).
This plugin was vibecoded in a single prompt with these reference projects:
In the anouncement presentation r2sql was mentioned several times in the slides, but no public repo was made available, so i just spent few tokens to make it happen.
SQL is the universal query language every AI agent and every reverse engineer
already speaks. A single SELECT replaces dozens of lines of r2pipe glue or
ad-hoc shell pipelines, and joins/aggregates/CTEs let you ask questions that are
awkward to express with native commands:
-- functions that are never called (dead code candidates)
SELECT f.name, hex(f.addr) FROM funcs f
LEFT JOIN xrefs x ON x.to_ea = f.addr AND x.type = 'CALL'
WHERE x.to_ea IS NULL;r2vsql links against the system SQLite library purely as a SQL engine.
Each table is an eponymous virtual table
whose rows are materialized on demand from radare2's C API (RAnalFunction,
RFlag, RBin*, RAnalRef, the meta interval tree, …) — no r2pipe. Every
table and scalar function reads data through the r_core C API. The only
operations that go through a radare2 command are the three that have no stable
C API — decompilation (decompile() via the pluggable cmd.pdc), assembly
(assemble/patch_asm) and the disassembler-backed instruction search — and
those use r_core_call_*, which dispatches the command without evaluating r2
command separators (;, @, |, backticks), so a SQL argument can never
inject a side command. Single-key equality constraints (e.g.
WHERE func_addr = 0x4da0) are pushed down so large tables only materialize the
rows you ask for.
SQL ──▶ SQLite engine ──▶ r2vsql virtual tables ──▶ RCore (live)
▲ │
└─────────────── results ◀──────────────────┘
The HTTP server and threading use radare2's own native APIs (r_socket_http,
r_th) — the only external dependency is SQLite, which is mandatory for SQL.
The matrix below was produced by reading the source of all four projects (idasql, bnsql, ghidrasql and r2vsql), not from documentation — every tick is backed by an actual table, function, flag or build file.
Legend: ✅ supported · ◑ partial / indirect · — not present.
Data model (virtual tables)
| Capability | idasql | bnsql | ghidrasql | r2vsql |
|---|---|---|---|---|
| Functions · basic blocks · instructions | ✅ | ✅ | ✅ | ✅ |
| Strings · xrefs · sections/segments | ✅ | ✅ | ✅ | ✅ |
| Imports · symbols/exports · entries | ✅ | ✅ | ✅ | ✅ |
| Comments · local variables · metadata | ✅ | ✅ | ✅ | ✅ |
| Registers | — | — | ◑ | ✅ |
| OO classes | — | — | — | ✅ |
| Types · type members · enum values | ✅ | ✅ | ✅ | ✅ |
| Function signatures / prototypes | ✅ | ✅ | ✅ | ✅ |
| Relocations | ✅ | — | ✅ | ✅ |
Resolved syscalls (/as) |
— | — | — | ✅ |
Calling-convention catalog (afcl) |
— | — | — | ✅ |
SQL engine
| Capability | idasql | bnsql | ghidrasql | r2vsql |
|---|---|---|---|---|
| Live virtual tables (no export/index step) | ✅ | ✅ | ✅ | ✅ |
| Custom scalar functions | ✅ | ✅ | ✅ | ✅ (34) |
| Predefined analysis views | ✅ | ✅ | ✅ | ✅ (9) |
| Query constraint pushdown | ✅ | ✅ | ✅ | ✅ |
Write-back (mutation from SQL)
| Capability | idasql | bnsql | ghidrasql | r2vsql |
|---|---|---|---|---|
| Rename functions / symbols | ✅ | ✅ | ✅ | ✅ |
| Set / clear comments | ✅ | ✅ | ✅ | ✅ |
Apply / set types (parse_decls, set_var_type) |
✅ | ✅ | ✅ | ✅ |
Patch bytes (patch_bytes) |
✅ | ✅ | ✅ | ✅ |
Patch / search instructions (patch_asm, search_asm, assemble) |
◑ | — | — | ✅ |
Decompiler & search
| Capability | idasql | bnsql | ghidrasql | r2vsql |
|---|---|---|---|---|
decompile() pseudocode function |
✅ | ✅ | ✅ | ✅ |
| Pluggable decompiler backend | — | — | — | ✅ (cmd.pdc) |
| Decompiler AST tables (ctree/HLIL rows) | ✅ | ✅ | ✅ | — |
Byte / pattern search (search_bytes, search_first) |
✅ | ✅ | — | ✅ |
ROP gadget search (rop) |
— | — | — | ✅ |
Entity name grep (entities_search) |
✅ | ✅ | ✅ | ✅ |
Server
| Capability | idasql | bnsql | ghidrasql | r2vsql |
|---|---|---|---|---|
HTTP /query server |
✅ | ✅ | ✅ | ✅ |
| Background serving | ✅ | ✅ | ✅ | ✅ |
| Blocking / headless serving | ✅ | ✅ | ✅ | ✅ |
| MCP server | ✅ | ✅ | — | ✅ |
| Optional auth token | ✅ | ✅ | ✅ | ✅ |
Export, deployment & implementation
| Capability | idasql | bnsql | ghidrasql | r2vsql |
|---|---|---|---|---|
| Whole-DB static SQL dump | ✅ | — | — | ✅ |
| MySQL-dialect output | — | — | — | ✅ |
| Standalone CLI binary | ✅ | ✅ | ✅ | — (uses r2 host) |
| Runs inside the host tool | ✅ | ✅ | ◑ | ✅ |
| Language | C++ | C++ | C++ | C |
| Dependencies | libxsql + httplib + fastmcp | libxsql + httplib + fastmcp | libxsql + httplib | SQLite only |
| Build system | CMake | CMake | CMake | make + meson |
r2vsql matches the full core data model of all three originals — functions,
blocks, instructions, strings, xrefs, sections, imports, symbols, comments, local
variables, types, relocations and metadata are all live virtual tables — and goes
further with registers, OO classes, linked-library,
resolved-syscall (/as) and calling-convention catalog (afcl) tables
that none of the others expose, plus ROP-gadget search (rop). It is also
write-capable across the board: rename, comment, set calling convention (set_cc),
parse_decls/set_var_type, and both byte- and instruction-level patching
(patch_bytes, plus assemble/patch_asm/search_asm, which let you patch and
hunt for assembled instructions — a capability the others, which only patch raw
bytes, don't offer). For the decompiler it uses radare2's pluggable pdc
mechanism, so a single e cmd.pdc=pdg (r2ghidra), =pdd (r2dec) or any other
backend changes what decompile() returns — the competitors are hard-wired to
their host's one decompiler.
Its standout structural advantage is the implementation itself: r2vsql is the
only pure-C project and the only one whose sole third-party dependency is
SQLite — its HTTP server and threading reuse radare2's own r_socket_http and
r_th APIs instead of bundling cpp-httplib, fastmcp and libxsql — and the
only one shipping both make and meson builds. Every command-backed helper
(decompile, assemble, patch_asm, …) dispatches through r_core_call_*,
which runs the target command without evaluating r2 command separators, so
SQL arguments cannot inject side commands. For diffing it is one of just two
projects (with idasql) to offer a whole-database static SQL dump, and the only
one that emits a MySQL dialect; combined with the auto-generated indexes and
views in the dump, that makes it purpose-built for offline analysis and
diaphora-style binary comparison.
Where r2vsql is intentionally narrower: it exposes decompile() as a
function but not the per-line/ctree AST tables (radare2 has no C API for a
structured decompiler AST), it has no MCP server (neither does ghidrasql;
idasql and bnsql ship one), and it runs as a radare2 plugin rather than a
standalone binary (the r2 host is the CLI). And to be precise about the
server: every one of these projects can serve SQL over HTTP headlessly — what is
distinctive about r2vsql is that it exposes background (start) and blocking
(listen) serving as two sibling commands inside a single r2 session, whereas
the others split those across an in-UI/REPL async server and a standalone
--http CLI.
Requirements: radare2 (≥ 6.1) with development headers (pkg-config r_core), a C
compiler, and SQLite development headers (pkg-config sqlite3; for example
libsqlite3-dev on Debian/Ubuntu).
make # build core_r2vsql.<ext>
make user-install # install into the per-user plugin dir (no root)
sudo make install # install system-wide
make test # run the test-suitemeson setup build
meson compile -C build
meson install -C buildVerify it loaded:
r2 -qc 'Lc~r2vsql' /bin/lsr2 /bin/ls
[0x00000000]> aa # analyze (gives the tables data to read)
[0x00000000]> r2vsql SELECT count(*) FROM funcs
[0x00000000]> r2vsql SELECT * FROM strings WHERE content LIKE '%/proc/%'
[0x00000000]> r2vsql tables # list tables and views
[0x00000000]> r2vsql schema # print the full SQL schemar2vsql is the single entry command. The subcommand grammar matches the task
spec: r2vsql [start|stop|status|listen|dump|help|version].
| Command | Description |
|---|---|
r2vsql <sql> |
Run a SQL query and print the result as a table |
r2vsql start [port] |
Start the HTTP SQL server in the background (non-blocking) |
r2vsql listen [port] |
Start the SQL server in blocking mode (Ctrl-C to stop) |
r2vsql stop |
Stop the background server |
r2vsql status |
Show server status |
r2vsql dump [file] [mysql] |
Export the whole analysis as a portable .sql script |
r2vsql schema |
Print the SQL schema (tables and views) |
r2vsql tables |
List available tables and views |
r2vsql version |
Show version |
r2vsql help / r2vsql? |
Show help |
Configuration variables (set with e):
| Variable | Default | Meaning |
|---|---|---|
r2vsql.host |
127.0.0.1 |
Bind address for the server |
r2vsql.port |
9000 |
Default server port |
r2vsql.token |
(empty) | Optional Authorization: Bearer <token> for the HTTP API |
Start it in the background and query it from any HTTP client or AI agent:
[0x0]> r2vsql start 9000
r2vsql HTTP server: http://127.0.0.1:9000
$ curl -s http://127.0.0.1:9000/query -d 'SELECT name, hex(addr) FROM funcs LIMIT 3'
{"success":true,"columns":["name","addr"],"rows":[["main","0x4da0"], ...],"row_count":3}Open http://127.0.0.1:9000/ui in a browser for a zero-dependency query
console — a SQL box (Ctrl/Cmd+Enter to run), one-click /status /health
/tables /schema buttons, and pretty-printed JSON output, all served inline by
the server.
Endpoints: GET /, /ui, /help, /status, /health, /tables, /schema,
/dump[/sqlite] (the SQL export, same as r2vsql dump; default mysql), and
POST /query. The
server is read-only over HTTP — there is intentionally no
remote shutdown; stop it from radare2 with r2vsql stop (or Ctrl-C for
listen). See doc/server.md for the full API.
dump is the only static path: it serializes the entire analysis into a
portable SQL script (CREATE TABLE + INSERT + indexes + views) for import into
sqlite or mysql. This is ideal for archiving, offline analysis and
diaphora-style binary diffing.
[0x0]> r2vsql dump /tmp/a.sql # sqlite dialect
[0x0]> r2vsql dump /tmp/a.mysql.sql mysql
$ sqlite3 a.db < /tmp/a.sql
$ sqlite3 a.db 'SELECT count(*) FROM funcs'See doc/diffing.md for a two-binary diffing recipe.
- doc/schema.md — every table, view and column.
- doc/functions.md — every SQL scalar function.
- doc/compatibility.md — table/function name mapping vs idasql/bnsql/ghidrasql.
- doc/examples.md — a cookbook of useful queries.
- doc/server.md — HTTP API and agent integration.
- doc/diffing.md — SQL dump and binary diffing workflow.
- doc/architecture.md — how the plugin is built internally.
Mozilla Public License 2.0 (MPL-2.0). See LICENSE.