Skip to content

build: generate the Node startup snapshot on cross-arch builds too - #52874

Merged
MarshallOfSound merged 1 commit into
mainfrom
pr/cross-arch-node-snapshot
Aug 17, 2026
Merged

build: generate the Node startup snapshot on cross-arch builds too#52874
MarshallOfSound merged 1 commit into
mainfrom
pr/cross-arch-node-snapshot

Conversation

@codebytere

Copy link
Copy Markdown
Member

Description of Change

node_use_node_snapshot / node_use_node_code_cache were gated to host_cpu == target_cpu, so the targets we cross-build — linux arm64 and win arm64 (x64 hosts) and mac x64 (arm64 hosts) — ship without the embedded Node startup snapshot from #51703 and its builtin code cache, and every process there still runs the full Node bootstrap.

node_mksnapshot already builds in v8_snapshot_toolchain, like mksnapshot and v8_context_snapshot_generator. Unlike those it executes target-CPU code (the Node bootstrap runs in JS), so what it needs is a snapshot toolchain that can run what its V8 emits: V8's arm64 simulator on x64 hosts (//build/toolchain/linux:clang_x64_v8_arm64, the win host toolchain with v8_target_cpu = "arm64"), or an x64 binary under Rosetta on arm64 macs — which is what our v8_snapshot_toolchain="//build/toolchain/mac:clang_x64" override for mac x64 in build-electron/action.yml already provides. The one same-OS combination that cannot work is V8's default for mac arm64 → x64, clang_arm64_v8_x64 (an arm64 binary emitting x64 code, no simulator) — that is the crash the old TODO in node.gni described. Neither the snapshot nor the code cache contains machine code, so what a cross node_mksnapshot produces is valid on the target; the js2c code cache is already generated cross-arch the same way.

So this gates both flags on host_os == target_os except that toolchain, gives the node_snapshot / node_snapshot_stub source_sets the //base:debugging_buildflags dependency that node_builtins.h's base/dcheck_is_on.h include needs (a fresh out dir building only third_party/electron_node:node_snapshot fails on the missing generated header otherwise), and updates the patch description.

