trona_win32 Overview

The Rust files under lib/trona/win32/ (lib.rs, console.rs, handle.rs, error.rs, process.rs, crt.rs, paths.rs, protocol.rs) are not currently built by any meson target. They exist as design-intent source. The only Win32-related artifact actually produced by the trona build is kernel32.dll, which is compiled directly from the C source kernel32_pe.c — it has no Rust component.

This page documents both the existing Rust source (so future contributors know what is there) and the actual built artifacts (so today’s contributors do not get confused about what runs at runtime).

What is actually built

The single Win32-related artifact in the SaltyOS build is kernel32.dll:

Source Output

lib/trona/win32/kernel32_pe.c (1,503 lines, C)

kernel32.dll — a real PE/COFF DLL produced by clang --target=<arch>-w64-windows-gnu and lld-link -dll. Loaded by PE binaries at runtime via the PE dynamic linker.

kernel32_pe.c is freestanding C: it has no libc, makes its SaltyOS syscalls through inline assembly, and uses the same IpcContext / TronaMsg layout that libtrona.so uses (the structs are defined inline at the top of the file because the C file cannot include Rust headers). kernel32.dll PE Stub documents the C code in detail.

The Rust source — design intent

The 914 lines of Rust under lib/trona/win32/ declare a trona_win32 crate that would be built and linked alongside libtrona.so (or as a separate libtrona_win32.so) if any meson target referenced it. None do. The crate is currently dead source — it compiles cleanly when copied into a sandbox build, but no automated build invokes it.

This is documented here for two reasons:

  1. The doc-comments in the Rust files are the most thorough description of the intended Win32 architecture in the tree. Anyone planning to extend the Win32 personality should read them before writing new code.

  2. The same kernel32_pe.c code that gets built has accreted over time and partially duplicates what the Rust crate would provide. A future contributor consolidating the two needs to know which file owns which feature.

Below is the structure of the Rust crate as it exists today.

lib/trona/win32/lib.rs

//! trona_win32 -- Win32 API compatibility layer for SaltyOS
//!
//! Provides minimal Win32 console API for PE binaries running on SaltyOS.
//! Functions are exported with `#[unsafe(no_mangle)]` so PE import tables
//! can reference them directly.

#![no_std]
#![no_main]
#![feature(linkage)]

extern crate trona;

pub mod console;
pub mod crt;
pub mod error;
pub mod handle;
pub mod protocol;
pub mod paths;
pub mod process;

pub use handle::HANDLE;
pub use protocol::*;

It declares the crate as extern crate trona, which would link it against libtrona.so if it were built. The protocol module is mostly empty (3 lines) and exists as a placeholder for re-exporting the Win32 protocol labels from uapi/protocol/win32.rs.

handle.rs — the handle table

pub type HANDLE = isize;

#[repr(u32)]
pub enum HandleKind {
    Free = 0,
    VfsFd = 1,
    CapSlot = 2,
    ServerObject = 3,
}

pub struct HandleEntry {
    pub kind: HandleKind,
    pub vfs_fd: i32,
    pub cap_slot: u64,
}

The handle table is a fixed-size array of 64 slots. HANDLE values are encoded as (slot * 4) + 4, which is consistent with Windows pseudo-handles like STD_INPUT_HANDLE = -10 being distinct from any real slot index.

init_handle_table() would map slot 0 to VFS fd 0 (stdin), slot 1 to fd 1 (stdout), and slot 2 to fd 2 (stderr) — so GetStdHandle(STD_INPUT_HANDLE) resolves to slot 0 and ultimately to VFS fd 0.

alloc_vfs_fd(fd) would scan from slot 3 onwards for a free slot, allocate it, and return the encoded HANDLE.

None of this code runs today — kernel32_pe.c has its own minimal handle handling that is not derived from this Rust file.

error.rs — error mapping

pub fn trona_to_win32_error(trona_err: u64) -> u32 {
    match trona_err {
        TRONA_OK => ERROR_SUCCESS,
        TRONA_INVALID_CAPABILITY => ERROR_INVALID_HANDLE,
        TRONA_INVALID_OPERATION => ERROR_INVALID_FUNCTION,
        TRONA_INSUFFICIENT_RIGHTS => ERROR_ACCESS_DENIED,
        TRONA_INVALID_ARGUMENT => ERROR_INVALID_PARAMETER,
        TRONA_OUT_OF_MEMORY => ERROR_NOT_ENOUGH_MEMORY,
        TRONA_NOT_FOUND => ERROR_FILE_NOT_FOUND,
        TRONA_BUSY => ERROR_BUSY,
        TRONA_ALREADY_EXISTS => ERROR_ALREADY_EXISTS,
        _ => ERROR_INVALID_FUNCTION,
    }
}

