kit

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

env.h (18898B)


      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/jit.h>
      8 #include <stddef.h>
      9 #include <stdint.h>
     10 
     11 /* Shared host environment used by every tool that calls into libkit.
     12  * driver_env_init wires up the libc-backed heap, the stderr diag sink, and
     13  * a POSIX file_io implementation (open/read/write on real paths). It is
     14  * the single piece of glue that turns "the host" into a KitContext.
     15  *
     16  * The execmem / dbg_os vtables that used to live on KitEnv now live on
     17  * KitJitHost / KitDbgHost. They are still constructed here so that one
     18  * DriverEnv covers every libkit-using tool, but they're handed to libkit
     19  * per-call via the appropriate host struct. */
     20 typedef struct DriverEnv {
     21   KitHeap* heap;
     22   KitDiagSink* diag;
     23   KitFileIO file_io;
     24   const KitExecMem* execmem;
     25   const KitDbgOs* dbg_os;    /* NULL unless `kit dbg` paths run */
     26   const KitMetrics* metrics; /* optional scoped metrics sink */
     27   int64_t now;               /* unix seconds; -1 = unknown */
     28   const char* cache_dir;     /* base cache dir, e.g. ~/.cache/kit */
     29 } DriverEnv;
     30 
     31 void driver_env_init(DriverEnv*);
     32 void driver_env_fini(DriverEnv*);
     33 
     34 /* Build a KitContext value pointing at the DriverEnv's heap/diag/file_io
     35  * vtables. The returned value can be passed by const-pointer to any
     36  * libkit entry that takes `const KitContext *`. */
     37 KitContext driver_env_to_context(const DriverEnv*);
     38 
     39 /* Build a KitJitHost from the DriverEnv (execmem). The returned struct
     40  * holds a borrowed pointer to the vtable owned by g_execmem_posix;
     41  * callers must not outlive driver_env_fini. */
     42 KitJitHost driver_env_to_jit_host(const DriverEnv*);
     43 
     44 /* Build a KitDbgHost from the DriverEnv (dbg_os). */
     45 KitDbgHost driver_env_to_dbg_host(const DriverEnv*);
     46 
     47 /* Tells the stderr diag sink which compiler to use when resolving
     48  * SrcLoc.file_id to a path. The driver_compiler_{new,free} helpers
     49  * below already manage this; call this directly only when you hand a
     50  * KitCompiler from outside those helpers (none today). */
     51 void driver_diag_set_compiler(KitCompiler*);
     52 
     53 /* Lifecycle helpers around kit_compiler_{new,free}. Identical to the raw
     54  * entries except they register the active compiler with the stderr diag sink
     55  * so diagnostics resolve loc.file_id to its registered path. The compiler
     56  * borrows `target`; callers own it and must free it after driver_compiler_free.
     57  * Returns KIT_OK on success; on failure *out is NULL. */
     58 KitStatus driver_compiler_new(const KitTarget*, const KitContext*,
     59                               KitCompiler** out);
     60 void driver_compiler_free(KitCompiler*);
     61 
     62 /* Default target used by tools that don't expose a target-selection flag
     63  * yet. v1: native-looking host target (chosen at compile time). */
     64 KitTargetSpec driver_host_target(void);
     65 
     66 /* ----------------------------------------------------------------------
     67  * Hosted-libc search directories
     68  *
     69  * The hosted-libc resolver needs two sets of directories: include roots to add
     70  * as system header search paths, and library roots to search for the C runtime
     71  * objects and libc. They come from one of two producers -- expanding an
     72  * explicit --sysroot/KIT_SYSROOT (portable, in driver/lib/hosted.c) or probing
     73  * the live host (driver_default_hosted_dirs, below). The resolver copies what
     74  * it needs into its plan, then releases the whole set with
     75  * driver_hosted_dirs_fini; the strings stored here are transient scratch.
     76  *
     77  * DRIVER_HOSTED_MAX_INCDIRS must stay >= the plan's DRIVER_HOSTED_MAX_INCLUDES
     78  * (driver/lib/hosted.h): every emitted incdir becomes one owned plan include.
     79  * ---------------------------------------------------------------------- */
     80 #define DRIVER_HOSTED_MAX_INCDIRS 4
     81 #define DRIVER_HOSTED_MAX_LIBDIRS 8
     82 
     83 typedef struct DriverHostedDirs {
     84   DriverEnv* env;
     85   /* The single sysroot root these dirs came from (an explicit --sysroot/
     86    * KIT_SYSROOT, or a host probe that resolves to one tree, e.g. the macOS
     87    * SDK). NULL when the dirs come from a multi-dir source with no single root
     88    * (the Linux/FreeBSD live-system probe). Borrowed; not freed by fini. */
     89   const char* root;
     90   char* incdirs[DRIVER_HOSTED_MAX_INCDIRS];
     91   size_t incdir_sizes[DRIVER_HOSTED_MAX_INCDIRS];
     92   uint32_t nincdirs;
     93   char* libdirs[DRIVER_HOSTED_MAX_LIBDIRS];
     94   size_t libdir_sizes[DRIVER_HOSTED_MAX_LIBDIRS];
     95   uint32_t nlibdirs;
     96 } 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 /* Opaque directory-enumeration handle. Holds a snapshot of all entries
    179  * (excluding "." and "..") taken at driver_open_dir time. */
    180 typedef struct DriverDirHandle DriverDirHandle;
    181 
    182 /* Open a directory and snapshot its entries. Returns NULL on failure. */
    183 DriverDirHandle* driver_open_dir(DriverEnv*, const char* path);
    184 
    185 /* Read the entry at zero-based index. Sets *out_name to a pointer borrowed
    186  * from the handle (valid until driver_close_dir), *out_name_len to its byte
    187  * length (not NUL-terminated), and fills *out_ino, *out_size, *out_mtime_ns,
    188  * *out_filetype. Returns 0 on success, 1 when index >= entry count. */
    189 int driver_read_dir_entry(DriverDirHandle*, uint64_t index,
    190                           const char** out_name, uint32_t* out_name_len,
    191                           uint64_t* out_ino, uint64_t* out_size,
    192                           uint64_t* out_mtime_ns, uint8_t* out_filetype);
    193 
    194 /* Free a directory handle. Safe to call with NULL. */
    195 void driver_close_dir(DriverEnv*, DriverDirHandle*);
    196 
    197 /* Create a directory and any missing parents. Returns 0 on success. */
    198 int driver_mkdir_p(DriverEnv*, const char* path);
    199 
    200 /* Resolve the absolute path of the running kit executable into a freshly
    201  * heap-allocated, NUL-terminated buffer (*out / *out_size); free it with
    202  * driver_free(env, *out, *out_size). Returns 0 on success, nonzero on
    203  * failure (out untouched). Per-OS: /proc/self/exe (Linux), _NSGetExecutablePath
    204  * + realpath (macOS), KERN_PROC_PATHNAME sysctl (FreeBSD), GetModuleFileNameW
    205  * (Windows). Used by `install` to point freshly created links at the binary. */
    206 int driver_self_exe_path(DriverEnv*, char** out, size_t* out_size);
    207 
    208 /* Create a symbolic link named `link_path` that resolves to `target`. Returns
    209  * 0 on success. POSIX uses symlink(2); Windows uses CreateSymbolicLinkW, which
    210  * may require privilege or Developer Mode (so `install` defaults to hard links
    211  * on Windows). */
    212 int driver_create_symlink(const char* target, const char* link_path);
    213 
    214 /* Create a hard link named `link_path` referring to the same file as `target`.
    215  * Returns 0 on success. POSIX uses link(2); Windows uses CreateHardLinkW. Both
    216  * require `target` and `link_path` to live on the same filesystem/volume. */
    217 int driver_create_hardlink(const char* target, const char* link_path);
    218 
    219 /* Remove the file or symlink at `path`. Returns 0 when the entry was removed or
    220  * was already absent, nonzero on any other failure. POSIX unlink(2) / Windows
    221  * DeleteFileW. */
    222 int driver_remove_file(const char* path);
    223 
    224 /* Test whether a name exists at `path` WITHOUT following symlinks, so a
    225  * dangling symlink still counts as existing. Returns nonzero on existence.
    226  * POSIX lstat / Windows GetFileAttributesW. */
    227 int driver_path_lexists(const char* path);
    228 
    229 /* Set a linked binary output's final mode according to the active umask.
    230  * Returns 0 on success, nonzero on chmod failure. */
    231 int driver_mark_executable_output(const char* path);
    232 
    233 /* Walk regular files below `root`, reporting tree-relative paths with '/'
    234  * separators. The callback returns nonzero to abort the walk. Unsupported
    235  * filesystem entries cause a nonzero return from the walk helper. */
    236 typedef int (*DriverWalkFileFn)(void* user, const char* source_path,
    237                                 const char* tree_path, int executable);
    238 int driver_walk_regular_files(DriverEnv*, const char* root, DriverWalkFileFn,
    239                               void* user);
    240 
    241 /* Diagnostic printing to host stderr. Format is `"<tool>: <fmt>\n"`. */
    242 void driver_errf(const char* tool, const char* fmt, ...);
    243 
    244 /* Raw hosted stderr log. Used by optional metrics so libkit stays callback-
    245  * only and freestanding. */
    246 void driver_logf(const char* fmt, ...);
    247 
    248 /* Formatted output to stdout. */
    249 void driver_printf(const char* fmt, ...);
    250 
    251 /* Monotonic host time in nanoseconds, or 0 if unavailable. */
    252 uint64_t driver_now_ns(void);
    253 
    254 /* Fill `out` with `n` cryptographically-random bytes from the host CSPRNG.
    255  * Returns 0 on success, non-zero on failure (in which case `out` must not be
    256  * used). This is the single entropy source for key generation; the crypto
    257  * primitives themselves never source randomness — it always flows in here. */
    258 int driver_random_bytes(uint8_t* out, size_t n);
    259 
    260 /* Lookup a process environment variable; returns NULL if unset. The returned
    261  * pointer aliases libc-owned storage and is valid until the next setenv/
    262  * putenv from any caller. */
    263 const char* driver_getenv(const char* name);
    264 
    265 /* Borrow the process environment as NAME=VALUE strings. The array and strings
    266  * are libc-owned and remain valid until the process environment is mutated. */
    267 const char* const* driver_environ(void);
    268 
    269 /* Read all of stdin into a freshly-allocated buffer. On success returns 1
    270  * and stores the buffer/size in out_data/out_size; the caller frees via
    271  * driver_free(env, *out_data, *out_size). Returns 0 on read failure or
    272  * allocation failure. */
    273 int driver_read_stdin(DriverEnv*, uint8_t** out_data, size_t* out_size);
    274 
    275 /* Open a temporary file in $VISUAL, then $EDITOR, then vi. `suffix` should
    276  * include the leading dot when a language-specific extension is useful.
    277  * On success, returns the edited bytes in a freshly allocated buffer that the
    278  * caller frees with driver_free(env, *out_data, *out_size). */
    279 int driver_edit_temp(DriverEnv*, const char* suffix, const uint8_t* initial,
    280                      size_t initial_size, uint8_t** out_data, size_t* out_size);
    281 
    282 /* Path-shaped input loader. Wraps env.file_io.read_all so each tool can
    283  * convert a list of paths to a list of KitSlice without re-implementing
    284  * load/release/error bookkeeping. `loaded` is set to 1 on success; release is
    285  * idempotent and does nothing when loaded is already 0. driver_load_bytes
    286  * fills `in.name = path` plus the loaded data/len; driver_release_bytes hands
    287  * the buffer back through file_io.release. On failure an error is emitted via
    288  * driver_errf using the supplied tool tag. */
    289 typedef struct DriverLoad {
    290   KitFileData fd;
    291   int loaded;
    292 } DriverLoad;
    293 
    294 int driver_load_bytes(const KitFileIO*, const char* tool, const char* path,
    295                       DriverLoad* out, KitSlice* in);
    296 void driver_release_bytes(const KitFileIO*, DriverLoad*);
    297 
    298 /* Read one line from stdin into `buf` (cap >= 2). Strips the trailing
    299  * newline and NUL-terminates. Returns the line length on success, 0 at
    300  * EOF (with buf[0]='\0'), -1 on read error, or -2 when the read was
    301  * interrupted by a signal (caller should print a fresh prompt and
    302  * retry). Over-long lines are truncated to cap-1 bytes; the remainder
    303  * up to the next newline is consumed silently. */
    304 int driver_read_line(char* buf, size_t cap);
    305 
    306 typedef struct DriverLineHistory {
    307   char** items;
    308   size_t* sizes;
    309   uint32_t count;
    310   uint32_t cap;
    311 } DriverLineHistory;
    312 
    313 typedef struct DriverLineCompletion {
    314   char* text;
    315   size_t size;
    316 } DriverLineCompletion;
    317 
    318 typedef struct DriverLineCompletionList {
    319   DriverEnv* env;
    320   DriverLineCompletion* items;
    321   uint32_t count;
    322   uint32_t cap;
    323   size_t replace_start;
    324   size_t replace_end;
    325 } DriverLineCompletionList;
    326 
    327 typedef void (*DriverLineCompleteFn)(void* user, const char* line,
    328                                      size_t cursor,
    329                                      DriverLineCompletionList* out);
    330 
    331 int driver_line_completion_add(DriverLineCompletionList*, const char* text,
    332                                size_t len);
    333 int driver_read_line_edit(DriverEnv*, const char* prompt, char* buf, size_t cap,
    334                           DriverLineHistory*, DriverLineCompleteFn,
    335                           void* complete_user);
    336 void driver_line_history_fini(DriverEnv*, DriverLineHistory*);
    337 
    338 /* Flush the host stdout. The dbg REPL prompt has no trailing newline, so
    339  * without an explicit flush the prompt stays buffered until the next
    340  * line of output. */
    341 void driver_flush_stdout(void);
    342 
    343 /* Install / restore a SIGINT handler. While installed, SIGINT runs `cb(user)`
    344  * synchronously (so `cb` must be async-signal safe). Used by `dbg` to
    345  * forward Ctrl-C into kit_jit_session_interrupt while the worker is
    346  * running, and to restore SIG_DFL while sitting at the REPL prompt so
    347  * Ctrl-C terminates the program normally. Returns 0 on success. */
    348 int driver_install_sigint(void (*cb)(void*), void* user);
    349 void driver_restore_sigint(void);
    350 
    351 /* Crash-guarded execution for `kit run`.
    352  *
    353  * `entry` is the JITed program entry, invoked as `entry(argc, argv)`. On a
    354  * clean return, *ret_out receives the program's status and the call returns 0.
    355  *
    356  * On a fatal fault raised by the program (SIGSEGV/SIGBUS/SIGILL/SIGFPE/SIGABRT/
    357  * SIGTRAP — the last covers __builtin_trap / failed asserts), the guard walks
    358  * the frame-pointer chain *inside the signal handler* (where the faulting stack
    359  * is still intact, since `kit run` shares its stack with the program), captures
    360  * the return addresses innermost-first, and invokes `on_crash(user, signo, pcs,
    361  * npcs)` from normal context — so symbolization (DWARF/malloc/printf) never
    362  * runs async-signal. `arch` selects the FP register / pointer width for the
    363  * walk. The call then returns 1; the program's own return value is undefined on
    364  * this path, so the caller should treat the run as failed.
    365  *
    366  * Hosts without a fault guard (Windows) run `entry` directly, set *ret_out, and
    367  * return 0 without ever calling on_crash. */
    368 typedef int (*DriverRunEntryFn)(int argc, char** argv);
    369 typedef void (*DriverRunCrashFn)(void* user, int signo, const uint64_t* pcs,
    370                                  int npcs);
    371 int driver_run_with_crash_guard(DriverEnv* env, KitArchKind arch,
    372                                 DriverRunEntryFn entry, int argc, char** argv,
    373                                 int* ret_out, DriverRunCrashFn on_crash,
    374                                 void* user);
    375 
    376 /* Host-symbol resolver for JIT extern_resolver. Looks up `name` via
    377  * dlsym(RTLD_DEFAULT, ...) on POSIX hosts, returning NULL on miss. Stateless;
    378  * `user` is ignored and may be NULL. Wired into `kit run` so JITed code
    379  * can call libc symbols (printf, malloc, ...) without an explicit linker
    380  * step. */
    381 void* driver_dlsym_resolver(void* user, KitSlice name);
    382 
    383 #endif