Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion build.mcpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ int main() {
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;

Expand All @@ -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",
"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
Expand Down
9 changes: 5 additions & 4 deletions mcpp.toml
Original file line number Diff line number Diff line change
@@ -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"

Expand All @@ -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
Expand Down Expand Up @@ -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", "-lbcrypt"]

# Exceptions and run-time type information, on the one ABI where their absence
# is asserted.
Expand All @@ -67,7 +67,8 @@ ldflags = ["-lntdll", "-lsynchronization", "-lshell32", "-lkernel32"]
# 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.
Expand Down
18 changes: 18 additions & 0 deletions port/bcrypt.def
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
; 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 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
EXPORTS
BCryptGenRandom
88 changes: 88 additions & 0 deletions src/random.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// openkal.random on Windows --- BCryptGenRandom with the system-preferred RNG.
//
// ⭐ THE FLAG IS WHAT REMOVES THE HANDLE, AND THE HANDLE WAS THE WHOLE OBJECTION.
//
// `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 `ProcessPrng`, WHICH WAS TRIED FIRST AND FAILS ON A WINDOWS HOST.
//
// 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 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.
// 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 <openkal/random.h>

extern "C" {

// From bcrypt.dll. Declared here rather than by including <windows.h> 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;

// 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<unsigned char*>(out),
static_cast<unsigned long>(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
// 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"
Loading