From 36544924a8c3a8be1383cadcbc6e0e0f73603923 Mon Sep 17 00:00:00 2001 From: Sunrisepeak Date: Fri, 13 Nov 2026 17:35:29 +0900 Subject: [PATCH 1/6] 0.20.3: an image asks for the suspension primitive at run time In an ok image std::atomic::wait and notify import kal_task_wait and kal_task_wake weakly (OPENKAL_HAS_TASK=2, SPEC 12.4). Where they are bound the wait suspends; where they are not it returns and the caller polls, as in the configuration without openkal.task. The image's requirements name no openkal.task on this library's account, so an image runs on an implementation without threads, a firmware among them. Libraries built for a known implementation are unchanged. --- README.md | 15 ++++++++++++++- llvm/libcxx/src/atomic.cpp | 24 ++++++++++++++++++++++++ mcpp.toml | 11 ++++++++--- 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 76432b12..64662cea 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ C library. ```toml [dependencies] -openkal-llvm-runtime = "0.20.2" +openkal-llvm-runtime = "0.20.3" ``` > **Engine floor (mcpp 2026.11.2.1):** 0.17.0 is built over openkal-musl 0.26.0, @@ -328,6 +328,19 @@ openkal-musl 0.27.3 selects openkal-linux 0.24.1 on Linux and openkal-ld 0.4.3 for an image. Linux confinement requires Landlock ABI 3 to enforce read-only grants against raw file truncation. The runtime implementation is unchanged. +## An image asks for the suspension primitive (0.20.3) + +In an ok image `std::atomic::wait` and `notify` import `kal_task_wait` and +`kal_task_wake` weakly (`OPENKAL_HAS_TASK=2`, SPEC 12.4): an image is run by a +loader on the implementation of whatever machine runs it, a firmware among them. +Where the two names are bound the wait suspends as before; where they are not, +the wait returns and the caller polls, as in the configuration without +`openkal.task`. The image's requirements then name no `openkal.task` on this +library's account. With openkal-musl 0.29.6, which does the same for its own +optional interfaces, an image that uses neither files nor processes nor threads +requires only `openkal.abort`, `openkal.env`, `openkal.memory`, `openkal.stream` +and `openkal.time`. The libraries built for a known implementation are unchanged. + ## i686 (0.20.2) Built for `i686-linux-musl` above openkal-musl and openkal-linux on i386 diff --git a/llvm/libcxx/src/atomic.cpp b/llvm/libcxx/src/atomic.cpp index 6063743c..a15f8f5d 100644 --- a/llvm/libcxx/src/atomic.cpp +++ b/llvm/libcxx/src/atomic.cpp @@ -129,10 +129,30 @@ extern "C" { // the specification says they are. openkal derives them from the compiler for // exactly this reason: a consumer can restate them without a header and cannot // get a different type than the implementation was built with. +# if OPENKAL_HAS_TASK == 2 +// ⚠️ AN OK IMAGE ASKS AT RUN TIME (OPENKAL_HAS_TASK == 2, 0.20.3). The image is run by a loader on +// the implementation of whatever machine runs it, which a firmware may be: the two names are +// imported weakly (SPEC 12.4: bound where provided, zero otherwise, and kept out of the image's +// requirements), and where they are zero the wait is the baseline's --- it returns, and the caller +// polls --- as on a machine with one execution context (the `== 0` configuration). +__attribute__((__weak__)) +# endif int kal_task_wait(const unsigned int* __word, unsigned int __expected, unsigned long long __timeout_ns); +# if OPENKAL_HAS_TASK == 2 +__attribute__((__weak__)) +# endif int kal_task_wake(const unsigned int* __word, __okl_uptr __count, __okl_uptr* __woken); } +// Whether the suspension primitive is bound: always where the implementation is known to have it. +static inline bool __okl_task_bound() { +# if OPENKAL_HAS_TASK == 2 + return kal_task_wait != nullptr && kal_task_wake != nullptr; +# else + return true; +# endif +} + template static void __platform_wait_on_address(void const* __ptr, void const* __val, MaybeTimeout maybe_timeout_ns) { static_assert(_Size == 4, "Can only wait on 4 bytes value"); @@ -142,6 +162,8 @@ static void __platform_wait_on_address(void const* __ptr, void const* __val, May // reason: a wait that never times out cannot notice a wake it raced with. // Zero means "no timeout" to openkal, so the default is stated rather than // passed through. + if (!__okl_task_bound()) + return; unsigned long long __ns = 2'000'000'000ull; if constexpr (!is_same_v) { __ns = static_cast(maybe_timeout_ns); @@ -157,6 +179,8 @@ static void __platform_wake_by_address(void const* __ptr, bool __notify_one) { // ⚠️ `-1` for "all", which is openkal's spelling of what the other branches // write as `INT_MAX`. The count is a `kal_uintptr`, so the maximum is stated // as the wrap rather than as a header's constant. + if (!__okl_task_bound()) + return; __okl_uptr __woken = 0; kal_task_wake(reinterpret_cast(__ptr), __notify_one ? __okl_uptr(1) : __okl_uptr(-1), diff --git a/mcpp.toml b/mcpp.toml index 1f5d52b5..9b9388f5 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -1,7 +1,7 @@ [package] namespace = "mcpplibs" name = "openkal-llvm-runtime" -version = "0.20.2" +version = "0.20.3" description = "LLVM's C++ runtime libraries — libc++, libc++abi and libunwind — configured for openkal-musl rather than for a host C library." license = "Apache-2.0" authors = ["mcpplibs"] @@ -578,8 +578,13 @@ sources = [ # without this define the ok image was not one of them: every image holding a # `thread_local` with a destructor was refused by `okld check` for the undefined # symbol (measured building clangd as an ok image, 2026-10-29). -cflags = ["-DOPENKAL_TARGET_IMAGE=1"] -cxxflags = ["-DOPENKAL_TARGET_IMAGE=1"] +# +# AND THE SUSPENSION PRIMITIVE IS ASKED AT RUN TIME (0.20.3): an image is run on the implementation +# of whatever machine runs it, which a firmware may be. `atomic.cpp` imports `kal_task_wait` and +# `kal_task_wake` weakly there and waits as the baseline does where they are not bound; the image's +# requirements then name no `openkal.task` for them (openkal-musl 0.29.6 does the same for its own). +cflags = ["-DOPENKAL_TARGET_IMAGE=1", "-UOPENKAL_HAS_TASK", "-DOPENKAL_HAS_TASK=2"] +cxxflags = ["-DOPENKAL_TARGET_IMAGE=1", "-UOPENKAL_HAS_TASK", "-DOPENKAL_HAS_TASK=2"] [target.'cfg(windows)'.build] # WHICH TARGET THIS IS, STATED HERE RATHER THAN READ OFF `_WIN32`. From 70928b2a79a1154666d3cfc59bc4b00e325f6768 Mon Sep 17 00:00:00 2001 From: Sunrisepeak Date: Fri, 13 Nov 2026 21:20:40 +0900 Subject: [PATCH 2/6] 0.20.3: the PE rows by their container (Windows, UEFI); openkal.task from the implementation's statement --- README.md | 14 +++++++++++ llvm/PATCHES.md | 6 ++--- llvm/compiler-rt/lib/builtins/int_lib.h | 6 ++--- llvm/libcxxabi/src/cxa_thread_atexit.cpp | 6 ++--- llvm/libunwind/include/__libunwind_config.h | 14 ++++++++--- llvm/libunwind/src/AddressSpace.hpp | 6 ++--- llvm/libunwind/src/Registers.hpp | 12 +++++----- llvm/libunwind/src/UnwindRegistersRestore.S | 6 ++--- llvm/libunwind/src/UnwindRegistersSave.S | 6 ++--- llvm/libunwind/src/assembly.h | 8 +++---- llvm/libunwind/src/config.h | 14 +++++------ mcpp.toml | 26 ++++++++++++++++++--- port/include-pe/__openkal_target_pe.h | 10 ++++++++ port/src/dso_handle.c | 2 +- 14 files changed, 94 insertions(+), 42 deletions(-) create mode 100644 port/include-pe/__openkal_target_pe.h diff --git a/README.md b/README.md index 64662cea..a81d7f28 100644 --- a/README.md +++ b/README.md @@ -341,6 +341,20 @@ optional interfaces, an image that uses neither files nor processes nor threads requires only `openkal.abort`, `openkal.env`, `openkal.memory`, `openkal.stream` and `openkal.time`. The libraries built for a known implementation are unchanged. +## UEFI, and the PE container (0.20.3) + +Every question this package asked of Windows was a question about PE: the +compiler-rt sources of the container, emulated TLS, libunwind's register record +and its search for the unwind tables. They are now selected by +`cfg(object-format = "pe")` (mcpp 2026.11.13.1) and the private define +`OPENKAL_TARGET_PE`, so `x86_64-uefi`, a platform's PE row, is served as Windows +is. `__libunwind_config.h`, which a consumer's compile reads without this +package's defines, learns the container from `port/include-pe/__openkal_target_pe.h`, +a directory on the PE rows' include line alone. Where the implementation beneath +states its interfaces and `openkal.task` is not among them (openkal-uefi), +`OPENKAL_HAS_TASK` is 0 by `cfg(not(interface = "openkal.task"))`. This release +requires mcpp 2026.11.13.1. + ## i686 (0.20.2) Built for `i686-linux-musl` above openkal-musl and openkal-linux on i386 diff --git a/llvm/PATCHES.md b/llvm/PATCHES.md index b64b81eb..a686e2ec 100644 --- a/llvm/PATCHES.md +++ b/llvm/PATCHES.md @@ -172,7 +172,7 @@ openkal-musl 的 `[c-abi] presents = "posix"` 把它和 `_WIN32`、`__MINGW32__` 分支,保存例程仍旧往 `%rdi` 写,而 Win64 调用约定下调用者实际把指针传在 `%rcx`。表现 只在运行期:第一次 `throw` 就在 `unw_getcontext` 里对一个由错误寄存器读出的地址(测得 是空指针)写内存。已按 `libunwind` 已有的同一判据处理:内部四处(两个 `.S`、 -`Registers.hpp`)换成 `OPENKAL_TARGET_WINDOWS`;`__libunwind_config.h` 已安装(经 +`Registers.hpp`)换成 `OPENKAL_TARGET_PE`;`__libunwind_config.h` 已安装(经 `unwind.h` / `libunwind.h` 公开可达),换成 `__CYGWIN__`。下一次改这棵树,完整的检查是 `grep -rn "_WIN32\|_WIN64\|__MINGW32__\|__MINGW64__"`,不是只 grep 第一个。 @@ -197,7 +197,7 @@ openkal-musl 的 `[c-abi] presents = "posix"` 把它和 `_WIN32`、`__MINGW32__` 三支全部落空,直接 `#error`。 它**不是**已安装的头,只由本包自己的构建编译,所以按本文件的规矩用私有 define -`OPENKAL_TARGET_WINDOWS`——而 `__libunwind_config.h` 反过来用 `__MCPP_TARGET_WINDOWS__`, +`OPENKAL_TARGET_PE`——而 `__libunwind_config.h` 反过来用 `__MCPP_TARGET_WINDOWS__`, 因为它**是**已安装的。两者读不同的宏,回答同一个关于同一个目标的问题,这正是下面那一节 要求的性质。 @@ -223,7 +223,7 @@ extern "C" { int __cxa_thread_atexit(Dtor, void*, void*) throw(); } `doctest` 与 `spdlog` 两个成员在 `x86_64-windows-gnu` 上停在 `ld.lld: error: undefined symbol: __cxa_thread_atexit`。 -这个文件**不是**已安装的头,所以按本文的规矩用私有 define `OPENKAL_TARGET_WINDOWS`。 +这个文件**不是**已安装的头,所以按本文的规矩用私有 define `OPENKAL_TARGET_PE`。 ### 第二层:`__thread` 在 emutls 下活不过 key 析构 diff --git a/llvm/compiler-rt/lib/builtins/int_lib.h b/llvm/compiler-rt/lib/builtins/int_lib.h index f4fa1465..cbfcab05 100644 --- a/llvm/compiler-rt/lib/builtins/int_lib.h +++ b/llvm/compiler-rt/lib/builtins/int_lib.h @@ -49,7 +49,7 @@ #define SYMBOL_NAME(name) XSTR(__USER_LABEL_PREFIX__) #name // ─── openkal ─── BEGIN -// `OPENKAL_TARGET_WINDOWS` joins this guard, and the alternative was a hard +// `OPENKAL_TARGET_PE` joins this guard, and the alternative was a hard // `#error Unsupported target` a release from now. // // This target is PE, and PE takes the first branch: `__attribute__((alias))` @@ -64,12 +64,12 @@ // // The private define is the right name here, not `__mcpp_target_windows__`: // this file is NOT installed, it is compiled only by this package's own -// build, and `mcpp.toml` carries `OPENKAL_TARGET_WINDOWS` to it in `cflags`. +// build, and `mcpp.toml` carries `OPENKAL_TARGET_PE` to it in `cflags`. // That is the same rule the other five patches in this tree follow, and the // reason `__libunwind_config.h` does the opposite --- it IS installed. See // `llvm/PATCHES.md`. #if defined(__ELF__) || defined(__MINGW32__) || defined(__wasm__) || \ - defined(_AIX) || defined(__CYGWIN__) || defined(OPENKAL_TARGET_WINDOWS) + defined(_AIX) || defined(__CYGWIN__) || defined(OPENKAL_TARGET_PE) // ─── openkal ─── END #define COMPILER_RT_ALIAS(name, aliasname) \ COMPILER_RT_ABI __typeof(name) aliasname __attribute__((__alias__(#name))); diff --git a/llvm/libcxxabi/src/cxa_thread_atexit.cpp b/llvm/libcxxabi/src/cxa_thread_atexit.cpp index d5b8e916..6cc87ce0 100644 --- a/llvm/libcxxabi/src/cxa_thread_atexit.cpp +++ b/llvm/libcxxabi/src/cxa_thread_atexit.cpp @@ -171,7 +171,7 @@ namespace { // __cxa_thread_atexit`, measured on two members of the index's compatibility // suite (doctest, spdlog) on `x86_64-windows-gnu`. // -// `OPENKAL_TARGET_WINDOWS` IS THIS PACKAGE'S OWN DEFINE, not the engine's +// `OPENKAL_TARGET_PE` IS THIS PACKAGE'S OWN DEFINE, not the engine's // `__MCPP_TARGET_WINDOWS__`, and the difference is whose compile reads the // file: this one is a source of this package and is never installed, so a // package-private define reaches it. The same distinction chose the define in @@ -182,9 +182,9 @@ namespace { // // THE OK IMAGE IS THE FOURTH SUCH TARGET. Its triple names no operating system, // so the guard reads `OPENKAL_TARGET_IMAGE`, which `mcpp.toml` states for -// `cfg(os = "openkal")` as it states `OPENKAL_TARGET_WINDOWS` for PE. +// `cfg(os = "openkal")` as it states `OPENKAL_TARGET_PE` for PE. #if defined(__linux__) || defined(__Fuchsia__) \ - || defined(OPENKAL_TARGET_WINDOWS) || defined(OPENKAL_TARGET_IMAGE) \ + || defined(OPENKAL_TARGET_PE) || defined(OPENKAL_TARGET_IMAGE) \ || defined(__APPLE__) extern "C" { diff --git a/llvm/libunwind/include/__libunwind_config.h b/llvm/libunwind/include/__libunwind_config.h index 16d0283d..c2d11d39 100644 --- a/llvm/libunwind/include/__libunwind_config.h +++ b/llvm/libunwind/include/__libunwind_config.h @@ -52,7 +52,7 @@ // `okm_setjmp.S` sizes `jmp_buf` for, and it is gone on this target along // with `_WIN32`. THIS HEADER IS INSTALLED (`llvm/libunwind/include/`, read // through `unwind.h` and `libunwind.h`, both public), so it cannot read -// `OPENKAL_TARGET_WINDOWS` --- that is this package's own private build +// `OPENKAL_TARGET_PE` --- that is this package's own private build // define (mcpp.toml) and an application calling `unw_getcontext` directly // against `unw_context_t` does not see it. `__MCPP_TARGET_WINDOWS__` is read // instead: mcpp states the target under its OWN name, target-wide, an @@ -76,11 +76,19 @@ // packages doing exactly that and reaching `#include `. It // answered this file's question only by accident. // `UnwindRegistersSave.S` writes exactly the record sized here and reads -// `OPENKAL_TARGET_WINDOWS` rather than `__CYGWIN__`, because it is NOT +// `OPENKAL_TARGET_PE` rather than `__CYGWIN__`, because it is NOT // installed --- compiled only by this package's own build, like // `okm_setjmp.S`. The two answer the same question about the same target // without reading the same macro, which is what has to hold; see that file. -# if defined(_WIN64) || defined(__MCPP_TARGET_WINDOWS__) || defined(__CYGWIN__) +// 0.20.3: THE CONTAINER'S MARK, NOT A SYSTEM'S NAME. Every PE row presents +// POSIX through the same substitution (Windows's and UEFI's alike), so the +// record is the Microsoft x64 convention's on each; this package puts +// `port/include-pe' on those rows' include line alone (mcpp.toml), which +// carries the mark to a consumer's compile. +# if __has_include(<__openkal_target_pe.h>) +# include <__openkal_target_pe.h> +# endif +# if defined(_WIN64) || defined(OPENKAL_TARGET_PE) || defined(__MCPP_TARGET_WINDOWS__) || defined(__CYGWIN__) // ─── openkal ─── END # define _LIBUNWIND_CONTEXT_SIZE 54 # ifdef __SEH__ diff --git a/llvm/libunwind/src/AddressSpace.hpp b/llvm/libunwind/src/AddressSpace.hpp index ff5c17cd..921e7aa4 100644 --- a/llvm/libunwind/src/AddressSpace.hpp +++ b/llvm/libunwind/src/AddressSpace.hpp @@ -113,11 +113,11 @@ extern char __exidx_end; // `_WIN32` on this line is the OUTER question --- "is this even the PE // branch" --- and it is an identity question, not a platform-service one; // the service question is the `OPENKAL` that already decides what is INSIDE -// the branch, just below. `OPENKAL_TARGET_WINDOWS` (mcpp.toml) is what +// the branch, just below. `OPENKAL_TARGET_PE` (mcpp.toml) is what // answers the outer one now that `_WIN32` does not reach this target. See // `config.h` for the fuller account. #elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && \ - (defined(_WIN32) || defined(OPENKAL_TARGET_WINDOWS)) + (defined(_WIN32) || defined(OPENKAL_TARGET_PE)) // ─── openkal ─── END // ─── openkal ─── BEGIN @@ -602,7 +602,7 @@ inline bool LocalAddressSpace::findUnwindSections( // The same outer identity question as the declaration above, answered the // same way. #elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && \ - (defined(_WIN32) || defined(OPENKAL_TARGET_WINDOWS)) + (defined(_WIN32) || defined(OPENKAL_TARGET_PE)) // ─── openkal ─── END // ─── openkal ─── BEGIN #if defined(OPENKAL) diff --git a/llvm/libunwind/src/Registers.hpp b/llvm/libunwind/src/Registers.hpp index 1a9a05b0..90450f52 100644 --- a/llvm/libunwind/src/Registers.hpp +++ b/llvm/libunwind/src/Registers.hpp @@ -352,15 +352,15 @@ class _LIBUNWIND_HIDDEN Registers_x86_64 { // writes and `__libunwind_config.h` sizes `unw_context_t` for --- all three // have to agree, so all three read the target the same way. This file is // not installed (`llvm/libunwind/src/`, compiled only by this package's own -// build), so it reads `OPENKAL_TARGET_WINDOWS` (mcpp.toml) as the assembly +// build), so it reads `OPENKAL_TARGET_PE` (mcpp.toml) as the assembly // file does, rather than `__CYGWIN__` as the installed header does. -#if defined(_WIN64) || defined(OPENKAL_TARGET_WINDOWS) +#if defined(_WIN64) || defined(OPENKAL_TARGET_PE) // ─── openkal ─── END uint64_t __padding; // 16-byte align #endif }; GPRs _registers; -#if defined(_WIN64) || defined(OPENKAL_TARGET_WINDOWS) +#if defined(_WIN64) || defined(OPENKAL_TARGET_PE) v128 _xmm[16]; #endif }; @@ -579,7 +579,7 @@ inline bool Registers_x86_64::validVectorRegister(int regNum) const { // ─── openkal ─── BEGIN // Whether `_xmm` exists at all in this object, the same fact the struct // definition above reads the same way. -#if defined(_WIN64) || defined(OPENKAL_TARGET_WINDOWS) +#if defined(_WIN64) || defined(OPENKAL_TARGET_PE) // ─── openkal ─── END if (regNum < UNW_X86_64_XMM0) return false; @@ -593,7 +593,7 @@ inline bool Registers_x86_64::validVectorRegister(int regNum) const { } inline v128 Registers_x86_64::getVectorRegister(int regNum) const { -#if defined(_WIN64) || defined(OPENKAL_TARGET_WINDOWS) +#if defined(_WIN64) || defined(OPENKAL_TARGET_PE) assert(validVectorRegister(regNum)); return _xmm[regNum - UNW_X86_64_XMM0]; #else @@ -603,7 +603,7 @@ inline v128 Registers_x86_64::getVectorRegister(int regNum) const { } inline void Registers_x86_64::setVectorRegister(int regNum, v128 value) { -#if defined(_WIN64) || defined(OPENKAL_TARGET_WINDOWS) +#if defined(_WIN64) || defined(OPENKAL_TARGET_PE) assert(validVectorRegister(regNum)); _xmm[regNum - UNW_X86_64_XMM0] = value; #else diff --git a/llvm/libunwind/src/UnwindRegistersRestore.S b/llvm/libunwind/src/UnwindRegistersRestore.S index 061592b5..3dcc61ce 100644 --- a/llvm/libunwind/src/UnwindRegistersRestore.S +++ b/llvm/libunwind/src/UnwindRegistersRestore.S @@ -83,8 +83,8 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_x86_64_jumpto) # Same target, same reason as `UnwindRegistersSave.S`: the machine code is # still Win64, so the pointer still arrives in `%rcx`, and `_WIN64` is what # used to say so before this target stopped defining it along with -# `_WIN32`. `OPENKAL_TARGET_WINDOWS` (mcpp.toml) is read in its place. -#if defined(_WIN64) || defined(OPENKAL_TARGET_WINDOWS) +# `_WIN32`. `OPENKAL_TARGET_PE` (mcpp.toml) is read in its place. +#if defined(_WIN64) || defined(OPENKAL_TARGET_PE) # ─── openkal ─── END # On entry, thread_state pointer is in rcx; move it into rdi # to share restore code below. Since this routine restores and @@ -128,7 +128,7 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_x86_64_jumpto) # ─── openkal ─── BEGIN # The vector registers, the second half of the same fact. -#if defined(_WIN64) || defined(OPENKAL_TARGET_WINDOWS) +#if defined(_WIN64) || defined(OPENKAL_TARGET_PE) # ─── openkal ─── END movdqu 176(%rdi),%xmm0 movdqu 192(%rdi),%xmm1 diff --git a/llvm/libunwind/src/UnwindRegistersSave.S b/llvm/libunwind/src/UnwindRegistersSave.S index 4d84746d..2fe28a2f 100644 --- a/llvm/libunwind/src/UnwindRegistersSave.S +++ b/llvm/libunwind/src/UnwindRegistersSave.S @@ -127,10 +127,10 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) # table for this target), so a caller here still passes `thread_state` in # `%rcx`, and reading it out of `%rdi` instead is what a SysV callee does. # `_WIN64` is gone from this target along with `_WIN32`; -# `OPENKAL_TARGET_WINDOWS` (mcpp.toml) is this package's own manifest +# `OPENKAL_TARGET_PE` (mcpp.toml) is this package's own manifest # stating it, exactly as `okm_setjmp.S` states the equivalent fact for # `jmp_buf` --- this file is not installed, so it can read it. -#if defined(_WIN64) || defined(OPENKAL_TARGET_WINDOWS) +#if defined(_WIN64) || defined(OPENKAL_TARGET_PE) # ─── openkal ─── END #define PTR %rcx #define TMP %rdx @@ -169,7 +169,7 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) # caller's to preserve. On Win64 they are not, so this file must save them; # on SysV the callee owns none of them and there is nothing here to save. # Same target, same answer as above. -#if defined(_WIN64) || defined(OPENKAL_TARGET_WINDOWS) +#if defined(_WIN64) || defined(OPENKAL_TARGET_PE) # ─── openkal ─── END movdqu %xmm0,176(PTR) movdqu %xmm1,192(PTR) diff --git a/llvm/libunwind/src/assembly.h b/llvm/libunwind/src/assembly.h index c3d7829d..b91b9d5c 100644 --- a/llvm/libunwind/src/assembly.h +++ b/llvm/libunwind/src/assembly.h @@ -189,8 +189,8 @@ // Assembler directive syntax by object format, not a platform service --- // the same identity question `config.h` asks, asked again because this file // is included from `.S` sources rather than from C++. `_WIN32` does not -// reach this target; `OPENKAL_TARGET_WINDOWS` (mcpp.toml) does. -#elif defined(_WIN32) || defined(OPENKAL_TARGET_WINDOWS) +// reach this target; `OPENKAL_TARGET_PE` (mcpp.toml) does. +#elif defined(_WIN32) || defined(OPENKAL_TARGET_PE) // ─── openkal ─── END #define SYMBOL_IS_FUNC(name) \ @@ -210,10 +210,10 @@ #define HIDDEN_SYMBOL(name) // ─── openkal ─── BEGIN -// `__MINGW32__` is also gone on this target; `OPENKAL_TARGET_WINDOWS` takes +// `__MINGW32__` is also gone on this target; `OPENKAL_TARGET_PE` takes // this branch in its place, for the same reason and with the same // measurement behind it as `config.h`'s `_LIBUNWIND_WEAK_ALIAS`. -#if defined(__MINGW32__) || defined(OPENKAL_TARGET_WINDOWS) +#if defined(__MINGW32__) || defined(OPENKAL_TARGET_PE) // ─── openkal ─── END #define WEAK_ALIAS(name, aliasname) \ .globl SYMBOL_NAME(aliasname) SEPARATOR \ diff --git a/llvm/libunwind/src/config.h b/llvm/libunwind/src/config.h index f363fbac..3dcc9d03 100644 --- a/llvm/libunwind/src/config.h +++ b/llvm/libunwind/src/config.h @@ -37,9 +37,9 @@ // the sites `llvm/PATCHES.md` records, nothing below reaches ``. // openkal-musl's `[c-abi] presents = "posix"` leaves `_WIN32` undefined on // this target, so the identity has to come from somewhere else: -// `OPENKAL_TARGET_WINDOWS`, which this package's own manifest defines for +// `OPENKAL_TARGET_PE`, which this package's own manifest defines for // its build of this target (mcpp.toml, `[target.'cfg(windows)'.build]`). -#elif defined(_WIN32) || defined(OPENKAL_TARGET_WINDOWS) +#elif defined(_WIN32) || defined(OPENKAL_TARGET_PE) // ─── openkal ─── END #ifdef __SEH__ #define _LIBUNWIND_SUPPORT_SEH_UNWIND 1 @@ -114,7 +114,7 @@ // ─── openkal ─── BEGIN // // Same target, same reason: `_WIN32` is gone on this target, and -// `OPENKAL_TARGET_WINDOWS` (mcpp.toml) stands in for it. The inner choice is +// `OPENKAL_TARGET_PE` (mcpp.toml) stands in for it. The inner choice is // answered the same way: `__MINGW32__` is gone with it, so this always takes // the GNU form under openkal rather than the `__pragma(comment(linker, …))` // one, which is a construct of the MSVC driver this package is never built @@ -123,8 +123,8 @@ // `weak` omitted because this object format has no weak definition --- links // on this object format with this toolchain; that measurement is the reason // this is not a guess. -#elif defined(_WIN32) || defined(OPENKAL_TARGET_WINDOWS) -#if defined(__MINGW32__) || defined(OPENKAL_TARGET_WINDOWS) +#elif defined(_WIN32) || defined(OPENKAL_TARGET_PE) +#if defined(__MINGW32__) || defined(OPENKAL_TARGET_PE) // ─── openkal ─── END #define _LIBUNWIND_WEAK_ALIAS(name, aliasname) \ extern "C" _LIBUNWIND_EXPORT __typeof(name) aliasname \ @@ -159,7 +159,7 @@ // `__MINGW32__` is one of the names that already routes this to // `__builtin_alloca` rather than to the `_WIN32`-only branch below, which // calls `_malloca` / `_freea` --- routines of the MSVC CRT that this -// package's C library does not carry. `OPENKAL_TARGET_WINDOWS` has to join +// package's C library does not carry. `OPENKAL_TARGET_PE` has to join // it HERE, at the same tier as `__MINGW32__`, and not be added to the // `_WIN32`-only branch below: `__MINGW32__` is also gone on this target, and // without this the branch that assumes MSVC's CRT would be the one taken. @@ -167,7 +167,7 @@ #ifndef _LIBUNWIND_REMEMBER_HEAP_ALLOC #if defined(_LIBUNWIND_REMEMBER_STACK_ALLOC) || defined(__APPLE__) || \ defined(__linux__) || defined(__ANDROID__) || defined(__MINGW32__) || \ - defined(OPENKAL_TARGET_WINDOWS) || defined(_LIBUNWIND_IS_BAREMETAL) + defined(OPENKAL_TARGET_PE) || defined(_LIBUNWIND_IS_BAREMETAL) #define _LIBUNWIND_REMEMBER_ALLOC(_size) __builtin_alloca(_size) #define _LIBUNWIND_REMEMBER_FREE(_ptr) \ do { \ diff --git a/mcpp.toml b/mcpp.toml index 9b9388f5..6caa1b10 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -6,6 +6,9 @@ description = "LLVM's C++ runtime libraries — libc++, libc++abi and libunwind license = "Apache-2.0" authors = ["mcpplibs"] repo = "https://github.com/mcpplibs/openkal-llvm-runtime" +# 2026.11.13.1: `cfg(object-format = "pe")`, which selects what this package +# needs on every PE row (Windows, UEFI) rather than on Windows alone. +mcpp = ">=2026.11.13.1" # The language level of this package's own sources. libc++, libc++abi and # libunwind are written for C++23 and upstream compiles them at that level, @@ -586,7 +589,7 @@ sources = [ cflags = ["-DOPENKAL_TARGET_IMAGE=1", "-UOPENKAL_HAS_TASK", "-DOPENKAL_HAS_TASK=2"] cxxflags = ["-DOPENKAL_TARGET_IMAGE=1", "-UOPENKAL_HAS_TASK", "-DOPENKAL_HAS_TASK=2"] -[target.'cfg(windows)'.build] +[target.'cfg(object-format = "pe")'.build] # WHICH TARGET THIS IS, STATED HERE RATHER THAN READ OFF `_WIN32`. # # `openkal-musl`'s `[c-abi] presents = "posix"` leaves `_WIN32` (and @@ -605,8 +608,16 @@ cxxflags = ["-DOPENKAL_TARGET_IMAGE=1", "-UOPENKAL_HAS_TASK", "-DOPENKAL_HAS_TAS # block above records: it decides what a `throw` COMPILES INTO, so it is true of # the graph rather than of this package. mcpp derives it; see # `graph_runtime_compile_flags`. -cflags = ["-DOPENKAL_TARGET_WINDOWS=1"] -cxxflags = ["-DOPENKAL_TARGET_WINDOWS=1"] +# +# THE CONTAINER, NOT THE SYSTEM (0.20.3). Every question here is "is this PE": +# the rows are Windows's and UEFI's (`x86_64-uefi', a platform's row), so the +# section is selected by `object-format = "pe"' (mcpp 2026.11.13.1) and the +# define is OPENKAL_TARGET_PE. `port/include-pe' carries the same fact to an +# installed header (`__libunwind_config.h'), which a consumer's compile reads +# without this package's defines. +cflags = ["-DOPENKAL_TARGET_PE=1"] +cxxflags = ["-DOPENKAL_TARGET_PE=1"] +include_dirs = ["port/include-pe"] sources = [ "!llvm/libcxx/src/filesystem/int128_builtins.cpp", "llvm/compiler-rt/lib/builtins/*.c", @@ -793,6 +804,15 @@ sources = [ "!llvm/compiler-rt/lib/builtins/*bf*.c", ] + +# AND WHERE THE IMPLEMENTATION BENEATH STATES WHAT IT PROVIDES, IT IS ASKED +# (0.20.3, mcpp 2026.11.13.1 `cfg(interface = ...)'), as openkal-musl asks for +# OKM_HAS_TASK: a firmware's implementation (openkal-uefi) has one context and +# no `openkal.task'. A bare machine is answered above and an ok image at run +# time; one that states no list provides every interface. +[target.'cfg(all(not(os = "openkal"), not(os = "none"), not(interface = "openkal.task")))'.build] +cflags = ["-UOPENKAL_HAS_TASK", "-DOPENKAL_HAS_TASK=0"] +cxxflags = ["-UOPENKAL_HAS_TASK", "-DOPENKAL_HAS_TASK=0"] [build] cxxflags = [ "-nostdinc++", diff --git a/port/include-pe/__openkal_target_pe.h b/port/include-pe/__openkal_target_pe.h new file mode 100644 index 00000000..118683f2 --- /dev/null +++ b/port/include-pe/__openkal_target_pe.h @@ -0,0 +1,10 @@ +/* The PE container's mark, for a header this package installs. + * + * This directory is on the include line of the PE rows only (mcpp.toml, + * `cfg(object-format = "pe")`), Windows's and UEFI's alike, so a consumer's + * compile on one of them finds this file and every other compile does not. An + * installed header asks `__has_include(<__openkal_target_pe.h>)`: the manifest + * states the container, and no system is named in the header. */ +#ifndef OPENKAL_TARGET_PE +#define OPENKAL_TARGET_PE 1 +#endif diff --git a/port/src/dso_handle.c b/port/src/dso_handle.c index 62957ca3..8f79719e 100644 --- a/port/src/dso_handle.c +++ b/port/src/dso_handle.c @@ -29,6 +29,6 @@ * does not --- and supplying the symbol is the correct response to that, * not a workaround for it: the C++ ABI always required something to supply * this, and on every other format something already did. */ -#if defined(OPENKAL_TARGET_WINDOWS) +#if defined(OPENKAL_TARGET_PE) __attribute__((visibility("hidden"))) void *__dso_handle = &__dso_handle; #endif From e288e791990808889b9ddac3a8cea649c52c0e00 Mon Sep 17 00:00:00 2001 From: Sunrisepeak Date: Fri, 13 Nov 2026 22:35:59 +0900 Subject: [PATCH 3/6] ci: mcpp 2026.11.13.1, the engine this release requires --- .github/workflows/ci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fa4c1789..46909466 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,10 +31,11 @@ env: # 0.20.0: 2026.11.9.1, for the tests on the mtest library (a test file of # mtest's objects has no `main' from 2026.11.8.1, and `mcpp test' keeps its # runs in mtest's store from 2026.11.9.1). - MCPP_VERSION: 2026.11.9.1 + # 0.20.3: 2026.11.13.1, which selects by `cfg(object-format)' and `cfg(interface)'. + MCPP_VERSION: 2026.11.13.1 XLINGS_VERSION: v2026.10.26.1 # okrun is built from openkal-image, whose openkal-run states its own floor. - OKRUN_MCPP_VERSION: 2026.11.10.1 + OKRUN_MCPP_VERSION: 2026.11.13.1 XLINGS_NON_INTERACTIVE: '1' jobs: From 1d4be9c9690563d43c8cbef3e835536b248f3508 Mon Sep 17 00:00:00 2001 From: Sunrisepeak Date: Sat, 14 Nov 2026 00:54:39 +0900 Subject: [PATCH 4/6] 0.20.3: DOS (technical experiment only): compiler-rt's emutls for the row, mallocng, no __bf16 conversions --- README.md | 11 +++++++++++ mcpp.toml | 33 ++++++++++++++++++++++++++------- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index a81d7f28..fc77e6b1 100644 --- a/README.md +++ b/README.md @@ -355,6 +355,17 @@ states its interfaces and `openkal.task` is not among them (openkal-uefi), `OPENKAL_HAS_TASK` is 0 by `cfg(not(interface = "openkal.task"))`. This release requires mcpp 2026.11.13.1. +## DOS (0.20.3; technical experiment only) + +`i686-dos-musl`, a platform's row over openkal-dos, takes the Linux selection of +compiler-rt, the allocator of a bare machine (`malloc-mallocng`) and two +differences. compiler-rt's `emutls.c` is built: the row compiles with +`-femulated-tls`, there being no thread register the toolchain can address, and +nothing else answers `__emutls_get_address` (on Linux the access is the thread +register's, and in an ok image openkal-musl answers it). And the `__bf16` +conversions stay out: the row's processor is the Pentium (`-march=i586`), which +has no SSE2. + ## i686 (0.20.2) Built for `i686-linux-musl` above openkal-musl and openkal-linux on i386 diff --git a/mcpp.toml b/mcpp.toml index 6caa1b10..f1a9d73c 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -230,12 +230,14 @@ cflags = ["-DDISABLE_AARCH64_FMV=1"] # the build and says so. mimalloc wherever memory is an environment's regions # or its allocator's chunks; musl's own on bare metal, where `kal_alloc' is the # fixed heap the firmware's build sized and mimalloc's arenas do not fit in it -# (measured on OpenSBI: every request for an arena refused, ENOMEM). A -# target's edge replaces a general one, so each states every feature. -[target.'cfg(not(os = "none"))'.dependencies] +# (measured on OpenSBI: every request for an arena refused, ENOMEM). DOS (0.20.3, openkal-dos; +# technical experiment only) is the same: its DPMI memory is a machine's few megabytes, and +# mimalloc's arenas are placed at 256 MiB. A target's edge replaces a general one, so each +# states every feature. +[target.'cfg(not(any(os = "none", os = "dos")))'.dependencies] openkal-musl = { version = "^0.29.0", default-features = false, features = ["posix", "malloc-mimalloc"] } -[target.'cfg(os = "none")'.dependencies] +[target.'cfg(any(os = "none", os = "dos"))'.dependencies] openkal-musl = { version = "^0.29.0", default-features = false, features = ["posix", "malloc-mallocng"] } [build] @@ -495,13 +497,14 @@ sources = [ # THE OK IMAGE (`-openkal', openkal SPEC clause 12) TAKES THE LINUX # SELECTION: the same C library above openkal, the same ELF object format and # the same calling convention per architecture; what differs is the loader that -# binds the image's `kal_*' imports, which this package never names. -[target.'cfg(any(os = "linux", os = "openkal"))'.build] +# binds the image's `kal_*' imports, which this package never names. DOS (0.20.3, openkal-dos; +# technical experiment only) takes it too: an ELF executable with its headers in its first +# segment, which the stub loads. +[target.'cfg(any(os = "linux", os = "openkal", os = "dos"))'.build] sources = [ "llvm/compiler-rt/lib/builtins/*.c", "!llvm/compiler-rt/lib/builtins/atomic_*.c", "!llvm/compiler-rt/lib/builtins/clear_cache.c", - "!llvm/compiler-rt/lib/builtins/emutls.c", "!llvm/compiler-rt/lib/builtins/enable_execute_stack.c", "!llvm/compiler-rt/lib/builtins/eprintf.c", "!llvm/compiler-rt/lib/builtins/gcc_personality_v0.c", @@ -514,6 +517,15 @@ sources = [ "!llvm/libcxx/src/filesystem/int128_builtins.cpp", ] +# EMULATED THREAD-LOCAL STORAGE BELONGS TO THE ROW. On Linux a `thread_local' is the thread +# register's, and an ok image's emulated accesses are answered by openkal-musl's image sources; +# compiler-rt's `emutls.c' stays out of both. A DOS program has no thread register the toolchain +# can address (the row compiles with `-femulated-tls'), and nothing else answers the call, so +# `emutls.c' is built there --- on pthread keys and the allocator openkal-musl provides, as on PE +# and Mach-O (measured: `undefined symbol: __emutls_get_address', cxa_exception_storage.cpp). +[target.'cfg(any(os = "linux", os = "openkal"))'.build] +sources = ["!llvm/compiler-rt/lib/builtins/emutls.c"] + # FREEBSD ABOVE openkal-musl (`-freebsd-musl', 0.20.1) TAKES THE SAME # SELECTION AND KEEPS `clear_cache.c': nothing beneath a program of these rows # supplies `__clear_cache', which `__builtin___clear_cache' calls on aarch64 (a @@ -572,6 +584,13 @@ sources = [ "!llvm/compiler-rt/lib/builtins/atomic.c", ] +# DOS's processor (0.20.3): the i686-dos-musl row is compiled for the Pentium (-march=i586), +# which the emulators that run DOS programs model and which has no SSE2, so `__bf16' does not +# exist there, as compiler-rt's own build finds (COMPILER_RT_HAS_BFLOAT16); its conversions stay +# out (measured: `__bf16 is not supported on this target', fp_trunc.h). +[target.'cfg(os = "dos")'.build] +sources = ["!llvm/compiler-rt/lib/builtins/*bf2.c"] + [target.'cfg(os = "openkal")'.build] # WHICH TARGET THIS IS, FOR THE ONE FILE THAT MUST KNOW IT IS AN OK IMAGE. # From 6bc74739ca29bd1ba45b0525cfcad8e546f0f107 Mon Sep 17 00:00:00 2001 From: Sunrisepeak Date: Sat, 14 Nov 2026 01:57:12 +0900 Subject: [PATCH 5/6] 0.20.3: the __bf16 conversions stay out of i686-openkal too, compiled for the Pentium as the DOS row is --- README.md | 4 +++- mcpp.toml | 11 ++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index fc77e6b1..96b10d86 100644 --- a/README.md +++ b/README.md @@ -364,7 +364,9 @@ differences. compiler-rt's `emutls.c` is built: the row compiles with nothing else answers `__emutls_get_address` (on Linux the access is the thread register's, and in an ok image openkal-musl answers it). And the `__bf16` conversions stay out: the row's processor is the Pentium (`-march=i586`), which -has no SSE2. +has no SSE2. The same holds for `i686-openkal`, the ok image of i386, which +openkal-platform 0.28.0 compiles for the Pentium too; `i686-linux-musl` keeps the +toolchain's processor and the conversions. ## i686 (0.20.2) diff --git a/mcpp.toml b/mcpp.toml index f1a9d73c..9ea524e3 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -584,11 +584,12 @@ sources = [ "!llvm/compiler-rt/lib/builtins/atomic.c", ] -# DOS's processor (0.20.3): the i686-dos-musl row is compiled for the Pentium (-march=i586), -# which the emulators that run DOS programs model and which has no SSE2, so `__bf16' does not -# exist there, as compiler-rt's own build finds (COMPILER_RT_HAS_BFLOAT16); its conversions stay -# out (measured: `__bf16 is not supported on this target', fp_trunc.h). -[target.'cfg(os = "dos")'.build] +# THE PENTIUM'S ROWS (0.20.3): i686-dos-musl and i686-openkal are compiled for the Pentium +# (-march=i586), which the emulators that run DOS programs model and which has no SSE2, so +# `__bf16' does not exist there, as compiler-rt's own build finds (COMPILER_RT_HAS_BFLOAT16); its +# conversions stay out (measured: `__bf16 is not supported on this target', fp_trunc.h). +# i686-linux-musl keeps the toolchain's processor, which has SSE2, and the conversions. +[target.'cfg(any(os = "dos", all(os = "openkal", arch = "i686")))'.build] sources = ["!llvm/compiler-rt/lib/builtins/*bf2.c"] [target.'cfg(os = "openkal")'.build] From f16bbe8a3643956a21f071a00bc0d149ba095413 Mon Sep 17 00:00:00 2001 From: Sunrisepeak Date: Sat, 14 Nov 2026 02:27:56 +0900 Subject: [PATCH 6/6] 0.20.3: the i386 rows without __bf16 are the 486's (DOSBox Staging executes no CMPXCHG8B) --- README.md | 6 +++--- mcpp.toml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 96b10d86..587722c9 100644 --- a/README.md +++ b/README.md @@ -363,9 +363,9 @@ differences. compiler-rt's `emutls.c` is built: the row compiles with `-femulated-tls`, there being no thread register the toolchain can address, and nothing else answers `__emutls_get_address` (on Linux the access is the thread register's, and in an ok image openkal-musl answers it). And the `__bf16` -conversions stay out: the row's processor is the Pentium (`-march=i586`), which -has no SSE2. The same holds for `i686-openkal`, the ok image of i386, which -openkal-platform 0.28.0 compiles for the Pentium too; `i686-linux-musl` keeps the +conversions stay out: the row's processor is the 486 (`-march=i486`; DOSBox +Staging executes no CMPXCHG8B), which has no SSE2. The same holds for `i686-openkal`, the ok image of i386, which +openkal-platform 0.28.0 compiles for the 486 too; `i686-linux-musl` keeps the toolchain's processor and the conversions. ## i686 (0.20.2) diff --git a/mcpp.toml b/mcpp.toml index 9ea524e3..b22c9126 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -584,8 +584,8 @@ sources = [ "!llvm/compiler-rt/lib/builtins/atomic.c", ] -# THE PENTIUM'S ROWS (0.20.3): i686-dos-musl and i686-openkal are compiled for the Pentium -# (-march=i586), which the emulators that run DOS programs model and which has no SSE2, so +# THE 486'S ROWS (0.20.3): i686-dos-musl and i686-openkal are compiled for the 486 +# (-march=i486; DOSBox Staging, which runs DOS programs, executes no CMPXCHG8B), which has no SSE2, so # `__bf16' does not exist there, as compiler-rt's own build finds (COMPILER_RT_HAS_BFLOAT16); its # conversions stay out (measured: `__bf16 is not supported on this target', fp_trunc.h). # i686-linux-musl keeps the toolchain's processor, which has SSE2, and the conversions.