kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

env.h (24195B)


      1 #ifndef KIT_DRIVER_ENV_H
      2 #define KIT_DRIVER_ENV_H
      3 
      4 #include <kit/compile.h>
      5 #include <kit/core.h>
      6 #include <kit/dbg.h>
      7 #include <kit/exec.h>
      8 #include <kit/jit.h>
      9 #include <kit/os.h>
     10 #include <stdarg.h>
     11 #include <stddef.h>
     12 #include <stdint.h>
     13 
     14 /* Shared host environment used by every tool that calls into libkit.
     15  * driver_env_init wires up the libc-backed heap, the stderr diag sink, and
     16  * a POSIX file_io implementation (open/read/write on real paths). It is
     17  * the single piece of glue that turns "the host" into a KitContext.
     18  *
     19  * The execmem / dbg_os vtables that used to live on KitEnv now live on
     20  * KitJitHost / KitDbgHost. They are still constructed here so that one
     21  * DriverEnv covers every libkit-using tool, but they're handed to libkit
     22  * per-call via the appropriate host struct. */
     23 typedef struct DriverEnv {
     24   KitHeap* heap;
     25   KitDiagSink* diag;
     26   KitFileIO file_io;
     27   const KitExecMem* execmem;
     28   const KitDbgOs* dbg_os; /* NULL unless `kit dbg` paths run */
     29   KitProfiler* profiler;  /* optional low-overhead profiling storage */
     30   int64_t now;            /* unix seconds; -1 = unknown */
     31   const char* cache_dir;  /* base cache dir, e.g. ~/.cache/kit */
     32 } DriverEnv;
     33 
     34 void driver_env_init(DriverEnv*);
     35 void driver_env_fini(DriverEnv*);
     36 
     37 /* Build a KitContext value pointing at the DriverEnv's heap/diag/file_io
     38  * vtables. The returned value can be passed by const-pointer to any
     39  * libkit entry that takes `const KitContext *`. */
     40 KitContext driver_env_to_context(const DriverEnv*);
     41 
     42 /* Build a KitJitHost from the DriverEnv (execmem). The returned struct
     43  * holds a borrowed pointer to the vtable owned by g_execmem_posix;
     44  * callers must not outlive driver_env_fini. */
     45 KitJitHost driver_env_to_jit_host(const DriverEnv*);
     46 
     47 /* Build a KitDbgHost from the DriverEnv (dbg_os). */
     48 KitDbgHost driver_env_to_dbg_host(const DriverEnv*);
     49 
     50 /* Tells the stderr diag sink which compiler to use when resolving
     51  * SrcLoc.file_id to a path. The driver_compiler_{new,free} helpers
     52  * below already manage this; call this directly only when you hand a
     53  * KitCompiler from outside those helpers (none today). */
     54 void driver_diag_set_compiler(KitCompiler*);
     55 
     56 /* Lifecycle helpers around kit_compiler_{new,free}. Identical to the raw
     57  * entries except they register the active compiler with the stderr diag sink
     58  * so diagnostics resolve loc.file_id to its registered path. The compiler
     59  * borrows `target`; callers own it and must free it after driver_compiler_free.
     60  * Returns KIT_OK on success; on failure *out is NULL. */
     61 KitStatus driver_compiler_new(const KitTarget*, const KitContext*,
     62                               KitCompiler** out);
     63 void driver_compiler_free(KitCompiler*);
     64 
     65 /* Driver-level post-compile diagnostic gate, enforcing the warning policies the
     66  * C frontend does not yet honor from KitDiagnosticOptions. Call once after a
     67  * compile/link run completes. When `warnings_are_errors` is set and libkit's
     68  * diag sink recorded any warnings, emits an error and returns nonzero so the
     69  * caller can fail the tool (-Werror). When `max_errors` is nonzero, emits a
     70  * single note that -fmax-errors is unimplemented (the frontend does not bound
     71  * the error count). Returns 0 when the run should be considered successful. */
     72 int driver_diag_finish(DriverEnv* env, const char* tool,
     73                        int warnings_are_errors, uint32_t max_errors);
     74 
     75 /* Default target used by tools that don't expose a target-selection flag
     76  * yet. v1: native-looking host target (chosen at compile time). */
     77 KitTargetSpec driver_host_target(void);
     78 
     79 /* ----------------------------------------------------------------------
     80  * Hosted-libc search directories
     81  *
     82  * The hosted-libc resolver needs two sets of directories: include roots to add
     83  * as system header search paths, and library roots to search for the C runtime
     84  * objects and libc. They come from one of two producers -- expanding an
     85  * explicit --sysroot/KIT_SYSROOT (portable, in driver/lib/hosted.c) or probing
     86  * the live host (driver_default_hosted_dirs, below). The resolver copies what
     87  * it needs into its plan, then releases the whole set with
     88  * driver_hosted_dirs_fini; the strings stored here are transient scratch.
     89  *
     90  * DRIVER_HOSTED_MAX_INCDIRS must stay >= the plan's DRIVER_HOSTED_MAX_INCLUDES
     91  * (driver/lib/hosted.h): every emitted incdir becomes one owned plan include.
     92  * ---------------------------------------------------------------------- */
     93 #define DRIVER_HOSTED_MAX_INCDIRS KIT_OS_HOSTED_MAX_INCDIRS
     94 #define DRIVER_HOSTED_MAX_LIBDIRS KIT_OS_HOSTED_MAX_LIBDIRS
     95 
     96 typedef KitOsHostedDirs DriverHostedDirs;
     97 
     98 /* Append an include/library directory, heap-duplicated from `dir`. The _join
     99  * forms join `base` + `sub` with a single '/' separator first. A NULL/empty dir
    100  * is a successful no-op; returns nonzero on allocation failure or when the list
    101  * is full (loud overflow -- never a silent drop). dirs->env must be set. */
    102 int driver_hosted_dirs_add_inc(DriverHostedDirs* dirs, const char* dir);
    103 int driver_hosted_dirs_add_lib(DriverHostedDirs* dirs, const char* dir);
    104 int driver_hosted_dirs_add_inc_join(DriverHostedDirs* dirs, const char* base,
    105                                     const char* sub);
    106 int driver_hosted_dirs_add_lib_join(DriverHostedDirs* dirs, const char* base,
    107                                     const char* sub);
    108 /* Free all stored dir strings and zero the lists. Idempotent. */
    109 void driver_hosted_dirs_fini(DriverHostedDirs* dirs);
    110 
    111 /* Probe the live host for hosted include/library dirs for `target`, used when
    112  * no --sysroot/KIT_SYSROOT was given. Only the host's own platform is probed
    113  * (the caller additionally gates on target-OS == host-OS) -- never for cross-
    114  * compiles. Fills `out`, which the caller zero-inits with out->env set. Returns
    115  * 0 when it produced at least one library dir, nonzero otherwise (out left
    116  * empty). macOS resolves the SDK (the canonical Command Line Tools / Xcode
    117  * roots; no subprocess, no env var) into <sdk>/usr/{include,lib}; Linux
    118  * enumerates the multiarch dirs; FreeBSD the base dirs; Windows produces
    119  * nothing (its MinGW sysroot comes from --sysroot/KIT_SYSROOT in the cc
    120  * driver). The single env-var override, KIT_SYSROOT, is consulted by the
    121  * caller, not here. */
    122 int driver_default_hosted_dirs(DriverEnv* env, KitTargetSpec target,
    123                                DriverHostedDirs* out);
    124 
    125 /* ----------------------------------------------------------------------
    126  * Host-shim helpers
    127  *
    128  * driver/env.c is the only TU in the driver allowed to depend on libc
    129  * facilities that issue syscalls or touch host state -- host stdio, malloc,
    130  * POSIX I/O, environment, time. Other driver TUs are compiled freestanding
    131  * against rt/include plus libkit's public headers. The shims below exist
    132  * primarily for the syscall-shaped surface; other TUs may also call them for
    133  * consistency.
    134  * ---------------------------------------------------------------------- */
    135 
    136 /* String predicates and lookups. driver_streq / driver_strneq return
    137  * non-zero when the strings (or first n bytes) match -- sense-flipped
    138  * from libc's strcmp so call sites read naturally. */
    139 int driver_streq(const char* a, const char* b);
    140 int driver_strneq(const char* a, const char* b, size_t n);
    141 size_t driver_strlen(const char* s);
    142 const char* driver_strchr(const char* s, int c);
    143 const char* driver_basename(const char* path);
    144 int driver_has_suffix(const char* s, const char* suffix);
    145 
    146 /* Memory. Allocations route through DriverEnv.heap; release returns the
    147  * size used at allocation (so the heap implementation can track usage). */
    148 void* driver_alloc(DriverEnv*, size_t);
    149 void* driver_alloc_zeroed(DriverEnv*, size_t);
    150 void driver_free(DriverEnv*, void* p, size_t);
    151 void driver_memcpy(void* dst, const void* src, size_t n);
    152 
    153 /* Opens a Writer that writes to stdout. close frees the struct but does
    154  * not close stdout. */
    155 KitWriter* driver_stdout_writer(DriverEnv*);
    156 
    157 /* Opens a Writer that writes to stderr. close frees the struct but does
    158  * not close stderr. */
    159 KitWriter* driver_stderr_writer(DriverEnv*);
    160 
    161 /* Test whether `path` names an existing filesystem entry (any type).
    162  * Returns nonzero on existence, zero otherwise. Used by library-path
    163  * resolution; intentionally distinct from read_all so candidate-search
    164  * loops don't slurp file contents on a hit. */
    165 int driver_path_exists(const char* path);
    166 
    167 /* Read a path's last modification time in nanoseconds since the Unix epoch.
    168  * Returns 0 on success, nonzero on stat failure. */
    169 int driver_path_mtime_ns(const char* path, int64_t* out);
    170 
    171 /* Stat a host path. Fills *out_size, *out_mtime_ns, and *out_filetype using
    172  * the KIT_WASM_FILETYPE_* constants (0=unknown 1=block 2=char 3=dir
    173  * 4=regular 5=dgram 6=stream 7=symlink). Returns 0 on success, 1 if the
    174  * path does not exist, 2 on any other error. Does not require a DriverEnv. */
    175 int driver_path_stat(const char* path, uint64_t* out_size,
    176                      uint64_t* out_mtime_ns, uint8_t* out_filetype);
    177 
    178 /* Like driver_path_stat but does NOT follow a terminal symlink (lstat) and
    179  * additionally reports whether the entry is an executable regular file (any
    180  * execute bit set). *out_executable is 0 for non-regular entries and on hosts
    181  * without an executable bit (Windows). Fills *out_size and *out_filetype with
    182  * the same KIT_WASM_FILETYPE_* coding as driver_path_stat. Returns 0 on
    183  * success, 1 if the path does not exist, 2 on any other error. Used by
    184  * `cpio -o` to classify operands without dereferencing symlinks and to carry
    185  * the source executable bit into the archived mode. */
    186 int driver_path_lstat(const char* path, uint64_t* out_size,
    187                       uint8_t* out_filetype, int* out_executable);
    188 
    189 /* Opaque directory-enumeration handle. Holds a snapshot of all entries
    190  * (excluding "." and "..") taken at driver_open_dir time. */
    191 typedef struct DriverDirHandle DriverDirHandle;
    192 
    193 /* Open a directory and snapshot its entries. Returns NULL on failure. */
    194 DriverDirHandle* driver_open_dir(DriverEnv*, const char* path);
    195 
    196 /* Read the entry at zero-based index. Sets *out_name to a pointer borrowed
    197  * from the handle (valid until driver_close_dir), *out_name_len to its byte
    198  * length (not NUL-terminated), and fills *out_ino, *out_size, *out_mtime_ns,
    199  * *out_filetype. Returns 0 on success, 1 when index >= entry count. */
    200 int driver_read_dir_entry(DriverDirHandle*, uint64_t index,
    201                           const char** out_name, uint32_t* out_name_len,
    202                           uint64_t* out_ino, uint64_t* out_size,
    203                           uint64_t* out_mtime_ns, uint8_t* out_filetype);
    204 
    205 /* Free a directory handle. Safe to call with NULL. */
    206 void driver_close_dir(DriverEnv*, DriverDirHandle*);
    207 
    208 /* Create a directory and any missing parents. Returns 0 on success. */
    209 int driver_mkdir_p(DriverEnv*, const char* path);
    210 
    211 /* Resolve the absolute path of the running kit executable into a freshly
    212  * heap-allocated, NUL-terminated buffer (*out / *out_size); free it with
    213  * driver_free(env, *out, *out_size). Returns 0 on success, nonzero on
    214  * failure (out untouched). Per-OS: /proc/self/exe (Linux), _NSGetExecutablePath
    215  * + realpath (macOS), KERN_PROC_PATHNAME sysctl (FreeBSD), GetModuleFileNameW
    216  * (Windows). Used by `install` to point freshly created links at the binary. */
    217 int driver_self_exe_path(DriverEnv*, char** out, size_t* out_size);
    218 
    219 /* Resolve an existing host path to a normalized absolute path in a fresh
    220  * DriverEnv allocation. POSIX uses realpath; Windows uses GetFullPathNameW.
    221  * Returns 0 on success, nonzero for an invalid/missing path or allocation
    222  * failure. This is the normalization boundary for support/sysroot discovery;
    223  * callers free *out with driver_free(env, *out, *out_size). */
    224 int driver_path_canonicalize(DriverEnv*, const char* path, char** out,
    225                              size_t* out_size);
    226 
    227 /* Create a symbolic link named `link_path` that resolves to `target`. Returns
    228  * 0 on success. POSIX uses symlink(2); Windows uses CreateSymbolicLinkW, which
    229  * may require privilege or Developer Mode (so `install` defaults to hard links
    230  * on Windows). */
    231 int driver_create_symlink(const char* target, const char* link_path);
    232 
    233 /* Read the target of the symbolic link at `path` into `buf` (capacity `cap`,
    234  * including room for the terminating NUL), NUL-terminating it. Returns 0 on
    235  * success, nonzero on failure or when the target does not fit in `cap`. POSIX
    236  * uses readlink(2); Windows is best-effort and may always fail (initramfs
    237  * symlinks are a Linux artifact), in which case the caller diagnoses. */
    238 int driver_readlink(const char* path, char* buf, size_t cap);
    239 
    240 /* Create a hard link named `link_path` referring to the same file as `target`.
    241  * Returns 0 on success. POSIX uses link(2); Windows uses CreateHardLinkW. Both
    242  * require `target` and `link_path` to live on the same filesystem/volume. */
    243 int driver_create_hardlink(const char* target, const char* link_path);
    244 
    245 /* Mark a DriverEnv-created writer as failed before close. Atomic file writers
    246  * will remove their temp file and skip the final rename. Borrowed stdio writers
    247  * just record a failed status; already-written stdout/stderr bytes cannot be
    248  * recalled. */
    249 void driver_writer_abort(KitWriter* writer);
    250 
    251 /* Remove the file or symlink at `path`. Returns 0 when the entry was removed or
    252  * was already absent, nonzero on any other failure. POSIX unlink(2) / Windows
    253  * DeleteFileW. */
    254 int driver_remove_file(const char* path);
    255 
    256 /* Test whether a name exists at `path` WITHOUT following symlinks, so a
    257  * dangling symlink still counts as existing. Returns nonzero on existence.
    258  * POSIX lstat / Windows GetFileAttributesW. */
    259 int driver_path_lexists(const char* path);
    260 
    261 /* Rename/move `from` to `to`, replacing any existing entry at `to`. Within one
    262  * filesystem this is atomic, which `kit update` relies on for the version dir
    263  * move and the `current` pointer flip. Returns 0 on success, nonzero on
    264  * failure. POSIX rename(2); Windows MoveFileExW(MOVEFILE_REPLACE_EXISTING). */
    265 int driver_rename(const char* from, const char* to);
    266 
    267 /* Recursively remove `path` and everything beneath it (a file, symlink, or
    268  * directory tree). Used by `kit update` to clear a scratch unpack dir, replace
    269  * a reinstalled version, and prune old versions. Does NOT follow symlinks (it
    270  * unlinks the link itself). Returns 0 on success or when `path` is already
    271  * absent, nonzero on any other failure. */
    272 int driver_remove_tree(const char* path);
    273 
    274 /* Resolve the single-root kit home directory into `buf` (capacity `cap`,
    275  * NUL-terminated, no trailing slash): `$KIT_HOME`, else `$XDG_DATA_HOME/kit`,
    276  * else `$HOME/.local/share/kit` on POSIX (`%LOCALAPPDATA%\kit` on Windows).
    277  * Used by `kit update` for versions/, current, bin/, config/, cache/. Returns 0
    278  * on success, nonzero when no home could be determined or it does not fit. */
    279 int driver_kit_home(char* buf, size_t cap);
    280 
    281 /* Fetch `url` to the local file `dest`, overwriting it. The transport is
    282  * UNTRUSTED — the caller verifies a signature/content-id over the bytes. file://
    283  * URLs are copied internally; other schemes use curl on POSIX or curl.exe on
    284  * Windows, with the URL passed as a distinct argv element (no shell and no
    285  * downloader fallback). Returns 0 on success, nonzero on any failure. */
    286 int driver_fetch_url(const char* url, const char* dest);
    287 
    288 /* Set a linked binary output's final mode according to the active umask.
    289  * Returns 0 on success, nonzero on chmod failure. */
    290 int driver_mark_executable_output(const char* path);
    291 
    292 /* Capture and restore the permission mode of a regular output. POSIX carries
    293  * all permission/special bits accepted by chmod; Windows returns an opaque
    294  * zero mode and treats restoration as a successful no-op because execution is
    295  * extension/ACL based there. These helpers let transactional rewrites preserve
    296  * an input's mode even though the atomic writer replaces its inode. */
    297 int driver_path_mode_get(const char* path, uint32_t* mode_out);
    298 int driver_path_mode_set(const char* path, uint32_t mode);
    299 
    300 /* Walk regular files below `root`, reporting tree-relative paths with '/'
    301  * separators. The callback returns nonzero to abort the walk. Unsupported
    302  * filesystem entries cause a nonzero return from the walk helper. */
    303 typedef int (*DriverWalkFileFn)(void* user, const char* source_path,
    304                                 const char* tree_path, int executable);
    305 int driver_walk_regular_files(DriverEnv*, const char* root, DriverWalkFileFn,
    306                               void* user);
    307 
    308 /* Diagnostic printing to host stderr. Format is `"<tool>: <fmt>\n"`. */
    309 void driver_errf(const char* tool, const char* fmt, ...);
    310 void driver_verrf(const char* tool, const char* fmt, va_list ap);
    311 
    312 /* Raw hosted stderr log. Used by optional metrics/profiling output so libkit
    313  * stays free of hosted I/O. */
    314 void driver_logf(const char* fmt, ...);
    315 
    316 /* Formatted output to stdout. */
    317 void driver_printf(const char* fmt, ...);
    318 
    319 /* Monotonic host time in nanoseconds, or 0 if unavailable. */
    320 uint64_t driver_now_ns(void);
    321 
    322 /* Fill `out` with `n` cryptographically-random bytes from the host CSPRNG.
    323  * Returns 0 on success, non-zero on failure (in which case `out` must not be
    324  * used). This is the single entropy source for key generation; the crypto
    325  * primitives themselves never source randomness — it always flows in here. */
    326 int driver_random_bytes(uint8_t* out, size_t n);
    327 
    328 /* Lookup a process environment variable; returns NULL if unset. The returned
    329  * pointer aliases libc-owned storage and is valid until the next setenv/
    330  * putenv from any caller. */
    331 const char* driver_getenv(const char* name);
    332 
    333 /* Borrow the process environment as NAME=VALUE strings. The array and strings
    334  * are libc-owned and remain valid until the process environment is mutated. */
    335 const char* const* driver_environ(void);
    336 
    337 /* Build a KitExec (<kit/exec.h>) backed by this DriverEnv's heap: the shared
    338  * config-struct subprocess interface used by the build coordinator and `kit
    339  * make`. The returned value borrows `env` (its `user`); do not outlive env.
    340  * Implemented in driver/env/exec_{posix,windows}.c. */
    341 KitExec driver_exec(DriverEnv* env);
    342 /* Set path's mtime to now, creating it empty if absent (make -t). 0 on success. */
    343 int driver_touch(const char* path);
    344 
    345 /* Read all of stdin into a freshly-allocated buffer. On success returns 1
    346  * and stores the buffer/size in out_data/out_size; the caller frees via
    347  * driver_free(env, *out_data, *out_size). Returns 0 on read failure or
    348  * allocation failure. */
    349 int driver_read_stdin(DriverEnv*, uint8_t** out_data, size_t* out_size);
    350 
    351 /* Open a temporary file in $VISUAL, then $EDITOR, then vi. `suffix` should
    352  * include the leading dot when a language-specific extension is useful.
    353  * On success, returns the edited bytes in a freshly allocated buffer that the
    354  * caller frees with driver_free(env, *out_data, *out_size). */
    355 int driver_edit_temp(DriverEnv*, const char* suffix, const uint8_t* initial,
    356                      size_t initial_size, uint8_t** out_data, size_t* out_size);
    357 
    358 /* Path-shaped input loader. Wraps env.file_io.read_all so each tool can
    359  * convert a list of paths to a list of KitSlice without re-implementing
    360  * load/release/error bookkeeping. `loaded` is set to 1 on success; release is
    361  * idempotent and does nothing when loaded is already 0. driver_load_bytes
    362  * fills `in.name = path` plus the loaded data/len; driver_release_bytes hands
    363  * the buffer back through file_io.release. On failure an error is emitted via
    364  * driver_errf using the supplied tool tag. */
    365 typedef struct DriverLoad {
    366   KitFileData fd;
    367   int loaded;
    368 } DriverLoad;
    369 
    370 int driver_load_bytes(const KitFileIO*, const char* tool, const char* path,
    371                       DriverLoad* out, KitSlice* in);
    372 void driver_release_bytes(const KitFileIO*, DriverLoad*);
    373 
    374 /* Read one line from stdin into `buf` (cap >= 2). Strips the trailing
    375  * newline and NUL-terminates. Returns the line length on success, 0 at
    376  * EOF (with buf[0]='\0'), -1 on read error, or -2 when the read was
    377  * interrupted by a signal (caller should print a fresh prompt and
    378  * retry). Over-long lines are truncated to cap-1 bytes; the remainder
    379  * up to the next newline is consumed silently. */
    380 int driver_read_line(char* buf, size_t cap);
    381 
    382 typedef struct DriverLineHistory {
    383   char** items;
    384   size_t* sizes;
    385   uint32_t count;
    386   uint32_t cap;
    387 } DriverLineHistory;
    388 
    389 typedef struct DriverLineCompletion {
    390   char* text;
    391   size_t size;
    392 } DriverLineCompletion;
    393 
    394 typedef struct DriverLineCompletionList {
    395   DriverEnv* env;
    396   DriverLineCompletion* items;
    397   uint32_t count;
    398   uint32_t cap;
    399   size_t replace_start;
    400   size_t replace_end;
    401 } DriverLineCompletionList;
    402 
    403 typedef void (*DriverLineCompleteFn)(void* user, const char* line,
    404                                      size_t cursor,
    405                                      DriverLineCompletionList* out);
    406 
    407 int driver_line_completion_add(DriverLineCompletionList*, const char* text,
    408                                size_t len);
    409 /* Raw-mode line editor with history + tab completion. Ctrl-G drops the
    410  * currently typed line into $EDITOR (seeded via driver_edit_temp using
    411  * `edit_suffix` for the temp file's extension) and reloads the edited text
    412  * back into the buffer, so the prompt is populated with what was typed.
    413  * `edit_suffix` may be NULL to use an extension-less temp file; it is ignored
    414  * on hosts whose line editor reads in cooked mode (no key interception). */
    415 int driver_read_line_edit(DriverEnv*, const char* prompt, char* buf, size_t cap,
    416                           DriverLineHistory*, DriverLineCompleteFn,
    417                           void* complete_user, const char* edit_suffix);
    418 void driver_line_history_fini(DriverEnv*, DriverLineHistory*);
    419 
    420 /* Flush the host stdout. The dbg REPL prompt has no trailing newline, so
    421  * without an explicit flush the prompt stays buffered until the next
    422  * line of output. */
    423 void driver_flush_stdout(void);
    424 
    425 /* Install / restore a SIGINT handler. While installed, SIGINT runs `cb(user)`
    426  * synchronously (so `cb` must be async-signal safe). Used by `dbg` to
    427  * forward Ctrl-C into kit_dbg_session_interrupt while the worker is
    428  * running, and to restore SIG_DFL while sitting at the REPL prompt so
    429  * Ctrl-C terminates the program normally. Returns 0 on success. */
    430 int driver_install_sigint(void (*cb)(void*), void* user);
    431 void driver_restore_sigint(void);
    432 
    433 /* Crash-guarded execution for `kit run`.
    434  *
    435  * `entry` is the JITed program entry, invoked as `entry(argc, argv)`. On a
    436  * clean return, *ret_out receives the program's status and the call returns 0.
    437  *
    438  * On a fatal fault raised by the program (SIGSEGV/SIGBUS/SIGILL/SIGFPE/SIGABRT/
    439  * SIGTRAP — the last covers __builtin_trap / failed asserts), the guard walks
    440  * the frame-pointer chain *inside the signal handler* (where the faulting stack
    441  * is still intact, since `kit run` shares its stack with the program), captures
    442  * the return addresses innermost-first, and invokes `on_crash(user, signo, pcs,
    443  * npcs)` from normal context — so symbolization (DWARF/malloc/printf) never
    444  * runs async-signal. `arch` selects the FP register / pointer width for the
    445  * walk. The call then returns 1; the program's own return value is undefined on
    446  * this path, so the caller should treat the run as failed.
    447  *
    448  * Hosts without a fault guard (Windows) run `entry` directly, set *ret_out, and
    449  * return 0 without ever calling on_crash. */
    450 typedef int (*DriverRunEntryFn)(int argc, char** argv);
    451 typedef void (*DriverRunCrashFn)(void* user, int signo, const uint64_t* pcs,
    452                                  int npcs);
    453 int driver_run_with_crash_guard(DriverEnv* env, KitArchKind arch,
    454                                 DriverRunEntryFn entry, int argc, char** argv,
    455                                 int* ret_out, DriverRunCrashFn on_crash,
    456                                 void* user);
    457 
    458 /* Host-symbol resolver for JIT extern_resolver. Looks up `name` via
    459  * dlsym(RTLD_DEFAULT, ...) on POSIX hosts, returning NULL on miss. Stateless;
    460  * `user` is ignored and may be NULL. Wired into `kit run` so JITed code
    461  * can call libc symbols (printf, malloc, ...) without an explicit linker
    462  * step. */
    463 void* driver_dlsym_resolver(void* user, KitSlice name);
    464 
    465 #endif