Verified on a linux x64 host with target_cpu = "arm64": node_mksnapshot runs under the simulator (blob 4.9 MB, 392-entry / 2.8 MB code cache — same shape as native), the snapshot-keyed browser js2c cache tool runs against it, and electron links with the snapshot embedded. I have not booted the result on arm64 hardware; the arm64 test legs cover it (process.config.variables.node_use_node_snapshot, and the js2c code-cache spec's browser assertions only pass if the browser isolate really came from the snapshot). mac x64 and win arm64 are by construction and untested locally, so I'd like to see all the cross legs green here before merging.

Checklist

  • PR description included and stakeholders cc'd
  • npm test passes

Release Notes

Notes: Linux arm64, Windows arm64 and macOS x64 builds now start the main process from the embedded Node.js startup snapshot like the other platforms, improving startup time.

node_use_node_snapshot / node_use_node_code_cache were gated to
host_cpu == target_cpu, so the targets Electron cross-builds -- linux
arm64 and win arm64 (x64 hosts) and mac x64 (arm64 hosts) -- shipped
without the embedded Node snapshot and its builtin code cache, and every
process there still ran the full Node bootstrap.

node_mksnapshot already builds in v8_snapshot_toolchain, like mksnapshot
and v8_context_snapshot_generator. Unlike those it executes target-CPU
code (the Node bootstrap runs in JS), so the requirement is a snapshot
toolchain that can run what its V8 emits: V8's arm64 simulator on x64
hosts (linux clang_x64_v8_arm64, the win host toolchain with
v8_target_cpu = arm64), or an x64 binary under Rosetta on arm64 macs,
which is what Electron CI's v8_snapshot_toolchain =
"//build/toolchain/mac:clang_x64" override for mac x64 already provides.
The one same-OS combination that cannot work is V8's default for mac
arm64 -> x64, clang_arm64_v8_x64 (arm64 binary emitting x64 code, no
simulator) -- that is the crash the old TODO in node.gni described.
Neither the snapshot nor the code cache contains machine code, so what a
cross node_mksnapshot produces is valid on the target; the js2c code
cache is already generated cross-arch the same way.

So gate both on host_os == target_os except that toolchain, and give the
node_snapshot / node_snapshot_stub source_sets the
//base:debugging_buildflags dependency that node_builtins.h's
base/dcheck_is_on.h include needs (a fresh out dir building only
third_party/electron_node:node_snapshot failed on the missing generated
header). Also brings the patch description up to date.

Verified on a linux x64 host with target_cpu = "arm64": node_mksnapshot
runs under the simulator (blob 4.9 MB, 392-entry / 2.8 MB code cache, same
shape as native), the snapshot-keyed browser js2c cache tool runs, and
electron links with the snapshot embedded. Booting it needs arm64
hardware; the js2c code-cache spec's browser assertions and
process.config.variables.node_use_node_snapshot cover it on the arm64
test legs. mac x64 and win arm64 are by construction, untested locally.
@codebytere
codebytere requested a review from a team as a code owner August 16, 2026 21:12
@MarshallOfSound MarshallOfSound added target/42-x-y PR should also be added to the "42-x-y" branch. target/43-x-y PR should also be added to the "43-x-y" branch. target/44-x-y PR should also be added to the "44-x-y" branch. labels Aug 17, 2026
@MarshallOfSound
MarshallOfSound merged commit cbb5e25 into main Aug 17, 2026
102 checks passed
@release-clerk

release-clerk Bot commented Aug 17, 2026

Copy link
Copy Markdown

Release Notes Persisted

Linux arm64, Windows arm64 and macOS x64 builds now start the main process from the embedded Node.js startup snapshot like the other platforms, improving startup time.

@MarshallOfSound
MarshallOfSound deleted the pr/cross-arch-node-snapshot branch August 17, 2026 01:05
@trop

trop Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

I was unable to backport this PR to "43-x-y" cleanly;
you will need to perform this backport manually.

@trop trop Bot removed the target/43-x-y PR should also be added to the "43-x-y" branch. label Aug 17, 2026
@trop

trop Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

I was unable to backport this PR to "42-x-y" cleanly;
you will need to perform this backport manually.

@trop trop Bot added needs-manual-bp/43-x-y needs-manual-bp/42-x-y and removed target/42-x-y PR should also be added to the "42-x-y" branch. labels Aug 17, 2026
@trop

trop Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

I have automatically backported this PR to "44-x-y", please check out #52879

@trop trop Bot added in-flight/44-x-y and removed target/44-x-y PR should also be added to the "44-x-y" branch. labels Aug 17, 2026
@trop

trop Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

@MarshallOfSound has manually backported this PR to "42-x-y", please check out #52880

@trop

trop Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

@MarshallOfSound has manually backported this PR to "43-x-y", please check out #52881

codebytere added a commit that referenced this pull request Aug 18, 2026
Node's GN build set NODE_ARCH to "ia32" whenever the toolchain compiling
it was x86 and to target_cpu otherwise. That is the same thing for a
normal build, but not in V8's snapshot toolchain: the arm (32-bit)
snapshot is produced by an x86-hosted node_mksnapshot, which therefore
carried NODE_ARCH="ia32", and since #52874 the arm build boots its main
process from that snapshot - so process.arch read "ia32" there while
renderers and workers said "arm". Spell the ia32 special case off
target_cpu instead, which is right in the target toolchain and in every
host toolchain that builds node for the snapshot.
codebytere added a commit that referenced this pull request Aug 18, 2026
…52874)

