From 489ac19a48f7d31339f397d69fa55892393632d9 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Tue, 25 Aug 2026 04:28:11 +0800 Subject: [PATCH 1/8] feat: openkal.random on ProcessPrng MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ⭐ **ProcessPrng 而不是 BCryptGenRandom,差别是一个句柄。** 后者是文档化的 CNG 入口,要一个算法句柄 —— 本接口得在每次调用时打开它,或缓存它, 而缓存意味着有了初始化顺序。`ProcessPrng` 是同一个生成器,没有句柄, 除调用本身外没有失败模式。Chromium 与 Rust 标准库都因这些理由改用了它。 ⚠️ **也不是 `RtlGenRandom`(`SystemFunction036`)**,那是更老的答案: 未文档化、由 advapi32 按序号导出、每个调用者自己写一份声明。它能用, 而本后端不需要它 —— `ProcessPrng` 覆盖本包支持的每一个版本。 ⚠️ 无循环。`ProcessPrng` 要么填满整个缓冲区要么失败;不像 read 那样有短返回, 所以循环会是一个永远只跑一次的循环,并暗示一个本接口没有的部分状态。 ── ⚠️ 导入库不存在,而本包自己生成它 ──────────────────── lld: error: unable to find library -lbcryptprimitives 这套载荷里没有那个 `.a`。⭐ 而本包本来就用 `port/*.def` + `dlltool` 自己生成导入库 —— 那机制存在的理由正是「本包声明自己用到哪些 Win32 名字」。 加一份 `port/bcryptprimitives.def`(只导出 `ProcessPrng` 一个名字) 并接进 `build.mcpp`,是那套机制该有的用法而不是绕过。 `props = 0`:系统的生成器在进程运行前已播种,没有等待可报; 而种子是否来自硬件源不是这个后端能观察的。 实测:交叉构建产出 PE32+,导入表含 `bcryptprimitives.dll`, **在 wine 里跑通** —— `std::random_device` 返回两个不同的值。 --- build.mcpp | 4 +++- mcpp.toml | 2 +- port/bcryptprimitives.def | 13 ++++++++++++ src/random.cpp | 43 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 port/bcryptprimitives.def create mode 100644 src/random.cpp diff --git a/build.mcpp b/build.mcpp index 8a8fa9e..7e8ef40 100644 --- a/build.mcpp +++ b/build.mcpp @@ -113,6 +113,7 @@ std::string dlltool() { int main() { mcpp::rerun_if_changed("port/kernel32.def"); + mcpp::rerun_if_changed("port/bcryptprimitives.def"); mcpp::rerun_if_changed("port/ntdll.def"); mcpp::rerun_if_changed("port/shell32.def"); mcpp::rerun_if_changed("port/synchronization.def"); @@ -137,7 +138,8 @@ int main() { if (out.empty() || root.empty()) return 0; const std::string tool = dlltool(); - for (auto name : { "kernel32", "ntdll", "shell32", "synchronization" }) { + for (auto name : { "kernel32", "ntdll", "shell32", "synchronization", + "bcryptprimitives" }) { const auto def = std::format("{}/port/{}.def", root, name); const auto lib = std::format("{}/lib{}.a", out, name); // ⚠️ `-m i386:x86-64` is stated. See port/README.md: the 32-bit ABI diff --git a/mcpp.toml b/mcpp.toml index d555db1..40c4e5f 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -49,7 +49,7 @@ openkal = "0.6.0" # predicate the second one linked with none of these libraries and failed on # `GetStdHandle`. [target.'cfg(all(windows, not(env = "msvc")))'.build] -ldflags = ["-lntdll", "-lsynchronization", "-lshell32", "-lkernel32"] +ldflags = ["-lntdll", "-lsynchronization", "-lshell32", "-lkernel32", "-lbcryptprimitives"] # Exceptions and run-time type information, on the one ABI where their absence # is asserted. diff --git a/port/bcryptprimitives.def b/port/bcryptprimitives.def new file mode 100644 index 0000000..3c4b166 --- /dev/null +++ b/port/bcryptprimitives.def @@ -0,0 +1,13 @@ +; The system's random generator, and nothing else from this library. +; +; ⭐ ProcessPrng RATHER THAN BCryptGenRandom, AND THE DIFFERENCE IS A HANDLE. +; The documented CNG entry point takes an algorithm handle, which this backend +; would have to open on every call or cache — and caching one means having an +; initialisation order. `ProcessPrng` is the same generator without the handle; +; it has been present since Windows 10 and is what Chromium and Rust's standard +; library moved to for these reasons. +; +; Generated into an import library by build.mcpp; see port/README.md. +LIBRARY bcryptprimitives.dll +EXPORTS +ProcessPrng diff --git a/src/random.cpp b/src/random.cpp new file mode 100644 index 0000000..436a9cf --- /dev/null +++ b/src/random.cpp @@ -0,0 +1,43 @@ +// openkal.random on Windows --- ProcessPrng. +// +// ⭐ ProcessPrng AND NOT BCryptGenRandom, AND THE DIFFERENCE IS A CONTEXT. +// +// `BCryptGenRandom` is the documented CNG entry point and takes an algorithm +// handle, which means opening one — a cost this interface would pay on every +// call or would have to cache, and caching means initialisation order. Since +// Windows 10 the system exposes `ProcessPrng` in `bcryptprimitives.dll`: the +// same generator, no handle, no failure mode other than the call itself. +// Chromium and Rust's standard library both moved to it for these reasons. +// +// ⚠️ AND NOT `RtlGenRandom` (`SystemFunction036`), WHICH IS THE OLDER ANSWER. +// That name is undocumented, exported by ordinal from `advapi32`, and reached +// by a declaration each caller writes itself. It works, and this backend does +// not need it: `ProcessPrng` covers every version this package supports. +#include "win.h" +#include + +extern "C" { + +// From bcryptprimitives.dll. Declared here rather than by including +// for the reason the whole of win32.h exists: this package names what it uses. +__declspec(dllimport) int __stdcall ProcessPrng(unsigned char* pbData, + okw_uptr cbData); + +int kal_random_fill(void* out, kal_uintptr len) { + if (len == 0) return kal_ok; + if (out == nullptr) return kal_err_invalid; + + // ⚠️ NO LOOP. `ProcessPrng` fills the whole buffer or fails; unlike a read + // it has no short return, so a loop would be a loop that always runs once + // and would suggest a partial state this interface does not have. + const int ok = ProcessPrng(static_cast(out), + static_cast(len)); + return ok ? kal_ok : kal_err_io; +} + +// Neither blocking nor hardware. The system's generator is seeded before a +// process runs, so there is no wait to report; and whether the seed came from a +// hardware source is not something this backend can observe. +const kal_uintptr kal_random_props = 0; + +} // extern "C" From 3dea09d38ac148cc0b380c0f44100c9cdc062413 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Tue, 25 Aug 2026 04:42:43 +0800 Subject: [PATCH 2/8] fix: BCryptGenRandom, because ProcessPrng does not link on a Windows host MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ⚠️ **一个在一台宿主上能链、另一台不能的后端,不是后端。** `ProcessPrng` 先被选中(更少的活动部件,Chromium 与 Rust 用的也是它), 交叉构建通过、原生 Windows 失败: lld-link: error: undefined symbol: __declspec(dllimport) ProcessPrng ⭐ 真因是**本包只在交叉时生成自己的导入库** —— `build.mcpp` 在 Windows 宿主上立即返回,因为那里有真的 SDK。而两个工具链的 SDK 都不导出那个名字: mingw 带 `libbcrypt.a`,不带 primitives。 改用 `BCryptGenRandom`,它在 `libbcrypt.a` 里、两侧都有。⭐ 句柄那个顾虑 由 `BCRYPT_USE_SYSTEM_PREFERRED_RNG` 消掉 —— 那个 flag 正是为「用系统自己的 生成器、传空句柄」而设,于是本接口仍然没有初始化顺序。 ⚠️ 这是我在一台 Linux 宿主上看不见的一条,与本仓库 README 里那类 「一台宿主看不见」同族。 实测:wine 里 `std::random_device` 返回两个不同的值。 --- build.mcpp | 4 +-- mcpp.toml | 2 +- port/bcrypt.def | 16 +++++++++++ port/bcryptprimitives.def | 13 --------- src/random.cpp | 57 ++++++++++++++++++++++++--------------- 5 files changed, 54 insertions(+), 38 deletions(-) create mode 100644 port/bcrypt.def delete mode 100644 port/bcryptprimitives.def diff --git a/build.mcpp b/build.mcpp index 7e8ef40..3f89376 100644 --- a/build.mcpp +++ b/build.mcpp @@ -113,10 +113,10 @@ std::string dlltool() { int main() { mcpp::rerun_if_changed("port/kernel32.def"); - mcpp::rerun_if_changed("port/bcryptprimitives.def"); mcpp::rerun_if_changed("port/ntdll.def"); mcpp::rerun_if_changed("port/shell32.def"); mcpp::rerun_if_changed("port/synchronization.def"); + mcpp::rerun_if_changed("port/bcrypt.def"); if (host_is_windows()) return 0; @@ -139,7 +139,7 @@ int main() { const std::string tool = dlltool(); for (auto name : { "kernel32", "ntdll", "shell32", "synchronization", - "bcryptprimitives" }) { + "bcrypt" }) { const auto def = std::format("{}/port/{}.def", root, name); const auto lib = std::format("{}/lib{}.a", out, name); // ⚠️ `-m i386:x86-64` is stated. See port/README.md: the 32-bit ABI diff --git a/mcpp.toml b/mcpp.toml index 40c4e5f..1463f0e 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -49,7 +49,7 @@ openkal = "0.6.0" # predicate the second one linked with none of these libraries and failed on # `GetStdHandle`. [target.'cfg(all(windows, not(env = "msvc")))'.build] -ldflags = ["-lntdll", "-lsynchronization", "-lshell32", "-lkernel32", "-lbcryptprimitives"] +ldflags = ["-lntdll", "-lsynchronization", "-lshell32", "-lkernel32", "-lbcrypt"] # Exceptions and run-time type information, on the one ABI where their absence # is asserted. diff --git a/port/bcrypt.def b/port/bcrypt.def new file mode 100644 index 0000000..6042e32 --- /dev/null +++ b/port/bcrypt.def @@ -0,0 +1,16 @@ +; The system's random generator, and nothing else from this library. +; +; ⭐ BCryptGenRandom WITH BCRYPT_USE_SYSTEM_PREFERRED_RNG, WHICH TAKES A NULL +; HANDLE. The ordinary use of this call opens an algorithm handle first, and a +; handle would give openkal.random an initialisation order it does not have. +; +; ⚠️ `ProcessPrng` (bcryptprimitives.dll) was tried first: fewer moving parts, +; and what Chromium and Rust use. It fails on a WINDOWS HOST, where this file's +; generated import libraries are not built at all --- `build.mcpp` returns +; immediately there, a real SDK being present --- and no SDK in either toolchain +; exports that name. `libbcrypt.a` exports the name below on both. +; +; Generated into an import library by build.mcpp; see port/README.md. +LIBRARY bcrypt.dll +EXPORTS +BCryptGenRandom diff --git a/port/bcryptprimitives.def b/port/bcryptprimitives.def deleted file mode 100644 index 3c4b166..0000000 --- a/port/bcryptprimitives.def +++ /dev/null @@ -1,13 +0,0 @@ -; The system's random generator, and nothing else from this library. -; -; ⭐ ProcessPrng RATHER THAN BCryptGenRandom, AND THE DIFFERENCE IS A HANDLE. -; The documented CNG entry point takes an algorithm handle, which this backend -; would have to open on every call or cache — and caching one means having an -; initialisation order. `ProcessPrng` is the same generator without the handle; -; it has been present since Windows 10 and is what Chromium and Rust's standard -; library moved to for these reasons. -; -; Generated into an import library by build.mcpp; see port/README.md. -LIBRARY bcryptprimitives.dll -EXPORTS -ProcessPrng diff --git a/src/random.cpp b/src/random.cpp index 436a9cf..513f895 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -1,38 +1,51 @@ -// openkal.random on Windows --- ProcessPrng. +// openkal.random on Windows --- BCryptGenRandom with the system-preferred RNG. // -// ⭐ ProcessPrng AND NOT BCryptGenRandom, AND THE DIFFERENCE IS A CONTEXT. +// ⭐ THE FLAG IS WHAT REMOVES THE HANDLE, AND THE HANDLE WAS THE WHOLE OBJECTION. // -// `BCryptGenRandom` is the documented CNG entry point and takes an algorithm -// handle, which means opening one — a cost this interface would pay on every -// call or would have to cache, and caching means initialisation order. Since -// Windows 10 the system exposes `ProcessPrng` in `bcryptprimitives.dll`: the -// same generator, no handle, no failure mode other than the call itself. -// Chromium and Rust's standard library both moved to it for these reasons. +// `BCryptGenRandom` ordinarily takes an algorithm handle, which this backend +// would have to open on every call or cache — and caching one gives this +// interface an initialisation order it does not otherwise have. +// `BCRYPT_USE_SYSTEM_PREFERRED_RNG` says "use the system's own generator" and +// takes a null handle, which is exactly what is wanted here. // -// ⚠️ AND NOT `RtlGenRandom` (`SystemFunction036`), WHICH IS THE OLDER ANSWER. -// That name is undocumented, exported by ordinal from `advapi32`, and reached -// by a declaration each caller writes itself. It works, and this backend does -// not need it: `ProcessPrng` covers every version this package supports. +// ⚠️ AND NOT `ProcessPrng`, WHICH WAS TRIED FIRST AND FAILS ON A WINDOWS HOST. +// That name lives in `bcryptprimitives.dll` and no import library in either +// toolchain exports it: mingw ships `libbcrypt.a` and not the primitives, and +// this package generates its own import libraries only when cross-compiling +// (`build.mcpp` returns immediately on a Windows host, where a real SDK is +// present). Measured — the cross build linked and the native one did not: +// +// lld-link: error: undefined symbol: __declspec(dllimport) ProcessPrng +// +// ⭐ A backend that links on one host and not another is not a backend. The +// name below is in `libbcrypt.a` on both. #include "win.h" #include extern "C" { -// From bcryptprimitives.dll. Declared here rather than by including -// for the reason the whole of win32.h exists: this package names what it uses. -__declspec(dllimport) int __stdcall ProcessPrng(unsigned char* pbData, - okw_uptr cbData); +// From bcrypt.dll. Declared here rather than by including for the +// reason the whole of win.h exists: this package names what it uses. +__declspec(dllimport) long __stdcall BCryptGenRandom(void* hAlgorithm, + unsigned char* pbBuffer, + unsigned long cbBuffer, + unsigned long dwFlags); int kal_random_fill(void* out, kal_uintptr len) { if (len == 0) return kal_ok; if (out == nullptr) return kal_err_invalid; - // ⚠️ NO LOOP. `ProcessPrng` fills the whole buffer or fails; unlike a read - // it has no short return, so a loop would be a loop that always runs once - // and would suggest a partial state this interface does not have. - const int ok = ProcessPrng(static_cast(out), - static_cast(len)); - return ok ? kal_ok : kal_err_io; + // BCRYPT_USE_SYSTEM_PREFERRED_RNG. Spelled as its value for the reason the + // rest of this file spells things: the header it lives in is the system's. + constexpr unsigned long use_system_preferred_rng = 0x00000002ul; + + // ⚠️ NO LOOP. This call fills the whole buffer or fails; unlike a read it + // has no short return, so a loop would always run once and would suggest a + // partial state this interface does not have. + const long st = BCryptGenRandom(nullptr, static_cast(out), + static_cast(len), + use_system_preferred_rng); + return st == 0 /* STATUS_SUCCESS */ ? kal_ok : kal_err_io; } // Neither blocking nor hardware. The system's generator is seeded before a From 16ae67bcc57b06c331ee89ac274930d374ad593a Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Tue, 25 Aug 2026 04:43:48 +0800 Subject: [PATCH 3/8] docs: the first diagnosis read mingw's contents, and mingw is not part of this MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ⚠️ 我判断 `ProcessPrng` 不可用的依据是 `/usr/x86_64-w64-mingw32/lib` 里 没有它。**那个参照物是错的** —— mingw 不属于这套体系,它正是 `build.mcpp` 开头那段注释所说、这个包写出来就是为了不再依赖的东西: > files that exist on a machine with mingw installed and nowhere else … > a green that came from history the new machine does not have 本后端链接的要么是厂商 SDK(Windows 宿主),要么是本包从 `port/*.def` 自己生成的(交叉时),**从来不是第三方的**。 结论仍然成立,理由换成对的:Windows SDK 出 `bcrypt.lib` 而**不出** `bcryptprimitives.dll` 的导入库 —— `ProcessPrng` 文档化了却没有 `.lib`。 `BCryptGenRandom` 由 `bcrypt.lib` 列出,两条路都能解析。 --- port/bcrypt.def | 10 ++++++---- src/random.cpp | 24 ++++++++++++++++++------ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/port/bcrypt.def b/port/bcrypt.def index 6042e32..3f52272 100644 --- a/port/bcrypt.def +++ b/port/bcrypt.def @@ -5,10 +5,12 @@ ; handle would give openkal.random an initialisation order it does not have. ; ; ⚠️ `ProcessPrng` (bcryptprimitives.dll) was tried first: fewer moving parts, -; and what Chromium and Rust use. It fails on a WINDOWS HOST, where this file's -; generated import libraries are not built at all --- `build.mcpp` returns -; immediately there, a real SDK being present --- and no SDK in either toolchain -; exports that name. `libbcrypt.a` exports the name below on both. +; and what Chromium and Rust use. It fails on a WINDOWS HOST, where these +; generated libraries are not built at all --- `build.mcpp` returns immediately +; there, the vendor's own being present and complete --- and the Windows SDK +; ships no import library for `bcryptprimitives.dll`. The name below is listed +; by `bcrypt.lib`, so it resolves through the vendor's libraries and through +; this file alike. ; ; Generated into an import library by build.mcpp; see port/README.md. LIBRARY bcrypt.dll diff --git a/src/random.cpp b/src/random.cpp index 513f895..b007f41 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -9,16 +9,28 @@ // takes a null handle, which is exactly what is wanted here. // // ⚠️ AND NOT `ProcessPrng`, WHICH WAS TRIED FIRST AND FAILS ON A WINDOWS HOST. -// That name lives in `bcryptprimitives.dll` and no import library in either -// toolchain exports it: mingw ships `libbcrypt.a` and not the primitives, and -// this package generates its own import libraries only when cross-compiling -// (`build.mcpp` returns immediately on a Windows host, where a real SDK is -// present). Measured — the cross build linked and the native one did not: // // lld-link: error: undefined symbol: __declspec(dllimport) ProcessPrng // +// The cross build linked and the native one did not, and the asymmetry is in +// where the import library comes from. Cross-compiling, this package generates +// its own from `port/*.def` — a list of names, so a name it lists is a name it +// has. On a Windows host `build.mcpp` returns immediately and the VENDOR's +// import libraries are used, because they are present and complete; and the +// Windows SDK ships `bcrypt.lib` but no import library for +// `bcryptprimitives.dll`. `ProcessPrng` is documented and has no `.lib`. +// // ⭐ A backend that links on one host and not another is not a backend. The -// name below is in `libbcrypt.a` on both. +// name below is exported by `bcrypt.dll` and listed by `bcrypt.lib`, so it +// resolves through the vendor's libraries and through this package's generated +// one alike. +// +// ⚠️ THE FIRST DIAGNOSIS OF THIS WAS WRONG AND IS RECORDED SO IT IS NOT REPEATED: +// it read `/usr/x86_64-w64-mingw32/lib` and concluded from mingw's contents. +// mingw is not part of this ecosystem — it is the very thing `build.mcpp` +// exists to stop depending on, as the note at the top of that file says. What +// this backend links against is either the vendor's SDK or this package's own +// generated libraries, and never a third party's. #include "win.h" #include From 89e481fa65e167cc3956d32225b269e797793e0f Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Tue, 25 Aug 2026 04:49:03 +0800 Subject: [PATCH 4/8] deps: follow openkal 0.7.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 规范新增 `openkal.random`,版本升到 0.7.0。CI 的版本同步门要求实现与规范 同步声明 —— 它的诊断说得很准:「Nothing is wrong with either; they are not in step.」 --- mcpp.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mcpp.toml b/mcpp.toml index 1463f0e..2802a41 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -18,7 +18,7 @@ authors = ["mcpplibs"] repo = "https://github.com/mcpplibs/openkal-windows" [dependencies] -openkal = "0.6.0" +openkal = "0.7.0" # The package contributes definitions and no modules. The interface it # implements is declared by the specification package, which this package From 2aee72e896ae77a180358e57f0c982d975e238a1 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Tue, 25 Aug 2026 04:56:48 +0800 Subject: [PATCH 5/8] =?UTF-8?q?fix:=20name=20bcrypt=20on=20the=20MSVC=20si?= =?UTF-8?q?de=20too=20=E2=80=94=20it=20needed=20no=20library=20list=20unti?= =?UTF-8?q?l=20now?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 一致性矩阵的两个 Windows 格都失败: lld-link: error: undefined symbol: __declspec(dllimport) BCryptGenRandom ⚠️ 而链接行里**一个 `-l` 都没有** —— 不是库里没这个名字,是那段 `ldflags` 挂在 `cfg(all(windows, not(env = "msvc")))` 下,两格都被排除: 一格是 `msvc@system`,另一格 `llvm@20.1.7` 在 Windows 宿主上默认目标 就是 `windows-msvc`。 ⭐ MSVC 那侧此前**不需要任何库列表** —— SDK 隐式链接常用的一套,写 `ldflags` 纯属噪声。`openkal.random` 改变了这一点:`bcrypt.lib` 不在两个工具链的 隐式集合里。两侧各写一次,因为它们对库的拼法不同、不共用列表。 ⚠️ 我先前判断「Windows SDK 出 bcrypt.lib 所以能解析」——**那句是凭印象**, 与我几小时前查 mingw 目录犯的是同一个错。真正的判据是那个 job 的链接行, 而它一个 `-l` 都没有。 --- mcpp.toml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/mcpp.toml b/mcpp.toml index 2802a41..b772218 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -51,6 +51,24 @@ openkal = "0.7.0" [target.'cfg(all(windows, not(env = "msvc")))'.build] ldflags = ["-lntdll", "-lsynchronization", "-lshell32", "-lkernel32", "-lbcrypt"] +# ⚠️ AND THE SAME ONE LIBRARY ON THE MSVC SIDE, WHICH UNTIL NOW NEEDED NONE. +# +# The block above lists the four this implementation calls into, and the MSVC +# side never needed a list: its SDK links the usual set implicitly, so a +# `ldflags` entry there would have been noise. `openkal.random` changes that — +# `BCryptGenRandom` is in `bcrypt.lib`, and that one is not among the implicit +# set on either the MSVC toolchain or clang's `windows-msvc` target. +# +# Measured on the conformance matrix, both Windows rows: +# +# lld-link: error: undefined symbol: __declspec(dllimport) BCryptGenRandom +# +# ⚠️ The predicate above is `not(env = "msvc")`, so neither row saw the entry +# there. The library is named once per side because the two sides spell a +# library differently and share no list. +[target.'cfg(all(windows, env = "msvc"))'.build] +ldflags = ["-lbcrypt"] + # Exceptions and run-time type information, on the one ABI where their absence # is asserted. # From ffb96cfa415e6da1cf90e9b594c9161a2f2e70b2 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Tue, 25 Aug 2026 05:06:32 +0800 Subject: [PATCH 6/8] fix(random): LINK.EXE reads `-lbcrypt` as an option named `l` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MSVC conformance row still failed after the last commit named the library, because it named it in the other side's spelling. `ldflags` reaches the command line verbatim, and the two rows this predicate selects do not drive the same program: LINK : warning LNK4044: unrecognized option '/lbcrypt'; ignored random.obj : error LNK2019: unresolved external symbol __imp_BCryptGenRandom clang drives lld-link and translates GNU spellings for it; `msvc@system` drives LINK.EXE directly, and LINK reads `-` and `/` as the same option prefix. One spelling serves both. Measured, rather than assumed, before changing the row that was already green: clang --target=x86_64-pc-windows-msvc -fuse-ld=lld -### t.c bcrypt.lib → 'bcrypt.lib' clang --target=x86_64-pc-windows-msvc -fuse-ld=lld -### t.c -lbcrypt → "bcrypt.lib" The clang link line is byte-identical under both, so this cannot regress the row it is not aimed at. --- mcpp.toml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/mcpp.toml b/mcpp.toml index b772218..36bbc40 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -66,8 +66,24 @@ ldflags = ["-lntdll", "-lsynchronization", "-lshell32", "-lkernel32", "-lbcrypt" # ⚠️ The predicate above is `not(env = "msvc")`, so neither row saw the entry # there. The library is named once per side because the two sides spell a # library differently and share no list. +# +# ⭐ AND THE SPELLING HERE IS `bcrypt.lib`, NOT `-lbcrypt`. `ldflags` reaches +# the command line verbatim; mcpp's dialect abstraction covers the flags it +# generates itself, not the ones a manifest writes. The two rows this +# predicate selects do not drive the same program: clang drives lld-link and +# translates for it, while `msvc@system` drives LINK.EXE directly, and LINK +# reads `-` and `/` as the same prefix — so `-lbcrypt` arrives as an option +# named `l`: +# +# LINK : warning LNK4044: unrecognized option '/lbcrypt'; ignored +# random.obj : error LNK2019: unresolved external symbol __imp_BCryptGenRandom +# +# One spelling serves both, which is why this stays a single entry. Measured +# with `clang --target=x86_64-pc-windows-msvc -### … -fuse-ld=lld`: the driver +# emits `bcrypt.lib` to lld-link for `-lbcrypt` and for `bcrypt.lib` alike, so +# the clang link line is unchanged by this and only LINK.EXE sees a difference. [target.'cfg(all(windows, env = "msvc"))'.build] -ldflags = ["-lbcrypt"] +ldflags = ["bcrypt.lib"] # Exceptions and run-time type information, on the one ABI where their absence # is asserted. From 70b16fe09f2eab5d5012d05ef63aa64190aee170 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Tue, 25 Aug 2026 05:12:28 +0800 Subject: [PATCH 7/8] fix(random): name bcrypt the way this package already names libraries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `src/win.cpp` records the four Win32 libraries this backend calls into as linker directives in the object, under `#if defined(_MSC_VER)`, and `mcpp.toml` says so four lines below the block I was editing. Two manifest spellings were pushed before reading that, and neither could have worked: `ldflags` reaches the command line verbatim, and the two toolchains `env = "msvc"` selects reject each other's word for a library. LINK.EXE -lbcrypt → LNK4044: unrecognized option '/lbcrypt' clang++ bcrypt.lib → error: no such file or directory `cfg()` cannot separate them: its four keys name the target, and which compiler drives the link is not a property of the target. The directive mechanism needs no key, and both toolchains on that side read it. clang++ --target=x86_64-pc-windows-msvc -c → Directive(s): /DEFAULTLIB:bcrypt.lib clang++ --target=x86_64-w64-windows-gnu → _MSC_VER undefined, ignored; the GNU list in the manifest carries it The pragma is in `src/random.cpp` rather than beside the other four because a directive travels in the object that carries it, and the object a linker pulls in for `kal_random_fill` is that one. --- mcpp.toml | 37 ++----------------------------------- src/random.cpp | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 35 deletions(-) diff --git a/mcpp.toml b/mcpp.toml index 36bbc40..f7b919a 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -51,40 +51,6 @@ openkal = "0.7.0" [target.'cfg(all(windows, not(env = "msvc")))'.build] ldflags = ["-lntdll", "-lsynchronization", "-lshell32", "-lkernel32", "-lbcrypt"] -# ⚠️ AND THE SAME ONE LIBRARY ON THE MSVC SIDE, WHICH UNTIL NOW NEEDED NONE. -# -# The block above lists the four this implementation calls into, and the MSVC -# side never needed a list: its SDK links the usual set implicitly, so a -# `ldflags` entry there would have been noise. `openkal.random` changes that — -# `BCryptGenRandom` is in `bcrypt.lib`, and that one is not among the implicit -# set on either the MSVC toolchain or clang's `windows-msvc` target. -# -# Measured on the conformance matrix, both Windows rows: -# -# lld-link: error: undefined symbol: __declspec(dllimport) BCryptGenRandom -# -# ⚠️ The predicate above is `not(env = "msvc")`, so neither row saw the entry -# there. The library is named once per side because the two sides spell a -# library differently and share no list. -# -# ⭐ AND THE SPELLING HERE IS `bcrypt.lib`, NOT `-lbcrypt`. `ldflags` reaches -# the command line verbatim; mcpp's dialect abstraction covers the flags it -# generates itself, not the ones a manifest writes. The two rows this -# predicate selects do not drive the same program: clang drives lld-link and -# translates for it, while `msvc@system` drives LINK.EXE directly, and LINK -# reads `-` and `/` as the same prefix — so `-lbcrypt` arrives as an option -# named `l`: -# -# LINK : warning LNK4044: unrecognized option '/lbcrypt'; ignored -# random.obj : error LNK2019: unresolved external symbol __imp_BCryptGenRandom -# -# One spelling serves both, which is why this stays a single entry. Measured -# with `clang --target=x86_64-pc-windows-msvc -### … -fuse-ld=lld`: the driver -# emits `bcrypt.lib` to lld-link for `-lbcrypt` and for `bcrypt.lib` alike, so -# the clang link line is unchanged by this and only LINK.EXE sees a difference. -[target.'cfg(all(windows, env = "msvc"))'.build] -ldflags = ["bcrypt.lib"] - # Exceptions and run-time type information, on the one ABI where their absence # is asserted. # @@ -101,7 +67,8 @@ ldflags = ["bcrypt.lib"] # nothing to say about how a program that has a runtime unwinds. cxxflags = ["-fno-exceptions", "-fno-rtti"] -# The other ABI names them in src/win.cpp instead, where its compilers record +# The other ABI names them in the sources instead --- src/win.cpp for the four +# above and src/random.cpp for bcrypt --- where its compilers record # the requirement in the object they produce. A library named on the link line # there would have to be named again by every program that links this package; # a library named in the object travels with it. diff --git a/src/random.cpp b/src/random.cpp index b007f41..3882060 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -31,6 +31,26 @@ // exists to stop depending on, as the note at the top of that file says. What // this backend links against is either the vendor's SDK or this package's own // generated libraries, and never a third party's. +// The library this one interface lives in, named the way src/win.cpp names the +// other four and for the reason given there. It is here rather than beside +// them because a directive travels in the object that carries it, and the +// object a linker pulls in for `kal_random_fill` is this one. +// +// ⚠️ TWO SPELLINGS IN THE MANIFEST WERE TRIED BEFORE THIS AND BOTH WERE PUSHED, +// while the mechanism was already established in this package and named four +// lines below the block being edited. `ldflags` reaches the command line +// verbatim, and the two toolchains that `env = "msvc"` selects reject each +// other's word for a library: +// +// LINK.EXE -lbcrypt → LNK4044: unrecognized option '/lbcrypt' +// clang++ bcrypt.lib → error: no such file or directory +// +// `cfg()` cannot separate them — its four keys name the target, and which +// compiler drives the link is not a property of the target. +#if defined(_MSC_VER) +#pragma comment(lib, "bcrypt.lib") +#endif + #include "win.h" #include From 1d3c1862f7e2cbce349f133eb81e8e945b04450a Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Tue, 25 Aug 2026 05:21:36 +0800 Subject: [PATCH 8/8] release: 0.1.5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The branch carried 0.1.4, which is the version on `main` and the version already in the index. Following openkal 0.7.0 changes what this package declares, and on two of these repositories it also adds an interface, so the content behind that number is no longer the content published under it. openkal takes a minor bump for a new interface and an implementation following it takes a patch bump --- the shape of 0.5.2 → 0.6.0 with 0.5.2 → 0.5.3 beneath it. --- mcpp.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mcpp.toml b/mcpp.toml index f7b919a..2a6812b 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -1,7 +1,7 @@ [package] namespace = "mcpplibs" name = "openkal-windows" -version = "0.1.4" +version = "0.1.5" description = "An implementation of openkal for Windows, written on the Win32 interfaces and the object manager beneath them, using no C runtime symbol." license = "Apache-2.0"