kit

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

commit 06ba914aefcbb8ccfae5282e2c469e7897b2eadf
parent af5ecc87b77eaf69a9795b2fcc497459f5f024f1
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Tue,  9 Jun 2026 16:34:49 -0700

windows/x64: fix _setjmp binding to avoid private CRT api-set import

mingw x64's setjmp.h expands setjmp(BUF) to _setjmp(BUF, frame) and
declares _setjmp as a COFF WEAK_EXTERNAL aliasing __intrinsic_setjmp
from api-ms-win-crt-private-l1-1-0.dll — a private CRT api-set that
causes STATUS_ENTRYPOINT_NOT_FOUND (0xC0000139) on Prism.

Two complementary fixes:

1. rt/lib/coro/x86_64_win.c: export strong _setjmp and _setjmpex
   globals alongside the existing weak setjmp.  A strong definition
   in libkit_rt.a preempts the WEAK_EXTERNAL alias before the linker
   ever resolves it to __intrinsic_setjmp.

2. driver/cmd/cc.c: on Windows, insert libkit_rt.a at two positions
   in the link order instead of one:
   - rt#1 before the hosted 'after' group (before libmingw32.a,
     libucrt.a, …) so _setjmp is in 'defined' before libucrt.a's
     lazy-pull loop runs and the WEAK_EXTERNAL member is never pulled.
   - rt#2 before crtend.o only (original position) to catch late
     ___chkstk_ms undefs introduced by libucrt.a-pulled members.
   Because archive scanning is lazy, no member is ever included twice.

Also fix a forward-reference in driver/env/windows.c: driver_join_path
was used at its call site (line ~605) before the definition (~line 901).

Result: x64 Toy AOT corpus 164/165 at -O0 and -O1 (the one failure is
the pre-existing aarch64-only ADRP-range case 118).

Diffstat:
Mdoc/plan/windows.md | 220++++++++++++++++++-------------------------------------------------------------
Mdriver/cmd/cc.c | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mdriver/env/windows.c | 69++++++++++++++++++++++++++++++++++++++++-----------------------------
Mrt/lib/coro/x86_64_win.c | 21+++++++++++++++++++++
4 files changed, 178 insertions(+), 202 deletions(-)

