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:
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"