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 }