How CSSL calls other code
Connecting a CSSL program to functions supplied by another compiled library.
FFI means foreign function interface. It is the boundary that lets code written in one programming language call a function written in another. You only need the remaining details on this page when building or reviewing CSSL code.
The contract
CSSL keeps this boundary narrow. A source file declares the function it expects with extern "C" fn name(args) -> T ; and the auto-default-link mechanism in csslc resolves the symbol against either cssl-rt (allocator + panic + exit) or one of the host-side staticlibs (loa-host and the per-system cssl-host-* crates).
extern "C" fn __cssl_engine_run() -> i32 ;fn main() -> i32 { let exit_code: i32 = __cssl_engine_run() ; exit_code}Pointer + length convention
CSSL stage-0 does not yet pass Vec<T> across the FFI boundary. When a host symbol needs to expose a buffer, the convention is two adjacent arguments — one raw pointer and one length — both passed as primitive integers. This matches the C ABI exactly and works on every target Cranelift emits today.
// Host signature in Rust:// #[no_mangle]// pub extern "C" fn __cssl_audit_read(out_ptr: *mut u8, out_len: u32) -> u32extern "C" fn __cssl_audit_read(out_ptr: u64, out_len: u32) -> u32 ;fn read_audit(buf_ptr: u64, buf_cap: u32) -> u32 { let written: u32 = __cssl_audit_read(buf_ptr, buf_cap) ; written // 0 on no-data · ≤ buf_cap on success}u64 at the FFI boundary on 64-bit targets. This is verbose but unambiguous. A typed *mut T / *const T surface lands once the type system gains aliasing rules — see the language overview.u32 status-code pattern
Almost every host symbol returns a u32 status. Zero is success; non-zero is a structured error code that the caller can route to a typed error. This avoids exceptions, avoids Result-shaped FFI marshalling, and keeps the audit-trail emission cheap (one u32 to log per call).
| Code | Convention |
|---|---|
0 | OK · no error · result valid |
1..127 | Domain-specific error · stable per host crate |
128..255 | Permission error in the current internal convention |
256.. | Internal error that should be recorded for diagnosis |
Real example · scene FFI surface
From scenes/city_central_hub.csl: a complete scene surface with eleven extern symbols, all returning u32 status, all dispatched against loa-host wired-fn glue.
extern "C" fn scene_open(player_id: u64, world_seed: u128, city_id: u32) -> u32 ;extern "C" fn scene_close(handle: u32) -> u32 ;extern "C" fn scene_procgen_grid(handle: u32, biome_affinity: u32) -> u32 ;extern "C" fn scene_procgen_buildings(handle: u32, density_tier: u32) -> u32 ;extern "C" fn scene_procgen_interiors(handle: u32, enterable_pct: u32) -> u32 ;extern "C" fn scene_procgen_npc_population(handle: u32, target_count: u32) -> u32 ;extern "C" fn scene_kan_classify_template(handle: u32, hist_hash: u64, zone: u32, bias: u64) -> u32 ;extern "C" fn scene_query_npc_count(handle: u32) -> u32 ;extern "C" fn scene_apply_lod(handle: u32, player_x: i32, player_y: i32, player_z: i32) -> u32 ;extern "C" fn scene_audit_emit(handle: u32, event_kind: u32, payload_hash: u64) -> u32 ;extern "C" fn scene_tick(handle: u32, dt_micros: u32) -> u32 ;The auto-default-link mechanism
When csslc emits an object file, the linker invocation automatically prepends the canonical staticlibs found in compiler-rs/target/release/:
libcssl_rt.a· provides__cssl_alloc,__cssl_panic,__cssl_exit, etc.libloa_host.a· provides__cssl_engine_run, scene fns, render fns, MCP server
cssl-host-combat-sim, cssl-host-craft-graph, cssl-host-procgen-city, etc., so the sibling-module FFI surfaces resolve without manual --link flags.Symbol-name conventions
__cssl_*— runtime + engine symbols (cssl-rt, loa-host)scene_*— scene-orchestration symbolsrender.*— MCP-tool route names (dotted, with router lookup)- system tick fns are
__cssl_<system>_tick_<phase>