kit

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

posix.c (39321B)


      1 /* POSIX-shared environment: file I/O, mkdir, sigint, exec_dual registry,
      2  * single-mapping execmem, monotonic clock, stdin/edit, dlsym resolver,
      3  * driver_env_init for hosts where the POSIX TUs are compiled (Mac, Linux,
      4  * FreeBSD). Each function here behaves identically on those three; per-OS
      5  * specifics are isolated in macos.c / linux.c / freebsd.c. */
      6 
      7 #include <dirent.h>
      8 #include <errno.h>
      9 #include <fcntl.h>
     10 #include <pthread.h>
     11 #include <signal.h>
     12 #include <stdint.h>
     13 #include <stdio.h>
     14 #include <stdlib.h>
     15 #include <string.h>
     16 #include <sys/mman.h>
     17 #include <sys/stat.h>
     18 #include <sys/wait.h>
     19 #include <termios.h>
     20 #include <time.h>
     21 #include <unistd.h>
     22 
     23 #include "env_posix.h"
     24 
     25 extern char** environ;
     26 
     27 /* ---------------- exec memory: single-mapping core + registry ----------------
     28  */
     29 
     30 int kit_to_posix_prot(int prot) {
     31   int p = 0;
     32   if (prot & KIT_PROT_READ) p |= PROT_READ;
     33   if (prot & KIT_PROT_WRITE) p |= PROT_WRITE;
     34   if (prot & KIT_PROT_EXEC) p |= PROT_EXEC;
     35   return p;
     36 }
     37 
     38 /* Registry of EXEC reservations with distinct write/runtime aliases. The
     39  * dbg_os code_write_begin path uses this to translate a runtime address
     40  * into the corresponding write alias on dual-mapping hosts. Single-mapping
     41  * reservations (write == runtime) are not registered. JITs typically hold
     42  * 1-2 reservations live so a linked list keeps the lookup trivial. */
     43 typedef struct ExecDualNode {
     44   void* write_base;
     45   void* runtime_base;
     46   size_t size;
     47   struct ExecDualNode* next;
     48 } ExecDualNode;
     49 
     50 static ExecDualNode* g_jit_dual_map;
     51 static pthread_mutex_t g_jit_dual_map_mu = PTHREAD_MUTEX_INITIALIZER;
     52 
     53 void exec_dual_register(void* write_base, void* runtime_base, size_t size) {
     54   ExecDualNode* n;
     55   if (write_base == runtime_base) return;
     56   n = (ExecDualNode*)malloc(sizeof(*n));
     57   if (!n) return; /* registry is best-effort; lookup will fail open */
     58   n->write_base = write_base;
     59   n->runtime_base = runtime_base;
     60   n->size = size;
     61   pthread_mutex_lock(&g_jit_dual_map_mu);
     62   n->next = g_jit_dual_map;
     63   g_jit_dual_map = n;
     64   pthread_mutex_unlock(&g_jit_dual_map_mu);
     65 }
     66 
     67 void exec_dual_unregister(void* runtime_base) {
     68   ExecDualNode** pp;
     69   pthread_mutex_lock(&g_jit_dual_map_mu);
     70   for (pp = &g_jit_dual_map; *pp; pp = &(*pp)->next) {
     71     if ((*pp)->runtime_base == runtime_base) {
     72       ExecDualNode* dead = *pp;
     73       *pp = dead->next;
     74       free(dead);
     75       break;
     76     }
     77   }
     78   pthread_mutex_unlock(&g_jit_dual_map_mu);
     79 }
     80 
     81 int exec_dual_lookup(void* runtime_addr, size_t n, void** write_out) {
     82   ExecDualNode* cur;
     83   uintptr_t a = (uintptr_t)runtime_addr;
     84   pthread_mutex_lock(&g_jit_dual_map_mu);
     85   for (cur = g_jit_dual_map; cur; cur = cur->next) {
     86     uintptr_t base = (uintptr_t)cur->runtime_base;
     87     if (a >= base && a + n <= base + cur->size) {
     88       *write_out = (void*)((uintptr_t)cur->write_base + (a - base));
     89       pthread_mutex_unlock(&g_jit_dual_map_mu);
     90       return 0;
     91     }
     92   }
     93   pthread_mutex_unlock(&g_jit_dual_map_mu);
     94   return 1;
     95 }
     96 
     97 KitStatus execmem_reserve_single(size_t size, KitExecMemRegion* out) {
     98   void* p =
     99       mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
    100   if (p == MAP_FAILED) return KIT_NOMEM;
    101   out->write = p;
    102   out->runtime = p;
    103   out->size = size;
    104   out->token = NULL; /* munmap suffices on release */
    105   return KIT_OK;
    106 }
    107 
    108 static KitStatus execmem_reserve(void* user, size_t size, int prot,
    109                                  KitExecMemRegion* out) {
    110   (void)user;
    111   if (!out || !size) return KIT_INVALID;
    112   if (prot & KIT_PROT_EXEC) return os_execmem_reserve_exec(size, out);
    113   return execmem_reserve_single(size, out);
    114 }
    115 
    116 static KitStatus execmem_protect(void* user, void* addr, size_t size,
    117                                  int prot) {
    118   (void)user;
    119   return mprotect(addr, size, kit_to_posix_prot(prot)) == 0 ? KIT_OK : KIT_ERR;
    120 }
    121 
    122 static void execmem_release(void* user, KitExecMemRegion* region) {
    123   (void)user;
    124   if (!region || !region->size) return;
    125   if (region->token) {
    126     ExecMemToken* tok = (ExecMemToken*)region->token;
    127     if (tok->runtime_addr && tok->runtime_addr != tok->write_addr) {
    128       exec_dual_unregister(tok->runtime_addr);
    129       munmap(tok->runtime_addr, tok->size);
    130     }
    131     if (tok->write_addr) munmap(tok->write_addr, tok->size);
    132     free(tok);
    133   } else if (region->write) {
    134     munmap(region->write, region->size);
    135   }
    136   region->write = NULL;
    137   region->runtime = NULL;
    138   region->size = 0;
    139   region->token = NULL;
    140 }
    141 
    142 static void execmem_flush_icache(void* user, void* addr, size_t size) {
    143   (void)user;
    144   env_flush_icache(addr, size);
    145 }
    146 
    147 size_t driver_host_page_size(void) {
    148   long p = sysconf(_SC_PAGESIZE);
    149   return (p > 0) ? (size_t)p : (size_t)0x4000;
    150 }
    151 
    152 KitExecMem g_execmem_posix; /* page_size set in driver_env_init */
    153 
    154 /* ---------------- fd writer ---------------- */
    155 
    156 typedef struct DriverFdWriter {
    157   KitWriter base; /* must be first; libkit reads via this */
    158   KitHeap* heap;
    159   int fd;
    160   KitStatus status;
    161   uint64_t pos;
    162 } DriverFdWriter;
    163 
    164 static KitStatus fdw_write(KitWriter* w, const void* data, size_t n) {
    165   DriverFdWriter* fw = (DriverFdWriter*)w;
    166   const unsigned char* p = (const unsigned char*)data;
    167   if (fw->status != KIT_OK) return fw->status;
    168   while (n > 0) {
    169     ssize_t k = write(fw->fd, p, n);
    170     if (k < 0) {
    171       fw->status = KIT_IO;
    172       return KIT_IO;
    173     }
    174     p += (size_t)k;
    175     n -= (size_t)k;
    176     fw->pos += (uint64_t)k;
    177   }
    178   return KIT_OK;
    179 }
    180 
    181 static KitStatus fdw_seek(KitWriter* w, uint64_t off) {
    182   DriverFdWriter* fw = (DriverFdWriter*)w;
    183   if (fw->status != KIT_OK) return fw->status;
    184   if (lseek(fw->fd, (off_t)off, SEEK_SET) < 0) {
    185     fw->status = KIT_IO;
    186     return KIT_IO;
    187   }
    188   fw->pos = off;
    189   return KIT_OK;
    190 }
    191 
    192 static uint64_t fdw_tell(KitWriter* w) { return ((DriverFdWriter*)w)->pos; }
    193 static KitStatus fdw_status(KitWriter* w) {
    194   return ((DriverFdWriter*)w)->status;
    195 }
    196 
    197 static void fdw_close(KitWriter* w) {
    198   DriverFdWriter* fw = (DriverFdWriter*)w;
    199   if (fw->fd >= 0) close(fw->fd);
    200   fw->heap->free(fw->heap, fw, sizeof(*fw));
    201 }
    202 
    203 static KitWriter* driver_writer_fd(KitHeap* h, int fd) {
    204   DriverFdWriter* fw =
    205       (DriverFdWriter*)h->alloc(h, sizeof(*fw), _Alignof(DriverFdWriter));
    206   if (!fw) return NULL;
    207   fw->base.write = fdw_write;
    208   fw->base.seek = fdw_seek;
    209   fw->base.tell = fdw_tell;
    210   fw->base.status = fdw_status;
    211   fw->base.close = fdw_close;
    212   fw->heap = h;
    213   fw->fd = fd;
    214   fw->status = KIT_OK;
    215   fw->pos = 0;
    216   return &fw->base;
    217 }
    218 
    219 /* Stdout writer routes through stdio so it shares libc's buffer with
    220  * driver_printf. */
    221 typedef struct DriverStdioWriter {
    222   KitWriter base;
    223   KitHeap* heap;
    224   FILE* fp;
    225   KitStatus status;
    226 } DriverStdioWriter;
    227 
    228 static KitStatus stdio_w_write(KitWriter* w, const void* data, size_t n) {
    229   DriverStdioWriter* sw = (DriverStdioWriter*)w;
    230   if (n) {
    231     size_t got = fwrite(data, 1, n, sw->fp);
    232     if (got != n) {
    233       sw->status = KIT_IO;
    234       return KIT_IO;
    235     }
    236   }
    237   return KIT_OK;
    238 }
    239 static KitStatus stdio_w_seek(KitWriter* w, uint64_t off) {
    240   DriverStdioWriter* sw = (DriverStdioWriter*)w;
    241   return fseek(sw->fp, (long)off, SEEK_SET) == 0 ? KIT_OK : KIT_IO;
    242 }
    243 static uint64_t stdio_w_tell(KitWriter* w) {
    244   long t = ftell(((DriverStdioWriter*)w)->fp);
    245   return t < 0 ? 0u : (uint64_t)t;
    246 }
    247 static KitStatus stdio_w_status(KitWriter* w) {
    248   DriverStdioWriter* sw = (DriverStdioWriter*)w;
    249   if (sw->status != KIT_OK) return sw->status;
    250   return ferror(sw->fp) ? KIT_IO : KIT_OK;
    251 }
    252 static void stdio_w_close(KitWriter* w) {
    253   DriverStdioWriter* sw = (DriverStdioWriter*)w;
    254   fflush(sw->fp); /* flush but do not close the borrowed stdio stream */
    255   sw->heap->free(sw->heap, sw, sizeof(*sw));
    256 }
    257 
    258 static KitWriter* driver_stdio_writer(DriverEnv* e, FILE* fp) {
    259   DriverStdioWriter* sw = (DriverStdioWriter*)e->heap->alloc(
    260       e->heap, sizeof(*sw), _Alignof(DriverStdioWriter));
    261   if (!sw) return NULL;
    262   sw->base.write = stdio_w_write;
    263   sw->base.seek = stdio_w_seek;
    264   sw->base.tell = stdio_w_tell;
    265   sw->base.status = stdio_w_status;
    266   sw->base.close = stdio_w_close;
    267   sw->heap = e->heap;
    268   sw->fp = fp;
    269   sw->status = KIT_OK;
    270   return &sw->base;
    271 }
    272 
    273 KitWriter* driver_stdout_writer(DriverEnv* e) {
    274   return driver_stdio_writer(e, stdout);
    275 }
    276 
    277 KitWriter* driver_stderr_writer(DriverEnv* e) {
    278   return driver_stdio_writer(e, stderr);
    279 }
    280 
    281 const char* const* driver_environ(void) {
    282   return (const char* const*)environ;
    283 }
    284 
    285 /* ---------------- file_io (POSIX open/read/write/stat) ---------------- */
    286 
    287 static KitStatus posix_read_all(void* user, const char* path,
    288                                 KitFileData* out) {
    289   DriverEnv* env = (DriverEnv*)user;
    290   int fd;
    291   struct stat sb;
    292   size_t size;
    293   size_t got;
    294   void* buf;
    295 
    296   fd = open(path, O_RDONLY);
    297   if (fd < 0) return KIT_NOT_FOUND;
    298   if (fstat(fd, &sb) < 0) {
    299     close(fd);
    300     return KIT_IO;
    301   }
    302   size = (size_t)sb.st_size;
    303   buf = size ? env->heap->alloc(env->heap, size, 1) : NULL;
    304   if (size && !buf) {
    305     close(fd);
    306     return KIT_NOMEM;
    307   }
    308 
    309   got = 0;
    310   while (got < size) {
    311     ssize_t n = read(fd, (unsigned char*)buf + got, size - got);
    312     if (n <= 0) {
    313       env->heap->free(env->heap, buf, size);
    314       close(fd);
    315       return KIT_IO;
    316     }
    317     got += (size_t)n;
    318   }
    319   close(fd);
    320 
    321   out->data = (const uint8_t*)buf;
    322   out->size = size;
    323   out->token = buf;
    324   return KIT_OK;
    325 }
    326 
    327 static void posix_release(void* user, KitFileData* d) {
    328   DriverEnv* env = (DriverEnv*)user;
    329   if (d->token) env->heap->free(env->heap, d->token, d->size);
    330   d->data = NULL;
    331   d->size = 0;
    332   d->token = NULL;
    333 }
    334 
    335 static KitStatus posix_open_writer(void* user, const char* path,
    336                                    KitWriter** out) {
    337   DriverEnv* env = (DriverEnv*)user;
    338   int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    339   KitWriter* w;
    340   if (fd < 0) return KIT_IO;
    341   w = driver_writer_fd(env->heap, fd);
    342   if (!w) {
    343     close(fd);
    344     return KIT_NOMEM;
    345   }
    346   *out = w;
    347   return KIT_OK;
    348 }
    349 
    350 /* ---------------- path helpers ---------------- */
    351 
    352 int driver_path_exists(const char* path) {
    353   struct stat sb;
    354   if (!path) return 0;
    355   return stat(path, &sb) == 0;
    356 }
    357 
    358 int driver_path_mtime_ns(const char* path, int64_t* out) {
    359   struct stat sb;
    360   if (!path || !out) return 1;
    361   if (stat(path, &sb) != 0) return 1;
    362   return os_stat_mtime_ns(&sb, out);
    363 }
    364 
    365 static uint8_t posix_mode_to_wasm_filetype(mode_t m) {
    366   if (S_ISREG(m))  return 4;
    367   if (S_ISDIR(m))  return 3;
    368   if (S_ISLNK(m))  return 7;
    369   if (S_ISBLK(m))  return 1;
    370   if (S_ISCHR(m))  return 2;
    371   if (S_ISSOCK(m)) return 6;
    372   return 0;
    373 }
    374 
    375 int driver_path_stat(const char* path, uint64_t* out_size,
    376                      uint64_t* out_mtime_ns, uint8_t* out_filetype) {
    377   struct stat sb;
    378   int64_t mtime;
    379   if (!path || stat(path, &sb) != 0)
    380     return (errno == ENOENT || errno == ENOTDIR) ? 1 : 2;
    381   *out_size = (uint64_t)sb.st_size;
    382   *out_mtime_ns = os_stat_mtime_ns(&sb, &mtime) == 0 ? (uint64_t)mtime : 0u;
    383   *out_filetype = posix_mode_to_wasm_filetype(sb.st_mode);
    384   return 0;
    385 }
    386 
    387 typedef struct DriverDirEntryRec {
    388   char*    name;
    389   size_t   name_alloc;
    390   uint32_t name_len;
    391   uint64_t ino;
    392   uint64_t size;
    393   uint64_t mtime_ns;
    394   uint8_t  filetype;
    395 } DriverDirEntryRec;
    396 
    397 struct DriverDirHandle {
    398   DriverEnv*        env;
    399   DriverDirEntryRec* entries;
    400   size_t             entries_alloc;
    401   uint64_t           count;
    402 };
    403 
    404 DriverDirHandle* driver_open_dir(DriverEnv* env, const char* path) {
    405   DIR* d;
    406   struct dirent* ent;
    407   DriverDirHandle* h;
    408   uint64_t cap = 0;
    409   uint64_t count = 0;
    410   size_t plen;
    411   int slash;
    412 
    413   if (!env || !path) return NULL;
    414   d = opendir(path);
    415   if (!d) return NULL;
    416   plen = kit_slice_cstr(path).len;
    417   slash = plen && path[plen - 1u] != '/';
    418 
    419   h = (DriverDirHandle*)env->heap->alloc(env->heap, sizeof(*h),
    420                                          _Alignof(DriverDirHandle));
    421   if (!h) { closedir(d); return NULL; }
    422   memset(h, 0, sizeof(*h));
    423   h->env = env;
    424 
    425   while ((ent = readdir(d)) != NULL) {
    426     const char* name = ent->d_name;
    427     size_t name_len;
    428     DriverDirEntryRec* e;
    429     size_t child_alloc;
    430     char* child;
    431     struct stat sb;
    432     int64_t mtime;
    433 
    434     if (driver_streq(name, ".") || driver_streq(name, "..")) continue;
    435     name_len = kit_slice_cstr(name).len;
    436 
    437     /* grow entry array */
    438     if (count >= cap) {
    439       uint64_t new_cap = cap ? cap * 2u : 8u;
    440       size_t new_alloc = (size_t)new_cap * sizeof(DriverDirEntryRec);
    441       DriverDirEntryRec* nv = (DriverDirEntryRec*)env->heap->alloc(
    442           env->heap, new_alloc, _Alignof(DriverDirEntryRec));
    443       if (!nv) goto fail;
    444       if (h->entries) {
    445         memcpy(nv, h->entries, (size_t)count * sizeof(DriverDirEntryRec));
    446         env->heap->free(env->heap, h->entries, h->entries_alloc);
    447       }
    448       h->entries = nv;
    449       h->entries_alloc = new_alloc;
    450       cap = new_cap;
    451     }
    452 
    453     e = &h->entries[count];
    454     memset(e, 0, sizeof(*e));
    455     e->name_alloc = name_len + 1u;
    456     e->name = (char*)env->heap->alloc(env->heap, e->name_alloc, 1u);
    457     if (!e->name) goto fail;
    458     memcpy(e->name, name, name_len);
    459     e->name[name_len] = '\0';
    460     e->name_len = (uint32_t)name_len;
    461 
    462     /* lstat the entry to get metadata */
    463     child_alloc = plen + (size_t)slash + name_len + 1u;
    464     child = (char*)driver_alloc(env, child_alloc);
    465     if (child) {
    466       size_t off = 0;
    467       memcpy(child, path, plen); off += plen;
    468       if (slash) child[off++] = '/';
    469       memcpy(child + off, name, name_len);
    470       child[off + name_len] = '\0';
    471       if (lstat(child, &sb) == 0) {
    472         e->ino = (uint64_t)sb.st_ino;
    473         e->size = (uint64_t)sb.st_size;
    474         if (os_stat_mtime_ns(&sb, &mtime) == 0) e->mtime_ns = (uint64_t)mtime;
    475         e->filetype = posix_mode_to_wasm_filetype(sb.st_mode);
    476       }
    477       driver_free(env, child, child_alloc);
    478     }
    479     ++count;
    480   }
    481 
    482   closedir(d);
    483   h->count = count;
    484   return h;
    485 
    486 fail:
    487   closedir(d);
    488   driver_close_dir(env, h);
    489   return NULL;
    490 }
    491 
    492 int driver_read_dir_entry(DriverDirHandle* h, uint64_t index,
    493                           const char** out_name, uint32_t* out_name_len,
    494                           uint64_t* out_ino, uint64_t* out_size,
    495                           uint64_t* out_mtime_ns, uint8_t* out_filetype) {
    496   DriverDirEntryRec* e;
    497   if (!h || index >= h->count) return 1;
    498   e = &h->entries[index];
    499   *out_name     = e->name;
    500   *out_name_len = e->name_len;
    501   *out_ino      = e->ino;
    502   *out_size     = e->size;
    503   *out_mtime_ns = e->mtime_ns;
    504   *out_filetype = e->filetype;
    505   return 0;
    506 }
    507 
    508 void driver_close_dir(DriverEnv* env, DriverDirHandle* h) {
    509   uint64_t i;
    510   if (!h) return;
    511   if (!env) env = h->env;
    512   for (i = 0; i < h->count; ++i) {
    513     DriverDirEntryRec* e = &h->entries[i];
    514     if (e->name) env->heap->free(env->heap, e->name, e->name_alloc);
    515   }
    516   if (h->entries) env->heap->free(env->heap, h->entries, h->entries_alloc);
    517   env->heap->free(env->heap, h, sizeof(*h));
    518 }
    519 
    520 int driver_mkdir_p(DriverEnv* env, const char* path) {
    521   size_t len;
    522   char* buf;
    523   size_t i;
    524   struct stat sb;
    525 
    526   if (!path || !path[0]) return 1;
    527   len = kit_slice_cstr(path).len;
    528   buf = (char*)driver_alloc(env, len + 1);
    529   if (!buf) return 1;
    530   memcpy(buf, path, len + 1);
    531 
    532   for (i = 1; i <= len; ++i) {
    533     int at_end = (i == len);
    534     if (!at_end && buf[i] != '/') continue;
    535     if (!at_end) buf[i] = '\0';
    536     if (buf[0] != '\0' && !driver_streq(buf, ".")) {
    537       if (mkdir(buf, 0755) != 0 && errno != EEXIST) {
    538         driver_free(env, buf, len + 1);
    539         return 1;
    540       }
    541       if (stat(buf, &sb) != 0 || !S_ISDIR(sb.st_mode)) {
    542         driver_free(env, buf, len + 1);
    543         return 1;
    544       }
    545     }
    546     if (!at_end) buf[i] = '/';
    547   }
    548 
    549   driver_free(env, buf, len + 1);
    550   return 0;
    551 }
    552 
    553 int driver_mark_executable_output(const char* path) {
    554   mode_t mask;
    555   mode_t mode;
    556   if (!path) return 1;
    557   mask = umask(0);
    558   (void)umask(mask);
    559   mode = (mode_t)(0777 & ~mask);
    560   return chmod(path, mode) == 0 ? 0 : 1;
    561 }
    562 
    563 /* ---------------- link helpers (install) ---------------- */
    564 
    565 int driver_create_symlink(const char* target, const char* link_path) {
    566   if (!target || !link_path) return 1;
    567   return symlink(target, link_path) == 0 ? 0 : 1;
    568 }
    569 
    570 int driver_create_hardlink(const char* target, const char* link_path) {
    571   if (!target || !link_path) return 1;
    572   return link(target, link_path) == 0 ? 0 : 1;
    573 }
    574 
    575 int driver_remove_file(const char* path) {
    576   if (!path) return 1;
    577   if (unlink(path) == 0) return 0;
    578   return errno == ENOENT ? 0 : 1; /* already absent is success */
    579 }
    580 
    581 int driver_path_lexists(const char* path) {
    582   struct stat sb;
    583   if (!path) return 0;
    584   return lstat(path, &sb) == 0;
    585 }
    586 
    587 static char* driver_join_path(DriverEnv* env, const char* a, const char* b) {
    588   size_t al = kit_slice_cstr(a).len;
    589   size_t bl = kit_slice_cstr(b).len;
    590   int slash = al > 0 && a[al - 1u] != '/';
    591   char* out = (char*)driver_alloc(env, al + (slash ? 1u : 0u) + bl + 1u);
    592   if (!out) return NULL;
    593   memcpy(out, a, al);
    594   if (slash) out[al++] = '/';
    595   memcpy(out + al, b, bl);
    596   out[al + bl] = '\0';
    597   return out;
    598 }
    599 
    600 static int driver_walk_regular_files_at(DriverEnv* env, const char* dir,
    601                                         const char* rel, DriverWalkFileFn cb,
    602                                         void* user) {
    603   DIR* d;
    604   struct dirent* ent;
    605   int rc = 1;
    606   d = opendir(dir);
    607   if (!d) return 1;
    608   while ((ent = readdir(d)) != NULL) {
    609     const char* name = ent->d_name;
    610     char* child;
    611     char* child_rel;
    612     struct stat sb;
    613     int child_rc = 0;
    614     if (driver_streq(name, ".") || driver_streq(name, "..")) continue;
    615     child = driver_join_path(env, dir, name);
    616     if (!child) goto out;
    617     child_rel = rel && rel[0] ? driver_join_path(env, rel, name)
    618                               : driver_join_path(env, "", name);
    619     if (!child_rel) {
    620       driver_free(env, child, kit_slice_cstr(child).len + 1u);
    621       goto out;
    622     }
    623     if (lstat(child, &sb) != 0) {
    624       child_rc = 1;
    625     } else if (S_ISDIR(sb.st_mode)) {
    626       child_rc = driver_walk_regular_files_at(env, child, child_rel, cb, user);
    627     } else if (S_ISREG(sb.st_mode)) {
    628       int x = (sb.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0;
    629       child_rc = cb(user, child, child_rel, x);
    630     } else {
    631       child_rc = 1;
    632     }
    633     driver_free(env, child_rel, kit_slice_cstr(child_rel).len + 1u);
    634     driver_free(env, child, kit_slice_cstr(child).len + 1u);
    635     if (child_rc) goto out;
    636   }
    637   rc = 0;
    638 
    639 out:
    640   closedir(d);
    641   return rc;
    642 }
    643 
    644 int driver_walk_regular_files(DriverEnv* env, const char* root,
    645                               DriverWalkFileFn cb, void* user) {
    646   if (!env || !root || !root[0] || !cb) return 1;
    647   return driver_walk_regular_files_at(env, root, "", cb, user);
    648 }
    649 
    650 /* ---------------- time ---------------- */
    651 
    652 uint64_t driver_now_ns(void) {
    653   struct timespec ts;
    654   if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
    655     return (uint64_t)ts.tv_sec * 1000000000ull + (uint64_t)ts.tv_nsec;
    656   return 0;
    657 }
    658 
    659 int driver_random_bytes(uint8_t* out, size_t n) {
    660   /* /dev/urandom is the most portable CSPRNG across darwin/linux/freebsd and
    661    * avoids getentropy()'s per-platform header/feature-macro gymnastics. */
    662   size_t off = 0;
    663   int fd;
    664   if (!out) return 1;
    665   fd = open("/dev/urandom", O_RDONLY);
    666   if (fd < 0) return 1;
    667   while (off < n) {
    668     ssize_t r = read(fd, out + off, n - off);
    669     if (r > 0) {
    670       off += (size_t)r;
    671     } else if (r < 0 && errno == EINTR) {
    672       continue;
    673     } else {
    674       close(fd);
    675       return 1;
    676     }
    677   }
    678   close(fd);
    679   return 0;
    680 }
    681 
    682 /* ---------------- load helpers ---------------- */
    683 
    684 int driver_load_bytes(const KitFileIO* io, const char* tool, const char* path,
    685                       DriverLoad* out, KitSlice* in) {
    686   out->loaded = 0;
    687   out->fd.data = NULL;
    688   out->fd.size = 0;
    689   out->fd.token = NULL;
    690   if (!io || !io->read_all) {
    691     driver_errf(tool, "host file I/O unavailable");
    692     return 1;
    693   }
    694   if (io->read_all(io->user, path, &out->fd) != KIT_OK) {
    695     driver_errf(tool, "failed to read: %.*s",
    696                 KIT_SLICE_ARG(kit_slice_cstr(path)));
    697     return 1;
    698   }
    699   out->loaded = 1;
    700   in->data = out->fd.data;
    701   in->len = out->fd.size;
    702   return 0;
    703 }
    704 
    705 void driver_release_bytes(const KitFileIO* io, DriverLoad* lf) {
    706   if (!lf || !lf->loaded) return;
    707   if (io && io->release) io->release(io->user, &lf->fd);
    708   lf->loaded = 0;
    709 }
    710 
    711 /* ---------------- stdin / edit_temp / read_line ---------------- */
    712 
    713 int driver_read_stdin(DriverEnv* e, uint8_t** out_data, size_t* out_size) {
    714   size_t cap = 4096;
    715   size_t len = 0;
    716   uint8_t* buf = e->heap->alloc(e->heap, cap, 1);
    717   if (!buf) return 0;
    718   for (;;) {
    719     ssize_t n;
    720     if (len == cap) {
    721       size_t newcap = cap * 2;
    722       uint8_t* nb = e->heap->realloc(e->heap, buf, cap, newcap, 1);
    723       if (!nb) {
    724         e->heap->free(e->heap, buf, cap);
    725         return 0;
    726       }
    727       buf = nb;
    728       cap = newcap;
    729     }
    730     n = read(STDIN_FILENO, buf + len, cap - len);
    731     if (n == 0) break;
    732     if (n < 0) {
    733       e->heap->free(e->heap, buf, cap);
    734       return 0;
    735     }
    736     len += (size_t)n;
    737   }
    738   if (len < cap) {
    739     uint8_t* shrunk = len ? e->heap->realloc(e->heap, buf, cap, len, 1) : NULL;
    740     if (len && !shrunk) {
    741       *out_data = buf;
    742       *out_size = cap;
    743       return 1;
    744     }
    745     if (!len) {
    746       e->heap->free(e->heap, buf, cap);
    747       buf = NULL;
    748     } else {
    749       buf = shrunk;
    750     }
    751   }
    752   *out_data = buf;
    753   *out_size = len;
    754   return 1;
    755 }
    756 
    757 static int driver_write_fd_all(int fd, const uint8_t* data, size_t n) {
    758   size_t off = 0;
    759   while (off < n) {
    760     ssize_t wr = write(fd, data + off, n - off);
    761     if (wr < 0) {
    762       if (errno == EINTR) continue;
    763       return 0;
    764     }
    765     if (wr == 0) return 0;
    766     off += (size_t)wr;
    767   }
    768   return 1;
    769 }
    770 
    771 static char* driver_shell_quote_path(DriverEnv* e, const char* path,
    772                                      size_t path_len, size_t* quoted_len_out) {
    773   size_t i;
    774   size_t quoted_len = 2u;
    775   char* out;
    776   char* q;
    777   for (i = 0; i < path_len; ++i) quoted_len += path[i] == '\'' ? 4u : 1u;
    778   out = e->heap->alloc(e->heap, quoted_len + 1u, 1);
    779   if (!out) return NULL;
    780   q = out;
    781   *q++ = '\'';
    782   for (i = 0; i < path_len; ++i) {
    783     if (path[i] == '\'') {
    784       *q++ = '\'';
    785       *q++ = '\\';
    786       *q++ = '\'';
    787       *q++ = '\'';
    788     } else {
    789       *q++ = path[i];
    790     }
    791   }
    792   *q++ = '\'';
    793   *q = '\0';
    794   if (quoted_len_out) *quoted_len_out = quoted_len;
    795   return out;
    796 }
    797 
    798 int driver_edit_temp(DriverEnv* e, const char* suffix, const uint8_t* initial,
    799                      size_t initial_size, uint8_t** out_data,
    800                      size_t* out_size) {
    801   const char* editor;
    802   const char* tmpdir;
    803   const char* base = "/kit-dbg-XXXXXX";
    804   size_t tmpdir_len;
    805   size_t base_len;
    806   size_t suffix_len;
    807   size_t path_len;
    808   char* path;
    809   int fd = -1;
    810   int ok = 0;
    811   KitFileData fd_data;
    812 
    813   if (!out_data || !out_size) return 0;
    814   *out_data = NULL;
    815   *out_size = 0;
    816   suffix_len = suffix ? kit_slice_cstr(suffix).len : 0u;
    817   tmpdir = getenv("TMPDIR");
    818   if (!tmpdir || !*tmpdir) tmpdir = "/tmp";
    819   tmpdir_len = kit_slice_cstr(tmpdir).len;
    820   base_len = kit_slice_cstr(base).len;
    821   path_len = tmpdir_len + base_len + suffix_len;
    822   path = e->heap->alloc(e->heap, path_len + 1u, 1);
    823   if (!path) return 0;
    824   memcpy(path, tmpdir, tmpdir_len);
    825   memcpy(path + tmpdir_len, base, base_len);
    826   if (suffix_len) memcpy(path + tmpdir_len + base_len, suffix, suffix_len);
    827   path[path_len] = '\0';
    828 
    829   fd = mkstemps(path, (int)suffix_len);
    830   if (fd < 0) goto out;
    831   if (initial_size &&
    832       !driver_write_fd_all(fd, initial ? initial : (const uint8_t*)"",
    833                            initial_size))
    834     goto out;
    835   if (close(fd) != 0) {
    836     fd = -1;
    837     goto out;
    838   }
    839   fd = -1;
    840 
    841   editor = getenv("VISUAL");
    842   if (!editor || !*editor) editor = getenv("EDITOR");
    843   if (!editor || !*editor) editor = "vi";
    844   {
    845     size_t editor_len = kit_slice_cstr(editor).len;
    846     size_t quoted_len = 0;
    847     char* quoted = driver_shell_quote_path(e, path, path_len, &quoted_len);
    848     char* cmd;
    849     int status;
    850     pid_t pid;
    851     if (!quoted) goto out;
    852     cmd = e->heap->alloc(e->heap, editor_len + 1u + quoted_len + 1u, 1);
    853     if (!cmd) {
    854       e->heap->free(e->heap, quoted, quoted_len + 1u);
    855       goto out;
    856     }
    857     memcpy(cmd, editor, editor_len);
    858     cmd[editor_len] = ' ';
    859     memcpy(cmd + editor_len + 1u, quoted, quoted_len + 1u);
    860     e->heap->free(e->heap, quoted, quoted_len + 1u);
    861     pid = fork();
    862     if (pid == 0) {
    863       execl("/bin/sh", "sh", "-c", cmd, (char*)NULL);
    864       _exit(127);
    865     }
    866     e->heap->free(e->heap, cmd, editor_len + 1u + quoted_len + 1u);
    867     if (pid < 0) goto out;
    868     do {
    869       if (waitpid(pid, &status, 0) < 0) {
    870         if (errno == EINTR) continue;
    871         goto out;
    872       }
    873       break;
    874     } while (1);
    875     if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) goto out;
    876   }
    877 
    878   fd_data.data = NULL;
    879   fd_data.size = 0;
    880   fd_data.token = NULL;
    881   if (posix_read_all(e, path, &fd_data) != KIT_OK) goto out;
    882   *out_data = (uint8_t*)fd_data.data;
    883   *out_size = fd_data.size;
    884   ok = 1;
    885 
    886 out:
    887   if (fd >= 0) close(fd);
    888   if (path) {
    889     unlink(path);
    890     e->heap->free(e->heap, path, path_len + 1u);
    891   }
    892   return ok;
    893 }
    894 
    895 int driver_read_line(char* buf, size_t cap) {
    896   size_t len = 0;
    897   if (!buf || cap < 2) return -1;
    898   for (;;) {
    899     int c;
    900     errno = 0;
    901     c = fgetc(stdin);
    902     if (c == EOF) {
    903       buf[len] = '\0';
    904       if (errno == EINTR) {
    905         clearerr(stdin);
    906         return -2;
    907       }
    908       if (ferror(stdin)) return -1;
    909       if (len == 0) return 0;
    910       return (int)len;
    911     }
    912     if (c == '\n') {
    913       buf[len] = '\0';
    914       return (int)len;
    915     }
    916     if (len + 1 < cap) buf[len++] = (char)c;
    917   }
    918 }
    919 
    920 static void line_completion_list_fini(DriverLineCompletionList* l) {
    921   uint32_t i;
    922   if (!l || !l->env) return;
    923   for (i = 0; i < l->count; ++i) {
    924     if (l->items[i].text)
    925       driver_free(l->env, l->items[i].text, l->items[i].size);
    926   }
    927   if (l->items)
    928     driver_free(l->env, l->items, (size_t)l->cap * sizeof(*l->items));
    929 }
    930 
    931 static int line_history_add(DriverEnv* env, DriverLineHistory* h,
    932                             const char* line, size_t len) {
    933   char** ni;
    934   size_t* ns;
    935   char* copy;
    936   uint32_t nc;
    937   size_t old_items_size;
    938   size_t new_items_size;
    939   size_t old_sizes_size;
    940   size_t new_sizes_size;
    941   if (!env || !h || !line || len == 0) return 0;
    942   if (h->count > 0 && h->items[h->count - 1] &&
    943       strlen(h->items[h->count - 1]) == len &&
    944       memcmp(h->items[h->count - 1], line, len) == 0)
    945     return 0;
    946   if (h->count == h->cap) {
    947     nc = h->cap ? h->cap * 2u : 32u;
    948     old_items_size = (size_t)h->cap * sizeof(*h->items);
    949     new_items_size = (size_t)nc * sizeof(*h->items);
    950     old_sizes_size = (size_t)h->cap * sizeof(*h->sizes);
    951     new_sizes_size = (size_t)nc * sizeof(*h->sizes);
    952     ni = (char**)env->heap->realloc(env->heap, h->items, old_items_size,
    953                                     new_items_size, _Alignof(char*));
    954     if (!ni) return 1;
    955     h->items = ni;
    956     ns = (size_t*)env->heap->realloc(env->heap, h->sizes, old_sizes_size,
    957                                      new_sizes_size, _Alignof(size_t));
    958     if (!ns) return 1;
    959     h->sizes = ns;
    960     h->cap = nc;
    961   }
    962   copy = (char*)driver_alloc(env, len + 1u);
    963   if (!copy) return 1;
    964   memcpy(copy, line, len);
    965   copy[len] = '\0';
    966   h->items[h->count] = copy;
    967   h->sizes[h->count] = len + 1u;
    968   h->count++;
    969   return 0;
    970 }
    971 
    972 static void line_redraw(const char* prompt, const char* buf, size_t len,
    973                         size_t cursor) {
    974   size_t back = len - cursor;
    975   fputc('\r', stdout);
    976   fputs(prompt, stdout);
    977   if (len) fwrite(buf, 1, len, stdout);
    978   fputs("\033[K", stdout);
    979   if (back) fprintf(stdout, "\033[%zuD", back);
    980   fflush(stdout);
    981 }
    982 
    983 static void line_set(char* buf, size_t cap, size_t* len, size_t* cursor,
    984                      const char* src) {
    985   size_t n = strlen(src);
    986   if (n >= cap) n = cap - 1u;
    987   memmove(buf, src, n);
    988   buf[n] = '\0';
    989   *len = n;
    990   *cursor = n;
    991 }
    992 
    993 static int line_replace(char* buf, size_t cap, size_t* len, size_t* cursor,
    994                         size_t start, size_t end, const char* rep,
    995                         size_t rep_len) {
    996   size_t tail;
    997   if (start > end || end > *len) return 1;
    998   tail = *len - end;
    999   if (start + rep_len + tail + 1u > cap) return 1;
   1000   memmove(buf + start + rep_len, buf + end, tail + 1u);
   1001   if (rep_len) memcpy(buf + start, rep, rep_len);
   1002   *len = start + rep_len + tail;
   1003   *cursor = start + rep_len;
   1004   return 0;
   1005 }
   1006 
   1007 static size_t line_common_prefix(const DriverLineCompletionList* l) {
   1008   size_t n;
   1009   uint32_t i;
   1010   if (!l || l->count == 0) return 0;
   1011   n = strlen(l->items[0].text);
   1012   for (i = 1; i < l->count; ++i) {
   1013     size_t j = 0;
   1014     const char* s = l->items[i].text;
   1015     while (j < n && s[j] && s[j] == l->items[0].text[j]) ++j;
   1016     n = j;
   1017   }
   1018   return n;
   1019 }
   1020 
   1021 static void line_default_complete_range(const char* buf, size_t cursor,
   1022                                         size_t* start, size_t* end) {
   1023   size_t s = cursor;
   1024   while (s > 0 && buf[s - 1] != ' ' && buf[s - 1] != '\t') --s;
   1025   *start = s;
   1026   *end = cursor;
   1027 }
   1028 
   1029 static void line_complete(DriverEnv* env, char* buf, size_t cap, size_t* len,
   1030                           size_t* cursor, const char* prompt,
   1031                           DriverLineCompleteFn complete, void* complete_user) {
   1032   DriverLineCompletionList list;
   1033   size_t common;
   1034   size_t cur_len;
   1035   uint32_t i;
   1036   if (!complete) return;
   1037   {
   1038     DriverLineCompletionList z = {0};
   1039     list = z;
   1040   }
   1041   list.env = env;
   1042   line_default_complete_range(buf, *cursor, &list.replace_start,
   1043                               &list.replace_end);
   1044   complete(complete_user, buf, *cursor, &list);
   1045   if (list.replace_end > *len) list.replace_end = *len;
   1046   if (list.replace_start > list.replace_end)
   1047     list.replace_start = list.replace_end;
   1048   if (list.count == 1) {
   1049     size_t rn = strlen(list.items[0].text);
   1050     if (line_replace(buf, cap, len, cursor, list.replace_start,
   1051                      list.replace_end, list.items[0].text, rn) != 0)
   1052       fputc('\a', stdout);
   1053     line_redraw(prompt, buf, *len, *cursor);
   1054     line_completion_list_fini(&list);
   1055     return;
   1056   }
   1057   if (list.count > 1) {
   1058     common = line_common_prefix(&list);
   1059     cur_len = *cursor > list.replace_start ? *cursor - list.replace_start : 0;
   1060     if (common > cur_len) {
   1061       if (line_replace(buf, cap, len, cursor, list.replace_start,
   1062                        list.replace_end, list.items[0].text, common) != 0)
   1063         fputc('\a', stdout);
   1064       line_redraw(prompt, buf, *len, *cursor);
   1065     } else {
   1066       fputc('\n', stdout);
   1067       for (i = 0; i < list.count; ++i)
   1068         fprintf(stdout, "  %s\n", list.items[i].text);
   1069       line_redraw(prompt, buf, *len, *cursor);
   1070     }
   1071   } else {
   1072     fputc('\a', stdout);
   1073     fflush(stdout);
   1074   }
   1075   line_completion_list_fini(&list);
   1076 }
   1077 
   1078 int driver_read_line_edit(DriverEnv* env, const char* prompt, char* buf,
   1079                           size_t cap, DriverLineHistory* hist,
   1080                           DriverLineCompleteFn complete, void* complete_user) {
   1081   struct termios orig;
   1082   struct termios raw;
   1083   char* saved = NULL;
   1084   size_t saved_size = 0;
   1085   size_t len = 0;
   1086   size_t cursor = 0;
   1087   uint32_t hist_pos = 0;
   1088   int have_saved = 0;
   1089   int raw_enabled = 0;
   1090   int out_rc = -1;
   1091 
   1092   if (!buf || cap < 2) return -1;
   1093   if (!prompt) prompt = "";
   1094   if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) {
   1095     int n;
   1096     fputs(prompt, stdout);
   1097     fflush(stdout);
   1098     n = driver_read_line(buf, cap);
   1099     if (n > 0 && hist) (void)line_history_add(env, hist, buf, (size_t)n);
   1100     return n;
   1101   }
   1102 
   1103   if (tcgetattr(STDIN_FILENO, &orig) != 0) goto out;
   1104   raw = orig;
   1105   raw.c_lflag &= (tcflag_t) ~(ICANON | ECHO | IEXTEN);
   1106   raw.c_iflag &= (tcflag_t) ~(IXON);
   1107   raw.c_cc[VMIN] = 1;
   1108   raw.c_cc[VTIME] = 0;
   1109   if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) != 0) goto out;
   1110   raw_enabled = 1;
   1111 
   1112   saved = (char*)driver_alloc(env, cap);
   1113   if (!saved) goto out;
   1114   saved_size = cap;
   1115   hist_pos = hist ? hist->count : 0;
   1116   buf[0] = '\0';
   1117 
   1118   fputs(prompt, stdout);
   1119   fflush(stdout);
   1120   for (;;) {
   1121     unsigned char c;
   1122     ssize_t r = read(STDIN_FILENO, &c, 1);
   1123     if (r < 0) {
   1124       if (errno == EINTR) {
   1125         out_rc = -2;
   1126         goto out;
   1127       }
   1128       out_rc = -1;
   1129       goto out;
   1130     }
   1131     if (r == 0) {
   1132       out_rc = len ? (int)len : 0;
   1133       goto out;
   1134     }
   1135     if (c == '\r' || c == '\n') {
   1136       buf[len] = '\0';
   1137       fputc('\n', stdout);
   1138       if (hist) (void)line_history_add(env, hist, buf, len);
   1139       out_rc = (int)len;
   1140       goto out;
   1141     }
   1142     if (c == 4) {
   1143       if (len == 0) {
   1144         buf[0] = '\0';
   1145         out_rc = 0;
   1146         goto out;
   1147       }
   1148       continue;
   1149     }
   1150     if (c == '\t') {
   1151       buf[len] = '\0';
   1152       line_complete(env, buf, cap, &len, &cursor, prompt, complete,
   1153                     complete_user);
   1154       continue;
   1155     }
   1156     if (c == 1) {
   1157       cursor = 0;
   1158       line_redraw(prompt, buf, len, cursor);
   1159       continue;
   1160     }
   1161     if (c == 5) {
   1162       cursor = len;
   1163       line_redraw(prompt, buf, len, cursor);
   1164       continue;
   1165     }
   1166     if (c == 11) {
   1167       buf[cursor] = '\0';
   1168       len = cursor;
   1169       line_redraw(prompt, buf, len, cursor);
   1170       continue;
   1171     }
   1172     if (c == 127 || c == 8) {
   1173       if (cursor == 0) {
   1174         fputc('\a', stdout);
   1175         fflush(stdout);
   1176         continue;
   1177       }
   1178       memmove(buf + cursor - 1u, buf + cursor, len - cursor + 1u);
   1179       --cursor;
   1180       --len;
   1181       line_redraw(prompt, buf, len, cursor);
   1182       continue;
   1183     }
   1184     if (c == 27) {
   1185       unsigned char seq[3];
   1186       ssize_t r1 = read(STDIN_FILENO, seq, 1);
   1187       ssize_t r2 = read(STDIN_FILENO, seq + 1, 1);
   1188       if (r1 != 1 || r2 != 1 || seq[0] != '[') continue;
   1189       if (seq[1] == 'A') {
   1190         if (hist && hist->count > 0 && hist_pos > 0) {
   1191           if (!have_saved) {
   1192             memcpy(saved, buf, len + 1u);
   1193             have_saved = 1;
   1194           }
   1195           --hist_pos;
   1196           line_set(buf, cap, &len, &cursor, hist->items[hist_pos]);
   1197           line_redraw(prompt, buf, len, cursor);
   1198         }
   1199       } else if (seq[1] == 'B') {
   1200         if (hist && have_saved && hist_pos < hist->count) {
   1201           ++hist_pos;
   1202           if (hist_pos == hist->count)
   1203             line_set(buf, cap, &len, &cursor, saved);
   1204           else
   1205             line_set(buf, cap, &len, &cursor, hist->items[hist_pos]);
   1206           line_redraw(prompt, buf, len, cursor);
   1207         }
   1208       } else if (seq[1] == 'C') {
   1209         if (cursor < len) {
   1210           ++cursor;
   1211           line_redraw(prompt, buf, len, cursor);
   1212         }
   1213       } else if (seq[1] == 'D') {
   1214         if (cursor > 0) {
   1215           --cursor;
   1216           line_redraw(prompt, buf, len, cursor);
   1217         }
   1218       } else if (seq[1] == 'H') {
   1219         cursor = 0;
   1220         line_redraw(prompt, buf, len, cursor);
   1221       } else if (seq[1] == 'F') {
   1222         cursor = len;
   1223         line_redraw(prompt, buf, len, cursor);
   1224       } else if (seq[1] >= '1' && seq[1] <= '9') {
   1225         ssize_t r3 = read(STDIN_FILENO, seq + 2, 1);
   1226         if (r3 == 1 && seq[1] == '3' && seq[2] == '~' && cursor < len) {
   1227           memmove(buf + cursor, buf + cursor + 1u, len - cursor);
   1228           --len;
   1229           line_redraw(prompt, buf, len, cursor);
   1230         }
   1231       }
   1232       continue;
   1233     }
   1234     if (c >= 32 && c != 127) {
   1235       if (len + 1u >= cap) {
   1236         fputc('\a', stdout);
   1237         fflush(stdout);
   1238         continue;
   1239       }
   1240       memmove(buf + cursor + 1u, buf + cursor, len - cursor + 1u);
   1241       buf[cursor] = (char)c;
   1242       ++cursor;
   1243       ++len;
   1244       line_redraw(prompt, buf, len, cursor);
   1245     }
   1246   }
   1247 
   1248 out:
   1249   if (raw_enabled) tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig);
   1250   if (saved) driver_free(env, saved, saved_size);
   1251   return out_rc;
   1252 }
   1253 
   1254 /* ---------------- dlsym resolver ---------------- */
   1255 
   1256 void* driver_dlsym_resolver(void* user, KitSlice name_s) {
   1257   /* The linker hands us interned/pool slices that are NUL-terminated, so
   1258    * we can pass .s straight through to the OS-specific os_dlsym. */
   1259   (void)user;
   1260   if (!name_s.s || name_s.len == 0) return NULL;
   1261   return os_dlsym(name_s.s);
   1262 }
   1263 
   1264 /* ---------------- SIGINT handler for the dbg REPL ---------------- */
   1265 
   1266 static void (*s_sigint_cb)(void*);
   1267 static void* s_sigint_cb_user;
   1268 
   1269 static void sigint_trampoline(int sig) {
   1270   (void)sig;
   1271   if (s_sigint_cb) s_sigint_cb(s_sigint_cb_user);
   1272 }
   1273 
   1274 int driver_install_sigint(void (*cb)(void*), void* user) {
   1275   struct sigaction sa;
   1276   s_sigint_cb = cb;
   1277   s_sigint_cb_user = user;
   1278   sa.sa_handler = sigint_trampoline;
   1279   sigemptyset(&sa.sa_mask);
   1280   sa.sa_flags = 0; /* no SA_RESTART: fgetc returns EINTR */
   1281   return sigaction(SIGINT, &sa, NULL) == 0 ? 0 : 1;
   1282 }
   1283 
   1284 void driver_restore_sigint(void) {
   1285   struct sigaction sa;
   1286   s_sigint_cb = NULL;
   1287   s_sigint_cb_user = NULL;
   1288   sa.sa_handler = SIG_DFL;
   1289   sigemptyset(&sa.sa_mask);
   1290   sa.sa_flags = 0;
   1291   sigaction(SIGINT, &sa, NULL);
   1292 }
   1293 
   1294 /* ---------------- host target ---------------- */
   1295 
   1296 static KitArchKind host_arch_self(void) {
   1297 #if defined(__x86_64__)
   1298   return KIT_ARCH_X86_64;
   1299 #elif defined(__aarch64__)
   1300   return KIT_ARCH_ARM_64;
   1301 #elif defined(__arm__)
   1302   return KIT_ARCH_ARM_32;
   1303 #elif defined(__i386__)
   1304   return KIT_ARCH_X86_32;
   1305 #elif defined(__riscv) && (__riscv_xlen == 64)
   1306   return KIT_ARCH_RV64;
   1307 #elif defined(__riscv) && (__riscv_xlen == 32)
   1308   return KIT_ARCH_RV32;
   1309 #elif defined(__wasm__)
   1310   return KIT_ARCH_WASM;
   1311 #else
   1312   return KIT_ARCH_X86_64;
   1313 #endif
   1314 }
   1315 
   1316 KitTargetSpec driver_host_target(void) {
   1317   KitTargetSpec t;
   1318   t.arch = host_arch_self();
   1319   os_host_target_fill(&t);
   1320   t.ptr_size = (uint8_t)sizeof(void*);
   1321   t.ptr_align = (uint8_t)sizeof(void*);
   1322   t.big_endian = 0;
   1323   t.pic = driver_default_pic(t.obj, t.os);
   1324   t.code_model = KIT_CM_DEFAULT;
   1325   return t;
   1326 }
   1327 
   1328 /* ---------------- env wiring (POSIX) ---------------- */
   1329 
   1330 char g_cache_dir[4096];
   1331 
   1332 void driver_env_init(DriverEnv* e) {
   1333   e->heap = &g_heap_libc;
   1334   e->diag = &g_diag_stderr;
   1335   e->file_io.read_all = posix_read_all;
   1336   e->file_io.release = posix_release;
   1337   e->file_io.open_writer = posix_open_writer;
   1338   e->file_io.user = e;
   1339 
   1340   g_execmem_posix.page_size = driver_host_page_size();
   1341   g_execmem_posix.reserve = execmem_reserve;
   1342   g_execmem_posix.protect = execmem_protect;
   1343   g_execmem_posix.release = execmem_release;
   1344   g_execmem_posix.flush_icache = execmem_flush_icache;
   1345   g_execmem_posix.user = NULL;
   1346   e->execmem = &g_execmem_posix;
   1347 
   1348   e->dbg_os = &g_dbg_os_posix;
   1349   e->metrics = NULL;
   1350 
   1351   {
   1352     const char* xdg = getenv("XDG_CACHE_HOME");
   1353     const char* home = getenv("HOME");
   1354     if (xdg && *xdg) {
   1355       snprintf(g_cache_dir, sizeof(g_cache_dir), "%s/kit", xdg);
   1356     } else if (home && *home) {
   1357       snprintf(g_cache_dir, sizeof(g_cache_dir), "%s/.cache/kit", home);
   1358     } else {
   1359       snprintf(g_cache_dir, sizeof(g_cache_dir), "build/kit-cache");
   1360     }
   1361     e->cache_dir = g_cache_dir;
   1362   }
   1363 
   1364   /* Reproducible-build precedent: SOURCE_DATE_EPOCH wins over wall clock. */
   1365   {
   1366     const char* sde = getenv("SOURCE_DATE_EPOCH");
   1367     if (sde && *sde) {
   1368       char* endp = NULL;
   1369       long long v = strtoll(sde, &endp, 10);
   1370       e->now = (endp != sde && v >= 0) ? (int64_t)v : (int64_t)-1;
   1371     } else {
   1372       time_t t = time(NULL);
   1373       e->now = (t == (time_t)-1) ? (int64_t)-1 : (int64_t)t;
   1374     }
   1375   }
   1376 }
   1377 
   1378 void driver_env_fini(DriverEnv* e) {
   1379   /* Singletons; nothing to release. */
   1380   (void)e;
   1381 }
   1382 
   1383 KitContext driver_env_to_context(const DriverEnv* e) {
   1384   KitContext c;
   1385   c.heap = e->heap;
   1386   c.file_io = &e->file_io;
   1387   c.diag = e->diag;
   1388   c.metrics = e->metrics;
   1389   c.now = e->now;
   1390   return c;
   1391 }
   1392 
   1393 KitJitHost driver_env_to_jit_host(const DriverEnv* e) {
   1394   KitJitHost h;
   1395   h.execmem = e->execmem;
   1396   return h;
   1397 }
   1398 
   1399 KitDbgHost driver_env_to_dbg_host(const DriverEnv* e) {
   1400   KitDbgHost h;
   1401   h.os = e->dbg_os;
   1402   return h;
   1403 }