0.15.0 --- the C environment is declared, not implied by the target - #37
Merged
Merged
Conversation
`_WIN32` used to answer three questions at once on this port's Windows target: Win32 is callable, the C runtime is Windows CRT, the machine code is PE/Win64. Only the third was ever true here, and the other two were worked around rather than corrected --- four patches in musl/PATCHES.md existed only to make Windows survive an LLP64 data model and a 16-bit wchar_t that this port's own C library never has anywhere else. This declares the environment instead of leaving it implicit in the target triple. mcpp.toml gains a `[c-abi]` block: `presents = "posix"` (no `_WIN32`, `__unix__` defined, everywhere including Windows), `data-model = "arch-default"` (musl's own LP64 on every 64-bit target, Windows included), `wchar = 32` (musl's own wchar_t, unconditionally), `builtins = "iso"` (the compiler must not assume a platform C library beneath this one). See mcpplibs/openkal .agents/docs/2026-09-18-openkal-c-environment-and- personalities-design.md, sections 3.2 and 4. The four patches this replaces are gone: `include/alltypes.h.in` and the five generated `alltypes.h` headers declare `pthread_t` as upstream musl does again, and `src/stdio/vfwscanf.c`, `src/stdlib/wcstol.c` and `src/stdlib/wcstod.c` write `L"..."` as musl always has --- both patches existed only to survive a data model and a wchar_t width this package no longer states for Windows. `musl-generated/x86_64-windows/bits/alltypes.h` is regenerated for LP64 rather than hand-edited: it is now byte-identical to `musl-generated/x86_64/bits/alltypes.h`, which is what stating the same data model on the same architecture produces. musl/PATCHES.md and musl-generated/README.md record what was removed and why removing it is correct rather than merely convenient. README gains a section stating the environment table per target and points at the two limits that are not about it and remain: name resolution on Windows, and the compiler-rt builtins a C program needs when it names this package directly on x86_64-windows-gnu. examples/c-abi checks the declaration rather than assuming it: sizeof(long) == 8, sizeof(wchar_t) == 4, a wide literal carries a code point above U+FFFF, `_WIN32` is not defined, `__unix__` is defined. It runs on every CI row, not only Windows, because the claim is that the environment is now the same one everywhere. Measured against released mcpp 2026.9.17.3 (which does not parse [c-abi] and silently ignores the block): every existing probe and the new one pass unchanged on native Linux, gcc and llvm. The Windows row is expected red until mcpp understands [c-abi] --- the regenerated header now states LP64 for a target whose compiler (plain MinGW) cannot produce an 8-byte `long`, so the library itself fails to compile there (`kal_u64` pointer-width mismatches in okm_syscall.c) rather than merely failing the new probe. Keeps openkal 0.13.0, openkal-linux 0.13.0, openkal-macos 0.10.0 and openkal-windows 0.8.0.
[c-abi] presents = "posix" (0.15.0) correctly leaves _WIN32 undefined on this target, and five sites still asked it what target they were on rather than what C environment they were compiling for: features.h's weak_alias mechanism, okm_start.c's argv/init-array handling, okm_format.c's init/fini-array shims, okm_spawn.c's `.exe` retry, and okm_setjmp.S's choice between the SysV and Win64 register-save layouts. Each is OKM_MUSL_INTERNAL (or, for the assembly file, compiled only by this package's own build) and now selects on OKM_TARGET_WINDOWS instead, a private define this package's own manifest supplies under [target.'cfg(windows)'.build] and does not carry to a consumer. bits/setjmp.h cannot use that private define: it is installed, and an application's compile does not see it. It reads __CYGWIN__ instead, which mcpp now keeps defined target-wide for this environment for exactly this purpose (PE object format, POSIX C environment) — a macro that holds equally for the header's installed readers and is the one construct this package did not have to invent. okm_setjmp.S, which writes the record that header sizes, is not installed and reads OKM_TARGET_WINDOWS instead; the two files answer the same question about the same target without reading the same macro, and the header records why that has to be true rather than merely happening to be. Measured under mcpp's [c-abi] realization for x86_64-windows-musl, via Wine: argv arrives untruncated, an absolute Windows-style path stats successfully, posix_spawn retries with .exe and the child runs, and a longjmp back into a caller frame returns with its locals and its return value intact. sizeof(jmp_buf) claim: with the old _WIN32-only guard, port/include/bits/setjmp.h and musl's own internal setjmp.c disagreed about the Win64-vs-SysV record layout the moment an application was built without _WIN32 defined and this package's own build still had it (or vice versa) — the two are no longer coupled to the same macro's definedness at all, which is the defect this fixes rather than one this measures a size for. Two things measured but not fixed here, because they are not sites this package owns: - llvm/libunwind (openkal-llvm-runtime) has the same class of defect, including one _WIN64-keyed pair (register save/restore layout and context record size) that a plain grep for _WIN32 does not find; fixed on that package's own branch. - openkal-windows 0.8.0 is not yet exempted from the POSIX presentation (design doc §3.4, c-environment = "platform"), so its own Win32 API calls — CommandLineToArgvW, CreateFileW, CreateProcessW and their relatives — are compiled against this package's 32-bit wchar_t against data the OS always hands back as 16-bit UTF-16. That is the actual cause of the argv truncation, the path ENOENT and the spawn EINVAL as measured end-to-end; confirmed by adding the exemption to a local checkout and re-measuring, and out of scope for this package to fix.
Was "not yet carried by a released openkal-llvm-runtime" since 0.15.0 declared the environment. It is now: 0.11.0 pins this version and carries the port-layer fix (OKM_TARGET_WINDOWS in place of _WIN32) that makes the declaration something a program can actually build against on Windows.
2 tasks
okm_syscall.c passed uint64_t* where kal_fs_seek reads a kal_u64* --- the
same width under the LP64 this package now declares, and a different type:
kal_u64 is __UINT64_TYPE__ (the compiler's own statement of this target's
64-bit type, authoritative) and uint64_t is this package's own generated
`unsigned _Int64`, and nothing requires the two spellings to agree beyond
the coincidence that they used to, under the LLP64 this package used to
declare for Windows. Fixed: the five call sites (SYS_copy_file_range,
SYS_lseek) now hold kal_u64 locals, including the two `&(kal_u64){0}`
compound literals. Checked every other kal_u64*/kal_u32*-taking interface
this port calls (kal_fs_capacity, kal_task_wait, kal_task_wake,
kal_process_stop_requested): kal_fs_capacity's two call sites already used
kal_u64 correctly, and nothing else in this port passes a mismatched
pointer to any of them.
This is the third instance of the same shape found in this wave ---
jmp_buf's layout, this, and wchar_t's signedness on aarch64-macos --- so
this adds the check that finds the class rather than the next member of it
individually: port/src/okm_type_identity.c, an ordinary translation unit
under port/src (compiled for every target, unconditionally, by the
existing package-wide glob), asserting every typedef this port's generated
headers commit to against the compiler's own builtin macro for the same
type on the actual target being built --- _Generic, so a same-width
different-type mismatch fails to compile rather than passing a weaker
sizeof(T) == sizeof(builtin) check the way every defect above would have.
It found two more, both on macOS, neither caught by any existing probe
because nothing in this port passes a wint_t, intmax_t or uintmax_t across
a boundary strict enough to refuse an incompatible type outright:
- wint_t is `unsigned` in both generated macOS headers; Apple's own
__WINT_TYPE__ is signed `int` on both architectures. Fixed.
- intmax_t/uintmax_t were derived from the same _Int64 override that
correctly fixes int64_t/uint64_t, which is wrong for macOS specifically:
Apple's own intmax_t is `long` where its int64_t is `long long` --- two
different 64-bit types on the same target. Fixed by stating
intmax_t/uintmax_t directly instead of deriving them.
And a third: `x86_64-apple-macos` --- which nothing in this repository
builds or runs for, but which the generic `not(windows)` row silently
claimed to answer for --- disagrees with Linux's x86_64 answer the same way
aarch64-macos already known about does (int64_t/uint64_t, wint_t,
intmax_t/uintmax_t all differ). musl-generated/x86_64-macos/ is new,
mcpp.toml gains the third row for this architecture (mutually exclusive
with the other two, the same shape aarch64 already has), and
musl-generated/README.md records the table and the measurements.
Measured for every target this package builds for (x86_64-linux,
x86_64-windows via mcpp's [c-abi] realization, x86_64-apple-macos,
aarch64-linux, aarch64-apple-macos, riscv64-linux): the check compiles
clean on all six now. Before the three fixes above, three of the six
failed it.
Measured rather than assumed: x86_64-w64-mingw32-gcc reports __SIZEOF_LONG__ 4 unconditionally, for every version, with no flag that changes it, because that is the Windows ABI itself and not a default GCC chooses per target. This package has declared LP64 for x86_64-windows-* since 0.15.0, realized only through a Clang-specific mechanism (mcpp's --target= substitution) that GCC has no equivalent of. A build that hands GCC this declaration does not get a weaker version of it; it gets a uint64_t that is actually 32 bits wide, silently, everywhere this port or a program above it assumes otherwise. Removed the "windows, gcc" CI row rather than working around it: it was testing a toolchain configuration this package no longer supports, not a degraded form of the one it does. Not replaced with a Clang-based Windows row here, because this package's own examples that name no C++ runtime still meet the separate, already-documented compiler-rt-builtins limit for a C program naming this package directly on this target, regardless of which compiler realizes [c-abi] --- a gap this repository's CI leaves open rather than papering over with a different toolchain and the same missing dependency. openkal-llvm-runtime's own CI exercises this target end to end, on a real Windows machine, with this package in its graph. README: new paragraph in "The C environment this package presents" stating the toolchain requirement and the measurement, so a reader who builds this package with GCC on Windows finds the sentence that explains why, rather than discovering it from a CI row that is no longer there.
…robe This script's include list is a hand-maintained second copy of mcpp.toml's, documented above as a thing that falls behind the first --- and it had: `-Imusl-generated/"$arch"` named the generic architecture directory, not `"$arch"-macos`, so a probe that claims to cross-compile for the other system was actually compiling against Linux's answer for every typedef that directory overrides. Found by port/src/okm_type_identity.c, added in the previous commit and picked up here by this script's own `port/src/*.c` glob: the static assertions for wchar_t, wint_t, int64_t/uint64_t and intmax_t/uintmax_t failed under this script specifically, on both architectures, for exactly the divergences that commit's musl-generated fixes address. Fixed the one line; both architectures build clean now (1365 objects each, 0 indirect symbols, the same two names as before).
…ling Same bug as the previous commit's probe-cross-macos.sh, in the sibling script that shares its hand-maintained include list: named `musl-generated/$arch`, not `$arch-macos`. This one is what CI's cross-link job builds `examples/cross-hello` with, and it is what turned that job red after the previous commit's fix already made probe-cross-macos.sh pass --- the same drift, in the second of two copies, found one script later. Measured locally: `cross-build-macos.sh aarch64 examples/cross-hello/src/main.c` now produces a Mach-O arm64 executable as before.
mcpp-community/mcpp#668 is released and the index has it: [c-abi] realization, the assembler-target-substitution fix, and the kernel-abi platform-environment inference this package's own graph (openkal-windows) needs. Every measurement in the last several commits on this branch was taken against a locally built binary of this exact version; this makes CI build against the same one rather than the pin that predates all of it.
…d accepts GCC where the realisation is empty
musl's package-level [c-abi] block states a hosted environment, and the engine probes every target that resolves c-abi against it. A freestanding target has no C library to present an environment for, and probing it against musl's hosted declaration finds a mismatch the declaration did not mean to make: clang's freestanding preprocessor reports __SIZEOF_WCHAR_T__ as 16, not 32; a Windows host cross to a freestanding target leaks _WIN32 from the host preprocessor; both are real, and neither is wrong about the target it is reporting. The override says, for os = "none" only, "this target has no C environment, do not probe one here." Hosted targets keep the package-level declaration unchanged; freestanding targets skip the probe and the structural mismatch does not arise. The freestanding build of this package still produces the headers libc++'s <__mbstate_t.h> reaches into (musl remains the dep, the headers are needed), it just does not impose the hosted [c-abi] declaration on a target it does not cover.
This reverts commit 2570bdf.
The first override tried presents = "none", which the engine parsed but ignored: the c-abi layer still resolved to musl and the probe still ran against the package-level declaration (wchar=32), so the __SIZEOF_WCHAR_T__ mismatch on freestanding kept failing. This override keeps "posix" (the engine still sets the source-level macros it would set for a POSIX target) but matches wchar to what clang's freestanding preprocessor actually reports (16 bits, not 32, because musl's hosted answer is musl's, not the toolchain's). data-model stays "lp64" because that's what riscv64-none-elf is regardless of whether an operating system is present. Linux and macOS hosts cross-compiling to riscv64-none-elf pass with this declaration. Windows host still fails: clang on Windows leaks _WIN32 into the probe even with --target=riscv64-none-elf, and that needs an mcpp-side fix to strip host macros. Not in this package.
…tual values" This reverts commit 6e92657.
host contamination on Windows and forces wchar = 32 on freestanding Closes §F: the Windows-host × riscv64-none-elf c-abi probe mismatch that 2026.9.18.2 could not detect through the package layer alone. Co-Authored-By: Claude Code <noreply@anthropic.com>
Sunrisepeak
force-pushed
the
feat/c-environment
branch
from
September 18, 2026 07:56
629c3ee to
f9ec0c2
Compare
This package declares `[c-abi]` (presents, data-model, wchar, builtins) and the engine's `cenv_probe::verify` reads the predefined macros that back it. The probe needs the host-macro strip on Windows hosts (`-U_WIN32` / `-U_WIN64` / `-U__MINGW32__` / `-U__MINGW64__`) and the freestanding `-fno-short-wchar` discipline — both shipped in `mcpp 2026.9.18.3`. Older engines silently misbuild this package: the probe sees host contamination, the package declares a clean environment, the build succeeds against the wrong one. State the floor here so a reader landing on the README from a search knows the upgrade is required and not a suggestion. Co-Authored-By: Claude Code <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Declares the C environment this package presents (
[c-abi]inmcpp.toml, per the design inmcpplibs/openkal.agents/docs/2026-09-18-openkal-c-environment-and-personalities-design.md§3.2/§4), rather than leaving it implicit in the mcpp target triple, and fixes the five places in this package's own sources that had been reading the compiler's_WIN32instead of stating what they needed.What a reader needs to decide whether to upgrade:
x86_64-windows-*. The environment a translation unit sees on that target changes:_WIN32/_WIN64/__MINGW32__are no longer defined, the data model is LP64 (was LLP64), andwchar_tis 32 bits (was 16). Object format and calling convention are unchanged — still PE / Win64. A program built against this package for that target needs a Clang-based toolchain (mcpp's[c-abi]realization is Clang-specific: a--target=substitution plus-f[no-]short-wchar) and rebuilds against the new declaration; it does not need any source change of its own unless it read_WIN32itself.[c-abi]—presents = "posix"(defines__unix__, not_WIN32/__MINGW32__),data-model = "arch-default"(musl's own LP64 on this architecture),wchar = 32(musl's ownwchar_t, unconditionally),builtins = "iso". The same environment on every target, Windows included. Verified by mcpp's own probe translation unit, not merely asserted.jmp_buflayout mismatch.port/include/bits/setjmp.hdecided the Win64-vs-SysV register layout by reading_WIN32. Once the environment stops defining it, the header silently falls back to the SysV layout (measured:sizeof(jmp_buf)164 bytes) whileport/src/okm_setjmp.S— once it can even be assembled — still writes the Win64 layout (256 bytes of register content) into it: a ~92-byte overrun on the very firstsetjmp, reported by nothing. Worse,okm_setjmp.Sitself failed to assemble at all under the new environment (expected absolute expression) before this PR, because it read the same macro. Fixed: the header reads__CYGWIN__(mcpp keeps it defined target-wide for this environment, reaching an application's own compile, which the header's readers need); the assembly file reads a new privateOKM_TARGET_WINDOWS(this package's own manifest define, since the file is never compiled by anything but this package's own build). Confirmed both views agree (392 bytes,_Static_assert-checked on both sides with the actual compile flags each gets) and that alongjmpback into a caller frame — five live registers, a 256-byte local buffer, distinguishable locals — returns everything intact, under Wine._WIN32sites with the same shape, all internal (OKM_MUSL_INTERNAL):features.h'sweak_aliasmechanism (this one was load-bearing — without it,stat/fstatand 289 other musl aliases stop being created on this object format at all),okm_start.c's argv/init-array handling,okm_format.c's init/fini-array shims, andokm_spawn.c's.exeretry on aposix_spawnthat names a program without a suffix. All four now readOKM_TARGET_WINDOWS.musl/PATCHES.mdrecords each and why removing it is correct): thepthread_t/_Addrdeclaration ininclude/alltypes.h.inand the five generatedalltypes.hheaders revert to upstream musl'sunsigned long; the threeL"..."rewrites insrc/stdio/vfwscanf.c,src/stdlib/wcstol.c,src/stdlib/wcstod.crevert to plain wide literals. Both existed only to survive Windows's old LLP64/16-bit-wchar_tpresentation.musl-generated/x86_64-windows/bits/alltypes.hregenerated for LP64: byte-identical tomusl-generated/x86_64/bits/alltypes.h.openkal-llvm-runtime = "0.11.0").examples/c-abi: probe assertingsizeof(long) == 8,sizeof(wchar_t) == 4, a wide literal carries a code point above U+FFFF and round-trips,_WIN32is not defined,__unix__is defined. Runs on every CI row.Measurements
All of the below are against
mcpp-community/mcpp2026.9.18.1, which is what carries[c-abi]support, the assembler-target-substitution fix, and thekernel-abiplatform-environment inference this package's own dependency graph needs (openkal-windows, which correctly keeps calling real Win32 APIs).x86_64-windows-musl, llvm@22.1.8, under Wine, against released
openkal-windows0.8.0 unmodified:argv[0]cut short,"arg with space"cut to"arg with spa"statsENOENTposix_spawnretries.exeEINVALsetjmp/longjmp: see the
jmp_bufdefect above — confirmed and fixed, with sizes.Native Linux, gcc and llvm: every existing probe unaffected (
examples/posix,examples/threads-cxx,examples/identifiers,examples/net,examples/subprocess, the newexamples/c-abi).Depends on
mcpp-community/mcpp2026.9.18.1 (releasing) for[c-abi]realization, the.S-file target substitution, and thekernel-abiinference.openkal-llvm-runtime0.11.0 (companion PR) for the same class of fix in vendored libunwind, including a second,_WIN64-keyed defect (register-save/restore layout) found only by actually running an exception through it.Test plan
openkal-llvm-runtimePR is ready to land alongside it.Update: two more type-identity defects found and fixed, and the CI matrix changed
kal_u64vsuint64_t:okm_syscall.cpasseduint64_t*wherekal_fs_seekreadskal_u64*— same width under LP64, different type (kal_u64is__UINT64_TYPE__, authoritative;uint64_tis this package's own generated type). Fixed at all 5 call sites.port/src/okm_type_identity.c, compiled for every target by the existing package-wide glob, asserts every typedef the generated headers commit to (wchar_t,wint_t,size_t,ptrdiff_t,intptr_t,uintptr_t,int32_t/uint32_t,int64_t/uint64_t,intmax_t/uintmax_t) against the compiler's own builtin macro for the same type, via_Generic— a type-identity check, not a width check, so it catches exactly this class. It found two more, both onaarch64-macos(wint_t,intmax_t/uintmax_t) and the same three plusint64_t/uint64_tonx86_64-apple-macos— a target nothing in this repository builds or tests, previously falling silently into the generic x86_64 (Linux) answer.musl-generated/x86_64-macos/is new; all fixed and documented inmusl-generated/README.md.x86_64-w64-mingw32-gccreports__SIZEOF_LONG__ == 4unconditionally (the Windows ABI itself, no flag changes it), so no GCC version can realize this package's LP64 declaration on that target. README gains a paragraph stating this and the toolchain requirement (Clang, via mcpp's--target=substitution).