@scriptc/compiler 0.0.27 → 0.0.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -255,9 +255,12 @@ export interface LibArchiveOptions {
255
255
  * symbol. Toolchain sanitizer ABI remains external as required,
256
256
  * so N archives built under pairwise-distinct prefixes link into one
257
257
  * process with no symbol collisions and no shared mutable runtime state.
258
- * Undefined references (libc/libm, plus sanitizer ABI in instrumented
259
- * builds) keep their global binding. Omitted = the classic archive,
260
- * byte-for-byte. Host-native builds only. */
258
+ * Undefined references (the target C/math runtime and system APIs, plus
259
+ * sanitizer ABI in instrumented builds) keep their global binding. Windows
260
+ * embedders additionally link advapi32, iphlpapi, and ws2_32. Omitted = the
261
+ * classic archive, byte-for-byte. Admitted for darwin/linux/win32 native
262
+ * hosts, linux and windows cross triples from any host, and macos cross
263
+ * triples from a darwin host (compileLibrary owns the refusal fence). */
261
264
  localizeSymbols?: readonly string[];
262
265
  /** Thread-instanced state (the profile's abi.instance_per_thread): every
263
266
  * TU of the archive compiles with -DSCR_THREAD_INSTANCES, moving the
@@ -6,6 +6,7 @@ import { access, chmod, copyFile, link, mkdir, mkdtemp, readdir, readFile, realp
6
6
  import { availableParallelism, homedir, tmpdir } from "node:os";
7
7
  import { basename, delimiter, dirname, extname, isAbsolute, join, resolve } from "node:path";
8
8
  import { promisify } from "node:util";
9
+ import { localizeElfObject, mergeAndLocalizeCoffObjects } from "./object-localize.js";
9
10
  const execFileAsync = promisify(execFile);
10
11
  /** Test lanes run against one immutable checkout and one immutable toolchain
11
12
  * for the lifetime of each Vitest worker. Production deliberately rediscovers
@@ -712,9 +713,12 @@ async function ensureTlsArchive(sanitize, driver, buildIdentity, cacheRoot = ven
712
713
  * units a library may reach are the pure-data ones (regex + the vendored matcher, assert,
713
714
  * inspect, symbol, searchParams, emitter+dyn_handle, zlib); every
714
715
  * loop-hooked or ambient unit was refused at SC4005 before emission.
715
- * External-symbol contract: undefined references only to libc/libm — which
716
- * is why zlib rides the VENDORED per-flavor objects here even on hosts
717
- * (the executable lane's system `-lz` cannot ride inside an archive). */
716
+ * External-symbol contract: undefined references only to the target's C/math
717
+ * runtime and system APIs. Windows embedders additionally link advapi32,
718
+ * iphlpapi, and ws2_32; the platform driver supplies its ordinary CRT and
719
+ * kernel imports. Zlib rides the VENDORED per-flavor objects here even on
720
+ * hosts because the executable lane's system `-lz` cannot ride inside an
721
+ * archive. */
718
722
  /** The library base: the executable lane's unconditional sources minus the
719
723
  * fiber/loop and child-process units, plus the library-mode TU. */
720
724
  const LIB_RUNTIME_SOURCES = [
@@ -817,9 +821,10 @@ export async function compileLibArchive(opts) {
817
821
  }
818
822
  const vendorBuildIdentity = await currentVendorCacheBuildIdentity(driver, `${toolchainEnv}\0${implicitToolchain ?? "<uncached>"}`);
819
823
  // Runtime-localized archives skip the completed-archive tier: their bytes
820
- // additionally depend on the host's ld/objcopy identities, which the
821
- // archive key does not fingerprint. The runtime-object tier still serves
822
- // them (localization consumes the same per-flavor objects).
824
+ // additionally depend on the localization toolchain's identity (host ld/
825
+ // objcopy or the cross driver's lld), which the archive key does not
826
+ // fingerprint. The runtime-object tier still serves them (localization
827
+ // consumes the same per-flavor objects).
823
828
  const cacheCompleteArchive = opts.localizeSymbols === undefined &&
824
829
  root !== null && await archiverSupportsPersistentCache(arArgv, driver);
825
830
  let cachedArchive = null;
@@ -1054,9 +1059,11 @@ export async function compileLibArchive(opts) {
1054
1059
  * renamed apart — they stop being visible to the embedder's linker at all,
1055
1060
  * so a second archive built under a different prefix brings its own private
1056
1061
  * copy of the whole runtime (allocator, collector, arena, panic sink) into
1057
- * the same process. Undefined references (libc/libm, plus sanitizer ABI in
1058
- * instrumented builds) keep their global binding: the C library and
1059
- * sanitizer are the embedder's, shared by design.
1062
+ * the same process. Undefined references (the target C/math runtime and
1063
+ * system APIs, plus sanitizer ABI in instrumented builds) keep their global
1064
+ * binding: those platform services and the sanitizer are the embedder's,
1065
+ * shared by design. Windows embedders additionally link advapi32, iphlpapi,
1066
+ * and ws2_32.
1060
1067
  *
1061
1068
  * Member selection matters: a classic archive's unused members (and their
1062
1069
  * undefined references to units library mode excludes, like the
@@ -1064,26 +1071,37 @@ export async function compileLibArchive(opts) {
1064
1071
  * blind merge of every object would carry those references into the one
1065
1072
  * combined member. Staging the support objects into an intermediate
1066
1073
  * archive keeps the linker's own member semantics: `ld -r` pulls only the
1067
- * members the program object transitively needs.
1074
+ * members the program object transitively needs (the COFF arm implements
1075
+ * the same member semantics in process).
1068
1076
  *
1069
- * darwin — one ld64 invocation: -r merges program + needed members,
1077
+ * Mach-O — one host-ld64 invocation: -r merges program + needed members,
1070
1078
  * -exported_symbols_list demotes every unlisted global to
1071
1079
  * private extern, and -r without -keep_private_externs writes
1072
1080
  * private externs out as non-external symbols. Apple ASan's
1073
1081
  * image-registration COMMON remains shared so the final Mach-O
1074
- * image registers its globals once.
1075
- * linux — ld -r merges with --force-group-allocation (ASan's
1076
- * instrumented globals ride ELF section groups whose signatures
1077
- * repeat across archives sharing runtime objects; resolving the
1078
- * groups into the combined member keeps a later multi-archive
1079
- * link from discarding one archive's copies), then binutils
1080
- * objcopy --keep-global-symbols localizes every other DEFINED
1081
- * global (objcopy leaves undefined symbols global by its own
1082
- * rule).
1083
- *
1084
- * Host-native only: compileLibrary refuses localization for cross targets
1085
- * before emission (this step runs the host's ld/objcopy over host-format
1086
- * objects). */
1082
+ * image registers its globals once. ld64 reads every macOS
1083
+ * architecture, so darwin-host macos cross triples ride the
1084
+ * same arm; compileLibrary refuses macos targets elsewhere.
1085
+ * ELF, native linux host — ld -r merges with --force-group-allocation
1086
+ * (ASan's instrumented globals ride ELF section groups whose
1087
+ * signatures repeat across archives sharing runtime objects;
1088
+ * resolving the groups into the combined member keeps a later
1089
+ * multi-archive link from discarding one archive's copies),
1090
+ * then binutils objcopy --keep-global-symbols localizes every
1091
+ * other DEFINED global (objcopy leaves undefined symbols
1092
+ * global by its own rule). The exact historical recipe.
1093
+ * ELF, cross triples from any host — `zig cc -target <triple> -r`
1094
+ * merges (zig is already the cross driver's hard requirement),
1095
+ * then localizeElfObject demotes in process and resolves
1096
+ * section groups the way --force-group-allocation does, with
1097
+ * no host binutils/llvm-objcopy dependency.
1098
+ * COFF, native win32 hosts and windows cross triples from any host —
1099
+ * mergeAndLocalizeCoffObjects performs member selection, the
1100
+ * merge, cross-object symbol resolution, and demotion in
1101
+ * process: no linker offers a COFF relocatable mode (lld-link
1102
+ * mirrors MSVC link.exe; zig's COFF driver refuses multi-object
1103
+ * merges) and llvm-objcopy rejects symbol-scope flags for
1104
+ * COFF, so no tool pairing exists to shell out to. */
1087
1105
  async function localizeLibraryObjects(driver, arArgv, buildDir, programObject, supportObjects, keepSymbols, stem) {
1088
1106
  const platform = targetPlatform(driver);
1089
1107
  const combined = join(buildDir, `${stem}.localized.o`);
@@ -1096,21 +1114,56 @@ async function localizeLibraryObjects(driver, arArgv, buildDir, programObject, s
1096
1114
  catch (err) {
1097
1115
  const stderr = err.stderr ?? String(err);
1098
1116
  throw new Error(`${argv[0]} failed while localizing the library archive's runtime symbols (abi.localize_runtime).\n` +
1099
- `Runtime symbol localization needs the host toolchain's ${platform === "darwin" ? "ld" : "ld and objcopy"} beside the C compiler.\n\n${stderr}`);
1117
+ `Runtime symbol localization needs the ${platform === "darwin" ? "host toolchain's ld" : driver.target === null ? "host toolchain's ld and objcopy" : "cross driver's relocatable link"} beside the C compiler.\n\n${stderr}`);
1100
1118
  }
1101
1119
  };
1120
+ if (platform === "win32") {
1121
+ // COFF has no relocatable-link tool to stage through; the member
1122
+ // selection and combine+demote happen in process over the object bytes.
1123
+ const [program, ...support] = await Promise.all([programObject, ...supportObjects].map((path) => readFile(path)));
1124
+ try {
1125
+ await writeFile(combined, mergeAndLocalizeCoffObjects(program, support, new Set(keepSymbols), {
1126
+ program: basename(programObject),
1127
+ support: supportObjects.map((path) => basename(path)),
1128
+ }));
1129
+ }
1130
+ catch (err) {
1131
+ throw new Error(`COFF symbol localization failed while localizing the library archive's runtime symbols (abi.localize_runtime).\n\n${err.message}`);
1132
+ }
1133
+ return combined;
1134
+ }
1102
1135
  await run([arArgv[0] ?? "ar", ...arArgv.slice(1), "rcs", staging, ...supportObjects]);
1103
1136
  if (platform === "darwin") {
1104
1137
  await writeFile(keepFile, keepSymbols.map((s) => `_${s}\n`).join(""));
1105
1138
  await run(["ld", "-r", programObject, staging, "-o", combined, "-exported_symbols_list", keepFile]);
1106
1139
  }
1107
- else if (platform === "linux") {
1140
+ else if (platform === "linux" && driver.target === null) {
1108
1141
  await writeFile(keepFile, keepSymbols.map((s) => `${s}\n`).join(""));
1109
1142
  await run(["ld", "-r", "--force-group-allocation", programObject, staging, "-o", combined]);
1110
1143
  await run(["objcopy", `--keep-global-symbols=${keepFile}`, combined]);
1111
1144
  }
1145
+ else if (platform === "linux") {
1146
+ // Cross ELF: the cross driver's own lld performs the relocatable merge
1147
+ // (with the staging archive's member semantics); demotion and section-
1148
+ // group resolution happen in process. -nostdlib keeps zig from feeding
1149
+ // libc/compiler-rt inputs into the merge.
1150
+ await run([
1151
+ driver.argv[0] ?? "zig",
1152
+ ...driver.argv.slice(1),
1153
+ "-target", driver.target,
1154
+ "-nostdlib",
1155
+ "-r", programObject, staging,
1156
+ "-o", combined,
1157
+ ]);
1158
+ try {
1159
+ await writeFile(combined, localizeElfObject(await readFile(combined), new Set(keepSymbols)));
1160
+ }
1161
+ catch (err) {
1162
+ throw new Error(`ELF symbol localization failed while localizing the library archive's runtime symbols (abi.localize_runtime).\n\n${err.message}`);
1163
+ }
1164
+ }
1112
1165
  else {
1113
- throw new Error(`runtime symbol localization (abi.localize_runtime) supports darwin and linux host builds; this build targets ${platform}`);
1166
+ throw new Error(`runtime symbol localization (abi.localize_runtime) has no ${platform} arm; compileLibrary admits darwin, linux, and win32 builds only`);
1114
1167
  }
1115
1168
  return combined;
1116
1169
  }