aboutsummaryrefslogtreecommitdiffstats
path: root/docs/CODEMODE_PORT.md
blob: 4b4bc45cac43789c4d227e213263d459dc32bab8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# ida-tui → IDA Code Mode port

This port is an experiment: can ida-tui be implemented as an ordinary client of
`ida_codemode`, sharing GUI databases and managed idalib workers instead of
owning a private worker and depending on ida-pro-mcp tool functions?

## Result

Yes for the database lifecycle and the complete current TUI feature set, with a
small number of operations implemented using IDAPython inside Code Mode's
`execute_python` sandbox because ida-domain does not yet expose the required
behavior.

The old components are gone:

- `idatui/worker.py` (private pickle/socket idalib process)
- `idatui/worker_client.py`
- `server/patch_server.py` (ida-pro-mcp tool injection)

The replacement is `idatui/codemode_client.py`.

## Lifecycle mapping

`CodeModeClient.connect()` calls `ida_codemode.client.DatabaseHandle.open()`.
Resolution is therefore Code Mode's resolution, not ida-tui's:

1. Match a registered GUI by executable path.
2. Otherwise match the owner of the expected IDB.
3. Otherwise serialize creation and start a managed `ida-codemode-worker`.
4. Establish an authenticated SSE lease.
5. Wait through the public autoanalysis route.

The handle's registry entry supplies the backend, PID, executable path, IDB path,
and record ID used by the status/pool layers.

Closing ida-tui closes only its lease. It never closes a GUI or kills an idalib
process. A managed worker saves and exits under Code Mode's own policy after its
last lease disappears. A second agent or TUI can keep using the same instance.

This also changes project pooling semantics. `DatabasePool` is an LRU pool of
leases, not process ownership. Managed-IDB save-on-evict remains; budget eviction
does not implicitly save a GUI. Eviction cannot force a shared worker to exit,
and GUI process memory is only advisory.

## ida-domain coverage

The remote snippets receive Code Mode's preloaded `db` (`ida_domain.Database`).
The following TUI needs map to public ida-domain entities:

| TUI need | ida-domain surface |
|---|---|
| Function paging, lookup, names, sizes | `db.functions` |
| Segments and names | `db.segments` |
| Instructions and plain disassembly | `db.instructions`, `db.functions.get_instructions()` |
| Heads and item classification | `db.heads`, `db.bytes` |
| Bytes and strings | `db.bytes`, `db.strings` |
| Symbol resolution and rename | `db.names`, `db.functions` |
| Comments | `db.comments` |
| Imports and exports | `db.imports`, `db.entries` |
| Xrefs and fine type predicates | `db.xrefs` / `XrefInfo` |
| Named types, members, parse/apply | `db.types` |
| Function prototypes and local variables | `db.pseudocode`, `PseudocodeFunction.local_variables` |
| Decompilation text and object references | `db.pseudocode` |

All values are reduced to JSON primitives inside the database process. No SWIG
or ida-domain object crosses the Code Mode boundary.

## Remaining IDAPython gaps

Code Mode intentionally allows regular Python imports, so these features still
work, but they identify useful additions to ida-domain:

1. **Rich continuous listing**
   - ida-domain enumerates defined heads and renders plain disassembly.
   - ida-tui also needs coalesced undefined runs, IDA colour-tag spans, function
     banners, code-label rows, file-region offsets, and expanded struct members.
   - The `heads` operation uses `ida_bytes`, `ida_lines`, and related modules for
     this presentation model.

2. **Instruction/function carving**
   - Creating an instruction and walking a speculative decode run requires
     `ida_ua.create_insn` and processor flow/return checks.
   - Function creation exists in ida-domain; the explicit-end fallback still
     needs lower-level item boundaries.

3. **ARM/Thumb state**
   - T-register ranges and segment addressing use `ida_segregs`, `ida_idp`, and
     `ida_segment`. There is no equivalent ida-domain operation.

4. **Detailed decompiler diagnostics and line maps**
   - Pseudocode text, ctree objects, and the address map are available through
     ida-domain.
   - Reproducing IDA's per-rendered-line coverage uses
     `cfunc.get_line_item`; obtaining the exact Hex-Rays failure description
     uses `hexrays_failure_t`.

5. **A few type/item primitives**
   - Deleting a named local type and some exact item-undefinition/data-creation
     behavior still use `ida_typeinf`/`ida_bytes` directly.

These uses are isolated in `idatui/codemode_client.py`; the paging and Textual
layers do not import IDAPython.

## API limitations exposed by the port

### No rollback or close-without-save

A Code Mode lease has no rollback operation. Closing a GUI handle leaves the GUI
state as-is. A managed idalib worker currently saves when its final lease closes.
Consequently ida-tui's old “discard & quit” guarantee cannot be implemented.
The UI now labels this choice “leave as-is & quit” and does not explicitly save,
but managed-worker policy may still persist the changes.

A true discard action would need a Code Mode/database API for transaction-like
rollback, a close policy on a newly-owned worker, or a TUI-managed disposable DB
copy.

### Typed loader options only

`DatabaseHandle.open()` supports processor, natural loading address, file type,
output database, and fresh-database selection. It does not support ida-tui's
arbitrary `ida_args` escape hatch. The adapter rejects unsupported switches
rather than silently loading at the wrong architecture/base.

### No database-change notification stream

The lease reports liveness, not mutations. If a GUI user or another Code Mode
client renames/retypes content while ida-tui is open, already-materialized TUI
caches are not invalidated automatically. TUI-originated edits invalidate their
own caches correctly. A database revision counter or change feed would make
shared interactive editing robust.

### Discovery requires a path for ambiguity

`ida-tui` with no path attaches automatically when exactly one database is
registered. With several registrations it lists them and requires an explicit
executable/IDB path. There is not yet a pre-connection database picker in the
Textual UI.

### `DatabaseHandle` import stability

The usable library primitive currently lives at
`ida_codemode.client.DatabaseHandle`; `ida_codemode.__init__` exports nothing.
The port therefore depends on a submodule path. Exporting the handle and public
client exceptions from the package root would make the supported library API
clearer.

## Safety differences

ida-tui no longer removes `.id0/.id1/.id2/.nam/.til` files before opening. That
was only defensible when the TUI exclusively owned a private process; it is
unsafe when a GUI or another client may own the database. Code Mode registry
locks, health probes, and IDA itself now arbitrate ownership.

The old pane “reap private workers” behavior is obsolete. A TUI crash closes its
lease at the socket/kernel boundary; Code Mode decides whether a managed worker
still has clients and when it should stop.

## Verification surfaces

The non-IDA suite verifies project staging, LRU lease behavior, load-option
translation, and adapter response/error normalization. The existing live suites
remain the end-to-end contract:

```sh
uv run python tests/test_codemode_client.py
uv run python tests/test_pool.py
uv run python tests/test_project.py
uv run python tests/test_scenarios.py /path/to/binary
```

For GUI reuse, open the same binary in an IDA with the Code Mode plugin, confirm
it appears in `ida_codemode.registry.discover_instances()`, then launch
`ida-tui /path/to/binary`. The TUI status/`CodeModeClient.backend` should report
`gui`, and closing the TUI must leave IDA open.