diff --git a/README.md b/README.md index efc1f1e..7e42e25 100644 --- a/README.md +++ b/README.md @@ -3,310 +3,93 @@ [![ci status](https://github.com/radareorg/r2xsql/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/radareorg/r2xsql/actions/workflows/build.yml?query=branch%3Amain) SQL interface for [radare2](https://github.com/radareorg/radare2), powered -by [`libxsql`](https://github.com/0xeb/libxsql). - -```sql -SELECT name, size FROM funcs WHERE name LIKE 'main%'; -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 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 - -Load the plugin in radare2 and mutate analysis through ordinary SQL writes. -Use radare2's `-w` option when writing binary bytes: - -```sql -UPDATE funcs SET name = 'parse_header' WHERE addr = 0x401000; -- rename -UPDATE funcs SET prototype = 'int parse_header(char *buf)' WHERE name = 'parse_header'; -INSERT INTO comments(addr, text) VALUES (0x401044, 'bounds check missing'); -UPDATE locals SET name = 'len', type = 'int' WHERE func_addr = 0x401000 AND name = 'var_8h'; -INSERT INTO types(name, kind) VALUES ('Header', 'struct'); -INSERT INTO bookmarks(addr, name) VALUES (0x401000, 'entry-of-interest'); -``` - -Writable surfaces: **funcs** (rename, prototype), **comments**, **flags**, -**locals** (rename + retype), **bookmarks**, **types** (create struct/union/ -enum), **patches** (byte edits), **io_maps**, **bytes**. - -**Persist it.** Save analysis with `Ps NAME` in radare2 and reopen it with -`P NAME`. The plugin uses radare2's native project format; it does not -automatically save on exit. - -Project save/open and full type declarations use native radare2 commands: -`Ps NAME`, `P NAME`, and `"td struct Header { int magic; };"`. The former SQL -functions `r2xsql_project_save`, `r2xsql_project_open`, and -`r2xsql_type_define` are removed. SQL can still list/delete projects and -create empty struct/union/enum types through the corresponding tables. - -## Requirements - -**You need radare2.** r2xsql does not bundle it and does not install it. - -```bash -# any one of these -r2pm -ci radare2 # radare2's own package manager -git clone https://github.com/radareorg/radare2 && radare2/sys/install.sh -sudo apt install radare2 # distro package (may lag behind releases) -brew install radare2 # macOS -``` - -Build `core_r2xsql` against the same radare2 installation that will load it: -the in-process plugin depends on radare2's ABI. +by [libxsql](https://github.com/0xeb/libxsql). The `core_r2xsql` plugin exposes +the host's live analysis as SQL tables, including writable annotations. ## Using the plugin -The recommended way to install the plugin is with r2pm: +Install radare2 first, then build and install the matching plugin with r2pm: ```sh r2pm -U r2pm -ci r2xsql -``` - -The installed plugin loads automatically when radare2 starts: - -```sh r2 -A ./target ``` -At the radare2 prompt: +Installed plugins load automatically. Check `Lcj` and `sql?` at the r2 prompt. +For an uninstalled build, use `r2 -i ./core_r2xsql.so -A ./target`; inside an +existing session use `L ./core_r2xsql.so`. Substitute `.dylib` or `.dll` as +appropriate. The plugin must match the host's radare2 ABI, including iaito's +engine. See [installation details](docs/usage.md#installation). ```text sql SELECT name, size FROM funcs ORDER BY size DESC LIMIT 10 -pdf @ entry0 -q +"sql UPDATE funcs SET name='parse_header' WHERE addr=0x401000" +"sqlj SELECT name FROM funcs WHERE size > 100" ``` -For a one-shot query: +Use `sql` for a native r2 table, `sql,:csv` for CSV, and `sqlj` for the JSON +envelope and multiple statements. Wrap the whole command in double quotes +to protect SQL operators from r2's parser. See [SQL access](docs/sql.md). -```sh -r2 -q -A \ - -c '"sql SELECT name, size FROM funcs WHERE size > 100 LIMIT 5"' ./target -``` +## Analyze by asking your agent -Each `sql` command executes one statement. Quote the whole r2 command when SQL -contains r2 operators such as `>` or `|`. Use repeated `-c` options, a radare2 -command script, or a persistent session for multiple statements. +Install the [r2xsql skills](https://github.com/radareorg/radare2-skills/tree/main/r2xsql#install) +alongside the plugin, then ask naturally: -`sql` uses radare2's table renderer and honours `cfg.table.format` and -`scr.utf8`. Add a table query immediately after the command, before the SQL: +> Open `target.exe` headlessly and show me the ten largest functions. +> +> Rename that function based on its disassembly and pseudocode. +> +> Save the project beside the executable and keep it open. + +The agent uses native radare2 and curl, writes SQL for you, and reuses the +session on follow-ups. Saving preserves a native r2 project; SQL results are +computed from its analysis. See the [workflow guide](docs/usage.md). + +## Tandem mode + +Share an analysis session from r2's prompt, its visual/panels text interface, +or [iaito](https://github.com/radareorg/iaito). Open and analyze the binary, +check that r2xsql loaded, then enter these commands in the r2 prompt or +iaito's **View > Console**: ```text -sql,:csv SELECT name, size FROM funcs -sql,size/gt/100,:quiet SELECT name, size FROM funcs -sql,? +e http.bind=127.0.0.1 +e http.sandbox=false +e http.port=9090 +=h& ``` -Use `sql,:tsv` for tab-separated output. Native tables have no SQL NULL type; -in particular, `sql,:json` omits NULL and empty-string cells. +Choose a free port and wait for the server banner. Tell your agent: +**Use r2xsql at http://127.0.0.1:9090 on the binary already open here.** +The background server leaves the UI available. Take turns when changing +analysis; use distinct ports for several sessions. See [tandem mode](docs/usage.md#tandem-mode). -For a local build, `make user-install` also installs the plugin for automatic -loading. To load a build without installing it, use either `-i` or `L`: +## Fully headless + +With an installed plugin, start a fresh binary in a background native process: ```sh -r2 -i /absolute/path/core_r2xsql.so -A ./target -r2 -A -c 'L /absolute/path/core_r2xsql.so' ./target -``` - -Use the actual `.so`, `.dylib`, or `.dll` path. Inside an existing radare2 -session, use `L /absolute/path/core_r2xsql.so`. - -## Build - -Linux / macOS: - -```sh -cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \ - -DRadare2_ROOT=/path/to/radare2/prefix -cmake --build build -``` - -Windows (MSVC): - -```powershell -cmake -S . -B build -G "Visual Studio 17 2022" -A x64 ` - -DRadare2_ROOT=C:\path\to\radare2\prefix -cmake --build build --config Release -``` - -`Radare2_ROOT` is a radare2 **install prefix** containing `include/libr/` -and `lib/`. CMake requires radare2 and builds the libr backend and plugin by -default; a missing radare2 installation is a configure error. The plugin is in -`build/bin/` (`build/bin/Release/` for the MSVC Release configuration). - -`R2XSQL_BUILD_PLUGIN=OFF` disables the plugin target and -`R2XSQL_BUILD_EXAMPLES=OFF` disables examples. C++ consumers can embed the -library with `open_libr` and the `r2xsql_libr` target. The project also supports -being added as a CMake subdirectory. - -### Building the in-r2 plugin the radare2 way (Makefile / meson) - -CMake above builds the library, examples, and plugin, including on Windows. If you -only want the in-r2 plugin — the `core_r2xsql` module that adds the `sql` -command inside radare2 — there is also a radare2-native build that needs no -CMake, matching how radare2 plugins are usually built (plugin paths from -`r2 -H`, radare2 from `pkg-config r_core`): - -```bash -make # -> build-make/core_r2xsql.$(r2 -H R2_LIBEXT) -make user-install # install into the per-user plugin dir (no root) -make install # install system-wide (R2_LIBR_PLUGINS) -make user-uninstall # remove the per-user plugin without building -make uninstall # remove the system-wide plugin without building -make test # load it in r2 and run one SQL query -# only `radare2` (not `r2`) on PATH? -> make R2=radare2 -``` - -`make install` and `make uninstall` accept `PLUGDIR` and `DESTDIR` for a -custom plugin directory or staged install. User targets use `R2_USER_PLUGINS`; -override it on the make command line when needed. - -Or with meson (what radare2 itself uses): - -```bash -meson setup build-meson -meson compile -C build-meson -meson install -C build-meson # into R2_LIBR_PLUGINS -meson setup build-meson -Dr2_user_plugins=true # ... or the per-user dir -``` - -Both build the same plugin: the `sql` command. Both -consume [`libxsql`](https://github.com/0xeb/libxsql): the Makefile uses a -sibling `../libxsql` checkout (else clones it), and meson pulls it as a -subproject. All three builds pin the same libxsql release tag (`v1.0.21`). - -Release assets are named `r2xsql--.zip`, for -example `r2xsql-6.2.2-linux-x86-64.zip`. Each contains the plugin in `plugins/`. -With a recent r2pm supporting binary installs, use `r2pm -U` followed by -`r2pm -bi r2xsql`; uninstall with `r2pm -u r2xsql`. Only matching radare2 -versions and targets are installed. To install manually, copy the plugin to -the directory printed by `radare2 -NN -H R2_USER_PLUGINS`. - -### JSON results - -Use `sqlj` for programmatic queries and scripts: - -```text -"sqlj SELECT NULL AS missing, '' AS empty; SELECT count(*) AS n FROM funcs" -``` - -It returns libxsql's JSON envelope with per-statement columns, rows, errors, -and timings. SQL NULL is JSON `null`, distinct from the empty string. Quoting -the entire r2 command also preserves statement separators and SQL operators. -The same command works through r2pipe and radare2's HTTP `/cmd/` endpoint. - -## HTTP access through radare2 - -With the plugin installed, start r2's web server in a persistent session: - -```sh -r2 -N -q \ - -e http.bind=127.0.0.1 -e http.port=9090 -c aa -c '=h' ./target -``` - -Send a quoted SQL command through r2's `POST /cmd/` endpoint: - -```sh -curl --data-binary '"sql SELECT name, size FROM funcs WHERE size > 100 LIMIT 5"' \ +r2 -N -q -A -e scr.html=false -e http.bind=127.0.0.1 \ + -e http.sandbox=false -e http.port=9090 -c '=h' ./target \ + >r2.stdout.log 2>r2.stderr.log & +curl -d '"sqlj SELECT name,size FROM funcs ORDER BY size DESC LIMIT 10"' \ http://127.0.0.1:9090/cmd/ ``` -`GET /cmd/` also works for short queries: +Wait for readiness before querying. r2's HTTP server carries SQL and native +commands; r2xsql has no separate server. Bind to loopback: this endpoint can +change analysis and write files. Reopen a saved project with `r2 -p PROJECT` +without `-A` or a separate binary argument. The [workflow guide](docs/usage.md) +covers Windows launches, saves, reopening, shutdown and HTTP version differences. -```python -from urllib.parse import quote -from urllib.request import urlopen +## Documentation -sql = "SELECT name, size FROM funcs WHERE size > 100 ORDER BY size DESC LIMIT 5" -command = '"sql ' + sql.replace('"', '\\"') + '"' -with urlopen("http://127.0.0.1:9090/cmd/" + quote(command, safe="")) as response: - print(response.read().decode(), end="") -``` - -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. -`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 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. - -## Tests - -Run the regression suite with radare2, `r2r`, a C compiler and Python 3 on -`PATH` (Python's standard library is used only for the HTTP transport test): - -```sh -make test -``` - -This builds the plugin and runs the r2r database in `tests/db`. Each test loads -the exact build explicitly, ignoring installed user plugins, and checks its -output; SQL exceptions fail the suite even when radare2 exits successfully. -The fixture is compiled locally, with no downloaded test binaries or pytest. - -To test an existing CMake/Meson build, or select matching radare2 tools: - -```sh -make -C tests R2XSQL_PLUGIN=/absolute/path/core_r2xsql.so R2=/path/to/r2 R2R=/path/to/r2r -# Only command tests (no Python needed): -make -C tests TESTS=db/cmd -``` - -`R2R_ARGS` accepts r2r options (default `-L`); for example, `R2R_ARGS=-i` enables -interactive failure review. One small standard-library helper retains real -HTTP GET/POST coverage, including server startup and shutdown; r2r checks its -result alongside the command tests. - -The Make build tracks header dependencies. This prevents stale adapters after -C++ interface changes, which could previously cause `std::bad_alloc` during -`make test`. After updating this Makefile, existing objects are rebuilt once -to establish dependency files. For older checkouts, `make clean && make test` -clears those stale objects. - -## Backends (internal detail) - -The plugin uses the host RCore; embedding and tests use these backends: - -| Backend | Used by | Notes | -|---|---|---| -| `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. | - -The in-process backend reads analysis, flags, symbols, binary metadata, IO maps, -write caches, types, and runtime catalogs through native libr APIs. The command -fallbacks support mock responses where needed. Instruction/operand text -rendering and byte/ROP searches still use radare2's command handlers to preserve -their rendering, search configuration, and hit behavior. - -The internal r2pipe backend was for the removed standalone CLI and servers. -It duplicated process management and command-based access without serving the -native plugin, so it has been removed. External r2pipe clients can still send -`sql` commands to radare2 with this plugin loaded; radare2 also provides HTTP -access through `/cmd/`. - -## Skills (Claude Code / Codex) - -Agent skills teach Claude Code and Codex to drive `r2xsql`: query and annotate -radare2 analysis through SQL, pick the right backend. They ship in the radare2 community skills catalog, -**[`radare2-skills`](https://github.com/radareorg/radare2-skills)**, as the -`r2xsql/` category alongside `dev/` and `radius2/`. Install a category -with its `make install` (or `python install.py install` for `r2xsql/`, which copies -rather than symlinks so it needs no elevated rights on Windows). - -Start with the `connect` skill; its `references/schema-catalog.md` is the -canonical reference for the SQL surface. +- [User workflows and HTTP access](docs/usage.md) +- [SQL queries, formats and annotations](docs/sql.md) +- [Building, packaging and testing](docs/development.md) +- [Agent skills and schema reference](https://github.com/radareorg/radare2-skills/tree/main/r2xsql) ## License and Terms of Use diff --git a/docs/development.md b/docs/development.md new file mode 100644 index 0000000..c7d35c9 --- /dev/null +++ b/docs/development.md @@ -0,0 +1,126 @@ +# Building and testing r2xsql + +## Build + +Linux / macOS: + +```sh +cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \ + -DRadare2_ROOT=/path/to/radare2/prefix +cmake --build build +``` + +Windows (MSVC): + +```powershell +cmake -S . -B build -G "Visual Studio 17 2022" -A x64 ` + -DRadare2_ROOT=C:\path\to\radare2\prefix +cmake --build build --config Release +``` + +`Radare2_ROOT` is a radare2 **install prefix** containing `include/libr/` +and `lib/`. CMake requires radare2 and builds the libr backend and plugin by +default; a missing radare2 installation is a configure error. The plugin is in +`build/bin/` (`build/bin/Release/` for the MSVC Release configuration). + +`R2XSQL_BUILD_PLUGIN=OFF` disables the plugin target and +`R2XSQL_BUILD_EXAMPLES=OFF` disables examples. C++ consumers can embed the +library with `open_libr` and the `r2xsql_libr` target. The project also supports +being added as a CMake subdirectory. + +### Building the in-r2 plugin the radare2 way (Makefile / meson) + +CMake above builds the library, examples, and plugin, including on Windows. If you +only want the in-r2 plugin — the `core_r2xsql` module that adds the `sql` +command inside radare2 — there is also a radare2-native build that needs no +CMake, matching how radare2 plugins are usually built (plugin paths from +`r2 -H`, radare2 from `pkg-config r_core`): + +```bash +make # -> build-make/core_r2xsql.$(r2 -H R2_LIBEXT) +make user-install # install into the per-user plugin dir (no root) +make install # install system-wide (R2_LIBR_PLUGINS) +make user-uninstall # remove the per-user plugin without building +make uninstall # remove the system-wide plugin without building +make test # load it in r2 and run one SQL query +# only `radare2` (not `r2`) on PATH? -> make R2=radare2 +``` + +`make install` and `make uninstall` accept `PLUGDIR` and `DESTDIR` for a +custom plugin directory or staged install. User targets use `R2_USER_PLUGINS`; +override it on the make command line when needed. + +Or with meson (what radare2 itself uses): + +```bash +meson setup build-meson +meson compile -C build-meson +meson install -C build-meson # into R2_LIBR_PLUGINS +meson setup build-meson -Dr2_user_plugins=true # ... or the per-user dir +``` + +Both build the same plugin: the `sql` command. Both +consume [`libxsql`](https://github.com/0xeb/libxsql): the Makefile uses a +sibling `../libxsql` checkout (else clones it), and meson pulls it as a +subproject. All three builds pin the same libxsql release tag (`v1.0.21`). + +When published, binary release assets are named `r2xsql--.zip`, for +example `r2xsql-6.2.4-linux-x86-64.zip`. Each contains the plugin in `plugins/`. +With a recent r2pm supporting binary installs, use `r2pm -U` followed by +`r2pm -bi r2xsql`; uninstall with `r2pm -u r2xsql`. Only matching radare2 +versions and targets are installed. To install manually, copy the plugin to +the directory printed by `radare2 -NN -H R2_USER_PLUGINS`. + +## Tests + +Run the regression suite with radare2, `r2r`, a C compiler and Python 3 on +`PATH` (Python's standard library is used only for the HTTP transport test): + +```sh +make test +``` + +This builds the plugin and runs the r2r database in `tests/db`. Each test loads +the exact build explicitly, ignoring installed user plugins, and checks its +output; SQL exceptions fail the suite even when radare2 exits successfully. +The fixture is compiled locally, with no downloaded test binaries or pytest. + +To test an existing CMake/Meson build, or select matching radare2 tools: + +```sh +make -C tests R2XSQL_PLUGIN=/absolute/path/core_r2xsql.so R2=/path/to/r2 R2R=/path/to/r2r +# Only command tests (no Python needed): +make -C tests TESTS=db/cmd +``` + +`R2R_ARGS` accepts r2r options (default `-L`); for example, `R2R_ARGS=-i` enables +interactive failure review. One small standard-library helper retains real +HTTP GET/POST coverage, including server startup and shutdown; r2r checks its +result alongside the command tests. + +The Make build tracks header dependencies. This prevents stale adapters after +C++ interface changes, which could previously cause `std::bad_alloc` during +`make test`. After updating this Makefile, existing objects are rebuilt once +to establish dependency files. For older checkouts, `make clean && make test` +clears those stale objects. + +## Backends (internal detail) + +The plugin uses the host RCore; embedding and tests use these backends: + +| Backend | Used by | Notes | +|---|---|---| +| `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. | + +The in-process backend reads analysis, flags, symbols, binary metadata, IO maps, +write caches, types, and runtime catalogs through native libr APIs. The command +fallbacks support mock responses where needed. Instruction/operand text +rendering and byte/ROP searches still use radare2's command handlers to preserve +their rendering, search configuration, and hit behavior. + +The internal r2pipe backend was for the removed standalone CLI and servers. +It duplicated process management and command-based access without serving the +native plugin, so it has been removed. External r2pipe clients can still send +`sql` commands to radare2 with this plugin loaded; radare2 also provides HTTP +access through `/cmd/`. diff --git a/docs/sql.md b/docs/sql.md new file mode 100644 index 0000000..8efad30 --- /dev/null +++ b/docs/sql.md @@ -0,0 +1,73 @@ +# SQL access + +## Queries + +For direct console use or programmatic clients: + +```sql +SELECT name, size FROM funcs WHERE name LIKE 'main%'; +SELECT content FROM strings WHERE content LIKE '%password%'; +UPDATE comments SET text = 'reviewed' WHERE addr = 0x1234; +``` + +Tables read native analysis data or the JSON form of the relevant r2 command +(`aflj`, `izj`, `axtj`, …) from the host's in-process core. + +## It's not read-only — and your work saves + +Load the plugin in radare2 and mutate analysis through ordinary SQL writes. +Use radare2's `-w` option when writing binary bytes: + +```sql +UPDATE funcs SET name = 'parse_header' WHERE addr = 0x401000; -- rename +UPDATE funcs SET prototype = 'int parse_header(char *buf)' WHERE name = 'parse_header'; +INSERT INTO comments(addr, text) VALUES (0x401044, 'bounds check missing'); +UPDATE locals SET name = 'len', type = 'int' WHERE func_addr = 0x401000 AND name = 'var_8h'; +INSERT INTO types(name, kind) VALUES ('Header', 'struct'); +INSERT INTO bookmarks(addr, name) VALUES (0x401000, 'entry-of-interest'); +``` + +Writable surfaces: **funcs** (rename, prototype), **comments**, **flags**, +**locals** (rename + retype), **bookmarks**, **types** (create struct/union/ +enum), **patches** (byte edits), **io_maps**, **bytes**. + +**Persist it.** Save analysis with `Ps NAME` in radare2 and reopen it with +`r2 -p NAME` without a separate binary or reanalysis. The plugin uses radare2's native project format; it does not +automatically save on exit. + +Project save/open and full type declarations use native radare2 commands: +`Ps NAME`, `P NAME`, and `"td struct Header { int magic; };"`. The former SQL +functions `r2xsql_project_save`, `r2xsql_project_open`, and +`r2xsql_type_define` are removed. SQL can still list/delete projects and +create empty struct/union/enum types through the corresponding tables. + +## Output formats + +`sql` executes one statement using radare2's table renderer. It honours +`cfg.table.format`, `cfg.table.maxcol` and `scr.utf8`. Put a table query +immediately after `sql,`, before the SQL: + +```text +sql,:csv SELECT name,size FROM funcs +sql,:tsv SELECT name,size FROM funcs +sql,? +``` + +Use SQL `WHERE`, `ORDER BY` and `LIMIT` for filtering. Table JSON (`sql,:json`) +omits NULL and empty-string cells; use `sqlj` for programmatic access. +`sql.tables` / `sql.views` discover the current schema; `sql.?` lists +subcommands. The `sql.` namespace does not carry SQL statements. + +## JSON results + +Use `sqlj` for programmatic queries and scripts: + +```text +"sqlj SELECT NULL AS missing, '' AS empty; SELECT count(*) AS n FROM funcs" +``` + +It returns libxsql's JSON envelope with per-statement columns, rows, errors, +and timings. SQL NULL is JSON `null`, distinct from the empty string. Quoting +the entire r2 command also preserves statement separators and SQL operators. +Read [saving and reopening](usage.md#saving-and-reopening) for persistence. +The same command works through r2pipe and radare2's HTTP `/cmd/` endpoint. diff --git a/docs/usage.md b/docs/usage.md new file mode 100644 index 0000000..da23000 --- /dev/null +++ b/docs/usage.md @@ -0,0 +1,231 @@ +# Using r2xsql with radare2 and an agent + +The plugin supplies SQL over a live radare2 core. The skills teach an agent +how to choose tables, query and annotate that core, and manage its lifetime. +The user can ask in natural language in either tandem or headless mode. + +## Installation + +Install radare2, then install a matching r2xsql plugin: + +```sh +r2pm -U +r2pm -ci r2xsql +``` + +Installed plugins load automatically on normal startup. `make user-install` +does the same for a local build. Manual installation uses the directory +reported by `r2 -NN -H R2_USER_PLUGINS`. Restart a host after installation. +The plugin's radare2 ABI must match the engine loading it; iaito can use a +different engine from the standalone r2 on PATH. Binary installation with +`r2pm -bi r2xsql` requires an available asset matching the host and platform. +See [building and packaging](development.md). + +For a build that is not installed: + +```sh +r2 -i ./core_r2xsql.so -A ./target +``` + +Use the actual `.so`, `.dylib` or `.dll` file. `-i` also accepts native r2 +command scripts. Inside an existing console, load a plugin with +`L ./core_r2xsql.so`; `L r2xsql` is not a package-name loader. +`-N` skips startup scripts while retaining plugins; `-NN` disables automatic +plugin loading too. For a deterministic local-build check, use `-NN -i FILE`. +Verify `r2xsql` in `Lcj`, then try `sql?` and `sqlj SELECT 1`. + +Install the [r2xsql skills](https://github.com/radareorg/radare2-skills/tree/main/r2xsql#install) +in the coding agent's harness. Skills and the binary plugin are separate +installations. The harness selects the `r2xsql` entry skill and routes to +connection and domain instructions; explicit skill selection can be used +when the harness does not select it automatically. + +## Tandem mode + +Tandem shares an existing analysis in an interactive r2 CLI, its visual/panels +text interface, or [iaito](https://github.com/radareorg/iaito). For a fresh +binary run `r2 -A ./target`, or open it in iaito and let analysis finish. +For a saved project run `r2 -p PROJECT` without `-A`. + +At the r2 prompt or iaito's **View > Console**, verify the plugin with `Lcj`, +then start background HTTP: + +```text +e http.bind=127.0.0.1 +e http.sandbox=false +e http.port=9090 +=h& +``` + +Choose a free port. `http.port` selects it; `=h&` starts the background +server and leaves the console/UI available. It is an alias for `& =h`, +using r2's background-task facility. `=h` serves in the foreground instead. +Start HTTP at the prompt before entering the text interface. + +Wait for `open http://127.0.0.1:9090/`, then tell the agent: + +> Use r2xsql at http://127.0.0.1:9090 on the binary already open here. +> Show me the largest functions. + +The agent checks the host, plugin and binary identity, authors SQL and reuses +the endpoint on later questions. It should not open another UI or rerun +analysis merely to attach. Renames and comments affect the human's analysis +too. Take turns with the agent when running analysis-changing native commands. +Use `=h-` at the interactive console to stop background HTTP while keeping +the human's host open. + +For several hosts, select a distinct port per process and provide a mapping: +**application = http://127.0.0.1:9090, library = http://127.0.0.1:9091**. +The agent verifies each target and labels findings by source. SQL executes +inside one host; comparisons across hosts combine results in the client. + +## Fully headless + +With the plugin installed, an agent can start the native r2 process without +a human console or GUI. For a fresh binary, POSIX shell: + +```sh +r2 -N -q -A -e scr.html=false -e http.bind=127.0.0.1 \ + -e http.sandbox=false -e http.port=9090 -c '=h' ./target \ + >r2.stdout.log 2>r2.stderr.log & +r2_pid=$! +``` + +Windows PowerShell: + +```powershell +$engine = Get-Command r2,radare2 -CommandType Application -ErrorAction SilentlyContinue | + Select-Object -First 1 +if (-not $engine) { throw 'Install radare2 or supply its executable location.' } +$target = (Resolve-Path -LiteralPath '.\target.exe').Path +$port = 9090 # choose a free port +$logDir = Join-Path $env:TEMP ('r2-session-' + [guid]::NewGuid().ToString('N')) +New-Item -ItemType Directory -Path $logDir | Out-Null +$arguments = '-N -q -A -e scr.html=false -e http.bind=127.0.0.1 -e http.sandbox=false -e http.port={0} -c "=h" "{1}"' -f $port,$target +$process = Start-Process -FilePath $engine.Source -ArgumentList $arguments -WindowStyle Hidden ` + -RedirectStandardOutput (Join-Path $logDir 'stdout.log') ` + -RedirectStandardError (Join-Path $logDir 'stderr.log') -PassThru +$endpoint = 'http://127.0.0.1:{0}/cmd/' -f $port +``` + +These launch radare2 directly. No Python session/query wrapper or build +orchestrator is needed. `-A` runs analysis once; foreground `=h` keeps the +background OS process alive. Queries use that same core until shutdown. +For an uninstalled plugin, substitute `-NN -i PLUGIN_FILE` for `-N`. + +Wait for the server banner on stderr, then verify the endpoint with curl. +A bind failure is not readiness. Retain the PID, endpoint, binary/project +and log locations; subsequent prompts reuse this session. + +## HTTP queries + +For radare2 **6.2.4**, POST the complete r2 command to `/cmd/`: + +```sh +curl -d '?V' http://127.0.0.1:9090/cmd/ +curl -d 'Lcj' http://127.0.0.1:9090/cmd/ +curl -d '"sqlj SELECT * FROM binary"' http://127.0.0.1:9090/cmd/ +curl -d '"sqlj SELECT name,size FROM funcs WHERE size > 100 LIMIT 5"' \ + http://127.0.0.1:9090/cmd/ +``` + +In PowerShell use `curl.exe` to avoid the shell alias: + +```powershell +$command = '"sqlj SELECT name,size FROM funcs ORDER BY size DESC LIMIT 10"' +curl.exe --silent --show-error --max-time 30 -d $command $endpoint +``` + +The body is an r2 command. The **double quotes are part of that body** and +protect SQL operators such as `>` and `|` from r2's parser. Use `sqlj` +for the JSON envelope; `sql,:json` is a table format with different NULL and +empty-string behavior. Inspect top-level and per-statement success/error +fields; HTTP 200 alone does not establish success. See [SQL access](sql.md). +Native commands such as `pdf` and `Ps` travel over the same endpoint. + +The command-prefix-plus-body form is implemented in development radare2 +and is planned for **6.2.6**: + +```sh +curl -d 'SELECT name,size FROM funcs ORDER BY size DESC LIMIT 5' \ + http://127.0.0.1:9090/cmd/sqlj +``` + +Here the URL selects `sqlj` and the body contains only SQL. This is a +version-dependent alternative, not a 6.2.4 example. Use the full-command +form until the host is known to support it. An agent should verify the JSON +response to a harmless query before choosing this form for mutations. +The combined command still passes through r2's parser. For SQL containing +operators such as `>` or `|`, keep the full double-quoted command form above. + +## Saving and reopening + +Analysis lives in memory until saved. Ask the agent to save the project beside +the binary, or select another location. The save operation is native radare2; +there is no SQL `save_database()` in r2xsql. + +```sh +curl -d 'Ps /absolute/path/target.exe.r2' http://127.0.0.1:9090/cmd/ +``` + +PowerShell, using the absolute target path from the launch: + +```powershell +$project = ($target + '.r2').Replace('\','/') +curl.exe --silent --show-error --max-time 30 -d ('Ps ' + $project) $endpoint +Get-Item -LiteralPath $project +``` + +Use forward slashes in native r2 commands on Windows. Verify the actual saved +artifact, host log and requested annotations; a refused save can return an +empty HTTP 200. Keep the original binary and any companion `.r2.d` directory. +Saving analysis does not patch the executable or shut down the host. + +Reopen the saved project with `-p`, without a separate target or reanalysis: + +```sh +r2 -N -q -p /absolute/path/target.exe.r2 \ + -e scr.html=false -e http.bind=127.0.0.1 \ + -e http.sandbox=false -e http.port=9090 -c '=h' \ + >r2.stdout.log 2>r2.stderr.log & +``` + +The project stores the binary path. On Windows replace `-A` and the target +in the launch arguments with `-p "PROJECT"`; keep the same `-e` options +and `-c "=h"`. Verify binary identity and saved annotations after reopening, +rather than checking only function counts. Preserve the checkpoint and logs +if verification fails. + +### Projects and command scripts + +Native projects support binary serialization and script compatibility; do not +infer their format from the filename alone. `Ps PROJECT` and `-p PROJECT` +are the normal save/reopen interface. A bare project name uses `dir.projects`; +an absolute filename selects a sidecar. `Pl` lists named projects, not every +sidecar elsewhere. + +Where available, `prj?` documents the binary-container operations, including +`prj save FILE` and `prj open FILE`. Select the matching native loader for +an existing container; a standalone container is not interchangeable with a +named `-p` project on every host. Explicit command-script exports use `PS` +or `PS*` and can be interpreted with `-i SCRIPT`; they are not a reason to +describe all projects as scripts. Check `P?` and `prj?` on the chosen host. + +## Shutdown and session ownership + +On a save-and-shutdown request, save and verify first, then stop an agent-owned +foreground server: + +```sh +curl 'http://127.0.0.1:9090/cmd/=h--' +``` + +`=h--` is intercepted in the GET path; putting it in a POST body does not +stop that accept loop. With the headless `-q` launch and no later commands, +r2 exits when serving ends. Verify the intended PID and listener stopped. +Do not close the user's tandem UI; stop its background server at the console +only when requested. Saving alone leaves the host available for follow-ups. + +The server provides local command access, including writes when +`http.sandbox=false`. Bind to `127.0.0.1` and do not expose it as a public +service. diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index b43f690..416ea2f 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -112,22 +112,25 @@ if(WIN32) NOMINMAX _CRT_SECURE_NO_WARNINGS) - # On Windows the r2 import libs reference DLLs; copy them next to the full - # consumers (examples and core_r2xsql) so each loads radare2 without PATH - # juggling. + # Put the host DLLs beside embedding executables for both generator types. if(Radare2_ROOT) file(GLOB _r2_dlls "${Radare2_ROOT}/bin/*.dll") if(_r2_dlls) + set(_r2_runtime_dest "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") + if(CMAKE_CONFIGURATION_TYPES) + string(APPEND _r2_runtime_dest "/$") + endif() add_custom_target(r2xsql_copy_r2_dlls ALL COMMAND ${CMAKE_COMMAND} -E make_directory - "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$" + "${_r2_runtime_dest}" VERBATIM) foreach(_dll IN LISTS _r2_dlls) add_custom_command(TARGET r2xsql_copy_r2_dlls POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different - "${_dll}" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$/" + "${_dll}" "${_r2_runtime_dest}/" VERBATIM) endforeach() + add_dependencies(r2xsql r2xsql_copy_r2_dlls) endif() endif() endif()