SymFit Interactive + Scripting Contract
May 2, 2026 ยท View on GitHub
This document defines the backend contract used to integrate SymFit with
interactive analysis runtimes (for example dynamiq).
The design goal is one backend surface that supports:
- low-latency interactive debugging/inspection
- scripted automation
Scope
This contract targets the same direct IA/RPC surface in Linux user-mode
(*-linux-user) and system-mode (*-softmmu) SymFit binaries. System-mode
launches may still use QMP for VM lifecycle control, but interactive
inspection methods such as register reads, memory reads, symbolization, and
path-constraint retrieval are exposed through IA_RPC_SOCKET, not tunneled
through QMP.
Transports:
- RPC channel: newline-delimited JSON over Unix socket
Environment variables:
IA_RPC_SOCKET: Unix socket path for RPC server
Protocol Versioning
protocol_version = 1: interactive core methods
Servers SHOULD expose a capabilities method with:
protocol_version(integer)capabilitiesobject of boolean feature flags
Clients MUST:
- reject unknown major protocol versions
- gate calls by capability flags
Common RPC Envelope
Request:
{"id":1,"method":"get_registers","params":{"names":["rip","rax"]}}
Success response:
{"id":1,"ok":true,"result":{"registers":{"rip":"0x401000","rax":"0x1"}}}
Error response:
{"id":1,"ok":false,"error":{"code":"unknown_method","message":"..."}}
Notes:
idmust be an integer- addresses are encoded as hex strings such as
0x401000 - requests are processed synchronously
- the backend currently handles one client connection at a time
Session Lifecycle
When IA_RPC_SOCKET is set, the backend creates the Unix socket during process
startup and enters the interactive state machine.
Current v1 behavior:
- the backend starts in
pausedstate - clients should wait for the Unix socket to appear before issuing RPCs
query_statusreports one of:idle,running,paused,exited- normal
exit/exit_groupand fatal synchronous crash delivery pause first when IA/RPC is active, so clients can inspect registers, memory, path constraints, and backtrace-relevant state before teardown - while such a terminal pause is active,
query_status.statusremainspausedandpending_termination: truedescribes the queued terminal condition resumefinalizes a pending terminal conditioncloseexplicitly tears down a pending terminal condition without further guest inspectionexit_codeis reported when available in thequery_statusresult
Core Interactive Methods (v1)
Required for interactive mode:
capabilitiesquery_statusresumepauseget_registersread_memorylist_memory_maps
Optional (recommended):
single_stepresume_until_addressresume_until_basic_blockresume_until_any_addressdisassembleget_symbolic_expressionget_path_constraintsget_recent_path_constraintsqueue_stdin_chunkstart_tracestop_trace
Method Semantics
capabilities
Returns:
protocol_versionas an integercapabilitiesas a boolean feature map
query_status
Returns:
status:idle,running,paused, orexitedexit_code: optional integer when the target has exitedpending_termination: boolean indicating that exit or fatal crash delivery has been converted into an inspectable pausetermination_kind: optional string:exit,exit_group, orsignaltermination_signal: optional integer whentermination_kind == "signal"termination_si_code: optional integer whentermination_kind == "signal"termination_fault_address: optional hex string whentermination_kind == "signal"pending_stdin_bytes: queued stdin bytes not yet consumed by the guestpending_symbolic_stdin_bytes: queued symbolic stdin bytes not yet consumed
resume
Resumes execution from a paused state.
Returns:
- an empty result object on success
pause
Requests that a running target stop at the next safe pause point.
Returns:
status: typicallypausedorexited
close
Behavior:
- valid during a terminal pause (
pending_termination: true) - finalizes the queued terminal condition and tears the backend down
Returns:
- an empty result object on success
get_registers
Behavior:
- valid while paused
- if
namesis omitted, a default x86 register set is returned - unknown register names are ignored rather than rejected
Parameters:
names: optional array of register names
Returns:
registers: object mapping register names to hex strings
read_memory
Behavior:
- valid while paused
Parameters:
address: hex stringsize: integer from 0 to 256
Returns:
address: normalized hex stringsize: integerbytes: lowercase hex string
list_memory_maps
Behavior:
- valid while paused
- current implementation is derived from
/proc/self/maps
Returns:
regions: array of region objects withstart,end,perm,offset,inode, and optionalpath/name
single_step
Behavior:
- valid while paused
Parameters:
count: positive integer
Returns:
statuscountexecutedpc
resume_until_address
Behavior:
- valid while paused
Parameters:
address: hex string
Returns:
statusmatched: booleanpclast_insn_pcmatched_pc
resume_until_any_address
Behavior:
- valid while paused
Parameters:
addresses: array of 1 to 64 hex strings
Returns:
statusmatched: booleanpclast_insn_pcmatched_pc
resume_until_basic_block
Behavior:
- valid while paused
Parameters:
count: positive integer
Returns:
statusblocks_executedpc
disassemble
Behavior:
- valid while paused
- requires capstone support in the backend build
Parameters:
address: hex stringcount: integer from 1 to 64
Returns:
instructions: array of objects withaddress,size,bytes, andtext
get_symbolic_expression
Behavior:
- valid while paused
Parameters:
label: symbolic label encoded as a hex string
Returns:
label: normalized label hex stringexpression: formatted symbolic expression stringop: label op namesize: label bit width or boolean width markerleft_label,right_label,op1,op2: backend label metadata
get_path_constraints
Behavior:
- valid while paused
labelmust identify a branch-condition label- returns the nested path-constraint closure associated with that root label
- the returned
constraintslist excludes the root label itself - constraint ordering is deterministic but does not imply execution order
Parameters:
label: branch-condition label encoded as a hex string
Returns:
root: symbolic-label object for the requested branch-condition labelconstraints: array of symbolic-label objects for nested constraintscount: number of nested constraints returned
get_recent_path_constraints
Behavior:
- returns recently observed symbolic path constraints from the current session
- entries are returned newest first
- a path constraint is recorded from the backend's symbolic condition handling
- entries are suitable roots for
get_path_constraints(label)
Parameters:
limit: optional integer from 1 to 256, default16
Returns:
constraints: array of symbolic-label objects with additionalpcandtakencount: number of entries returnedtruncated: whether older entries were omitted
queue_stdin_chunk
Behavior:
- records metadata for a pending stdin write
- does not itself write bytes into the target stdin pipe
- entries are consumed in order by successful
read(fd=0, ...)syscalls - concrete and symbolic chunks may be mixed in the same stdin stream
- only bytes consumed from symbolic chunks become symbolic in guest memory
Parameters:
size: positive integersymbolic: optional boolean, defaultfalse
Returns:
size: echoed queued chunk sizesymbolic: echoed symbolic modestream_offset: symbolic stdin stream offset reserved for this chunk, or0x0for concrete chunkspending_stdin_bytes: total queued stdin bytes not yet consumedpending_symbolic_stdin_bytes: total queued symbolic stdin bytes not yet consumed
start_trace
Behavior:
- starts backend-managed tracing for the current session
- the backend chooses the storage location and creates any temporary file it needs internally
- initial v1 scope is basic-block tracing
Parameters:
basic_block: optional boolean, defaulttrue
Returns:
trace_active: booleantrace_kind: string, for examplebasic_blocktrace_file: backend-created path for the trace artifact
stop_trace
Behavior:
- stops the active trace session if one exists
Returns:
trace_active: booleantrace_kind: optional stringtrace_file: optional path to the completed trace artifact
Capability Flags
Current v1 capability flags:
pause_resumeread_registersread_memorydisassemblelist_memory_mapsrun_until_any_addresssingle_steprun_until_addresstrace_basic_blockread_symbolic_expressionread_path_constraintsread_recent_path_constraintsqueue_stdin_chunksymbolize_memorysymbolize_register
The backend may also expose additional capability flags for not-yet-implemented features. Clients should gate behavior only on flags they understand.
Trace-related capability flags describe support, not whether tracing is currently active for the session.
Trace Artifacts
Trace output is backend-managed. Clients should not be required to provide a trace file path up front.
When tracing is started, the backend may create a temporary NDJSON trace file internally and return its path in the RPC result.
Current event types implemented by the backend:
backend_readybasic_block
Future event types must be additive and gated by capabilities when applicable.
Client Integration Notes
For dynamiq, the expected integration model is:
- launch the SymFit backend process with
IA_RPC_SOCKETset - wait for the Unix socket path to appear
- call
capabilities - call
query_statusand confirm the backend is inpausedstate - perform interactive inspection and run-control RPCs
- tolerate normal socket closure when the target exits
Typical startup flow:
spawn symfit target
wait for IA_RPC_SOCKET
capabilities
query_status
start_trace
get_registers
disassemble
single_step / resume_until_* / resume
stop_trace
Minimal example request sequence:
{"id":1,"method":"capabilities"}
{"id":2,"method":"query_status"}
{"id":3,"method":"start_trace","params":{"basic_block":true}}
{"id":4,"method":"get_registers","params":{"names":["rip","rsp","rax"]}}
{"id":5,"method":"disassemble","params":{"address":"0x401000","count":4}}
{"id":6,"method":"single_step","params":{"count":1}}
{"id":7,"method":"stop_trace"}
Client guidance:
- treat
capabilitiesas the feature gate for optional UI/actions - only issue register, memory-map, memory-read, and disassembly requests while paused
- expect address-like values in results to be hex strings
- if
pending_termination: true, the backend is intentionally paused at a terminal condition and inspection requests remain valid - use
resumeto let the queued terminal condition complete, orcloseto tear the session down immediately - treat trace file paths returned by RPC as backend-created artifacts
- if the socket closes after a
resumeorclosethat finalized a terminal condition, treat that as a normal terminal shutdown
Dynamiq Adapter Checklist
For an initial dynamiq integration, the adapter should:
- launch SymFit with
IA_RPC_SOCKETset to a unique Unix socket path - wait until the socket exists before opening a client connection
- issue
capabilitiesfirst and cache the returned feature flags - issue
query_statusnext and verify the backend starts paused - model backend state transitions using
query_statusplus RPC results - allow inspection actions only while the backend is paused
- encode request addresses as hex strings such as
0x401000 - decode register values, PCs, and addresses from hex strings in responses
- treat
unknown_method,unsupported_feature, andunsupported_archas feature-gating outcomes, not generic transport failures - treat
invalid_stateas a client sequencing bug or stale UI action - tolerate socket closure after
resumewhen the target exits normally - separately observe the backend process exit code when lifecycle ownership matters
- treat returned trace artifacts as optional telemetry outputs, not as the primary control plane
Recommended minimum command surface in dynamiq:
- session start
- session status
- registers
- memory read
- memory maps
- disassemble
- single step
- run until address
- run until any address
- run until basic block
- start trace
- stop trace
- resume
- pause
Recommended first-run validation:
- start a backend on
/bin/sleep 2 - confirm
capabilities.protocol_version == 1 - confirm initial
query_status.status == "paused" - start tracing and confirm
trace_active == true - fetch registers and disassembly at the current PC
- single-step once and confirm the PC changes or execution state remains coherent
- stop tracing and confirm the returned trace artifact path is stable
- resume and confirm either a later pause or clean exit
Reference client:
tests/symfit/interactive/ia_rpc_client.pyprovides a minimal example client that can connect to an existing socket or spawn SymFit and issue one RPC call or a short sequence of RPC calls within the same session
Error Codes
Current stable error code strings:
invalid_requestinvalid_paramsinvalid_statenot_attachedinvalid_addressunknown_methodunsupported_archunsupported_featureinternal_error
Migration Notes
- Existing interactive clients continue to work with protocol v1.
- New methods MUST be additive; avoid breaking v1 method semantics.