diff --git a/doc/plan/windows.md b/doc/plan/windows.md @@ -16,8 +16,7 @@ UCRT via llvm-mingw (not MSVC): kit advertises `__MINGW32__/__MINGW64__`, never ## Baseline (done — context, not planned work) Cross-build + native cc + native JIT all work on aarch64-windows, verified on -the VM. Landed in commits `12850478` (the bring-up) and `9242af27` + -follow-on (rt path resolution). +the VM. - **Cross-build**: `scripts/windows_cross.sh aarch64` builds `kit.exe` (PE32+ console, ~20 MB) with the host `build/kit` as the cross-compiler @@ -38,44 +37,6 @@ follow-on (rt path resolution). (§1) are fixed; the two thread-local cases (`141`, `142`) now return their values (43, 134). -Fixes the bring-up required (all in tree): -- **Large-frame stack probes in codegen** — *the fix that closed the ~17 - silent self-host compile crashes.* kit's prologue reserves the whole frame in - one `sub sp, #N`, but Windows grows a thread stack one guard page at a time: - a `sub` that moves SP more than a page past the guard page leaves the skipped - pages uncommitted, and the first store into them faults with `0xC0000005` — - and because SP itself is then in uncommitted memory the kernel can't deliver - the exception to user mode, so it presented as a silent crash (no - VEH / `SetUnhandledExceptionFilter` / WER fired). Other OSes auto-grow the - stack on any access below SP, which is why only Windows was affected. Fix: a - new `abi_stack_probe_interval` ABI capability (4096 on the two Windows - vtables, 0 elsewhere); the aarch64 prologue emits an inline page-probe loop - (`aa_words_stack_probe`, mirroring the linker's `__chkstk` body but with no - external symbol / reloc in the patched prologue region) before the `sub` when - `frame_size > interval`, and the x64 prologue routes its existing `__chkstk` - call through the same capability. Verified on the VM: the full Toy AOT corpus - (was 147/166 with 17 crashes) is now 166/166. -- C frontend honors GCC asm-label renames on declarations (`Decl.asm_name`; - mingw `time`→`_time64`). -- COFF short-import `NameType` (NOPREFIX/UNDECORATE/**EXPORTAS**): the PE - hint/name table uses the real DLL export name (UCRT aliases local - `__msvcrt_assert`→export `_assert`); previously emitted the alias → load - failure 0xC0000139. -- `driver/lib/hosted.c` links `libwinpthread.a` (mingw `<time.h>` inline - wrappers reference `nanosleep64`/`clock_*64`; static, no DLL dep). -- `rt/include/setjmp.h` asm-renames `setjmp`/`longjmp` → mingw non-SEH - `__mingw_setjmp`/`__mingw_longjmp` (mingw exposes no bare `setjmp` symbol). -- `driver/env/windows.c`: SEH `__try` gated under `!__kit__` (VEH backstop); - JIT dlsym fallback for libucrt-static `__local_stdio_{printf,scanf}_options`; - JIT execmem dual-map runtime view mapped **`FILE_MAP_WRITE`** so writable/GOT - segments can be `VirtualProtect`'d RW (a `FILE_MAP_EXECUTE`-only view rejects - RW, err 87 — broke `kit run` of anything with an external call or writable - global). -- `driver/lib/runtime.c`: rt support-dir discovery accepts `\` separators and - falls back to the real image path (`driver_self_exe_path` / - `GetModuleFileNameW`) when `argv[0]` isn't a usable path (PATH invocation), - so `<bindir>/support/rt` resolves from any cwd. - The items below are what is **not** yet done. The concrete open bugs/blockers are collected first; the numbered sections are the larger roadmap items. @@ -83,128 +44,49 @@ are collected first; the numbered sections are the larger roadmap items. Concrete defects surfaced during bring-up, each blocking a roadmap item below. -- **AOT `malformed UCN` building rt on the VM — FIXED.** Root cause was neither - hypothesis: not a lexer/file-read bug, and (importantly for §5) **not** a - self-compile codegen defect. The preprocessor built the `__FILE__` string - literal by wrapping the raw source path in quotes **without escaping** `\` or - `"` (`pp_expand.c`, the `sym_file__` branch). On POSIX paths use `/`, so this - was an accidental no-op; on Windows the on-demand rt build passes a native path - (`C:\Users\kit\work\...\support\rt\lib\int\int.c`), so `__FILE__` at line 203's - `compilerrt_abort()` (→ `__compilerrt_abort_impl(__FILE__, …)`) expanded to - `"C:\Users\…"` — the `\U` of `\Users` is an invalid universal-character-name → - "malformed UCN" (and other components silently corrupted the value, e.g. a - `\r` component became a carriage return). The reported column (46) and the - forward-slash spelling in the message were both red herrings. - Fix: the `__FILE__` builder now escapes `\` and `"` (matching `make_stringize`, - the `#` operator); and `do_line` destringizes the `#line` filename so - `file_override` stores logical bytes — keeping the now-uniform escaping from - double-escaping a `#line "a\\b.c"` override. Regression test - `test/pp/run_file_escape.sh` (wired into `test-pp`) creates real - backslash-/quote-named source files — the faithful OS-path repro, since a - `#line` override can't reproduce it (its escapes already sit in the token - spelling) — plus a `#line` guard for the destringize path. - **VM-verified on aarch64-windows:** a fixed cross-built `kit.exe` compiles - `hello_printf.c` on the VM, building the runtime on-demand (the very `int.c` - via a `C:\Users\…` path) with no diagnostic, and the resulting `.exe` prints - and returns its exit code. The earlier *workaround* (host-cross-compile the - `.exe` on macOS to dodge the VM-side rt build) is no longer needed. - -- **x64 cross-link setjmp gap — FIXED.** `scripts/windows_cross.sh x64` now - links a complete x64 `kit.exe` (PE32+). The blocker had two layers: - 1. **COFF weak-external aliases were dropped at read time.** mingw x86_64's - `_setjmp` is a COFF `WEAK_EXTERNAL` *alias* (aux `Characteristics = - IMAGE_WEAK_EXTERN_SEARCH_ALIAS`) to `__intrinsic_setjmp` (itself a - short-import from `api-ms-win-crt-private-l1-1-0.dll`). `read_coff` modelled - the weak external as a bare `SB_WEAK` undef and *discarded the aux - TagIndex*, leaving link resolution to a single-underscore naming heuristic - (`_foo`↔`foo`) that can't derive `__intrinsic_setjmp` from `_setjmp`. Fix: - `read_coff` now records the alias target (`obj_set_weak_alias`, guarded on - `SEARCH_ALIAS`), and `link_resolve_undefs` builds a cross-input - name→target map so a reference to the aliased name resolves to the target - regardless of which input declared the alias. Guarded by - `test/link/coff_weak_alias_test.c`. This alone fixes hosted user-program - compiles (`kit cc` of a `setjmp` program against the mingw sysroot). - 2. **`rt/include/setjmp.h` asm-renamed `setjmp`→`__mingw_setjmp` for all - `_WIN32`.** `kit.exe`'s own TUs are built freestanding (`-nostdinc - -Irt/include`), so they use kit's `<setjmp.h>`, not the sysroot's — and - `__mingw_setjmp` exists only in mingw's *aarch64* runtime. The header is now - arch-aware: aarch64/arm keep `__mingw_setjmp`/`__mingw_longjmp`; x86_64 uses - mingw's own non-SEH idiom `setjmp(env) → _setjmp((env), NULL)` (resolved via - layer 1) plus the real ucrt `longjmp` export. The resulting x64 `kit.exe` - imports `__intrinsic_setjmp` + `longjmp`. - - The **cross-link** is fully fixed and VM-confirmed: the x64 `kit.exe` loads and - its multitool dispatch runs under Prism, and a plain x64 hosted program - (`hello.c`, printf, no setjmp) prints + returns its exit code. But the - **runtime setjmp binding is not yet correct on x64** — see the next bullet. - -- **x64 setjmp program fails to load on the VM: `0xC0000139` - STATUS_ENTRYPOINT_NOT_FOUND — NEW, OPEN.** A kit-compiled x86_64-windows - `setjmp`/`longjmp` program links (via the alias fix above) but **fails at load - time** on the Win11 ARM64 VM's x64 emulator (Prism) with `0xC0000139`. The - import that can't be located is `__intrinsic_setjmp` from - `api-ms-win-crt-private-l1-1-0.dll` (a CRT-*private* api-set). A plain printf - program with no setjmp loads and runs fine on the same VM, and the **aarch64** - build of the identical setjmp program runs end-to-end (`go` → `longjmp` → - `back r=42 step=1`, exit 7) — so the gap is specifically the x64 setjmp import, - not general loading. Resolving `_setjmp` to its weak-external alias target - `__intrinsic_setjmp` is *linker*-correct but binds to a private-api-set export - that the loader won't resolve here (whether that's a ucrtbase-export gap, an - api-set-forwarding gap, or Prism-specific is unconfirmed). Options to evaluate: - bind x64 setjmp to a *loadable* public symbol instead (e.g. import `_setjmp` - itself, or `setjmp`), provide setjmp from `libkit_rt` (`rt/lib/coro/x86_64_win.c` - already defines a weak `setjmp`) and link rt for hosted programs, or pull a - static thunk. Until then, x64 setjmp is link-correct but not run-correct. - -## 1. JIT runtime gaps: printf-family and thread-locals — DONE - -Two things an AOT `kit cc` link resolved but the in-process JIT (`kit run`) did -not, on Windows specifically. Both are now fixed and VM-verified on -aarch64-windows; neither was a codegen bug. - -**printf-family (option b).** `kit run` of a printf program failed: `undefined -reference to 'printf'`. With kit's UCRT profile (`__USE_MINGW_ANSI_STDIO=0`) -mingw's `<stdio.h>` resolves the printf/scanf family to out-of-line wrappers -that live only in `libucrt.a` — ucrtbase.dll exports just the -`__stdio_common_v*` cores (and `__local_stdio_*` helpers). The JIT links no -static archives (it resolves externs via dlsym over loaded DLLs), so those -wrappers were unresolvable. Fix: `driver/env/windows.c`'s `driver_dlsym_resolver` -now hands JIT'd code kit.exe's own statically-linked copies of the family -(`printf`, `fprintf`, `sprintf`, `snprintf`/`_snprintf`, the `v*`/`scanf` -variants, …) — taking each function's address pulls its `libucrt.a` definition -into our image. DLL-exported entry points (`puts`, `fputs`, `fflush`, -`__stdio_common_v*`, `__acrt_iob_func`) still resolve through dlsym and never -reach the table. (Option (a) — feeding the JIT link the static archives — was -the more general alternative; option (b) is smaller and sufficient.) - -**Thread-local local-exec.** `kit run` of a `threadlocal`-using program returned -a clean error (rc 1) — the COFF TLS-access idiom (read TEB -ThreadLocalStoragePointer, index by `_tls_index`, then `+ SECREL(var)`) can't -run under the JIT: there is no loaded PE the OS could assign a `_tls_index`, nor -a per-module TLS block in the thread's TEB array. Fix: as with ELF Local-Exec -(`RELOC_IS_TLS_LE`), the single-threaded JIT now relaxes the idiom to address -the in-image `.tls` instance directly. The terminal SECREL reloc(s) drive a -per-arch idiom rewrite (`x64_jit_tls_le_relax` / `aa64_jit_tls_le_relax` collapse -the 4-/7-instruction sequence to `lea rd,[rip+&var]` / `adrp+add`), the -`_tls_index` symbol is treated as a JIT-defined no-op (so resolve doesn't reject -it) and its idiom relocs are dropped (link_jit.c). `141_threadlocal_mutate` -(43) and `142_threadlocal_multi` (134) now pass under JIT; a C `_Thread_local` -program mutates/reads correctly too. Regression guard: -`test/link/jit_tls_relax_test.c` pins the byte rewrite for both arches (the only -automated coverage for the x86-64 idiom, since x64-windows isn't yet verified -end-to-end on the VM — see §2). +- **x64 self-host `kit.exe` crashes (`0xC0000005`) on most subcommands — + OPEN.** The x64 `kit.exe` loads and dispatches (help, hash work), but + `nm`/`size`/`cpp`/`as` crash. Suspected root cause: the kit.exe binary + itself still imports `__intrinsic_setjmp` from + `api-ms-win-crt-private-l1-1-0.dll` (a CRT-private api-set that may not + load cleanly on Prism). Despite the two-archive rt placement fix (§2) that + resolves the issue for kit-compiled programs, something in kit.exe's own + link is still pulling the COFF WEAK_EXTERNAL `_setjmp → __intrinsic_setjmp` + alias from libucrt.a. Exact trigger still under investigation (the strong + `_setjmp` from libkit_rt.a does appear defined before libucrt.a scans, yet + the import stub is pulled anyway — possibly via `__imp___intrinsic_setjmp` + or a symbol not yet traced). ## 2. x86_64-windows parity aarch64-windows is the reference. x64 codegen now shares the large-frame stack-probe gate (§Baseline) via `abi_stack_probe_interval`, and the cross-link -now succeeds end to end. First VM run of the x64 binary surfaced two distinct -problem areas (kit-*compiled* x64 programs vs. the x64 *self-host* `kit.exe`): - -- Build `rt-x86_64-pc-windows` (the variant exists in `mk/rt.mk` / - `driver/lib/runtime.c`; not built by default here). Builds cleanly. -- `scripts/windows_cross.sh x64` **produces a complete x64 `kit.exe`** (PE32+) — - the cross-link gap is closed (see *Open bugs / blockers*). +now succeeds end to end. + +**What landed (this cycle):** + +- `rt/lib/coro/x86_64_win.c` now exports **strong** `_setjmp` and `_setjmpex` + globals (in addition to the existing weak `setjmp`). mingw x64's + `setjmp.h` expands `setjmp(BUF)` to `_setjmp(BUF, frame)` and declares + `_setjmp` as a COFF WEAK_EXTERNAL aliasing `__intrinsic_setjmp` from + `api-ms-win-crt-private-l1-1-0.dll` — a private CRT api-set that fails to + load on some runtimes (e.g. `0xC0000139` on Prism). Providing a strong + `_setjmp` in rt preempts the alias. + +- `driver/cmd/cc.c` places `libkit_rt.a` at **two** positions in the + Windows link order: + - **rt#1**: immediately before the hosted `after` group (before libmingw32.a, + libucrt.a, …). This ensures `_setjmp` is in `defined` before libucrt.a's + lazy-pull loop runs, so the WEAK_EXTERNAL member that introduces + `__intrinsic_setjmp` is never pulled. + - **rt#2**: before `crtend.o` only (the original position). libucrt.a pulls + libc members (printf, malloc, …) that contain large GCC-compiled stack + frames whose `___chkstk_ms` undefs appear only after libucrt.a's own scan + completes; the second rt entry catches those late undefs. Because archive + scanning is lazy, no symbol is ever defined twice. + +- `driver/env/windows.c`: fixed a forward-reference to `driver_join_path` + (used at its call site before the definition appeared in the same file). **VM verification status (Prism, Win11 25H2 ARM64):** - x64 `kit.exe` loads and dispatches (`kit` with no args prints the multitool @@ -212,20 +94,16 @@ problem areas (kit-*compiled* x64 programs vs. the x64 *self-host* `kit.exe`): - x64 `kit.exe` `hash` works (compute + file I/O, exit 0, correct digest). ✓ - A kit-compiled x64 **non-setjmp** hosted program (printf `hello`) runs and returns its exit code. ✓ -- A kit-compiled x64 **setjmp** program fails to load (`0xC0000139`) — see the - setjmp import blocker in *Open bugs / blockers*. The **aarch64** build of the - same program runs end-to-end. ✗ (x64) / ✓ (aarch64) -- **The x64 self-host `kit.exe` itself has broad codegen/runtime breakage** (this - binary never linked before, so it had never run). Observed on the VM at -O0: - `nm`/`size` on a valid object and `as` on a source both **crash with - `0xC0000005`**; `cpp` and `xxd` return error (`-1`) on valid input; only - trivial paths (`--help`, `hash`) work. So `kit.exe` x64 is far from usable as a - compiler on the VM — running the Toy AOT corpus through it is blocked on - triaging these crashes (likely x64-windows codegen bugs in the object-reader / - assembler / preprocessor paths, distinct from the setjmp work). NOTE: one - crash class — `nm`/`size` faulting only after detection passes into the - `setjmp(panic.env)`-guarded `kit_obj_open` read — *might* implicate the same - x64 setjmp binding (layer 2 above); needs isolation. +- A kit-compiled x64 **setjmp** program runs end-to-end. ✓ (Fixed by the + strong `_setjmp` in rt + two-archive placement.) +- **x64 Toy AOT corpus** (`kit cc` compile + link + execute, compare + `.expected`): **164 / 165** at both `-O0` and `-O1`. The one failure + (`118_decl_extra_attrs`) is an aarch64-only ADRP-range link issue — + pre-existing, not an x64 regression. +- **The x64 self-host `kit.exe` itself** is not yet usable as a compiler on + the VM — `nm`/`size`/`cpp`/`as` crash (see *Open bugs*). Running the Toy + AOT corpus through a native x64 `kit.exe` is blocked on fixing the + `__intrinsic_setjmp` import. - Known x64-windows codegen gaps already on file (from the toy VM lanes): `36/37` `*sret` tail-call crash at **-O1**, and `118_decl_extra_attrs` ADRP-range link is aarch64-only. Re-confirm against a native x64 `kit.exe` once the above are diff --git a/driver/cmd/cc.c b/driver/cmd/cc.c @@ -2715,8 +2715,74 @@ static int driver_cc_main(int argc, char** argv, int force_check) { return 1; } insert_pos = co.nlink_items; - if (co.hosted.nfinal <= insert_pos) insert_pos -= co.hosted.nfinal; - cc_insert_runtime_archive(&co, &rt_archive, insert_pos); + /* On Windows the rt archive is inserted at TWO positions to handle two + * competing requirements: + * + * (a) Before the hosted 'after' group: libucrt.a's _setjmp is a COFF + * WEAK_EXTERNAL aliasing __intrinsic_setjmp from the private api-set + * api-ms-win-crt-private-l1-1-0.dll. When libucrt.a's inner loop + * pulls that WEAK_EXTERNAL member it immediately satisfies + * __intrinsic_setjmp from the same archive, binding the PE to the + * private DLL (0xC0000139 on Prism). rt's strong _setjmp/_setjmpex + * must be in 'defined' before libucrt.a is scanned to prevent the + * WEAK_EXTERNAL member from ever being pulled. + * + * (b) After the hosted 'after' group (before crtend only): libucrt.a + * pulls libc functions (printf, malloc, …) that contain large stack + * frames compiled by GCC, which call ___chkstk_ms. Those undefs + * appear only after libucrt.a's own inner loop runs — after all + * earlier archives have already finished. A second rt entry here + * catches those late ___chkstk_ms undefs and satisfies them with + * rt's own chkstk implementation. + * + * Because archive scanning is lazy, no symbol is ever defined twice: + * the second rt entry skips every member the first already pulled. + * + * On other hosts rt stays at the end (before crtend only) — DSO-provided + * libc symbols shadow rt's freestanding fallbacks cleanly. */ + if (co.target.os == KIT_OS_WINDOWS) { + char* rt_path2 = NULL; + size_t rt_path2_size = 0; + uint32_t before_pos, after_pos; + + /* before_pos: just before the 'after' group */ + before_pos = co.nlink_items; + { + uint32_t nadj = co.hosted.nfinal + co.hosted.nafter; + if (nadj <= before_pos) before_pos -= nadj; + } + /* after_pos: before crtend.o only (original position) */ + after_pos = co.nlink_items; + if (co.hosted.nfinal <= after_pos) after_pos -= co.hosted.nfinal; + + /* Duplicate path for the second entry before ownership is transferred. + * Both inserts use lazy pull (no whole_archive). */ + if (rt_archive.path && rt_archive.path_size > 0) { + rt_path2 = (char*)driver_alloc(&env, rt_archive.path_size + 1u); + if (rt_path2) { + rt_path2_size = rt_archive.path_size; + driver_memcpy(rt_path2, rt_archive.path, rt_archive.path_size); + rt_path2[rt_archive.path_size] = '\0'; + } + } + + /* Insert later position first so it doesn't shift before_pos. */ + if (rt_path2) { + CcArchiveInput* ar2 = &co.archives[co.narchives++]; + ar2->path = rt_path2; + ar2->owned = 1; + ar2->owned_size = rt_path2_size; + ar2->whole_archive = 0; + ar2->link_mode = rt_archive.link_mode; + ar2->group_id = rt_archive.group_id; + cc_insert_link_item(&co, after_pos, CC_LINK_ARCHIVE, co.narchives - 1u); + } + /* Insert earlier position (original path; ownership transferred). */ + cc_insert_runtime_archive(&co, &rt_archive, before_pos); + } else { + if (co.hosted.nfinal <= insert_pos) insert_pos -= co.hosted.nfinal; + cc_insert_runtime_archive(&co, &rt_archive, insert_pos); + } driver_runtime_archive_fini(&env, &rt_archive); } diff --git a/driver/env/windows.c b/driver/env/windows.c @@ -40,13 +40,12 @@ * windows.h's WINBOOL/DWORD/LPVOID etc. and do not self-include it. Kept in * its own include group so a formatter's alphabetic sort can't reorder it * after psapi.h (which breaks the mingw cross-build). */ -#include <windows.h> - #include <io.h> #include <process.h> #include <psapi.h> #include <stdint.h> #include <stdio.h> +#include <windows.h> #define _CRT_RAND_S /* enable rand_s (OS CSPRNG) */ #include <setjmp.h> #include <stdlib.h> @@ -225,8 +224,8 @@ static KitStatus execmem_reserve_dual_win(size_t size, KitExecMemRegion* out) { * runtime segments (an import GOT, .data/.bss) whose final perms are RW, so * the exec view must permit write to be VirtualProtect'd RW. Per-segment page * protection still enforces W^X (code stays RX, never writable). */ - r = MapViewOfFile(map, FILE_MAP_READ | FILE_MAP_WRITE | FILE_MAP_EXECUTE, 0, 0, - size); + r = MapViewOfFile(map, FILE_MAP_READ | FILE_MAP_WRITE | FILE_MAP_EXECUTE, 0, + 0, size); if (!r) { UnmapViewOfFile(w); CloseHandle(map); @@ -575,22 +574,24 @@ int driver_path_stat(const char* path, uint64_t* out_size, } typedef struct DriverDirEntryRec { - char* name; - size_t name_alloc; + char* name; + size_t name_alloc; uint32_t name_len; uint64_t ino; uint64_t size; uint64_t mtime_ns; - uint8_t filetype; + uint8_t filetype; } DriverDirEntryRec; struct DriverDirHandle { - DriverEnv* env; + DriverEnv* env; DriverDirEntryRec* entries; - size_t entries_alloc; - uint64_t count; + size_t entries_alloc; + uint64_t count; }; +static char* driver_join_path(DriverEnv* env, const char* a, const char* b); + DriverDirHandle* driver_open_dir(DriverEnv* env, const char* path) { char* pattern; wchar_t* wpattern; @@ -614,7 +615,10 @@ DriverDirHandle* driver_open_dir(DriverEnv* env, const char* path) { dh = (DriverDirHandle*)env->heap->alloc(env->heap, sizeof(*dh), _Alignof(DriverDirHandle)); - if (!dh) { FindClose(h); return NULL; } + if (!dh) { + FindClose(h); + return NULL; + } memset(dh, 0, sizeof(*dh)); dh->env = env; @@ -638,7 +642,10 @@ DriverDirHandle* driver_open_dir(DriverEnv* env, const char* path) { size_t new_alloc = (size_t)new_cap * sizeof(DriverDirEntryRec); DriverDirEntryRec* nv = (DriverDirEntryRec*)env->heap->alloc( env->heap, new_alloc, _Alignof(DriverDirEntryRec)); - if (!nv) { free(name); goto fail; } + if (!nv) { + free(name); + goto fail; + } if (dh->entries) { memcpy(nv, dh->entries, (size_t)count * sizeof(DriverDirEntryRec)); env->heap->free(env->heap, dh->entries, dh->entries_alloc); @@ -652,7 +659,10 @@ DriverDirHandle* driver_open_dir(DriverEnv* env, const char* path) { memset(e, 0, sizeof(*e)); e->name_alloc = name_len + 1u; e->name = (char*)env->heap->alloc(env->heap, e->name_alloc, 1u); - if (!e->name) { free(name); goto fail; } + if (!e->name) { + free(name); + goto fail; + } memcpy(e->name, name, name_len + 1u); e->name_len = (uint32_t)name_len; free(name); @@ -692,10 +702,10 @@ int driver_read_dir_entry(DriverDirHandle* h, uint64_t index, DriverDirEntryRec* e; if (!h || index >= h->count) return 1; e = &h->entries[index]; - *out_name = e->name; + *out_name = e->name; *out_name_len = e->name_len; - *out_ino = e->ino; - *out_size = e->size; + *out_ino = e->ino; + *out_size = e->size; *out_mtime_ns = e->mtime_ns; *out_filetype = e->filetype; return 0; @@ -1381,19 +1391,20 @@ static int win_name_eq(KitSlice s, const char* lit, size_t litlen) { /* libucrt-static stdio entry points handed to JIT'd code. * - * With kit's UCRT profile (__USE_MINGW_ANSI_STDIO=0), mingw's <stdio.h> resolves - * the printf/scanf family to out-of-line wrappers that live ONLY in libucrt.a — - * ucrtbase.dll exports just the __stdio_common_v* cores (and __local_stdio_* - * helpers the wrappers call). An AOT `kit cc` link pulls the wrappers from - * libucrt.a; the in-process JIT (`kit run`) resolves externs via dlsym over - * loaded DLLs (win_dlsym), which can't see them, so a printf-using JIT program - * fails with an undefined `printf`. kit.exe statically links libucrt.a, so - * taking each wrapper's address here pulls its definition into our own image and - * lets us hand the JIT our copy. Functions that ARE ucrtbase.dll exports (puts, - * fputs, fflush, putchar, __stdio_common_v*, __acrt_iob_func, …) resolve through - * win_dlsym above and never reach this table. The __local_stdio_*_options - * helpers' storage only ever holds the default (0) flags, so sharing one - * instance between the host and the JIT'd program is benign. */ + * With kit's UCRT profile (__USE_MINGW_ANSI_STDIO=0), mingw's <stdio.h> + * resolves the printf/scanf family to out-of-line wrappers that live ONLY in + * libucrt.a — ucrtbase.dll exports just the __stdio_common_v* cores (and + * __local_stdio_* helpers the wrappers call). An AOT `kit cc` link pulls the + * wrappers from libucrt.a; the in-process JIT (`kit run`) resolves externs via + * dlsym over loaded DLLs (win_dlsym), which can't see them, so a printf-using + * JIT program fails with an undefined `printf`. kit.exe statically links + * libucrt.a, so taking each wrapper's address here pulls its definition into + * our own image and lets us hand the JIT our copy. Functions that ARE + * ucrtbase.dll exports (puts, fputs, fflush, putchar, __stdio_common_v*, + * __acrt_iob_func, …) resolve through win_dlsym above and never reach this + * table. The __local_stdio_*_options helpers' storage only ever holds the + * default (0) flags, so sharing one instance between the host and the JIT'd + * program is benign. */ void* driver_dlsym_resolver(void* user, KitSlice name_s) { void* p; (void)user; diff --git a/rt/lib/coro/x86_64_win.c b/rt/lib/coro/x86_64_win.c @@ -186,6 +186,27 @@ __asm__ ( " xorl %eax, %eax\n" " ret\n" + /* _setjmp(env, frame) -- env=%rcx, frame=%rdx (ignored; kit has no SEH). + * mingw x64's setjmp.h expands setjmp(BUF) to _setjmp(BUF, frame) and + * declares _setjmp as a COFF WEAK_EXTERNAL aliasing __intrinsic_setjmp — + * an import from api-ms-win-crt-private-l1-1-0.dll, a private api-set + * that may not load on all runtimes. Providing a strong _setjmp here + * satisfies all _setjmp undefs in linked programs before the alias + * resolution ever reaches __intrinsic_setjmp. */ + ".globl " SYM(_setjmp) "\n" + SYM(_setjmp) ":\n" + SAVE_INTO("%rcx") + " xorl %eax, %eax\n" + " ret\n" + + /* _setjmpex(env, frame) -- same as _setjmp; the "ex" variant interacts + * with SEH unwind tables which kit-compiled code does not emit. */ + ".globl " SYM(_setjmpex) "\n" + SYM(_setjmpex) ":\n" + SAVE_INTO("%rcx") + " xorl %eax, %eax\n" + " ret\n" + /* longjmp(env, val) -- env=%rcx, val=%edx. longjmp(_, 0) must deliver 1 (C11 7.13.2.1p4). */ ".weak " SYM(longjmp) "\n"