From 4934259ee19da5d8eb7e57f8ee94e72d8eded8bf Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Wed, 12 Aug 2026 17:25:29 -0500 Subject: [PATCH] docs: clarify Windows library link dependencies --- packages/compiler/src/backend/cc.ts | 28 +++++++++++-------- .../compiler/src/backend/object-localize.ts | 9 +++--- packages/compiler/src/library/profile.ts | 15 ++++++---- tests/harness/library-multi.test.ts | 9 +++--- 4 files changed, 36 insertions(+), 25 deletions(-) diff --git a/packages/compiler/src/backend/cc.ts b/packages/compiler/src/backend/cc.ts index 51600d907..67e9b9e03 100644 --- a/packages/compiler/src/backend/cc.ts +++ b/packages/compiler/src/backend/cc.ts @@ -1101,9 +1101,12 @@ async function ensureTlsArchive( * units a library may reach are the pure-data ones (regex + the vendored matcher, assert, * inspect, symbol, searchParams, emitter+dyn_handle, zlib); every * loop-hooked or ambient unit was refused at SC4005 before emission. - * External-symbol contract: undefined references only to libc/libm — which - * is why zlib rides the VENDORED per-flavor objects here even on hosts - * (the executable lane's system `-lz` cannot ride inside an archive). */ + * External-symbol contract: undefined references only to the target's C/math + * runtime and system APIs. Windows embedders additionally link advapi32, + * iphlpapi, and ws2_32; the platform driver supplies its ordinary CRT and + * kernel imports. Zlib rides the VENDORED per-flavor objects here even on + * hosts because the executable lane's system `-lz` cannot ride inside an + * archive. */ /** The library base: the executable lane's unconditional sources minus the * fiber/loop and child-process units, plus the library-mode TU. */ @@ -1129,11 +1132,12 @@ export interface LibArchiveOptions { * symbol. Toolchain sanitizer ABI remains external as required, * so N archives built under pairwise-distinct prefixes link into one * process with no symbol collisions and no shared mutable runtime state. - * Undefined references (libc/libm, plus sanitizer ABI in instrumented - * builds) keep their global binding. Omitted = the classic archive, - * byte-for-byte. Admitted for darwin/linux/win32 native hosts, linux and - * windows cross triples from any host, and macos cross triples from a - * darwin host (compileLibrary owns the refusal fence). */ + * Undefined references (the target C/math runtime and system APIs, plus + * sanitizer ABI in instrumented builds) keep their global binding. Windows + * embedders additionally link advapi32, iphlpapi, and ws2_32. Omitted = the + * classic archive, byte-for-byte. Admitted for darwin/linux/win32 native + * hosts, linux and windows cross triples from any host, and macos cross + * triples from a darwin host (compileLibrary owns the refusal fence). */ localizeSymbols?: readonly string[]; /** Thread-instanced state (the profile's abi.instance_per_thread): every * TU of the archive compiles with -DSCR_THREAD_INSTANCES, moving the @@ -1559,9 +1563,11 @@ export async function compileLibArchive(opts: LibArchiveOptions): Promise * renamed apart — they stop being visible to the embedder's linker at all, * so a second archive built under a different prefix brings its own private * copy of the whole runtime (allocator, collector, arena, panic sink) into - * the same process. Undefined references (libc/libm, plus sanitizer ABI in - * instrumented builds) keep their global binding: the C library and - * sanitizer are the embedder's, shared by design. + * the same process. Undefined references (the target C/math runtime and + * system APIs, plus sanitizer ABI in instrumented builds) keep their global + * binding: those platform services and the sanitizer are the embedder's, + * shared by design. Windows embedders additionally link advapi32, iphlpapi, + * and ws2_32. * * Member selection matters: a classic archive's unused members (and their * undefined references to units library mode excludes, like the diff --git a/packages/compiler/src/backend/object-localize.ts b/packages/compiler/src/backend/object-localize.ts index e7b6ec7e6..979bac009 100644 --- a/packages/compiler/src/backend/object-localize.ts +++ b/packages/compiler/src/backend/object-localize.ts @@ -29,10 +29,11 @@ * symbol. * * Shared demotion rule (GNU objcopy --keep-global-symbols semantics): - * undefined references keep their global binding (libc/libm and sanitizer - * ABI are the embedder's by design), and COMMON symbols stay global — - * sanitizer image-registration guards are COMMONs whose merging across - * archives is the documented single-registration discipline. */ + * undefined references keep their global binding (the target C/math runtime, + * system APIs, and sanitizer ABI are the embedder's by design), and COMMON + * symbols stay global — sanitizer image-registration guards are COMMONs whose + * merging across archives is the documented single-registration discipline. + * Windows embedders additionally link advapi32, iphlpapi, and ws2_32. */ const textDecoder = new TextDecoder(); const textEncoder = new TextEncoder(); diff --git a/packages/compiler/src/library/profile.ts b/packages/compiler/src/library/profile.ts index b86af7351..706c8d362 100644 --- a/packages/compiler/src/library/profile.ts +++ b/packages/compiler/src/library/profile.ts @@ -90,12 +90,15 @@ * runtime internals — allocator, cycle collector, result arena, panic-sink * registration, every other piece of runtime state — are demoted to LOCAL * symbols behind the profile's prefix, so an ordinary archive's only - * external definitions are the profile-declared symbols and its only - * external references are libc/libm. Sanitized artifacts additionally carry - * their sanitizer ABI; Darwin ASan's image-registration COMMON deliberately - * remains shared so the final Mach-O image registers its globals exactly - * once. N such archives, built under pairwise-distinct prefixes, then link - * into ONE process with no scriptc-runtime symbol collisions or shared + * external definitions are the profile-declared symbols. External references + * are the target's C/math runtime and system APIs: Windows embedders + * additionally link advapi32, iphlpapi, and ws2_32, while the platform driver + * supplies its ordinary CRT and kernel imports. Sanitized artifacts also + * carry their sanitizer ABI; Darwin ASan's image-registration COMMON + * deliberately remains shared so the final Mach-O image registers its + * globals exactly once. N such archives, built under pairwise-distinct + * prefixes, then link into ONE process with no scriptc-runtime symbol + * collisions or shared * mutable state: each instance owns a private copy of the whole runtime, * panic sinks register per instance, and a trap poisons only the instance it * fired in. The embedder contract that makes this sound: diff --git a/tests/harness/library-multi.test.ts b/tests/harness/library-multi.test.ts index 80c67b447..9d976d867 100644 --- a/tests/harness/library-multi.test.ts +++ b/tests/harness/library-multi.test.ts @@ -10,8 +10,9 @@ * M1 symbols-exact nm over a localized archive: the external defined * set equals the profile-declared set EXACTLY (plus * ASan's one image-registration common in a - * sanitized build), and undefineds stay libc/libm- - * shaped apart from sanitizer ABI references + * sanitized build), and undefineds stay target- + * runtime/system-API-shaped apart from sanitizer ABI + * references * M2 two-instance run the acceptance probe: two archives (ma_/mb_), two * embedder threads (one per instance — the * documented contract), independent init and @@ -207,8 +208,8 @@ describe.each(EMISSIONS)("localized archive symbols, %s emission", (emission) => : []; expect([...defined].sort()).toEqual([...declared, ...toolchainDefinitions].sort()); // Undefineds: no runtime-internal or prefix-carrying reference - // escapes; libc/libm (and sanitizer ABI) references keep their global - // binding. + // escapes; target-runtime/system-API (and sanitizer ABI) references + // keep their global binding. expect([...undef].filter((s) => s.startsWith("scr_") || s.startsWith("ma_") || s.startsWith("mb_"))).toEqual([]); // The ambient audit holds through the combine step: no // process-disposition or threading surface, no atexit teardown.