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