The mapping table from TRONA_* error codes to Win32 ERROR_* codes. GetLastError() and SetLastError(err) would read and write a thread-local last-error value.

kernel32_pe.c reimplements both — its GetLastError reads a __thread DWORD last_error (via the file’s local TLS), and the conversion table is duplicated inline at the function call sites.

console.rs — VFS-direct console I/O

//! Console I/O (WriteConsoleA/W, ReadConsoleA) delegates to the win32_csrss
//! server via IPC, which in turn forwards to the console server.

Despite the doc comment, the actual console.rs Rust source bypasses csrss and writes directly to VFS file descriptors. The doc comment was written for an earlier intended architecture in which csrss owned the console data path; the implementation has since moved console data to the same VFS code path that POSIX uses.

The functions defined are:

  • WriteConsoleA(handle, buf, len, written_out, reserved) — chunks buf into 144-byte pieces and sends VFS_WRITE (3) to the VFS endpoint.

  • WriteConsoleW(handle, buf, len, written_out, reserved) — Unicode variant; converts from UTF-16 LE to UTF-8 inline as it writes to VFS.

  • ReadConsoleA(handle, buf, len, read_out, control_chars) — sends VFS_READ (2) to the VFS endpoint.

  • GetConsoleMode(handle, mode_out) — returns the constant DEFAULT_INPUT_MODE = 0x0007 for input handles and DEFAULT_OUTPUT_MODE = 0x0003 for output handles. Does not consult any external state.

  • SetConsoleMode(handle, mode) — accepts the call and returns success without storing the mode anywhere.

The W32_CONSOLE_WRITE (0x101), W32_CONSOLE_READ (0x102), W32_GET_CONSOLE_MODE (0x103), and W32_SET_CONSOLE_MODE (0x104) protocol labels are defined in uapi/protocol/win32.rs but they are not used by any code currently in the tree. The console.rs Rust source talks directly to VFS, bypassing these labels. The labels are reserved for a future architecture in which csrss takes over the console data path; until then, they only exist as numeric reservations.

process.rs — process operations

Three functions:

  • ExitProcess(exit_code) — sends a best-effort W32_CLIENT_EXIT (0x106, non-blocking) to win32_csrss to notify it the client is exiting, then sends PM_EXIT (blocking) to procmgr. Never returns.

  • GetCurrentProcess() — returns the pseudo-handle -1, matching Windows.

  • GetCurrentProcessId() — sends PM_GETPID to procmgr and returns the result.

crt.rs — CRT-style initialization

crt.rs defines a static __win32srv_ep slot that would cache the win32_csrss endpoint capability after a one-time W32_CLIENT_REGISTER (0x105) round-trip. The setup function would also wire up the basic Win32 environment block (current directory, environment strings, command line) before calling the user’s main.

This is the only place in the trona_win32 Rust source that actually uses one of the W32_* protocol labels (W32_CLIENT_REGISTER). Plus the use in process.rs of W32_CLIENT_EXIT, those two are the only W32_* labels currently exercised by any code in the tree.

paths.rs

paths.rs is 7 lines — basically a placeholder for path canonicalization (Windows backslash → forward slash, drive letter handling, \\?\ prefix stripping). None of the code is wired in.

Why is this code unbuilt?

The most likely explanation is iterative development: the Rust crate was scaffolded as a planned design but the immediate goal — getting a single C-language kernel32.dll running so a basic PE binary could spawn — was easier to satisfy by writing a self-contained kernel32_pe.c from scratch. Once that worked, no one went back to wire up the Rust crate.

A future cleanup pass would either:

  1. Build the Rust crate. Add a trona_win32 custom_target to lib/trona/meson.build, link it into a separate libtrona_win32.so, and rewrite kernel32_pe.c so its functions delegate to the Rust crate via function calls. This would make kernel32.dll a thin C shim over Rust, matching how libc.so is a thin C shim over Rust in basaltc.

  2. Delete the Rust crate. Remove lib/trona/win32/*.rs entirely and accept that all Win32 functionality lives in kernel32_pe.c.

Either decision would resolve the current "two parallel implementations, one of which is dead code" situation. Until that happens, the Rust files are reference material for design intent, not active code.

What does run today

For the actual runtime behavior — WriteConsoleA actually writing to the serial console, CreateFileA opening files, ExitProcess actually killing a PE binary — the answer is always kernel32_pe.c. Every Windows API a PE binary on SaltyOS calls is implemented in that single C file, and the implementation talks directly to substrate endpoints via inline syscalls.

kernel32.dll PE Stub is the page that documents what kernel32_pe.c actually exports and how each function works.