node_use_node_snapshot / node_use_node_code_cache were gated to
host_cpu == target_cpu, so the targets Electron cross-builds -- linux
arm64 and win arm64 (x64 hosts) and mac x64 (arm64 hosts) -- shipped
without the embedded Node snapshot and its builtin code cache, and every
process there still ran the full Node bootstrap.

node_mksnapshot already builds in v8_snapshot_toolchain, like mksnapshot
and v8_context_snapshot_generator. Unlike those it executes target-CPU
code (the Node bootstrap runs in JS), so the requirement is a snapshot
toolchain that can run what its V8 emits: V8's arm64 simulator on x64
hosts (linux clang_x64_v8_arm64, the win host toolchain with
v8_target_cpu = arm64), or an x64 binary under Rosetta on arm64 macs,
which is what Electron CI's v8_snapshot_toolchain =
"//build/toolchain/mac:clang_x64" override for mac x64 already provides.
The one same-OS combination that cannot work is V8's default for mac
arm64 -> x64, clang_arm64_v8_x64 (arm64 binary emitting x64 code, no
simulator) -- that is the crash the old TODO in node.gni described.
Neither the snapshot nor the code cache contains machine code, so what a
cross node_mksnapshot produces is valid on the target; the js2c code
cache is already generated cross-arch the same way.

So gate both on host_os == target_os except that toolchain, and give the
node_snapshot / node_snapshot_stub source_sets the
//base:debugging_buildflags dependency that node_builtins.h's
base/dcheck_is_on.h include needs (a fresh out dir building only
third_party/electron_node:node_snapshot failed on the missing generated
header). Also brings the patch description up to date.

Verified on a linux x64 host with target_cpu = "arm64": node_mksnapshot
runs under the simulator (blob 4.9 MB, 392-entry / 2.8 MB code cache, same
shape as native), the snapshot-keyed browser js2c cache tool runs, and
electron links with the snapshot embedded. Booting it needs arm64
hardware; the js2c code-cache spec's browser assertions and
process.config.variables.node_use_node_snapshot cover it on the arm64
test legs. mac x64 and win arm64 are by construction, untested locally.

(cherry picked from commit cbb5e25)
codebytere added a commit that referenced this pull request Aug 18, 2026
Node's GN build set NODE_ARCH to "ia32" whenever the toolchain compiling
it was x86 and to target_cpu otherwise. That is the same thing for a
normal build, but not in V8's snapshot toolchain: the arm (32-bit)
snapshot is produced by an x86-hosted node_mksnapshot, which therefore
carried NODE_ARCH="ia32", and since #52874 the arm build boots its main
process from that snapshot - so process.arch read "ia32" there while
renderers and workers said "arm". Spell the ia32 special case off
target_cpu instead, which is right in the target toolchain and in every
host toolchain that builds node for the snapshot.
codebytere added a commit that referenced this pull request Aug 18, 2026
…52874)

node_use_node_snapshot / node_use_node_code_cache were gated to
host_cpu == target_cpu, so the targets Electron cross-builds -- linux
arm64 and win arm64 (x64 hosts) and mac x64 (arm64 hosts) -- shipped
without the embedded Node snapshot and its builtin code cache, and every
process there still ran the full Node bootstrap.

node_mksnapshot already builds in v8_snapshot_toolchain, like mksnapshot
and v8_context_snapshot_generator. Unlike those it executes target-CPU
code (the Node bootstrap runs in JS), so the requirement is a snapshot
toolchain that can run what its V8 emits: V8's arm64 simulator on x64
hosts (linux clang_x64_v8_arm64, the win host toolchain with
v8_target_cpu = arm64), or an x64 binary under Rosetta on arm64 macs,
which is what Electron CI's v8_snapshot_toolchain =
"//build/toolchain/mac:clang_x64" override for mac x64 already provides.
The one same-OS combination that cannot work is V8's default for mac
arm64 -> x64, clang_arm64_v8_x64 (arm64 binary emitting x64 code, no
simulator) -- that is the crash the old TODO in node.gni described.
Neither the snapshot nor the code cache contains machine code, so what a
cross node_mksnapshot produces is valid on the target; the js2c code
cache is already generated cross-arch the same way.

