kit

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

jit_runner.c (24483B)


      1 /* jit_runner — Path J harness driver.
      2  *
      3  * Usage:
      4  *   jit_runner [--gc-sections] [--use-resolver]
      5  *              [--check-absent SYM] [--check-present SYM]
      6  *              [--linker-script <path>]
      7  *              [--archive [--whole-archive] <lib.a>] <in.o> ...
      8  *
      9  * Reads .o (and optionally .a) inputs, calls kit_link_jit (which runs
     10  * init_array ctors), calls test_main(), calls kit_jit_run_dtors(), then
     11  * calls test_post_fini() if it exists. Exits with the combined result.
     12  *
     13  * --use-resolver: enables a default extern resolver that returns the
     14  *   address of a static int == 42 for any unresolved symbol. Used for
     15  *   the extern_resolver test case (28).
     16  *
     17  * Sanity: after every successful JIT load, verifies that
     18  * kit_jit_lookup(jit, "__kit_test_sentinel__") returns NULL (tests
     19  * the lookup-miss path, covering case 29).
     20  *
     21  * Compiled against libkit.a with -I$(ROOT)/include. */
     22 
     23 #ifndef _GNU_SOURCE
     24 #define _GNU_SOURCE
     25 #endif
     26 
     27 #include <fcntl.h>
     28 #include <kit/core.h>
     29 #include <kit/jit.h>
     30 #include <kit/link.h>
     31 #include <kit/wasm.h>
     32 #include <stdarg.h>
     33 #include <stdint.h>
     34 #include <stdio.h>
     35 #include <stdlib.h>
     36 #include <string.h>
     37 #include <sys/mman.h>
     38 #include <sys/stat.h>
     39 #include <unistd.h>
     40 
     41 #include "lib/kit_test_target.h"
     42 
     43 static void* h_alloc(KitHeap* h, size_t n, size_t a) {
     44   (void)h;
     45   (void)a;
     46   return malloc(n);
     47 }
     48 static void* h_realloc(KitHeap* h, void* p, size_t o, size_t n, size_t a) {
     49   (void)h;
     50   (void)o;
     51   (void)a;
     52   return realloc(p, n);
     53 }
     54 static void h_free(KitHeap* h, void* p, size_t n) {
     55   (void)h;
     56   (void)n;
     57   free(p);
     58 }
     59 static KitHeap g_heap = {h_alloc, h_realloc, h_free, NULL};
     60 
     61 /* Canned host import for wasm test fixtures (e.g. host_import_add.wat).
     62  * Signature matches the kit-instance ABI: explicit instance + wasm
     63  * params. */
     64 int32_t test_host_add(KitWasmInstance* inst, int32_t a, int32_t b) {
     65   (void)inst;
     66   return a + b;
     67 }
     68 
     69 static void diag_fn(KitDiagSink* s, KitDiagKind k, KitSrcLoc loc,
     70                     const char* fmt, va_list ap) {
     71   static const char* names[] = {"note", "warning", "error", "fatal"};
     72   (void)s;
     73   (void)loc;
     74   fprintf(stderr, "%s: ", names[k]);
     75   vfprintf(stderr, fmt, ap);
     76   fputc('\n', stderr);
     77 }
     78 static KitDiagSink g_diag = {diag_fn, NULL, 0, 0};
     79 
     80 static void free_compiler_target(KitCompiler* compiler, KitTarget* target) {
     81   kit_compiler_free(compiler);
     82   kit_target_free(target);
     83 }
     84 
     85 /* Mirrors driver/env.c — see that file for the strict-W^X dual-mapping
     86  * rationale. */
     87 #if defined(__APPLE__)
     88 #include <mach/mach.h>
     89 #include <mach/mach_vm.h>
     90 #define XM_DUAL_APPLE 1
     91 #else
     92 #define XM_DUAL_APPLE 0
     93 #endif
     94 #if defined(__linux__)
     95 #include <sys/syscall.h>
     96 #define XM_DUAL_LINUX 1
     97 #else
     98 #define XM_DUAL_LINUX 0
     99 #endif
    100 static int xm_to_posix(int p) {
    101   int q = 0;
    102   if (p & KIT_PROT_READ) q |= PROT_READ;
    103   if (p & KIT_PROT_WRITE) q |= PROT_WRITE;
    104   if (p & KIT_PROT_EXEC) q |= PROT_EXEC;
    105   return q;
    106 }
    107 #if XM_DUAL_LINUX && defined(__x86_64__) && defined(MAP_32BIT)
    108 #define XM_MAP_32BIT MAP_32BIT
    109 static uintptr_t g_xm_low_runtime_hint = 0x40000000u;
    110 static void* xm_low_runtime_hint(size_t n) {
    111   uintptr_t p = g_xm_low_runtime_hint;
    112   uintptr_t step = (uintptr_t)((n + 0xffffu) & ~(size_t)0xffffu);
    113   if (step < 0x10000u) step = 0x10000u;
    114   g_xm_low_runtime_hint = p + step + 0x10000u;
    115   if (g_xm_low_runtime_hint > 0x78000000u) g_xm_low_runtime_hint = 0x40000000u;
    116   return (void*)p;
    117 }
    118 #elif XM_DUAL_LINUX
    119 #define XM_MAP_32BIT 0
    120 static void* xm_low_runtime_hint(size_t n) {
    121   (void)n;
    122   return NULL;
    123 }
    124 #endif
    125 typedef struct XmTok {
    126   void* w;
    127   void* r;
    128   size_t n;
    129 } XmTok;
    130 static KitStatus xm_reserve_single(size_t n, KitExecMemRegion* out) {
    131   void* p =
    132       mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
    133   if (p == MAP_FAILED) return KIT_NOMEM;
    134   out->write = out->runtime = p;
    135   out->size = n;
    136   out->token = NULL;
    137   return KIT_OK;
    138 }
    139 static KitStatus xm_reserve(void* u, size_t n, int p, KitExecMemRegion* out) {
    140   (void)u;
    141   if (!out || !n) return KIT_INVALID;
    142   if (!(p & KIT_PROT_EXEC)) return xm_reserve_single(n, out);
    143 #if XM_DUAL_APPLE
    144   {
    145     void* w =
    146         mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
    147     mach_vm_address_t r = 0;
    148     vm_prot_t cur = 0, max = 0;
    149     XmTok* tok;
    150     if (w == MAP_FAILED) return KIT_NOMEM;
    151     if (mach_vm_remap(mach_task_self(), &r, (mach_vm_size_t)n, 0,
    152                       VM_FLAGS_ANYWHERE, mach_task_self(),
    153                       (mach_vm_address_t)(uintptr_t)w, FALSE, &cur, &max,
    154                       VM_INHERIT_NONE) != KERN_SUCCESS) {
    155       munmap(w, n);
    156       return KIT_NOMEM;
    157     }
    158     if (mprotect((void*)(uintptr_t)r, n, PROT_READ) != 0) {
    159       munmap((void*)(uintptr_t)r, n);
    160       munmap(w, n);
    161       return KIT_NOMEM;
    162     }
    163     tok = (XmTok*)malloc(sizeof(*tok));
    164     if (!tok) {
    165       munmap((void*)(uintptr_t)r, n);
    166       munmap(w, n);
    167       return KIT_NOMEM;
    168     }
    169     tok->w = w;
    170     tok->r = (void*)(uintptr_t)r;
    171     tok->n = n;
    172     out->write = w;
    173     out->runtime = (void*)(uintptr_t)r;
    174     out->size = n;
    175     out->token = tok;
    176     return KIT_OK;
    177   }
    178 #elif XM_DUAL_LINUX
    179   {
    180     int fd = (int)syscall(SYS_memfd_create, "kit-jit-test", 0u);
    181     void *w, *r;
    182     XmTok* tok;
    183     if (fd < 0) return KIT_NOMEM;
    184     if (ftruncate(fd, (off_t)n) != 0) {
    185       close(fd);
    186       return KIT_NOMEM;
    187     }
    188     w = mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_SHARED | XM_MAP_32BIT, fd, 0);
    189     if (w == MAP_FAILED) {
    190       close(fd);
    191       return KIT_NOMEM;
    192     }
    193     r = mmap(xm_low_runtime_hint(n), n, PROT_READ, MAP_SHARED | XM_MAP_32BIT,
    194              fd, 0);
    195     close(fd);
    196     if (r == MAP_FAILED) {
    197       munmap(w, n);
    198       return KIT_NOMEM;
    199     }
    200     tok = (XmTok*)malloc(sizeof(*tok));
    201     if (!tok) {
    202       munmap(r, n);
    203       munmap(w, n);
    204       return KIT_NOMEM;
    205     }
    206     tok->w = w;
    207     tok->r = r;
    208     tok->n = n;
    209     out->write = w;
    210     out->runtime = r;
    211     out->size = n;
    212     out->token = tok;
    213     return KIT_OK;
    214   }
    215 #else
    216   return xm_reserve_single(n, out);
    217 #endif
    218 }
    219 static KitStatus xm_protect(void* u, void* a, size_t n, int p) {
    220   (void)u;
    221   return mprotect(a, n, xm_to_posix(p)) == 0 ? KIT_OK : KIT_IO;
    222 }
    223 static void xm_release(void* u, KitExecMemRegion* region) {
    224   (void)u;
    225   if (!region || !region->size) return;
    226   if (region->token) {
    227     XmTok* tok = (XmTok*)region->token;
    228     if (tok->r && tok->r != tok->w) munmap(tok->r, tok->n);
    229     if (tok->w) munmap(tok->w, tok->n);
    230     free(tok);
    231   } else if (region->write) {
    232     munmap(region->write, region->size);
    233   }
    234   region->write = region->runtime = NULL;
    235   region->size = 0;
    236   region->token = NULL;
    237 }
    238 static void xm_flush(void* u, void* a, size_t n) {
    239   (void)u;
    240 #if defined(__aarch64__) || defined(__arm__) || defined(__riscv)
    241 #if defined(__riscv)
    242   __asm__ __volatile__("fence.i" ::: "memory");
    243 #endif
    244   __builtin___clear_cache((char*)a, (char*)a + n);
    245 #else
    246   (void)a;
    247   (void)n;
    248 #endif
    249 }
    250 static KitExecMem g_execmem = {
    251     16 * 1024, xm_reserve, xm_protect, xm_release, xm_flush, NULL,
    252 };
    253 
    254 static int slurp(const char* path, uint8_t** out, size_t* len) {
    255   int fd = open(path, O_RDONLY);
    256   if (fd < 0) return -1;
    257   struct stat sb;
    258   if (fstat(fd, &sb) < 0) {
    259     close(fd);
    260     return -1;
    261   }
    262   size_t n = (size_t)sb.st_size;
    263   uint8_t* buf = malloc(n ? n : 1);
    264   if (!buf) {
    265     close(fd);
    266     return -1;
    267   }
    268   size_t got = 0;
    269   while (got < n) {
    270     ssize_t k = read(fd, buf + got, n - got);
    271     if (k <= 0) {
    272       free(buf);
    273       close(fd);
    274       return -1;
    275     }
    276     got += (size_t)k;
    277   }
    278   close(fd);
    279   *out = buf;
    280   *len = n;
    281   return 0;
    282 }
    283 
    284 /* Default extern resolver: returns address of a static int == 42.
    285  * Used by case 28 (extern_resolver); harmless for any case without
    286  * unresolved symbols since the resolver is never invoked. */
    287 static int g_extern_default_value = 42;
    288 static void* low_extern_default_value(void) {
    289 #if defined(__linux__) && defined(__x86_64__) && defined(MAP_32BIT)
    290   static int* p;
    291   if (!p) {
    292     void* mem = mmap((void*)0x30000000u, 4096, PROT_READ | PROT_WRITE,
    293                      MAP_PRIVATE | MAP_ANON | MAP_32BIT, -1, 0);
    294     if (mem != MAP_FAILED) {
    295       p = (int*)mem;
    296       *p = 42;
    297     }
    298   }
    299   return p ? (void*)p : (void*)&g_extern_default_value;
    300 #else
    301   return &g_extern_default_value;
    302 #endif
    303 }
    304 static void* extern_resolver(void* user, KitSlice name) {
    305   (void)user;
    306   (void)name;
    307   return low_extern_default_value();
    308 }
    309 
    310 /* The JIT resolves thread-local accesses to in-image storage (single-threaded:
    311  * the in-image .tdata/.tbss is the single instance — see the TLS relaxation in
    312  * src/link/link_jit.c). The host still seeds the thread pointer below so the
    313  * freestanding ELF startup convention is honored on hosts that run the image
    314  * natively, but no KitJitTls vtable / per-thread block is needed. */
    315 #if defined(__aarch64__) || defined(__arm64__)
    316 __attribute__((noinline, no_sanitize("address", "undefined"))) static int
    317 call_with_aarch64_tls(int (*fn)(void), void* tls_block) {
    318   void* old_tp;
    319   int result;
    320   __asm__ volatile("mrs %0, tpidr_el0" : "=r"(old_tp)::"memory");
    321   __asm__ volatile("msr tpidr_el0, %0" ::"r"(tls_block) : "memory");
    322   result = fn();
    323   __asm__ volatile("msr tpidr_el0, %0" ::"r"(old_tp) : "memory");
    324   return result;
    325 }
    326 #endif
    327 
    328 #if defined(__x86_64__) && defined(__linux__)
    329 static long x64_arch_prctl_raw(long code, unsigned long addr) {
    330   register long rax __asm__("rax") = 158; /* SYS_arch_prctl */
    331   register long rdi __asm__("rdi") = code;
    332   register long rsi __asm__("rsi") = (long)addr;
    333   __asm__ volatile("syscall"
    334                    : "+r"(rax)
    335                    : "r"(rdi), "r"(rsi)
    336                    : "rcx", "r11", "memory");
    337   return rax;
    338 }
    339 #endif
    340 
    341 typedef struct WasmRunnerMemoryPrefix {
    342   uint8_t* data;
    343   uint64_t pages;
    344   uint64_t max_pages;
    345   uint32_t flags;
    346 } WasmRunnerMemoryPrefix;
    347 
    348 typedef struct WasmRunnerInstanceAlloc {
    349   uint8_t* instance;
    350   uint8_t** memories;
    351   uint32_t nmemories;
    352 } WasmRunnerInstanceAlloc;
    353 
    354 typedef void (*WasmRunnerInitFn)(void*);
    355 typedef int (*WasmRunnerMainFn)(void*);
    356 
    357 #define WASM_RUNNER_MAX_INSTANCE_BYTES (64ull * 1024ull * 1024ull)
    358 #define WASM_RUNNER_MAX_TOTAL_MEMORY_BYTES (1024ull * 1024ull * 1024ull)
    359 
    360 static int wasm_runner_u64_to_size(uint64_t n, size_t* out,
    361                                    const char* what) {
    362   if (n > (uint64_t)SIZE_MAX) {
    363     fprintf(stderr, "jit-runner: wasm %s exceeds host size_t\n", what);
    364     return 1;
    365   }
    366   *out = (size_t)n;
    367   return 0;
    368 }
    369 
    370 static int wasm_runner_page_bytes(uint64_t pages, uint64_t* out) {
    371   if (pages > UINT64_MAX / (uint64_t)KIT_WASM_PAGE_SIZE) {
    372     fprintf(stderr, "jit-runner: wasm memory size overflows\n");
    373     return 1;
    374   }
    375   *out = pages * (uint64_t)KIT_WASM_PAGE_SIZE;
    376   return 0;
    377 }
    378 
    379 static void wasm_runner_free_instance(WasmRunnerInstanceAlloc* alloc) {
    380   if (!alloc) return;
    381   if (alloc->memories) {
    382     for (uint32_t i = 0; i < alloc->nmemories; ++i) free(alloc->memories[i]);
    383     free(alloc->memories);
    384   }
    385   free(alloc->instance);
    386   memset(alloc, 0, sizeof *alloc);
    387 }
    388 
    389 static int wasm_runner_make_instance(KitJit* jit,
    390                                      WasmRunnerInstanceAlloc* out) {
    391   KitWasmRuntimeLayout layout;
    392   WasmRunnerInstanceAlloc alloc;
    393   uint64_t instance_bytes;
    394   uint64_t total_memory_bytes = 0;
    395   size_t instance_size;
    396   KitStatus st;
    397   memset(&alloc, 0, sizeof alloc);
    398   if (!out) return 1;
    399   memset(out, 0, sizeof *out);
    400   st = kit_wasm_get_runtime_layout(jit, &layout);
    401   if (st != KIT_OK) {
    402     fprintf(stderr, "jit-runner: wasm runtime layout metadata %s\n",
    403             st == KIT_NOT_FOUND ? "missing" : "malformed");
    404     return 1;
    405   }
    406   instance_bytes = layout.instance_size ? layout.instance_size : 1u;
    407   if (instance_bytes > WASM_RUNNER_MAX_INSTANCE_BYTES) {
    408     fprintf(stderr, "jit-runner: wasm instance too large: %llu bytes\n",
    409             (unsigned long long)instance_bytes);
    410     return 1;
    411   }
    412   if (wasm_runner_u64_to_size(instance_bytes, &instance_size, "instance") != 0)
    413     return 1;
    414   alloc.instance = (uint8_t*)calloc(1, instance_size);
    415   alloc.nmemories = layout.nmemories;
    416   if (!alloc.instance) {
    417     fprintf(stderr, "jit-runner: out of memory\n");
    418     return 1;
    419   }
    420   if (layout.nmemories) {
    421     if ((uint64_t)layout.nmemories > (uint64_t)SIZE_MAX / sizeof(uint8_t*)) {
    422       fprintf(stderr, "jit-runner: wasm memory count too large\n");
    423       wasm_runner_free_instance(&alloc);
    424       return 1;
    425     }
    426     alloc.memories = (uint8_t**)calloc(layout.nmemories, sizeof(uint8_t*));
    427     if (!alloc.memories) {
    428       fprintf(stderr, "jit-runner: out of memory\n");
    429       wasm_runner_free_instance(&alloc);
    430       return 1;
    431     }
    432   }
    433   for (uint32_t i = 0; i < layout.nmemories; ++i) {
    434     const KitWasmMemoryLayout* ml = &layout.memories[i];
    435     uint64_t mem_bytes;
    436     size_t mem_size;
    437     WasmRunnerMemoryPrefix* rec;
    438     if (ml->max_pages < ml->min_pages) {
    439       fprintf(stderr, "jit-runner: wasm memory maximum below minimum\n");
    440       wasm_runner_free_instance(&alloc);
    441       return 1;
    442     }
    443     if (ml->offset > instance_bytes ||
    444         instance_bytes - ml->offset < sizeof(WasmRunnerMemoryPrefix)) {
    445       fprintf(stderr, "jit-runner: wasm memory record outside instance\n");
    446       wasm_runner_free_instance(&alloc);
    447       return 1;
    448     }
    449     if (wasm_runner_page_bytes(ml->max_pages, &mem_bytes) != 0) {
    450       wasm_runner_free_instance(&alloc);
    451       return 1;
    452     }
    453     if (mem_bytes > WASM_RUNNER_MAX_TOTAL_MEMORY_BYTES ||
    454         total_memory_bytes > WASM_RUNNER_MAX_TOTAL_MEMORY_BYTES - mem_bytes) {
    455       fprintf(stderr,
    456               "jit-runner: wasm linear memory reservation exceeds %llu bytes\n",
    457               (unsigned long long)WASM_RUNNER_MAX_TOTAL_MEMORY_BYTES);
    458       wasm_runner_free_instance(&alloc);
    459       return 1;
    460     }
    461     total_memory_bytes += mem_bytes;
    462     if (wasm_runner_u64_to_size(mem_bytes, &mem_size, "linear memory") != 0) {
    463       wasm_runner_free_instance(&alloc);
    464       return 1;
    465     }
    466     if (mem_size) {
    467       alloc.memories[i] = (uint8_t*)calloc(1, mem_size);
    468       if (!alloc.memories[i]) {
    469         fprintf(stderr, "jit-runner: out of memory\n");
    470         wasm_runner_free_instance(&alloc);
    471         return 1;
    472       }
    473     }
    474     rec = (WasmRunnerMemoryPrefix*)(alloc.instance + ml->offset);
    475     rec->data = alloc.memories[i];
    476   }
    477   *out = alloc;
    478   return 0;
    479 }
    480 
    481 static int wasm_runner_host_add_type(const KitWasmImportType* type) {
    482   return type && type->nparams == 2u && type->nresults == 1u &&
    483          type->params[0] == KIT_WASM_VAL_I32 &&
    484          type->params[1] == KIT_WASM_VAL_I32 &&
    485          type->results[0] == KIT_WASM_VAL_I32;
    486 }
    487 
    488 static void* wasm_runner_resolve_import(void* user, const char* module,
    489                                         const char* field,
    490                                         const KitWasmImportType* type) {
    491   (void)user;
    492   if (!module || !field) return NULL;
    493   if (strcmp(module, "env") == 0 && strcmp(field, "host_add") == 0 &&
    494       wasm_runner_host_add_type(type))
    495     return (void*)(uintptr_t)test_host_add;
    496   return NULL;
    497 }
    498 
    499 int main(int argc, char** argv) {
    500   {
    501     long ps = sysconf(_SC_PAGESIZE);
    502     if (ps > 0) g_execmem.page_size = (size_t)ps;
    503   }
    504   int gc_sections = 0;
    505   int use_resolver = 0;
    506   int next_archive = 0;
    507   int next_whole = 0;
    508   /* --check-absent SYM:  after link, verify symbol is not in image.
    509    * --check-present SYM: after link, verify symbol IS in image. */
    510   const char* check_absent = NULL;
    511   const char* check_present = NULL;
    512   const char* entry_sym = "test_main";
    513   const char* script_path = NULL;
    514 
    515   KitSlice objs[64];
    516   const char* obj_names[64];
    517   KitLinkArchiveInput archives[16];
    518   uint32_t nobj = 0, narc = 0;
    519   uint8_t* bufs[80];
    520   int nbufs = 0;
    521 
    522   for (int i = 1; i < argc; i++) {
    523     if (!strcmp(argv[i], "--gc-sections")) {
    524       gc_sections = 1;
    525     } else if (!strcmp(argv[i], "--use-resolver")) {
    526       use_resolver = 1;
    527     } else if (!strcmp(argv[i], "--archive")) {
    528       next_archive = 1;
    529     } else if (!strcmp(argv[i], "--whole-archive")) {
    530       next_whole = 1;
    531       next_archive = 1;
    532     } else if (!strcmp(argv[i], "--check-absent") && i + 1 < argc) {
    533       check_absent = argv[++i];
    534     } else if (!strcmp(argv[i], "--check-present") && i + 1 < argc) {
    535       check_present = argv[++i];
    536     } else if (!strcmp(argv[i], "--linker-script") && i + 1 < argc) {
    537       script_path = argv[++i];
    538     } else if (!strcmp(argv[i], "--entry") && i + 1 < argc) {
    539       entry_sym = argv[++i];
    540     } else {
    541       uint8_t* data;
    542       size_t len;
    543       if (slurp(argv[i], &data, &len)) {
    544         fprintf(stderr, "jit-runner: cannot read %s\n", argv[i]);
    545         return 2;
    546       }
    547       bufs[nbufs++] = data;
    548       if (next_archive) {
    549         KitLinkArchiveInput* a = &archives[narc++];
    550         memset(a, 0, sizeof(*a));
    551         a->name = kit_slice_cstr(argv[i]);
    552         a->bytes.data = data;
    553         a->bytes.len = len;
    554         a->whole_archive = (uint8_t)next_whole;
    555         next_archive = 0;
    556         next_whole = 0;
    557       } else {
    558         obj_names[nobj] = argv[i];
    559         KitSlice* o = &objs[nobj++];
    560         memset(o, 0, sizeof(*o));
    561         o->data = data;
    562         o->len = len;
    563       }
    564     }
    565   }
    566 
    567   KitTargetSpec target;
    568   if (kit_test_target_init(&target) != 0) {
    569     fprintf(stderr, "jit_runner: kit_test_target_init failed\n");
    570     return 2;
    571   }
    572 
    573   KitContext ctx;
    574   memset(&ctx, 0, sizeof(ctx));
    575   ctx.heap = &g_heap;
    576   ctx.diag = &g_diag;
    577   ctx.now = -1;
    578 
    579   KitTargetOptions target_opts;
    580   memset(&target_opts, 0, sizeof target_opts);
    581   target_opts.spec = target;
    582   KitTarget* kt = NULL;
    583   if (kit_target_new(&ctx, &target_opts, &kt) != KIT_OK || !kt) {
    584     fprintf(stderr, "jit-runner: target_new failed\n");
    585     return 2;
    586   }
    587   KitCompiler* c = NULL;
    588   if (kit_compiler_new(kt, &ctx, &c) != KIT_OK || !c) {
    589     fprintf(stderr, "jit-runner: compiler_new failed\n");
    590     kit_target_free(kt);
    591     return 2;
    592   }
    593 
    594   KitJitHost jhost;
    595   memset(&jhost, 0, sizeof(jhost));
    596   jhost.execmem = &g_execmem;
    597 
    598   KitLinkSessionOptions opts;
    599   KitLinkSession* link = NULL;
    600   KitLinkScript* script = NULL;
    601   memset(&opts, 0, sizeof(opts));
    602   opts.output_kind = KIT_LINK_OUTPUT_JIT;
    603   opts.entry = kit_slice_cstr(entry_sym);
    604   opts.gc_sections = gc_sections;
    605   opts.jit_host = &jhost;
    606   if (use_resolver) {
    607     opts.extern_resolver = extern_resolver;
    608     opts.extern_resolver_user = NULL;
    609   }
    610 
    611   if (script_path) {
    612     uint8_t* sbytes;
    613     size_t slen;
    614     if (slurp(script_path, &sbytes, &slen)) {
    615       fprintf(stderr, "jit-runner: cannot read %s\n", script_path);
    616       free_compiler_target(c, kt);
    617       return 2;
    618     }
    619     KitSlice script_text = {.s = (const char*)sbytes, .len = slen};
    620     KitStatus prc = kit_link_script_parse(&ctx, script_text, &script);
    621     free(sbytes);
    622     if (prc != KIT_OK) {
    623       fprintf(stderr, "jit-runner: linker script parse failed: %s\n",
    624               script_path);
    625       free_compiler_target(c, kt);
    626       return 1;
    627     }
    628     opts.linker_script = script;
    629   }
    630 
    631   KitJit* jit = NULL;
    632   KitStatus rc = kit_link_session_new(c, &opts, &link);
    633   for (uint32_t i = 0; rc == KIT_OK && i < nobj; ++i)
    634     rc = kit_link_session_add_obj_bytes(link, kit_slice_cstr(obj_names[i]),
    635                                         &objs[i]);
    636   for (uint32_t i = 0; rc == KIT_OK && i < narc; ++i)
    637     rc = kit_link_session_add_archive_bytes(link, &archives[i]);
    638   if (rc == KIT_OK) rc = kit_link_session_jit(link, &jit);
    639   if (rc != KIT_OK || !jit) {
    640     kit_link_session_free(link);
    641     if (script) kit_link_script_free(&ctx, script);
    642     for (int i = 0; i < nbufs; i++) free(bufs[i]);
    643     free_compiler_target(c, kt);
    644     return 1;
    645   }
    646   kit_link_session_free(link);
    647   if (script) kit_link_script_free(&ctx, script);
    648   for (int i = 0; i < nbufs; i++) free(bufs[i]);
    649 
    650   /* Sanity: a never-defined sentinel must not be found (covers case 29). */
    651   if (kit_jit_lookup(jit, KIT_SLICE_LIT("__kit_test_sentinel__"))) {
    652     fprintf(stderr, "jit-runner: sentinel lookup unexpectedly non-NULL\n");
    653     kit_jit_run_dtors(jit);
    654     kit_jit_free(jit);
    655     free_compiler_target(c, kt);
    656     return 1;
    657   }
    658 
    659   /* --check-absent SYM: verify symbol was removed (e.g. by --gc-sections). */
    660   if (check_absent) {
    661     int absent_ok = kit_jit_lookup(jit, kit_slice_cstr(check_absent)) == NULL;
    662     if (!absent_ok)
    663       fprintf(stderr, "jit-runner: symbol '%s' present but expected absent\n",
    664               check_absent);
    665     kit_jit_run_dtors(jit);
    666     kit_jit_free(jit);
    667     free_compiler_target(c, kt);
    668     return absent_ok ? 0 : 1;
    669   }
    670 
    671   /* --check-present SYM: verify symbol survives the link / GC. */
    672   if (check_present) {
    673     int present_ok = kit_jit_lookup(jit, kit_slice_cstr(check_present)) != NULL;
    674     if (!present_ok)
    675       fprintf(stderr, "jit-runner: symbol '%s' missing but expected present\n",
    676               check_present);
    677     kit_jit_run_dtors(jit);
    678     kit_jit_free(jit);
    679     free_compiler_target(c, kt);
    680     return present_ok ? 0 : 1;
    681   }
    682 
    683   void* wasm_init = kit_jit_lookup(jit, KIT_SLICE_LIT("__kit_wasm_init"));
    684   void* entry = kit_jit_lookup(jit, kit_slice_cstr(entry_sym));
    685   int (*fn)(void) = entry;
    686 
    687   /* TLS local-exec setup.  Build the static TLS block and install the
    688    * target thread pointer immediately before entering JITed code. */
    689 #if defined(__aarch64__) || defined(__arm64__) || \
    690     (defined(__x86_64__) && defined(__linux__))
    691   static char tls_block[8192] __attribute__((aligned(16)));
    692 #if defined(__x86_64__) && defined(__linux__)
    693   unsigned long old_fs = 0;
    694   int restore_fs = 0;
    695 #endif
    696   {
    697     char* td_start = (char*)kit_jit_lookup(jit, KIT_SLICE_LIT("__tdata_start"));
    698     char* td_end = (char*)kit_jit_lookup(jit, KIT_SLICE_LIT("__tdata_end"));
    699     unsigned long bs_n = (unsigned long)(unsigned long long)kit_jit_lookup(
    700         jit, KIT_SLICE_LIT("__tbss_size"));
    701     if (td_start && td_end) {
    702       unsigned long td_n = (unsigned long)(td_end - td_start);
    703       unsigned long i;
    704       if (td_n == 0 && bs_n == 0) goto no_static_tls;
    705       /* Plain loops at -O0 stay loops; do NOT use memcpy/memset
    706        * here — those go through dyld's stub binder on first
    707        * call and clobber TPIDR_EL0. */
    708 #if defined(__x86_64__) && defined(__linux__)
    709       char* tcb = tls_block + sizeof(tls_block) - 64;
    710       char* tls = tcb - (td_n + bs_n);
    711       *(void**)tcb = tcb;
    712       for (i = 0; i < td_n; ++i) tls[i] = td_start[i];
    713       for (i = 0; i < bs_n; ++i) tls[td_n + i] = 0;
    714       if (x64_arch_prctl_raw(0x1003, (unsigned long)&old_fs) == 0 &&
    715           x64_arch_prctl_raw(0x1002, (unsigned long)tcb) == 0) {
    716         restore_fs = 1;
    717       }
    718 #else
    719       for (i = 0; i < td_n; ++i) tls_block[16 + i] = td_start[i];
    720       for (i = 0; i < bs_n; ++i) tls_block[16 + td_n + i] = 0;
    721 #endif
    722     }
    723   no_static_tls:;
    724   }
    725 #endif
    726 
    727   int result;
    728   if (wasm_init && entry) {
    729     WasmRunnerInstanceAlloc alloc;
    730     if (wasm_runner_make_instance(jit, &alloc) != 0) {
    731       kit_jit_run_dtors(jit);
    732       kit_jit_free(jit);
    733       free_compiler_target(c, kt);
    734       return 1;
    735     }
    736     {
    737       KitStatus bind_st = kit_wasm_bind_host_imports(
    738           c, jit, (KitWasmInstance*)alloc.instance, NULL, 0,
    739           wasm_runner_resolve_import, NULL);
    740       if (bind_st != KIT_OK) {
    741         fprintf(stderr, "jit-runner: wasm host import %s\n",
    742                 bind_st == KIT_NOT_FOUND
    743                     ? "unresolved"
    744                     : (bind_st == KIT_UNSUPPORTED ? "kind unsupported"
    745                                                   : "bind failed"));
    746         wasm_runner_free_instance(&alloc);
    747         kit_jit_run_dtors(jit);
    748         kit_jit_free(jit);
    749         free_compiler_target(c, kt);
    750         return 1;
    751       }
    752     }
    753     ((WasmRunnerInitFn)wasm_init)(alloc.instance);
    754     result = ((WasmRunnerMainFn)entry)(alloc.instance);
    755     wasm_runner_free_instance(&alloc);
    756   } else if (fn) {
    757 #if defined(__aarch64__) || defined(__arm64__)
    758     result = call_with_aarch64_tls(fn, tls_block);
    759 #else
    760     result = fn();
    761 #endif
    762 #if defined(__x86_64__) && defined(__linux__)
    763     if (restore_fs) (void)x64_arch_prctl_raw(0x1002, old_fs);
    764 #endif
    765   } else {
    766     result = 1;
    767   }
    768 
    769 #if defined(__x86_64__) && defined(__linux__)
    770   if (restore_fs)
    771     (void)x64_arch_prctl_raw(
    772         0x1002, (unsigned long)(tls_block + sizeof(tls_block) - 64));
    773 #endif
    774   kit_jit_run_dtors(jit);
    775 #if defined(__x86_64__) && defined(__linux__)
    776   if (restore_fs) (void)x64_arch_prctl_raw(0x1002, old_fs);
    777 #endif
    778 
    779   if (result == 0) {
    780     int (*post)(void) = kit_jit_lookup(jit, KIT_SLICE_LIT("test_post_fini"));
    781     if (post) result = post();
    782   }
    783 
    784   kit_jit_free(jit);
    785   free_compiler_target(c, kt);
    786   return result;
    787 }