jit_runner.c (24474B)
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, const char* what) { 361 if (n > (uint64_t)SIZE_MAX) { 362 fprintf(stderr, "jit-runner: wasm %s exceeds host size_t\n", what); 363 return 1; 364 } 365 *out = (size_t)n; 366 return 0; 367 } 368 369 static int wasm_runner_page_bytes(uint64_t pages, uint64_t* out) { 370 if (pages > UINT64_MAX / (uint64_t)KIT_WASM_PAGE_SIZE) { 371 fprintf(stderr, "jit-runner: wasm memory size overflows\n"); 372 return 1; 373 } 374 *out = pages * (uint64_t)KIT_WASM_PAGE_SIZE; 375 return 0; 376 } 377 378 static void wasm_runner_free_instance(WasmRunnerInstanceAlloc* alloc) { 379 if (!alloc) return; 380 if (alloc->memories) { 381 for (uint32_t i = 0; i < alloc->nmemories; ++i) free(alloc->memories[i]); 382 free(alloc->memories); 383 } 384 free(alloc->instance); 385 memset(alloc, 0, sizeof *alloc); 386 } 387 388 static int wasm_runner_make_instance(KitJit* jit, 389 WasmRunnerInstanceAlloc* out) { 390 KitWasmRuntimeLayout layout; 391 WasmRunnerInstanceAlloc alloc; 392 uint64_t instance_bytes; 393 uint64_t total_memory_bytes = 0; 394 size_t instance_size; 395 KitStatus st; 396 memset(&alloc, 0, sizeof alloc); 397 if (!out) return 1; 398 memset(out, 0, sizeof *out); 399 st = kit_wasm_get_runtime_layout(jit, &layout); 400 if (st != KIT_OK) { 401 fprintf(stderr, "jit-runner: wasm runtime layout metadata %s\n", 402 st == KIT_NOT_FOUND ? "missing" : "malformed"); 403 return 1; 404 } 405 instance_bytes = layout.instance_size ? layout.instance_size : 1u; 406 if (instance_bytes > WASM_RUNNER_MAX_INSTANCE_BYTES) { 407 fprintf(stderr, "jit-runner: wasm instance too large: %llu bytes\n", 408 (unsigned long long)instance_bytes); 409 return 1; 410 } 411 if (wasm_runner_u64_to_size(instance_bytes, &instance_size, "instance") != 0) 412 return 1; 413 alloc.instance = (uint8_t*)calloc(1, instance_size); 414 alloc.nmemories = layout.nmemories; 415 if (!alloc.instance) { 416 fprintf(stderr, "jit-runner: out of memory\n"); 417 return 1; 418 } 419 if (layout.nmemories) { 420 if ((uint64_t)layout.nmemories > (uint64_t)SIZE_MAX / sizeof(uint8_t*)) { 421 fprintf(stderr, "jit-runner: wasm memory count too large\n"); 422 wasm_runner_free_instance(&alloc); 423 return 1; 424 } 425 alloc.memories = (uint8_t**)calloc(layout.nmemories, sizeof(uint8_t*)); 426 if (!alloc.memories) { 427 fprintf(stderr, "jit-runner: out of memory\n"); 428 wasm_runner_free_instance(&alloc); 429 return 1; 430 } 431 } 432 for (uint32_t i = 0; i < layout.nmemories; ++i) { 433 const KitWasmMemoryLayout* ml = &layout.memories[i]; 434 uint64_t mem_bytes; 435 size_t mem_size; 436 WasmRunnerMemoryPrefix* rec; 437 if (ml->max_pages < ml->min_pages) { 438 fprintf(stderr, "jit-runner: wasm memory maximum below minimum\n"); 439 wasm_runner_free_instance(&alloc); 440 return 1; 441 } 442 if (ml->offset > instance_bytes || 443 instance_bytes - ml->offset < sizeof(WasmRunnerMemoryPrefix)) { 444 fprintf(stderr, "jit-runner: wasm memory record outside instance\n"); 445 wasm_runner_free_instance(&alloc); 446 return 1; 447 } 448 if (wasm_runner_page_bytes(ml->max_pages, &mem_bytes) != 0) { 449 wasm_runner_free_instance(&alloc); 450 return 1; 451 } 452 if (mem_bytes > WASM_RUNNER_MAX_TOTAL_MEMORY_BYTES || 453 total_memory_bytes > WASM_RUNNER_MAX_TOTAL_MEMORY_BYTES - mem_bytes) { 454 fprintf(stderr, 455 "jit-runner: wasm linear memory reservation exceeds %llu bytes\n", 456 (unsigned long long)WASM_RUNNER_MAX_TOTAL_MEMORY_BYTES); 457 wasm_runner_free_instance(&alloc); 458 return 1; 459 } 460 total_memory_bytes += mem_bytes; 461 if (wasm_runner_u64_to_size(mem_bytes, &mem_size, "linear memory") != 0) { 462 wasm_runner_free_instance(&alloc); 463 return 1; 464 } 465 if (mem_size) { 466 alloc.memories[i] = (uint8_t*)calloc(1, mem_size); 467 if (!alloc.memories[i]) { 468 fprintf(stderr, "jit-runner: out of memory\n"); 469 wasm_runner_free_instance(&alloc); 470 return 1; 471 } 472 } 473 rec = (WasmRunnerMemoryPrefix*)(alloc.instance + ml->offset); 474 rec->data = alloc.memories[i]; 475 } 476 *out = alloc; 477 return 0; 478 } 479 480 static int wasm_runner_host_add_type(const KitWasmImportType* type) { 481 return type && type->nparams == 2u && type->nresults == 1u && 482 type->params[0] == KIT_WASM_VAL_I32 && 483 type->params[1] == KIT_WASM_VAL_I32 && 484 type->results[0] == KIT_WASM_VAL_I32; 485 } 486 487 static void* wasm_runner_resolve_import(void* user, const char* module, 488 const char* field, 489 const KitWasmImportType* type) { 490 (void)user; 491 if (!module || !field) return NULL; 492 if (strcmp(module, "env") == 0 && strcmp(field, "host_add") == 0 && 493 wasm_runner_host_add_type(type)) 494 return (void*)(uintptr_t)test_host_add; 495 return NULL; 496 } 497 498 int main(int argc, char** argv) { 499 { 500 long ps = sysconf(_SC_PAGESIZE); 501 if (ps > 0) g_execmem.page_size = (size_t)ps; 502 } 503 int gc_sections = 0; 504 int use_resolver = 0; 505 int next_archive = 0; 506 int next_whole = 0; 507 /* --check-absent SYM: after link, verify symbol is not in image. 508 * --check-present SYM: after link, verify symbol IS in image. */ 509 const char* check_absent = NULL; 510 const char* check_present = NULL; 511 const char* entry_sym = "test_main"; 512 const char* script_path = NULL; 513 514 KitSlice objs[64]; 515 const char* obj_names[64]; 516 KitLinkArchiveInput archives[16]; 517 uint32_t nobj = 0, narc = 0; 518 uint8_t* bufs[80]; 519 int nbufs = 0; 520 521 for (int i = 1; i < argc; i++) { 522 if (!strcmp(argv[i], "--gc-sections")) { 523 gc_sections = 1; 524 } else if (!strcmp(argv[i], "--use-resolver")) { 525 use_resolver = 1; 526 } else if (!strcmp(argv[i], "--archive")) { 527 next_archive = 1; 528 } else if (!strcmp(argv[i], "--whole-archive")) { 529 next_whole = 1; 530 next_archive = 1; 531 } else if (!strcmp(argv[i], "--check-absent") && i + 1 < argc) { 532 check_absent = argv[++i]; 533 } else if (!strcmp(argv[i], "--check-present") && i + 1 < argc) { 534 check_present = argv[++i]; 535 } else if (!strcmp(argv[i], "--linker-script") && i + 1 < argc) { 536 script_path = argv[++i]; 537 } else if (!strcmp(argv[i], "--entry") && i + 1 < argc) { 538 entry_sym = argv[++i]; 539 } else { 540 uint8_t* data; 541 size_t len; 542 if (slurp(argv[i], &data, &len)) { 543 fprintf(stderr, "jit-runner: cannot read %s\n", argv[i]); 544 return 2; 545 } 546 bufs[nbufs++] = data; 547 if (next_archive) { 548 KitLinkArchiveInput* a = &archives[narc++]; 549 memset(a, 0, sizeof(*a)); 550 a->name = kit_slice_cstr(argv[i]); 551 a->bytes.data = data; 552 a->bytes.len = len; 553 a->whole_archive = (uint8_t)next_whole; 554 next_archive = 0; 555 next_whole = 0; 556 } else { 557 obj_names[nobj] = argv[i]; 558 KitSlice* o = &objs[nobj++]; 559 memset(o, 0, sizeof(*o)); 560 o->data = data; 561 o->len = len; 562 } 563 } 564 } 565 566 KitTargetSpec target; 567 if (kit_test_target_init(&target) != 0) { 568 fprintf(stderr, "jit_runner: kit_test_target_init failed\n"); 569 return 2; 570 } 571 572 KitContext ctx; 573 memset(&ctx, 0, sizeof(ctx)); 574 ctx.heap = &g_heap; 575 ctx.diag = &g_diag; 576 ctx.now = -1; 577 578 KitTargetOptions target_opts; 579 memset(&target_opts, 0, sizeof target_opts); 580 target_opts.spec = target; 581 KitTarget* kt = NULL; 582 if (kit_target_new(&ctx, &target_opts, &kt) != KIT_OK || !kt) { 583 fprintf(stderr, "jit-runner: target_new failed\n"); 584 return 2; 585 } 586 KitCompiler* c = NULL; 587 if (kit_compiler_new(kt, &ctx, &c) != KIT_OK || !c) { 588 fprintf(stderr, "jit-runner: compiler_new failed\n"); 589 kit_target_free(kt); 590 return 2; 591 } 592 593 KitJitHost jhost; 594 memset(&jhost, 0, sizeof(jhost)); 595 jhost.execmem = &g_execmem; 596 597 KitLinkSessionOptions opts; 598 KitLinkSession* link = NULL; 599 KitLinkScript* script = NULL; 600 memset(&opts, 0, sizeof(opts)); 601 opts.output_kind = KIT_LINK_OUTPUT_JIT; 602 opts.entry = kit_slice_cstr(entry_sym); 603 opts.gc_sections = gc_sections; 604 opts.jit_host = &jhost; 605 if (use_resolver) { 606 opts.extern_resolver = extern_resolver; 607 opts.extern_resolver_user = NULL; 608 } 609 610 if (script_path) { 611 uint8_t* sbytes; 612 size_t slen; 613 if (slurp(script_path, &sbytes, &slen)) { 614 fprintf(stderr, "jit-runner: cannot read %s\n", script_path); 615 free_compiler_target(c, kt); 616 return 2; 617 } 618 KitSlice script_text = {.s = (const char*)sbytes, .len = slen}; 619 KitStatus prc = kit_link_script_parse(&ctx, script_text, &script); 620 free(sbytes); 621 if (prc != KIT_OK) { 622 fprintf(stderr, "jit-runner: linker script parse failed: %s\n", 623 script_path); 624 free_compiler_target(c, kt); 625 return 1; 626 } 627 opts.linker_script = script; 628 } 629 630 KitJit* jit = NULL; 631 KitStatus rc = kit_link_session_new(c, &opts, &link); 632 for (uint32_t i = 0; rc == KIT_OK && i < nobj; ++i) 633 rc = kit_link_session_add_obj_bytes(link, kit_slice_cstr(obj_names[i]), 634 &objs[i]); 635 for (uint32_t i = 0; rc == KIT_OK && i < narc; ++i) 636 rc = kit_link_session_add_archive_bytes(link, &archives[i]); 637 if (rc == KIT_OK) rc = kit_link_session_jit(link, &jit); 638 if (rc != KIT_OK || !jit) { 639 kit_link_session_free(link); 640 if (script) kit_link_script_free(&ctx, script); 641 for (int i = 0; i < nbufs; i++) free(bufs[i]); 642 free_compiler_target(c, kt); 643 return 1; 644 } 645 kit_link_session_free(link); 646 if (script) kit_link_script_free(&ctx, script); 647 for (int i = 0; i < nbufs; i++) free(bufs[i]); 648 649 /* Sanity: a never-defined sentinel must not be found (covers case 29). */ 650 if (kit_jit_lookup(jit, KIT_SLICE_LIT("__kit_test_sentinel__"))) { 651 fprintf(stderr, "jit-runner: sentinel lookup unexpectedly non-NULL\n"); 652 kit_jit_run_dtors(jit); 653 kit_jit_free(jit); 654 free_compiler_target(c, kt); 655 return 1; 656 } 657 658 /* --check-absent SYM: verify symbol was removed (e.g. by --gc-sections). */ 659 if (check_absent) { 660 int absent_ok = kit_jit_lookup(jit, kit_slice_cstr(check_absent)) == NULL; 661 if (!absent_ok) 662 fprintf(stderr, "jit-runner: symbol '%s' present but expected absent\n", 663 check_absent); 664 kit_jit_run_dtors(jit); 665 kit_jit_free(jit); 666 free_compiler_target(c, kt); 667 return absent_ok ? 0 : 1; 668 } 669 670 /* --check-present SYM: verify symbol survives the link / GC. */ 671 if (check_present) { 672 int present_ok = kit_jit_lookup(jit, kit_slice_cstr(check_present)) != NULL; 673 if (!present_ok) 674 fprintf(stderr, "jit-runner: symbol '%s' missing but expected present\n", 675 check_present); 676 kit_jit_run_dtors(jit); 677 kit_jit_free(jit); 678 free_compiler_target(c, kt); 679 return present_ok ? 0 : 1; 680 } 681 682 void* wasm_init = kit_jit_lookup(jit, KIT_SLICE_LIT("__kit_wasm_init")); 683 void* entry = kit_jit_lookup(jit, kit_slice_cstr(entry_sym)); 684 int (*fn)(void) = entry; 685 686 /* TLS local-exec setup. Build the static TLS block and install the 687 * target thread pointer immediately before entering JITed code. */ 688 #if defined(__aarch64__) || defined(__arm64__) || \ 689 (defined(__x86_64__) && defined(__linux__)) 690 static char tls_block[8192] __attribute__((aligned(16))); 691 #if defined(__x86_64__) && defined(__linux__) 692 unsigned long old_fs = 0; 693 int restore_fs = 0; 694 #endif 695 { 696 char* td_start = (char*)kit_jit_lookup(jit, KIT_SLICE_LIT("__tdata_start")); 697 char* td_end = (char*)kit_jit_lookup(jit, KIT_SLICE_LIT("__tdata_end")); 698 unsigned long bs_n = (unsigned long)(unsigned long long)kit_jit_lookup( 699 jit, KIT_SLICE_LIT("__tbss_size")); 700 if (td_start && td_end) { 701 unsigned long td_n = (unsigned long)(td_end - td_start); 702 unsigned long i; 703 if (td_n == 0 && bs_n == 0) goto no_static_tls; 704 /* Plain loops at -O0 stay loops; do NOT use memcpy/memset 705 * here — those go through dyld's stub binder on first 706 * call and clobber TPIDR_EL0. */ 707 #if defined(__x86_64__) && defined(__linux__) 708 char* tcb = tls_block + sizeof(tls_block) - 64; 709 char* tls = tcb - (td_n + bs_n); 710 *(void**)tcb = tcb; 711 for (i = 0; i < td_n; ++i) tls[i] = td_start[i]; 712 for (i = 0; i < bs_n; ++i) tls[td_n + i] = 0; 713 if (x64_arch_prctl_raw(0x1003, (unsigned long)&old_fs) == 0 && 714 x64_arch_prctl_raw(0x1002, (unsigned long)tcb) == 0) { 715 restore_fs = 1; 716 } 717 #else 718 for (i = 0; i < td_n; ++i) tls_block[16 + i] = td_start[i]; 719 for (i = 0; i < bs_n; ++i) tls_block[16 + td_n + i] = 0; 720 #endif 721 } 722 no_static_tls:; 723 } 724 #endif 725 726 int result; 727 if (wasm_init && entry) { 728 WasmRunnerInstanceAlloc alloc; 729 if (wasm_runner_make_instance(jit, &alloc) != 0) { 730 kit_jit_run_dtors(jit); 731 kit_jit_free(jit); 732 free_compiler_target(c, kt); 733 return 1; 734 } 735 { 736 KitStatus bind_st = 737 kit_wasm_bind_host_imports(c, jit, (KitWasmInstance*)alloc.instance, 738 NULL, 0, wasm_runner_resolve_import, NULL); 739 if (bind_st != KIT_OK) { 740 fprintf(stderr, "jit-runner: wasm host import %s\n", 741 bind_st == KIT_NOT_FOUND 742 ? "unresolved" 743 : (bind_st == KIT_UNSUPPORTED ? "kind unsupported" 744 : "bind failed")); 745 wasm_runner_free_instance(&alloc); 746 kit_jit_run_dtors(jit); 747 kit_jit_free(jit); 748 free_compiler_target(c, kt); 749 return 1; 750 } 751 } 752 ((WasmRunnerInitFn)wasm_init)(alloc.instance); 753 result = ((WasmRunnerMainFn)entry)(alloc.instance); 754 wasm_runner_free_instance(&alloc); 755 } else if (fn) { 756 #if defined(__aarch64__) || defined(__arm64__) 757 result = call_with_aarch64_tls(fn, tls_block); 758 #else 759 result = fn(); 760 #endif 761 #if defined(__x86_64__) && defined(__linux__) 762 if (restore_fs) (void)x64_arch_prctl_raw(0x1002, old_fs); 763 #endif 764 } else { 765 result = 1; 766 } 767 768 #if defined(__x86_64__) && defined(__linux__) 769 if (restore_fs) 770 (void)x64_arch_prctl_raw( 771 0x1002, (unsigned long)(tls_block + sizeof(tls_block) - 64)); 772 #endif 773 kit_jit_run_dtors(jit); 774 #if defined(__x86_64__) && defined(__linux__) 775 if (restore_fs) (void)x64_arch_prctl_raw(0x1002, old_fs); 776 #endif 777 778 if (result == 0) { 779 int (*post)(void) = kit_jit_lookup(jit, KIT_SLICE_LIT("test_post_fini")); 780 if (post) result = post(); 781 } 782 783 kit_jit_free(jit); 784 free_compiler_target(c, kt); 785 return result; 786 }