So gate both on host_os == target_os except that toolchain, and give the
node_snapshot / node_snapshot_stub source_sets the
//base:debugging_buildflags dependency that node_builtins.h's
base/dcheck_is_on.h include needs (a fresh out dir building only
third_party/electron_node:node_snapshot failed on the missing generated
header). Also brings the patch description up to date.

Verified on a linux x64 host with target_cpu = "arm64": node_mksnapshot
runs under the simulator (blob 4.9 MB, 392-entry / 2.8 MB code cache, same
shape as native), the snapshot-keyed browser js2c cache tool runs, and
electron links with the snapshot embedded. Booting it needs arm64
hardware; the js2c code-cache spec's browser assertions and
process.config.variables.node_use_node_snapshot cover it on the arm64
test legs. mac x64 and win arm64 are by construction, untested locally.

(cherry picked from commit cbb5e25)
codebytere added a commit that referenced this pull request Aug 18, 2026
Node's GN build set NODE_ARCH to "ia32" whenever the toolchain compiling
it was x86 and to target_cpu otherwise. That is the same thing for a
normal build, but not in V8's snapshot toolchain: the arm (32-bit)
snapshot is produced by an x86-hosted node_mksnapshot, which therefore
carried NODE_ARCH="ia32", and since #52874 the arm build boots its main
process from that snapshot - so process.arch read "ia32" there while
renderers and workers said "arm". Spell the ia32 special case off
target_cpu instead, which is right in the target toolchain and in every
host toolchain that builds node for the snapshot.
codebytere added a commit that referenced this pull request Aug 18, 2026
Node's GN build set NODE_ARCH to "ia32" whenever the toolchain compiling
it was x86 and to target_cpu otherwise. That is the same thing for a
normal build, but not in V8's snapshot toolchain: the arm (32-bit)
snapshot is produced by an x86-hosted node_mksnapshot, which therefore
carried NODE_ARCH="ia32", and since #52874 the arm build boots its main
process from that snapshot - so process.arch read "ia32" there while
renderers and workers said "arm". Spell the ia32 special case off
target_cpu instead, which is right in the target toolchain and in every
host toolchain that builds node for the snapshot.
codebytere added a commit that referenced this pull request Aug 18, 2026
Node's GN build set NODE_ARCH to "ia32" whenever the toolchain compiling
it was x86 and to target_cpu otherwise. That is the same thing for a
normal build, but not in V8's snapshot toolchain: the arm (32-bit)
snapshot is produced by an x86-hosted node_mksnapshot, which therefore
carried NODE_ARCH="ia32", and since #52874 the arm build boots its main
process from that snapshot - so process.arch read "ia32" there while
renderers and workers said "arm". Spell the ia32 special case off
target_cpu instead, which is right in the target toolchain and in every
host toolchain that builds node for the snapshot.
codebytere added a commit that referenced this pull request Aug 18, 2026
Node's GN build set NODE_ARCH to "ia32" whenever the toolchain compiling
it was x86 and to target_cpu otherwise. That is the same thing for a
normal build, but not in V8's snapshot toolchain: the arm (32-bit)
snapshot is produced by an x86-hosted node_mksnapshot, which therefore
carried NODE_ARCH="ia32", and since #52874 the arm build boots its main
process from that snapshot - so process.arch read "ia32" there while
renderers and workers said "arm". Spell the ia32 special case off
target_cpu instead, which is right in the target toolchain and in every
host toolchain that builds node for the snapshot.
jkleinsc pushed a commit that referenced this pull request Aug 18, 2026
fix: name node's arch after the target cpu in every toolchain

