Spec M19 — Windows arm64: COFF objects, a kernel32 system layer, lld-link
Goal: [target] os = "windows", arch = "aarch64" in mc.toml makes mc build emit a COFF .obj,
link it with lld-link against kernel32.lib, and the result runs on Windows on ARM. The machine
is arm64 (unchanged); only the object writer and the system layer are new. Nothing in stage0.
Depends on M17 (target registry, machine table).
COFF writer (src/backend_coff.mc, backend coff-obj-arm64) #
- Reuses
gen_lower+gen_encode_alllike the ELF writer;machine_use("arm64")first. IMAGE_FILE_HEADER:Machine 0xAA64,TimeDateStamp 0(determinism),SizeOfOptionalHeader 0,Characteristics 0. Sections in module order:.text(CODE|EXECUTE|READ, align 4),.rdata(INITIALIZED_DATA|READ, align 1) for__cstring,.data(INITIALIZED_DATA|READ|WRITE, align 16),.bss(UNINITIALIZED_DATA|READ|WRITE,SizeOfRawData= zsize, no raw data), custom#sectionas.seg.sectwith flags derived like ELF. Section alignment goes inCharacteristics(IMAGE_SCN_ALIGN_*).- Symbols (
IMAGE_SYMBOL, 18 bytes, 0 aux): name inline when <= 8 bytes else/offsetinto the string table (4-byte length prefix),Value= section offset,SectionNumber1-based (0 = undefined),Type 0x20for functions else 0,StorageClassEXTERNAL (2) for globals and undefined, STATIC (3) for locals. Names: no leading underscore on Windows arm64 (_main->main); string labelsl_strN->$str.N(STATIC). Same stable partition as Mach-O/ELF. - Relocations (
IMAGE_RELOCATION, 10 bytes,NumberOfRelocationsin the header; the 0xFFFF overflow form is an error with a clear message):R_BRANCH26->IMAGE_REL_ARM64_BRANCH26(0x0003),R_PAGE21->PAGEBASE_REL21(0x0004),R_PAGEOFF12->PAGEOFFSET_12A(0x0006) on anadd,PAGEOFFSET_12L(0x0007) onldr/str(the instruction carries the scale),R_UNSIGNED->ADDR64(0x000E). Sorted by offset. Verified field by field againstclang --target=aarch64-windows-msvc -cof equivalent C withllvm-readobj --coff-* --relocs --symbols --sectionsandllvm-objdump -dr.
System layer (lib/sys_windows.mc, bundled as sys_windows) #
externkernel32:GetStdHandle,WriteFile,ReadFile,CreateFileA,CloseHandle,ExitProcess,GetCommandLineA(only whatlib/io.mcand the tests need; all non-variadic; Windows arm64 ABI is AAPCS64 withx18reserved, which mc never writes — the ABI contract asserts it). Wrappers with thelib/sys.mcinterface:write(fd, p, n)(fd 1/2 ->GetStdHandle(-11/-12)),read(0, ...),open(GENERIC_READ,OPEN_EXISTING),creat(GENERIC_WRITE,CREATE_ALWAYS),close,exit->ExitProcess.- Entry:
void mc_start()callsmain(argc, argv)(argc/argv fromGetCommandLineAsplit on spaces, quotes honoured minimally) andExitProcess(result); linked with/entry:mc_start /subsystem:console /nodefaultlib.O_*constants for Windows live here (per-system, like Linux).
Sysroot without downloads #
scripts/sysroot-windows.sh [--arch aarch64] DIR: writeskernel32.def(the exports the layer uses) and buildskernel32.libwithllvm-dlltool -m arm64(Homebrew LLVM; the Windows runner has the Windows SDK'skernel32.libtoo). No mingw download needed for the test suite;mc sysroot fetch windows-*(M25) stays the path for full import libraries.
Driver, TOML, tests, CI #
target("windows", "aarch64", "coff-obj-arm64", 0);[target].os = "windows"requires[linker];{sysroot}; documentedmc.toml:cmd = "lld-link",args = ["/machine:arm64", "/subsystem:console", "/entry:mc_start", "/nodefaultlib", "/out:{out}", "{obj}", "{sysroot}/kernel32.lib"].scripts/test-windows.sh --build-only OUT(macOS:.obj+.expect+ manifest, using<sys_windows>;// skip-windows:on the svc/opcode/reloc tests and on Linux-only ones), and--run-only OUT(Windows: link withlld-link, run.exe, compare stdout/exit; bash from Git for Windows). Locally only--build-onlyplusllvm-readobjvalidation and alld-linklink test (a linked.exeinspected withllvm-readobj --file-headers; not executed).- CI leg
Link and run the suite (windows/arm64)onwindows-11-arm: download the objects, obtainlld-link(LLVM release for Windows on ARM, cached) or the SDK'slink.exe, run--run-only. After merge the architect adds the job to the required checks. make test-windows(build-only + readobj validation) insidemake check, self-skipping withoutllvm-readobj/lld-link.- Docs:
docs/build.md(windows target),docs/guide/50-cross-compile.md,docs/reference/objects.md(COFF),docs/reference/bundle.md(sys_windows),CLAUDE.md§ State.
Out of scope (recorded after the review) #
- No
.pdata/.xdata. The writer emits no unwind data for any function, whereclang --target=aarch64-windows-msvc -cemits both sections for every non-leaf one. Windows on ARM64 has no frame-pointer fallback: without aRUNTIME_FUNCTIONrecord the OS unwinder treats an mc frame as a leaf. Nothing in the language raises or catches and/nodefaultliblinks no C runtime, so the suite is unaffected; it matters only when something else unwinds through an mc frame. Seedocs/reference/objects.md§ No.pdata/.xdata.
Acceptance #
test-windows.sh --build-only produces objects for every non-skipped test; llvm-readobj output
matches clang's for headers, sections, symbols and relocation types; lld-link links 001 and
013 into .exe files on macOS; the CI leg runs the suite on windows-11-arm with the expected
stdout/exit; make check green; golden rewritten once.