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: diff --git a/README.md b/README.md index 76432b12..587722c9 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,46 @@ 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. + +## 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. + +## 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 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) 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/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/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 1f5d52b5..b22c9126 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -1,11 +1,14 @@ [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"] 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, @@ -227,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] @@ -492,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", @@ -511,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 @@ -569,6 +584,14 @@ sources = [ "!llvm/compiler-rt/lib/builtins/atomic.c", ] +# 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. +[target.'cfg(any(os = "dos", all(os = "openkal", arch = "i686")))'.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. # @@ -578,10 +601,15 @@ 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] +[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 @@ -600,8 +628,16 @@ cxxflags = ["-DOPENKAL_TARGET_IMAGE=1"] # 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", @@ -788,6 +824,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