Node's GN build set NODE_ARCH to "ia32" whenever the toolchain compiling
it was x86 and to target_cpu otherwise. That is the same thing for a
normal build, but not in V8's snapshot toolchain: the arm (32-bit)
snapshot is produced by an x86-hosted node_mksnapshot, which therefore
carried NODE_ARCH="ia32", and since #52874 the arm build boots its main
process from that snapshot - so process.arch read "ia32" there while
renderers and workers said "arm". Spell the ia32 special case off
target_cpu instead, which is right in the target toolchain and in every
host toolchain that builds node for the snapshot.
codebytere added a commit that referenced this pull request Aug 19, 2026
…52874)

node_use_node_snapshot / node_use_node_code_cache were gated to
host_cpu == target_cpu, so the targets Electron cross-builds -- linux
arm64 and win arm64 (x64 hosts) and mac x64 (arm64 hosts) -- shipped
without the embedded Node snapshot and its builtin code cache, and every
process there still ran the full Node bootstrap.

node_mksnapshot already builds in v8_snapshot_toolchain, like mksnapshot
and v8_context_snapshot_generator. Unlike those it executes target-CPU
code (the Node bootstrap runs in JS), so the requirement is a snapshot
toolchain that can run what its V8 emits: V8's arm64 simulator on x64
hosts (linux clang_x64_v8_arm64, the win host toolchain with
v8_target_cpu = arm64), or an x64 binary under Rosetta on arm64 macs,
which is what Electron CI's v8_snapshot_toolchain =
"//build/toolchain/mac:clang_x64" override for mac x64 already provides.
The one same-OS combination that cannot work is V8's default for mac
arm64 -> x64, clang_arm64_v8_x64 (arm64 binary emitting x64 code, no
simulator) -- that is the crash the old TODO in node.gni described.
Neither the snapshot nor the code cache contains machine code, so what a
cross node_mksnapshot produces is valid on the target; the js2c code
cache is already generated cross-arch the same way.

So gate both on host_os == target_os except that toolchain, and give the
node_snapshot / node_snapshot_stub source_sets the
//base:debugging_buildflags dependency that node_builtins.h's
base/dcheck_is_on.h include needs (a fresh out dir building only
third_party/electron_node:node_snapshot failed on the missing generated
header). Also brings the patch description up to date.

Verified on a linux x64 host with target_cpu = "arm64": node_mksnapshot
runs under the simulator (blob 4.9 MB, 392-entry / 2.8 MB code cache, same
shape as native), the snapshot-keyed browser js2c cache tool runs, and
electron links with the snapshot embedded. Booting it needs arm64
hardware; the js2c code-cache spec's browser assertions and
process.config.variables.node_use_node_snapshot cover it on the arm64
test legs. mac x64 and win arm64 are by construction, untested locally.

(cherry picked from commit cbb5e25)
codebytere added a commit that referenced this pull request Aug 19, 2026
Node's GN build set NODE_ARCH to "ia32" whenever the toolchain compiling
it was x86 and to target_cpu otherwise. That is the same thing for a
normal build, but not in V8's snapshot toolchain: the arm (32-bit)
snapshot is produced by an x86-hosted node_mksnapshot, which therefore
carried NODE_ARCH="ia32", and since #52874 the arm build boots its main
process from that snapshot - so process.arch read "ia32" there while
renderers and workers said "arm". Spell the ia32 special case off
target_cpu instead, which is right in the target toolchain and in every
host toolchain that builds node for the snapshot.
codebytere added a commit that referenced this pull request Aug 19, 2026
…52879)

* build: generate the Node startup snapshot on cross-arch builds too

node_use_node_snapshot / node_use_node_code_cache were gated to
host_cpu == target_cpu, so the targets Electron cross-builds -- linux
arm64 and win arm64 (x64 hosts) and mac x64 (arm64 hosts) -- shipped
without the embedded Node snapshot and its builtin code cache, and every
process there still ran the full Node bootstrap.

node_mksnapshot already builds in v8_snapshot_toolchain, like mksnapshot
and v8_context_snapshot_generator. Unlike those it executes target-CPU
code (the Node bootstrap runs in JS), so the requirement is a snapshot
toolchain that can run what its V8 emits: V8's arm64 simulator on x64
hosts (linux clang_x64_v8_arm64, the win host toolchain with
v8_target_cpu = arm64), or an x64 binary under Rosetta on arm64 macs,
which is what Electron CI's v8_snapshot_toolchain =
"//build/toolchain/mac:clang_x64" override for mac x64 already provides.
The one same-OS combination that cannot work is V8's default for mac
arm64 -> x64, clang_arm64_v8_x64 (arm64 binary emitting x64 code, no
simulator) -- that is the crash the old TODO in node.gni described.
Neither the snapshot nor the code cache contains machine code, so what a
cross node_mksnapshot produces is valid on the target; the js2c code
cache is already generated cross-arch the same way.

So gate both on host_os == target_os except that toolchain, and give the
node_snapshot / node_snapshot_stub source_sets the
//base:debugging_buildflags dependency that node_builtins.h's
base/dcheck_is_on.h include needs (a fresh out dir building only
third_party/electron_node:node_snapshot failed on the missing generated
header). Also brings the patch description up to date.

Verified on a linux x64 host with target_cpu = "arm64": node_mksnapshot
runs under the simulator (blob 4.9 MB, 392-entry / 2.8 MB code cache, same
shape as native), the snapshot-keyed browser js2c cache tool runs, and
electron links with the snapshot embedded. Booting it needs arm64
hardware; the js2c code-cache spec's browser assertions and
process.config.variables.node_use_node_snapshot cover it on the arm64
test legs. mac x64 and win arm64 are by construction, untested locally.

Co-authored-by: Shelley Vohr <[email protected]>

* fix: generate the Node startup snapshot once per build

run_node_mksnapshot was instantiated in every toolchain that pulled in
":node_snapshot", so a cross build ran it twice: once in the default
toolchain for the shipped framework, once in v8_snapshot_toolchain for the
host tool that generates the browser process's js2c code cache. Two runs
are not guaranteed to produce the same snapshot, and on Windows they do
not, so the tool built its cache against a read-only heap the framework
never embedded and V8 rejected every entry at startup with a read-only
snapshot checksum mismatch - the browser process compiled browser_init,
utility_init and node_init from source instead.

Define the action only in the default toolchain and have each toolchain's
":node_snapshot" compile that one output, so the snapshot the cache is
keyed to is the snapshot the framework ships.

Notes: none
(cherry picked from commit 3c44687)

* fix: name node's arch after the target cpu in every toolchain

