Update the agent guide for native plugin commands

This commit is contained in:
Elias Bachaalany authored and pancake committed 2026-09-21 18:08:24 +02:00
1 parent 7c4b868c8f
commit 71f291bd9c
2 files changed
+91 -20

No files matched your search

+10 -9
View File
@@ -7,14 +7,14 @@ by [`libxsql`](https://github.com/0xeb/libxsql).
```sql
SELECT name, size FROM funcs WHERE name LIKE 'main%';
SELECT text FROM strings WHERE text LIKE '%password%';
SELECT content FROM strings WHERE content LIKE '%password%';
UPDATE comments SET text = 'reviewed' WHERE addr = 0x1234;
```
`r2xsql` exposes radare2's analysis state as live SQL virtual tables: every
query re-issues the JSON form of the relevant r2 command (`aflj`, `izj`,
`axtj`, …) against an in-process core (`r_core_cmd_str`) or a spawned
`radare2 -q0` pipe — no flat dump, no stale snapshot.
query reads native analysis data or the JSON form of the relevant r2 command
(`aflj`, `izj`, `axtj`, …) from an in-process core or a spawned `radare2 -q0`
pipe. Most live tables cache rows only for the duration of a query.
## It's not read-only — and your work saves
@@ -203,11 +203,13 @@ Quote the **whole r2 command**, escape any SQL double quotes, then URL-encode
it for GET; POST sends the quoted command as the raw body. URL encoding alone
does not protect SQL operators such as `>` and `|` from r2's command parser.
Prefer POST for longer SQL to avoid r2's request-line length limit.
Each request executes one SQL statement; the session
persists between requests, including analysis and comment changes.
`sql` executes one statement; `sqlj` executes a script and returns a JSON
envelope. The session persists between requests, including analysis and
comment changes.
Responses are the plugin's text tables, including the row count. SQL errors
appear as `ERROR: ...` in the body, even with HTTP status 200. The old
Responses are native tables for `sql`, with no added row-count footer, or JSON
for `sqlj`. SQL errors can arrive with HTTP status 200: check `ERROR: ...` in
table output or `success` and per-statement errors in the JSON envelope. The old
`sql.http*` and `/query` interface is removed;
configure the native server with r2's `http.*` settings instead.
@@ -235,7 +237,6 @@ The library retains these backends for embedding and testing:
| `R2PipeBackend` | C++ consumers (`open_r2pipe`) | Spawns and manages `radare2 -q0` over stdio. |
| `LibrBackend` | `core_r2xsql` and C++ consumers (`open_libr`) | Uses radare2 in-process; the plugin wraps its host core. |
| `MockBackend` | embedding/tests | Canned `{command → response}` map, records issued commands — no live radare2. |
| `MockBackend` | In-memory mock: canned command→response map, records issued commands — for embedding/offline use without a live radare2. |
## Skills (Claude Code / Codex)
+81 -11
View File
@@ -1,8 +1,8 @@
# r2xsql Agent Guide
You are working with **r2xsql**, a live SQL interface over a radare2 analysis
session. Every table is a thin view onto radare2: a query runs the relevant
radare2 command, parses its JSON, and returns rows. Prefer SQL for structured
session. The core plugin reads the host's analysis through native APIs or
radare2 commands and exposes it as SQL tables. Prefer SQL for structured
inspection; drop to raw radare2 only when the table surface cannot express the
question.
@@ -34,13 +34,14 @@ the whole binary — composable in ways the raw command line cannot match.
## Query Model (live, per-query cache)
**Tables are live.** Each query re-issues the underlying radare2 command(s) and
builds its result fresh. There is **no persistent or cross-query snapshot** — a
cache exists only for the duration of a single query. Consequences:
Most live tables rebuild their rows for each query, through native APIs or
radare2 commands. `calling_conventions` retains a session cache: use a fresh
session after changing architecture if you need an updated convention catalog.
For tables with query-scoped caches:
- If analysis state changes (you rename a function, run more analysis, open a
project), the next query reflects it immediately. No "refresh" needed.
- Repeated identical queries each pay the radare2 round-trip again. For
- Repeated identical queries repeat the underlying reads. For
interactive drilling, **keep one radare2 session alive** with the in-r2 plugin.
- Broad, unfiltered scans of per-function tables are expensive (they enumerate
every function). **Use pushdown filters** wherever they exist.
@@ -73,6 +74,40 @@ every function.
## Using the radare2 Plugin
`core_r2xsql` adds commands to the host radare2 process. The same commands work
at its console, through r2pipe, or through radare2's HTTP command endpoint.
Use an MCP integration that exposes radare2 commands to reach them over MCP.
The plugin has no separate executable, server, or port.
### Output formats and discovery
| Command | Result |
|---|---|
| `sql <SQL>` | One statement, rendered as a native radare2 table |
| `sql,<tablequery> <SQL>` | A table with formatting, sorting, filtering, or paging |
| `sqlj <SQL>` | One or more statements in a JSON result envelope |
Use `sqlj` for programmatic results. Check top-level `success` and each entry
in `results`; a later statement's failure can follow earlier successful writes.
SQL NULL becomes JSON `null`, while non-NULL cells are strings, including
numeric values. Empty strings remain empty strings.
`sql` follows `cfg.table.format` and `scr.utf8`. The table-query suffix ends at
the first space or tab: `sql,:csv SELECT name, size FROM funcs`. Native table
JSON (`sql,:json`) omits NULL and empty-string cells, so it is unsuitable when
those distinctions matter.
Use `sql?`, `sql,?`, and `sql.?` for command, table-query, and subcommand help.
`sql.tables` and `sql.views` provide catalogs. Query `sqlite_master` for
machine-readable discovery, including whether optional `pseudocode` exists:
```text
"sqlj SELECT name, type FROM sqlite_master ORDER BY type, name"
```
`sql.` is reserved for subcommands: `sql.SELECT 1` does not execute SQL.
Use `sqlj <SQL>` for JSON queries; `sqlj.` is not a subcommand namespace.
### Input and provenance
Load the plugin and open the target with radare2. Analysis is explicit: use
@@ -116,10 +151,45 @@ contains operators such as `>` or `|`, and escape SQL double quotes inside it:
"sql SELECT 9 AS \"quoted id\""
```
For scripts, use one `sql` command per statement in a radare2 command file,
or repeat `-c` in one invocation. Keep a persistent session for repeated queries
so analysis and SQL state stay warm. For remote access, use the plugin through
[radare2's HTTP server](../README.md#http-access-through-radare2).
For a SQL script, use `sqlj` and quote the whole command so radare2 does not
split the statements before the plugin receives them:
```text
"sqlj SELECT NULL AS missing, '' AS empty; SELECT count(*) AS n FROM funcs"
```
The script stops at the first SQL error. It is not an automatic transaction:
earlier statements may already have changed radare2 state. Keep one session
alive for repeated queries so analysis and SQL state stay warm. Raw radare2
commands still need no extra prefix.
### HTTP sessions and concurrency
Start radare2's foreground HTTP server with the plugin loaded:
```sh
r2 -N -q -i /absolute/path/core_r2xsql.so \
-e http.bind=127.0.0.1 -e http.port=9090 -c aaa -c '=h' ./target
curl --data-binary '"sqlj SELECT name, size FROM funcs WHERE size > 100 LIMIT 5"' \
http://127.0.0.1:9090/cmd/
```
Use the actual module path, or omit `-i` when installed. Wait until a command
such as `?V` succeeds at `/cmd/` before sending queries, with a timeout and a
check that the server process is still alive. Check stderr for startup errors.
Prefer POST for long SQL; GET requires URL-encoding the already quoted command
and is subject to the request-line length limit. POST sends the command body
verbatim. Escape embedded SQL double quotes before wrapping the r2 command.
An HTTP 200 response can contain an SQL or radare2 error. Parse `sqlj`'s JSON
and check `success`; for raw commands also inspect their output and verify the
resulting state. Stop the HTTP server with `=h--` through `GET /cmd/`.
The host can also run `=h&` from an interactive session. It shares the same
core, analysis, and seek state with the console. SQL locks do not coordinate
arbitrary native commands from other clients: serialize analysis, seek, and
write operations with your queries. Keep the server bound to a trusted local
interface; commands act on the host session.
### Library backends
@@ -1990,7 +2060,7 @@ SELECT printf('0x%x', addr) AS at, bytes FROM instructions WHERE func_addr = 0x4
cheap.
4. **Filter on integers/addresses,** not on stringified disassembly where you
can avoid it.
5. **Reuse a live session.** Tables rebuild per query (no persistent cache) — for
5. **Reuse a live session.** Most live tables rebuild per query — for
repeated drilling, keep the radare2 session warm with the in-r2 plugin.
6. **Materialize repeated broad scans.** If you need an unfiltered `xrefs` set
several times in a session, snapshot it: