asm_runner.c (23195B)
1 /* asm-runner — file-driven assembler/disassembler test runner. 2 * 3 * asm-runner --encode IN.s OUT.hex # kit as -> raw .text bytes (hex) 4 * asm-runner --decode IN.hex OUT.txt # kit_disasm_iter_* over bytes 5 * asm-runner --listing IN.bin OUT.txt # kit_obj_disasm over an ELF 6 * asm-runner --emit IN.s OUT.o # kit_compile_obj_emit -> ELF .o 7 * asm-runner --jit IN.s # parse + link_jit -> test_main() 8 * 9 * Exclusively uses the public kit.h surface (same path real driver 10 * consumers take). Built once; the shell runner walks the sub-corpora 11 * and invokes one mode per case-path pair. 12 * 13 * Phase 1: asm_parse and the disasm iterator are still stubs in 14 * src/api/stubs.c. The runner returns nonzero when the underlying API 15 * fails; smoke cases each carry a .skip sidecar so the harness reports 16 * them cleanly until phases 3 and 4 land. 17 * 18 * The execmem (W^X) boilerplate mirrors test/parse/harness/parse_runner.c 19 * — strict dual-mapping on Apple/Linux, single mapping elsewhere. Only 20 * --jit exercises it. */ 21 22 #ifndef _GNU_SOURCE 23 #define _GNU_SOURCE 24 #endif 25 26 #include <ctype.h> 27 #include <fcntl.h> 28 #include <kit/compile.h> 29 #include <kit/core.h> 30 #include <kit/disasm.h> 31 #include <kit/jit.h> 32 #include <kit/link.h> 33 #include <kit/object.h> 34 #include <stdarg.h> 35 #include <stdint.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <sys/mman.h> 40 #include <sys/stat.h> 41 #include <unistd.h> 42 43 #include "lib/kit_test_target.h" 44 45 /* ---- env: heap, diag ---- */ 46 47 static void* h_alloc(KitHeap* h, size_t n, size_t a) { 48 (void)h; 49 (void)a; 50 return n ? malloc(n) : NULL; 51 } 52 static void* h_realloc(KitHeap* h, void* p, size_t o, size_t n, size_t a) { 53 (void)h; 54 (void)o; 55 (void)a; 56 return realloc(p, n); 57 } 58 static void h_free(KitHeap* h, void* p, size_t n) { 59 (void)h; 60 (void)n; 61 free(p); 62 } 63 static KitHeap g_heap = {h_alloc, h_realloc, h_free, NULL}; 64 65 static void diag_emit(KitDiagSink* s, KitDiagKind k, KitSrcLoc loc, 66 const char* fmt, va_list ap) { 67 static const char* names[] = {"note", "warning", "error", "fatal"}; 68 (void)s; 69 fprintf(stderr, "[%u]:%u:%u: %s: ", loc.file_id, loc.line, loc.col, names[k]); 70 vfprintf(stderr, fmt, ap); 71 fputc('\n', stderr); 72 } 73 static KitDiagSink g_diag = {diag_emit, NULL, 0, 0}; 74 75 /* ---- env: execmem (W^X) — copied verbatim from parse_runner.c. Only the 76 * --jit mode actually exercises it; the other modes never touch execmem, 77 * but the env is shared. */ 78 79 #if defined(__APPLE__) 80 #include <mach/mach.h> 81 #include <mach/mach_vm.h> 82 #define XM_DUAL_APPLE 1 83 #else 84 #define XM_DUAL_APPLE 0 85 #endif 86 #if defined(__linux__) 87 #include <sys/syscall.h> 88 #define XM_DUAL_LINUX 1 89 #else 90 #define XM_DUAL_LINUX 0 91 #endif 92 93 static int xm_to_posix(int p) { 94 int q = 0; 95 if (p & KIT_PROT_READ) q |= PROT_READ; 96 if (p & KIT_PROT_WRITE) q |= PROT_WRITE; 97 if (p & KIT_PROT_EXEC) q |= PROT_EXEC; 98 return q; 99 } 100 #if XM_DUAL_LINUX && defined(__x86_64__) && defined(MAP_32BIT) 101 #define XM_MAP_32BIT MAP_32BIT 102 static uintptr_t g_xm_low_runtime_hint = 0x40000000u; 103 static void* xm_low_runtime_hint(size_t n) { 104 uintptr_t p = g_xm_low_runtime_hint; 105 uintptr_t step = (uintptr_t)((n + 0xffffu) & ~(size_t)0xffffu); 106 if (step < 0x10000u) step = 0x10000u; 107 g_xm_low_runtime_hint = p + step + 0x10000u; 108 if (g_xm_low_runtime_hint > 0x78000000u) g_xm_low_runtime_hint = 0x40000000u; 109 return (void*)p; 110 } 111 #elif XM_DUAL_LINUX 112 #define XM_MAP_32BIT 0 113 static void* xm_low_runtime_hint(size_t n) { 114 (void)n; 115 return NULL; 116 } 117 #endif 118 typedef struct XmTok { 119 void* w; 120 void* r; 121 size_t n; 122 } XmTok; 123 static KitStatus xm_reserve_single(size_t n, KitExecMemRegion* out) { 124 void* p = 125 mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); 126 if (p == MAP_FAILED) return KIT_NOMEM; 127 out->write = out->runtime = p; 128 out->size = n; 129 out->token = NULL; 130 return KIT_OK; 131 } 132 static KitStatus xm_reserve(void* u, size_t n, int p, KitExecMemRegion* out) { 133 (void)u; 134 if (!out || !n) return KIT_INVALID; 135 if (!(p & KIT_PROT_EXEC)) return xm_reserve_single(n, out); 136 #if XM_DUAL_APPLE 137 { 138 void* w = 139 mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); 140 mach_vm_address_t r = 0; 141 vm_prot_t cur = 0, max = 0; 142 XmTok* tok; 143 if (w == MAP_FAILED) return KIT_NOMEM; 144 if (mach_vm_remap(mach_task_self(), &r, (mach_vm_size_t)n, 0, 145 VM_FLAGS_ANYWHERE, mach_task_self(), 146 (mach_vm_address_t)(uintptr_t)w, FALSE, &cur, &max, 147 VM_INHERIT_NONE) != KERN_SUCCESS) { 148 munmap(w, n); 149 return KIT_NOMEM; 150 } 151 if (mprotect((void*)(uintptr_t)r, n, PROT_READ) != 0) { 152 munmap((void*)(uintptr_t)r, n); 153 munmap(w, n); 154 return KIT_NOMEM; 155 } 156 tok = (XmTok*)malloc(sizeof(*tok)); 157 if (!tok) { 158 munmap((void*)(uintptr_t)r, n); 159 munmap(w, n); 160 return KIT_NOMEM; 161 } 162 tok->w = w; 163 tok->r = (void*)(uintptr_t)r; 164 tok->n = n; 165 out->write = w; 166 out->runtime = (void*)(uintptr_t)r; 167 out->size = n; 168 out->token = tok; 169 return KIT_OK; 170 } 171 #elif XM_DUAL_LINUX 172 { 173 int fd = (int)syscall(SYS_memfd_create, "kit-asm-jit", 0u); 174 void *w, *r; 175 XmTok* tok; 176 if (fd < 0) return KIT_NOMEM; 177 if (ftruncate(fd, (off_t)n) != 0) { 178 close(fd); 179 return KIT_NOMEM; 180 } 181 w = mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_SHARED | XM_MAP_32BIT, fd, 0); 182 if (w == MAP_FAILED) { 183 close(fd); 184 return KIT_NOMEM; 185 } 186 r = mmap(xm_low_runtime_hint(n), n, PROT_READ, MAP_SHARED | XM_MAP_32BIT, 187 fd, 0); 188 close(fd); 189 if (r == MAP_FAILED) { 190 munmap(w, n); 191 return KIT_NOMEM; 192 } 193 tok = (XmTok*)malloc(sizeof(*tok)); 194 if (!tok) { 195 munmap(r, n); 196 munmap(w, n); 197 return KIT_NOMEM; 198 } 199 tok->w = w; 200 tok->r = r; 201 tok->n = n; 202 out->write = w; 203 out->runtime = r; 204 out->size = n; 205 out->token = tok; 206 return KIT_OK; 207 } 208 #else 209 return xm_reserve_single(n, out); 210 #endif 211 } 212 static KitStatus xm_protect(void* u, void* a, size_t n, int p) { 213 (void)u; 214 return mprotect(a, n, xm_to_posix(p)) == 0 ? KIT_OK : KIT_IO; 215 } 216 static void xm_release(void* u, KitExecMemRegion* region) { 217 (void)u; 218 if (!region || !region->size) return; 219 if (region->token) { 220 XmTok* tok = (XmTok*)region->token; 221 if (tok->r && tok->r != tok->w) munmap(tok->r, tok->n); 222 if (tok->w) munmap(tok->w, tok->n); 223 free(tok); 224 } else if (region->write) { 225 munmap(region->write, region->size); 226 } 227 region->write = region->runtime = NULL; 228 region->size = 0; 229 region->token = NULL; 230 } 231 static void xm_flush(void* u, void* a, size_t n) { 232 (void)u; 233 #if defined(__aarch64__) || defined(__arm__) || defined(__riscv) 234 #if defined(__riscv) 235 __asm__ __volatile__("fence.i" ::: "memory"); 236 #endif 237 __builtin___clear_cache((char*)a, (char*)a + n); 238 #else 239 (void)a; 240 (void)n; 241 #endif 242 } 243 static KitExecMem g_execmem = { 244 16 * 1024, xm_reserve, xm_protect, xm_release, xm_flush, NULL, 245 }; 246 247 static void ctx_init(KitContext* ctx) { 248 memset(ctx, 0, sizeof *ctx); 249 ctx->heap = &g_heap; 250 ctx->diag = &g_diag; 251 ctx->now = -1; 252 } 253 254 static void target_from_env(KitTargetSpec* t) { 255 if (kit_test_target_init(t) != 0) { 256 fprintf(stderr, "asm-runner: kit_test_target_init failed\n"); 257 exit(2); 258 } 259 } 260 261 static KitStatus compiler_new_for_target(KitTargetSpec spec, KitContext* ctx, 262 KitTarget** target_out, 263 KitCompiler** compiler_out) { 264 KitTargetOptions opts; 265 KitStatus st; 266 if (target_out) *target_out = NULL; 267 if (compiler_out) *compiler_out = NULL; 268 memset(&opts, 0, sizeof opts); 269 opts.spec = spec; 270 st = kit_target_new(ctx, &opts, target_out); 271 if (st != KIT_OK) return st; 272 st = kit_compiler_new(*target_out, ctx, compiler_out); 273 if (st != KIT_OK) { 274 kit_target_free(*target_out); 275 *target_out = NULL; 276 } 277 return st; 278 } 279 280 static KitStatus compile_asm_obj(KitCompiler* c, 281 const KitAsmCompileOptions* opts, 282 KitSlice name, const KitSlice* in, 283 KitObjBuilder** out) { 284 KitCompileSessionOptions sopts; 285 KitCompileSession* session = NULL; 286 KitSourceInput sin; 287 KitStatus st; 288 memset(&sopts, 0, sizeof(sopts)); 289 sopts.lang = KIT_LANG_ASM; 290 sopts.compile.code = opts->code; 291 sopts.compile.diagnostics = opts->diagnostics; 292 sopts.compile.language_options = opts; 293 memset(&sin, 0, sizeof(sin)); 294 sin.name = name; 295 sin.bytes = *in; 296 sin.lang = KIT_LANG_ASM; 297 st = kit_compile_session_new(c, &sopts, &session); 298 if (st == KIT_OK) st = kit_compile_session_compile(session, &sin, out); 299 kit_compile_session_free(session); 300 return st; 301 } 302 303 static KitStatus compile_asm_emit(KitCompiler* c, 304 const KitAsmCompileOptions* opts, 305 KitSlice name, const KitSlice* in, 306 KitWriter* w) { 307 KitObjBuilder* ob = NULL; 308 KitStatus st = compile_asm_obj(c, opts, name, in, &ob); 309 if (st == KIT_OK) st = kit_obj_builder_emit(ob, w); 310 kit_obj_builder_free(ob); 311 return st; 312 } 313 314 static KitStatus link_one_obj_jit(KitCompiler* c, KitObjBuilder* ob, 315 const KitJitHost* host, const char* entry, 316 KitJit** out) { 317 KitLinkSessionOptions opts; 318 KitLinkSession* link = NULL; 319 KitStatus st; 320 memset(&opts, 0, sizeof(opts)); 321 opts.output_kind = KIT_LINK_OUTPUT_JIT; 322 opts.entry = kit_slice_cstr(entry); 323 opts.jit_host = host; 324 st = kit_link_session_new(c, &opts, &link); 325 if (st == KIT_OK) st = kit_link_session_add_obj(link, ob); 326 if (st == KIT_OK) st = kit_link_session_jit(link, out); 327 kit_link_session_free(link); 328 return st; 329 } 330 331 /* ---- file helpers ---- */ 332 333 static int read_file(const char* path, uint8_t** out, size_t* out_len) { 334 FILE* f = fopen(path, "rb"); 335 long n; 336 uint8_t* buf; 337 size_t got; 338 if (!f) return 1; 339 if (fseek(f, 0, SEEK_END) != 0) { 340 fclose(f); 341 return 1; 342 } 343 n = ftell(f); 344 if (n < 0 || fseek(f, 0, SEEK_SET) != 0) { 345 fclose(f); 346 return 1; 347 } 348 buf = (uint8_t*)malloc((size_t)n + 1); 349 if (!buf) { 350 fclose(f); 351 return 1; 352 } 353 got = fread(buf, 1, (size_t)n, f); 354 fclose(f); 355 if (got != (size_t)n) { 356 free(buf); 357 return 1; 358 } 359 buf[n] = 0; 360 *out = buf; 361 *out_len = (size_t)n; 362 return 0; 363 } 364 365 static int write_all(const char* path, const uint8_t* data, size_t len) { 366 int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); 367 size_t off = 0; 368 if (fd < 0) { 369 perror(path); 370 return 1; 371 } 372 while (off < len) { 373 ssize_t k = write(fd, data + off, len - off); 374 if (k <= 0) { 375 perror("write"); 376 close(fd); 377 return 1; 378 } 379 off += (size_t)k; 380 } 381 close(fd); 382 return 0; 383 } 384 385 /* Decode an ASCII hex stream — whitespace and a leading "0x" per token are 386 * tolerated. Used by --decode to read the .hex input fixture. */ 387 static int hex_decode(const uint8_t* in, size_t in_len, uint8_t** out, 388 size_t* out_len) { 389 uint8_t* buf = (uint8_t*)malloc(in_len / 2 + 1); 390 size_t n = 0; 391 int hi = -1; 392 size_t i; 393 if (!buf) return 1; 394 for (i = 0; i < in_len; ++i) { 395 int c = in[i]; 396 int v; 397 if (isspace(c)) continue; 398 if (c == '0' && i + 1 < in_len && (in[i + 1] == 'x' || in[i + 1] == 'X')) { 399 ++i; 400 continue; 401 } 402 if (c >= '0' && c <= '9') 403 v = c - '0'; 404 else if (c >= 'a' && c <= 'f') 405 v = 10 + c - 'a'; 406 else if (c >= 'A' && c <= 'F') 407 v = 10 + c - 'A'; 408 else { 409 free(buf); 410 fprintf(stderr, "asm-runner: bad hex byte 0x%02x\n", c); 411 return 1; 412 } 413 if (hi < 0) { 414 hi = v; 415 } else { 416 buf[n++] = (uint8_t)((hi << 4) | v); 417 hi = -1; 418 } 419 } 420 if (hi >= 0) { 421 free(buf); 422 fprintf(stderr, "asm-runner: odd hex nibble count\n"); 423 return 1; 424 } 425 *out = buf; 426 *out_len = n; 427 return 0; 428 } 429 430 /* ---- modes ---- */ 431 432 /* --encode: compile a .s through KIT_LANG_ASM, then walk the result via 433 * the public Obj reader to dump the raw bytes of every PROGBITS section 434 * marked executable. Output is lowercase hex, no separators. The .text- 435 * only choice keeps the golden simple for phase-1 smoke; multi-section 436 * cases will pivot to per-section dumps once the parser lands. */ 437 static int mode_encode(const char* src_path, const char* out_path) { 438 uint8_t* src = NULL; 439 size_t src_len = 0; 440 KitTargetSpec tgt; 441 KitContext ctx; 442 KitTarget* target = NULL; 443 KitCompiler* c = NULL; 444 KitSlice in; 445 KitAsmCompileOptions opts; 446 KitWriter* w = NULL; 447 const uint8_t* obj_bytes; 448 size_t obj_len = 0; 449 KitObjFile* of = NULL; 450 KitSlice obj_in; 451 uint32_t nsec, i; 452 uint8_t* hex = NULL; 453 size_t hex_len = 0; 454 int rc = 0; 455 456 if (read_file(src_path, &src, &src_len)) { 457 fprintf(stderr, "asm-runner: cannot read %s\n", src_path); 458 return 2; 459 } 460 target_from_env(&tgt); 461 ctx_init(&ctx); 462 if (compiler_new_for_target(tgt, &ctx, &target, &c) != KIT_OK || !c) { 463 free(src); 464 return 2; 465 } 466 467 memset(&in, 0, sizeof in); 468 in.data = src; 469 in.len = src_len; 470 memset(&opts, 0, sizeof opts); 471 472 (void)kit_writer_mem(&g_heap, &w); 473 if (compile_asm_emit(c, &opts, kit_slice_cstr(src_path), &in, w) != KIT_OK) { 474 kit_writer_close(w); 475 kit_compiler_free(c); 476 kit_target_free(target); 477 free(src); 478 return 1; 479 } 480 obj_bytes = kit_writer_mem_bytes(w, &obj_len); 481 482 memset(&obj_in, 0, sizeof obj_in); 483 obj_in.data = obj_bytes; 484 obj_in.len = obj_len; 485 if (kit_obj_open(&ctx, kit_slice_cstr(src_path), &obj_in, &of) != KIT_OK || 486 !of) { 487 kit_writer_close(w); 488 kit_compiler_free(c); 489 kit_target_free(target); 490 free(src); 491 return 1; 492 } 493 494 nsec = kit_obj_nsections(of); 495 for (i = 0; i < nsec; ++i) { 496 KitObjSecInfo s; 497 size_t n = 0; 498 const uint8_t* data = NULL; 499 size_t j; 500 char* p; 501 if (kit_obj_section(of, i, &s) != KIT_OK) continue; 502 if (s.kind != KIT_SEC_TEXT) continue; 503 if (kit_obj_section_data(of, i, &data, &n) != KIT_OK) continue; 504 if (!data || !n) continue; 505 p = (char*)realloc(hex, hex_len + n * 2 + 1); 506 if (!p) { 507 rc = 1; 508 break; 509 } 510 hex = (uint8_t*)p; 511 for (j = 0; j < n; ++j) { 512 static const char H[] = "0123456789abcdef"; 513 hex[hex_len + 2 * j + 0] = (uint8_t)H[data[j] >> 4]; 514 hex[hex_len + 2 * j + 1] = (uint8_t)H[data[j] & 0xf]; 515 } 516 hex_len += n * 2; 517 } 518 if (rc == 0 && hex_len > 0) { 519 /* Trailing newline so the golden file has a final \n (diff-friendly). */ 520 char* p = (char*)realloc(hex, hex_len + 1); 521 if (!p) { 522 rc = 1; 523 } else { 524 hex = (uint8_t*)p; 525 hex[hex_len++] = '\n'; 526 rc = write_all(out_path, hex, hex_len); 527 } 528 } else if (rc == 0) { 529 /* No text — emit an empty file so the diff still has a target. */ 530 rc = write_all(out_path, (const uint8_t*)"", 0); 531 } 532 533 free(hex); 534 kit_obj_free(of); 535 kit_writer_close(w); 536 kit_compiler_free(c); 537 kit_target_free(target); 538 free(src); 539 return rc; 540 } 541 542 /* --decode: read hex bytes and walk them through kit_disasm_iter_*. 543 * Output is one instruction per line: "<vaddr-hex>:\t<mnemonic>\t<operands>" 544 * (annotation appended when non-empty, prefixed by " ; "). vaddr starts at 545 * 0; the caller can post-process if they want addresses relative to a 546 * specific section. */ 547 static int mode_decode(const char* in_path, const char* out_path) { 548 uint8_t* raw = NULL; 549 size_t raw_len = 0; 550 uint8_t* bytes = NULL; 551 size_t nbytes = 0; 552 KitTargetSpec tgt; 553 KitContext ctx; 554 KitTarget* target = NULL; 555 KitCompiler* c = NULL; 556 KitDisasmIter* it = NULL; 557 KitDisasmContext dctx; 558 FILE* out; 559 KitInsn ins; 560 int rc = 0; 561 562 if (read_file(in_path, &raw, &raw_len)) { 563 fprintf(stderr, "asm-runner: cannot read %s\n", in_path); 564 return 2; 565 } 566 if (hex_decode(raw, raw_len, &bytes, &nbytes)) { 567 free(raw); 568 return 2; 569 } 570 free(raw); 571 572 target_from_env(&tgt); 573 ctx_init(&ctx); 574 if (compiler_new_for_target(tgt, &ctx, &target, &c) != KIT_OK || !c) { 575 free(bytes); 576 return 2; 577 } 578 579 memset(&dctx, 0, sizeof dctx); 580 dctx.target = target; 581 dctx.context = ctx; 582 if (kit_disasm_iter_new(&dctx, bytes, nbytes, 0, NULL, &it) != KIT_OK || 583 !it) { 584 kit_compiler_free(c); 585 kit_target_free(target); 586 free(bytes); 587 return 1; 588 } 589 590 out = fopen(out_path, "wb"); 591 if (!out) { 592 perror(out_path); 593 kit_disasm_iter_free(it); 594 kit_compiler_free(c); 595 kit_target_free(target); 596 free(bytes); 597 return 2; 598 } 599 600 for (;;) { 601 KitIterResult r = kit_disasm_iter_next(it, &ins); 602 if (r != KIT_ITER_ITEM) break; 603 fprintf(out, "%llx:\t%.*s", (unsigned long long)ins.vaddr, 604 KIT_SLICE_ARG(ins.mnemonic)); 605 if (ins.operands.len) { 606 fprintf(out, "\t%.*s", KIT_SLICE_ARG(ins.operands)); 607 } 608 if (ins.annotation.len) { 609 fprintf(out, " ; %.*s", KIT_SLICE_ARG(ins.annotation)); 610 } 611 fputc('\n', out); 612 } 613 if (fclose(out) != 0) rc = 1; 614 615 kit_disasm_iter_free(it); 616 kit_compiler_free(c); 617 kit_target_free(target); 618 free(bytes); 619 return rc; 620 } 621 622 /* --listing: walk a relocatable ELF and run kit_obj_disasm into a 623 * mem-Writer, then dump to the output path. Annotation overlay comes from 624 * the ELF's own sym/reloc tables; the harness just stores whatever the 625 * library emits. */ 626 static int mode_listing(const char* in_path, const char* out_path) { 627 uint8_t* bytes = NULL; 628 size_t nbytes = 0; 629 KitTargetSpec tgt; 630 KitContext ctx; 631 KitTarget* target = NULL; 632 KitCompiler* c = NULL; 633 KitSlice in; 634 KitWriter* w = NULL; 635 const uint8_t* data; 636 size_t len = 0; 637 int rc; 638 639 if (read_file(in_path, &bytes, &nbytes)) { 640 fprintf(stderr, "asm-runner: cannot read %s\n", in_path); 641 return 2; 642 } 643 target_from_env(&tgt); 644 ctx_init(&ctx); 645 if (compiler_new_for_target(tgt, &ctx, &target, &c) != KIT_OK || !c) { 646 free(bytes); 647 return 2; 648 } 649 650 memset(&in, 0, sizeof in); 651 in.data = bytes; 652 in.len = nbytes; 653 654 (void)kit_writer_mem(&g_heap, &w); 655 if (kit_disasm_obj_bytes(&ctx, &in, w) != KIT_OK) { 656 kit_writer_close(w); 657 kit_compiler_free(c); 658 kit_target_free(target); 659 free(bytes); 660 return 1; 661 } 662 data = kit_writer_mem_bytes(w, &len); 663 rc = write_all(out_path, data, len); 664 665 kit_writer_close(w); 666 kit_compiler_free(c); 667 kit_target_free(target); 668 free(bytes); 669 return rc; 670 } 671 672 /* --emit: assemble .s to a relocatable ELF .o on disk. Mirrors 673 * parse_runner's --emit so the path-J/E shell plumbing (link-exe-runner / 674 * jit-runner) can be reused verbatim. */ 675 static int mode_emit(const char* src_path, const char* out_path) { 676 uint8_t* src = NULL; 677 size_t src_len = 0; 678 KitTargetSpec tgt; 679 KitContext ctx; 680 KitTarget* target = NULL; 681 KitCompiler* c = NULL; 682 KitSlice in; 683 KitAsmCompileOptions opts; 684 KitWriter* w = NULL; 685 int rc = 0; 686 size_t len = 0; 687 const uint8_t* data; 688 689 if (read_file(src_path, &src, &src_len)) { 690 fprintf(stderr, "asm-runner: cannot read %s\n", src_path); 691 return 2; 692 } 693 target_from_env(&tgt); 694 ctx_init(&ctx); 695 if (compiler_new_for_target(tgt, &ctx, &target, &c) != KIT_OK || !c) { 696 free(src); 697 return 2; 698 } 699 700 memset(&in, 0, sizeof in); 701 in.data = src; 702 in.len = src_len; 703 memset(&opts, 0, sizeof opts); 704 705 (void)kit_writer_mem(&g_heap, &w); 706 if (compile_asm_emit(c, &opts, kit_slice_cstr(src_path), &in, w) != KIT_OK) { 707 kit_writer_close(w); 708 kit_compiler_free(c); 709 kit_target_free(target); 710 free(src); 711 return 1; 712 } 713 714 data = kit_writer_mem_bytes(w, &len); 715 rc = write_all(out_path, data, len); 716 717 kit_writer_close(w); 718 kit_compiler_free(c); 719 kit_target_free(target); 720 free(src); 721 return rc; 722 } 723 724 /* On AArch64 host, set up TLS Local-Exec image before invoking JITed 725 * code. Mirrors parse_runner / cg_runner: msr → call() must be back-to- 726 * back with no libc invocations in between. Cases that don't reference 727 * TLS see the lookups fail and the block stays zeroed — harmless. */ 728 #if defined(__aarch64__) || defined(__arm64__) 729 static char g_tls_block[8192] __attribute__((aligned(16))); 730 #endif 731 732 /* --jit: parse + link_jit + call test_main(). Exit code is test_main's 733 * return mod 256, matching the parse/cg conventions. */ 734 static int mode_jit(const char* src_path) { 735 uint8_t* src = NULL; 736 size_t src_len = 0; 737 KitTargetSpec tgt; 738 KitContext ctx; 739 KitTarget* target = NULL; 740 KitCompiler* c = NULL; 741 KitSlice in; 742 KitAsmCompileOptions opts; 743 KitObjBuilder* ob = NULL; 744 KitJitHost jhost; 745 KitJit* jit = NULL; 746 int (*fn)(void); 747 int result; 748 749 if (read_file(src_path, &src, &src_len)) { 750 fprintf(stderr, "asm-runner: cannot read %s\n", src_path); 751 return 2; 752 } 753 target_from_env(&tgt); 754 ctx_init(&ctx); 755 if (compiler_new_for_target(tgt, &ctx, &target, &c) != KIT_OK || !c) { 756 free(src); 757 return 2; 758 } 759 760 memset(&in, 0, sizeof in); 761 in.data = src; 762 in.len = src_len; 763 memset(&opts, 0, sizeof opts); 764 765 if (compile_asm_obj(c, &opts, kit_slice_cstr(src_path), &in, &ob) != KIT_OK || 766 !ob) { 767 kit_compiler_free(c); 768 kit_target_free(target); 769 free(src); 770 return 1; 771 } 772 773 memset(&jhost, 0, sizeof jhost); 774 jhost.execmem = &g_execmem; 775 776 if (link_one_obj_jit(c, ob, &jhost, "test_main", &jit) != KIT_OK || !jit) { 777 kit_compiler_free(c); 778 kit_target_free(target); 779 free(src); 780 return 1; 781 } 782 783 fn = (int (*)(void))kit_jit_lookup(jit, KIT_SLICE_LIT("test_main")); 784 785 #if defined(__aarch64__) || defined(__arm64__) 786 { 787 char* td_start = (char*)kit_jit_lookup(jit, KIT_SLICE_LIT("__tdata_start")); 788 char* td_end = (char*)kit_jit_lookup(jit, KIT_SLICE_LIT("__tdata_end")); 789 unsigned long bs_n = (unsigned long)(unsigned long long)kit_jit_lookup( 790 jit, KIT_SLICE_LIT("__tbss_size")); 791 if (td_start && td_end) { 792 unsigned long td_n = (unsigned long)(td_end - td_start); 793 unsigned long i; 794 for (i = 0; i < td_n; ++i) g_tls_block[16 + i] = td_start[i]; 795 for (i = 0; i < bs_n; ++i) g_tls_block[16 + td_n + i] = 0; 796 } 797 } 798 #endif 799 800 if (fn) { 801 #if defined(__aarch64__) || defined(__arm64__) 802 __asm__ volatile("msr tpidr_el0, %0" ::"r"(g_tls_block) : "memory"); 803 #endif 804 result = fn(); 805 } else { 806 result = 1; 807 } 808 809 kit_jit_free(jit); 810 kit_compiler_free(c); 811 kit_target_free(target); 812 free(src); 813 return result; 814 } 815 816 static int usage(void) { 817 fprintf(stderr, 818 "usage: asm-runner --encode IN.s OUT.hex\n" 819 " asm-runner --decode IN.hex OUT.txt\n" 820 " asm-runner --listing IN.bin OUT.txt\n" 821 " asm-runner --emit IN.s OUT.o\n" 822 " asm-runner --jit IN.s\n"); 823 return 2; 824 } 825 826 int main(int argc, char** argv) { 827 long ps = sysconf(_SC_PAGESIZE); 828 if (ps > 0) g_execmem.page_size = (size_t)ps; 829 if (argc < 2) return usage(); 830 if (!strcmp(argv[1], "--encode") && argc == 4) 831 return mode_encode(argv[2], argv[3]); 832 if (!strcmp(argv[1], "--decode") && argc == 4) 833 return mode_decode(argv[2], argv[3]); 834 if (!strcmp(argv[1], "--listing") && argc == 4) 835 return mode_listing(argv[2], argv[3]); 836 if (!strcmp(argv[1], "--emit") && argc == 4) 837 return mode_emit(argv[2], argv[3]); 838 if (!strcmp(argv[1], "--jit") && argc == 3) return mode_jit(argv[2]); 839 return usage(); 840 }