Node's GN build set NODE_ARCH to "ia32" whenever the toolchain compiling
it was x86 and to target_cpu otherwise. That is the same thing for a
normal build, but not in V8's snapshot toolchain: the arm (32-bit)
snapshot is produced by an x86-hosted node_mksnapshot, which therefore
carried NODE_ARCH="ia32", and since #52874 the arm build boots its main
process from that snapshot - so process.arch read "ia32" there while
renderers and workers said "arm". Spell the ia32 special case off
target_cpu instead, which is right in the target toolchain and in every
host toolchain that builds node for the snapshot.

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <[email protected]>
@trop trop Bot added merged/44-x-y PR was merged to the "44-x-y" branch. and removed in-flight/44-x-y labels Aug 19, 2026
codebytere added a commit that referenced this pull request Aug 19, 2026
…3-x-y) (#52881)

* build: generate the Node startup snapshot on cross-arch builds too (#52874)

node_use_node_snapshot / node_use_node_code_cache were gated to
host_cpu == target_cpu, so the targets Electron cross-builds -- linux
arm64 and win arm64 (x64 hosts) and mac x64 (arm64 hosts) -- shipped
without the embedded Node snapshot and its builtin code cache, and every
process there still ran the full Node bootstrap.

node_mksnapshot already builds in v8_snapshot_toolchain, like mksnapshot
and v8_context_snapshot_generator. Unlike those it executes target-CPU
code (the Node bootstrap runs in JS), so the requirement is a snapshot
toolchain that can run what its V8 emits: V8's arm64 simulator on x64
hosts (linux clang_x64_v8_arm64, the win host toolchain with
v8_target_cpu = arm64), or an x64 binary under Rosetta on arm64 macs,
which is what Electron CI's v8_snapshot_toolchain =
"//build/toolchain/mac:clang_x64" override for mac x64 already provides.
The one same-OS combination that cannot work is V8's default for mac
arm64 -> x64, clang_arm64_v8_x64 (arm64 binary emitting x64 code, no
simulator) -- that is the crash the old TODO in node.gni described.
Neither the snapshot nor the code cache contains machine code, so what a
cross node_mksnapshot produces is valid on the target; the js2c code
cache is already generated cross-arch the same way.

So gate both on host_os == target_os except that toolchain, and give the
node_snapshot / node_snapshot_stub source_sets the
//base:debugging_buildflags dependency that node_builtins.h's
base/dcheck_is_on.h include needs (a fresh out dir building only
third_party/electron_node:node_snapshot failed on the missing generated
header). Also brings the patch description up to date.

Verified on a linux x64 host with target_cpu = "arm64": node_mksnapshot
runs under the simulator (blob 4.9 MB, 392-entry / 2.8 MB code cache, same
shape as native), the snapshot-keyed browser js2c cache tool runs, and
electron links with the snapshot embedded. Booting it needs arm64
hardware; the js2c code-cache spec's browser assertions and
process.config.variables.node_use_node_snapshot cover it on the arm64
test legs. mac x64 and win arm64 are by construction, untested locally.

(cherry picked from commit cbb5e25)

* fix: generate the Node startup snapshot once per build

run_node_mksnapshot was instantiated in every toolchain that pulled in
":node_snapshot", so a cross build ran it twice: once in the default
toolchain for the shipped framework, once in v8_snapshot_toolchain for the
host tool that generates the browser process's js2c code cache. Two runs
are not guaranteed to produce the same snapshot, and on Windows they do
not, so the tool built its cache against a read-only heap the framework
never embedded and V8 rejected every entry at startup with a read-only
snapshot checksum mismatch - the browser process compiled browser_init,
utility_init and node_init from source instead.

Define the action only in the default toolchain and have each toolchain's
":node_snapshot" compile that one output, so the snapshot the cache is
keyed to is the snapshot the framework ships.

Notes: none
(cherry picked from commit 3c44687)

* fix: name node's arch after the target cpu in every toolchain

Node's GN build set NODE_ARCH to "ia32" whenever the toolchain compiling
it was x86 and to target_cpu otherwise. That is the same thing for a
normal build, but not in V8's snapshot toolchain: the arm (32-bit)
snapshot is produced by an x86-hosted node_mksnapshot, which therefore
carried NODE_ARCH="ia32", and since #52874 the arm build boots its main
process from that snapshot - so process.arch read "ia32" there while
renderers and workers said "arm". Spell the ia32 special case off
target_cpu instead, which is right in the target toolchain and in every
host toolchain that builds node for the snapshot.

---------

Co-authored-by: Shelley Vohr <[email protected]>
@trop trop Bot added merged/43-x-y PR was merged to the "43-x-y" branch. and removed in-flight/43-x-y labels Aug 19, 2026
jkleinsc pushed a commit that referenced this pull request Aug 19, 2026
…2-x-y) (#52880)

* build: generate the Node startup snapshot on cross-arch builds too (#52874)

node_use_node_snapshot / node_use_node_code_cache were gated to
host_cpu == target_cpu, so the targets Electron cross-builds -- linux
arm64 and win arm64 (x64 hosts) and mac x64 (arm64 hosts) -- shipped
without the embedded Node snapshot and its builtin code cache, and every
process there still ran the full Node bootstrap.

node_mksnapshot already builds in v8_snapshot_toolchain, like mksnapshot
and v8_context_snapshot_generator. Unlike those it executes target-CPU
code (the Node bootstrap runs in JS), so the requirement is a snapshot
toolchain that can run what its V8 emits: V8's arm64 simulator on x64
hosts (linux clang_x64_v8_arm64, the win host toolchain with
v8_target_cpu = arm64), or an x64 binary under Rosetta on arm64 macs,
which is what Electron CI's v8_snapshot_toolchain =
"//build/toolchain/mac:clang_x64" override for mac x64 already provides.
The one same-OS combination that cannot work is V8's default for mac
arm64 -> x64, clang_arm64_v8_x64 (arm64 binary emitting x64 code, no
simulator) -- that is the crash the old TODO in node.gni described.
Neither the snapshot nor the code cache contains machine code, so what a
cross node_mksnapshot produces is valid on the target; the js2c code
cache is already generated cross-arch the same way.

So gate both on host_os == target_os except that toolchain, and give the
node_snapshot / node_snapshot_stub source_sets the
//base:debugging_buildflags dependency that node_builtins.h's
base/dcheck_is_on.h include needs (a fresh out dir building only
third_party/electron_node:node_snapshot failed on the missing generated
header). Also brings the patch description up to date.

Verified on a linux x64 host with target_cpu = "arm64": node_mksnapshot
runs under the simulator (blob 4.9 MB, 392-entry / 2.8 MB code cache, same
shape as native), the snapshot-keyed browser js2c cache tool runs, and
electron links with the snapshot embedded. Booting it needs arm64
hardware; the js2c code-cache spec's browser assertions and
process.config.variables.node_use_node_snapshot cover it on the arm64
test legs. mac x64 and win arm64 are by construction, untested locally.

(cherry picked from commit cbb5e25)

* fix: generate the Node startup snapshot once per build

run_node_mksnapshot was instantiated in every toolchain that pulled in
":node_snapshot", so a cross build ran it twice: once in the default
toolchain for the shipped framework, once in v8_snapshot_toolchain for the
host tool that generates the browser process's js2c code cache. Two runs
are not guaranteed to produce the same snapshot, and on Windows they do
not, so the tool built its cache against a read-only heap the framework
never embedded and V8 rejected every entry at startup with a read-only
snapshot checksum mismatch - the browser process compiled browser_init,
utility_init and node_init from source instead.

Define the action only in the default toolchain and have each toolchain's
":node_snapshot" compile that one output, so the snapshot the cache is
keyed to is the snapshot the framework ships.

Notes: none
(cherry picked from commit 3c44687)

* fix: name node's arch after the target cpu in every toolchain

Node's GN build set NODE_ARCH to "ia32" whenever the toolchain compiling
it was x86 and to target_cpu otherwise. That is the same thing for a
normal build, but not in V8's snapshot toolchain: the arm (32-bit)
snapshot is produced by an x86-hosted node_mksnapshot, which therefore
carried NODE_ARCH="ia32", and since #52874 the arm build boots its main
process from that snapshot - so process.arch read "ia32" there while
renderers and workers said "arm". Spell the ia32 special case off
target_cpu instead, which is right in the target toolchain and in every
host toolchain that builds node for the snapshot.

---------

Co-authored-by: Shelley Vohr <[email protected]>
@trop trop Bot added merged/42-x-y PR was merged to the "42-x-y" branch. and removed in-flight/42-x-y labels Aug 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

45-x-y merged/42-x-y PR was merged to the "42-x-y" branch. merged/43-x-y PR was merged to the "43-x-y" branch. merged/44-x-y PR was merged to the "44-x-y" branch. semver/none

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

3 participants