Skip to content

Conversation

faulpeltz
Copy link

@faulpeltz faulpeltz commented Aug 29, 2025

This PR adds Node24 support

Node/V8 changes:

  • Move to new V8 syntax for some constructs (e.g. Cast<>)
  • Tracked down two issues which caused the read-only snapshot to be different across platforms:
    • in read-only-serializer:258 the "live" pointer gets overwritten by the encoded slot (EncodedTagged).
      If V8_COMPRESS_POINTERS is not enabled, EncodedTagged is 4 bytes, but the live pointer is 8 bytes.
      memcpy() only copies EncodeTagged leaving 4bytes of "garbage" (=previous upper pointer half) which is semi-random per run - fixed this by clearing the whole value first
    • the CSA_HOLE_SECURITY_CHECK macro somehow leaves a string with a platform-dependent filename in the readonly heap as a constant, replaced that with "placeholder"
  • add PKG_TRACE_READONLY_HEAP blocks for debugging further snapshot issues

Those fixes partially fix the sourceless cross-build issues introduced in Node 22 - For my test builds linux/linuxstatic/alpine/win on x64 are now compatible again. The heap contents on macos builds and other arm64 is wildly different, not sure why. I don't think this kind of compatibility is a goal for the V8 snapshot implementation in general. (This affects Node SEA as well, but SEA always has the source to fall back onto, which incurs a small performance hit)

Build Changes:

  • Update linux/linuxcross build to use gcc 12 (required for Node 24)
  • For aloine/linuxstatic: Node 24 doesn't build on the muslcc image used previously, the image is unmaintained (although the toolchain builder isn't, we could build our own image)
  • Moved alpine/linuxstatic to native Alpine x64/arm64 toolchains, which seems to work quite well
  • Some minor upgrades to the Windows build, but had to enable full-icu for now because the ICU code gen step crashes with small-icu on the GH action build runners (not on my Windows test machine though)
  • Moved the macos build to macos 14 which is now required for Node 24 - x64 is now cross-built from arm64 because GH doesn't offer free macos14 x64 build workers
  • Removed all builds for Node 18 and older

Notes:

  • Needs more testing, I did some basic testing for x64 builds as well as macos/arm64
  • I added a backport of the snapshot-determinism fixes to the latest Node 22 patch

- run arm64 build on arm64 GH action
- deprecate muslcc because docker image is no longer maintained
- keep muslcc around for older node versions, Node 22 should also work with the new setup
- crossbuild from macos14 to x64 because macos14 x64 runners are not available on GH without paid runners
- install newer python version (3.12+)
- use full-icu with Node 24 because of icu generator crash
- initial version is Node 24.7
- fixes some cross-build issues: x64 linux/win/alpine now work again
…tible again

- in CSA_HOLE_SECURITY_CHECK macro, don't use a filename because it ends up on the readonly heap as a string
- in readonly-serializer overwrite pointer with zero because if V8_COMPRESS_POINTERS is not enabled (node default), the tagged value is only 4 bytes leaving another 4 bytes of garbage in the serialized data
@robertsLando robertsLando requested a review from Copilot August 29, 2025 14:04
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds Node 24 support and implements several fixes to improve cross-platform build compatibility, particularly addressing Node 22 snapshot determinism issues.

Key changes include:

  • Added Node 24.7.0 patch and updated build configurations for Node 24 compatibility
  • Fixed V8 snapshot determinism issues that caused cross-build compatibility problems in Node 22+
  • Updated build infrastructure to use newer toolchains (GCC 12) and platforms (macOS 14)

Reviewed Changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
patches/patches.json Adds Node 24.7.0 patch entry
patches/node.v24.7.0.cpp.patch Complete Node 24.7.0 patch with V8 fixes and pkg-specific modifications
patches/node.v22.19.0.cpp.patch Backports snapshot determinism fixes to Node 22
lib/build.ts Updates build configuration for Node 24, macOS cross-compilation, and ICU handling
Dockerfile.* Updates build environments to use newer toolchains and Alpine versions
.github/workflows/*.yml Updates CI workflows to build Node 20/22/24 instead of 16/18/20/22

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +151 to +152
// disabled for now - can't get cmdline flag to work with all builds
// log.info(header.name);
Copy link
Preview

Copilot AI Aug 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commented-out logging should either be properly implemented with the cmdline flag or removed entirely. Leaving disabled code with TODO comments reduces code clarity.

Suggested change
// disabled for now - can't get cmdline flag to work with all builds
// log.info(header.name);
log.info(header.name);

Copilot uses AI. Check for mistakes.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would leave it for later, the tar file logging is not needed IMHO

@robertsLando
Copy link
Member

You did an awesome job here @faulpeltz ! Well done! 💪 took a quick look but will end up my review tomorrow as I have to go now, good to know you also find a way to fix the annoying cross compiling issue 🎉

@faulpeltz
Copy link
Author

@robertsLando the cross-compiling issue is unfortunately only partially fixed, and might be unfixable in some cases

Latest asset build from my fork:
https://github.com/faulpeltz/pkg-fetch/actions/runs/17324900949

@@ -204,8 +213,16 @@ async function compileOnWindows(
args.push('ltcg');
}

// Node24 builds on Windows crash with small-icu at icudat codegen
// workaround for now is to enable full-icu
// TODO check with newer node/tooling/gh-image versions
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would you like to open an issue for this so we can keep track of it or do you want to try that in this PR?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let'sl open an issue, I currently have no idea why this happens, the ICU codegen crashes on the GH windows runner with an Access Violation, on my local Windows VM it doesn't - I checked some of the tooling versions but couldn't find any differences, maybe try switching to 2025 images 🤷

Comment on lines +264 to +265
+ // PrintF("[pkg] ReadOnlySnapshotChecksum mismatch: code=%08x built=%08x\n",
+ // ro_snapshot_checksum, expected_ro_snapshot_checksum);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not keeping this uncommented?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had this uncommented but it logs to console everytime you run something cross-platform which doesn't work with code cache but then falls back to the source included (which then works)

Need to find a better place to log this somwhere in Node code where we know if there is an empty Script or not

@robertsLando
Copy link
Member

@faulpeltz Everything looks good to me, well done! Tell me how you want to proceed here, if you want to take some more time to do some other tries on the missing things or if we can merge and release 🙏🏼

Apart from this I would like to ask you a question. Do you think that the actual patches we are creating now could ever be ported to nodejs code? I mean could we make a proposal to NodeJS to accept our changes? Of course the PR should be different from the patch but something that could allow us to not having to deal with patches anymore... Unfortunately my knowledge is very limited on NodeJS source code so sorry if the question sounds dumb to you. I know some people working on NodeJS core team and I may ask them if they could ever accept this kind of PR but I would like your opinion first 🙏🏼

@robertsLando robertsLando changed the title [WIP] Add Node 24 support and attempt some Node 22 fixes feat: node 24 support and attempt some Node 22 fixes Sep 1, 2025
@faulpeltz
Copy link
Author

@faulpeltz Everything looks good to me, well done! Tell me how you want to proceed here, if you want to take some more time to do some other tries on the missing things or if we can merge and release 🙏🏼

Did you have the chance to test some of the binaries?
Ideally we would have some kind of smoke-test directly after the binary build step..

Apart from this I would like to ask you a question. Do you think that the actual patches we are creating now could ever be ported to nodejs code? I mean could we make a proposal to NodeJS to accept our changes? Of course the PR should be different from the patch but something that could allow us to not having to deal with patches anymore... Unfortunately my knowledge is very limited on NodeJS source code so sorry if the question sounds dumb to you. I know some people working on NodeJS core team and I may ask them if they could ever accept this kind of PR but I would like your opinion first 🙏🏼

I don't know any of the folks working on Node, but IMHO they have decided to go the SEA route which is the technically clean approach.

The way the patch currently works in pkg is 2 major parts:

  • bootstrapping injected code/virtual filesystem/some monkey patching -> changes are mostly in Node lib and some minor C++ code related to startup argument handling and turning off some features
  • V8 patches to get it to load the code cache without a script (thats why are are various "empty" script/checks fakeouts in the patch) and changes related to creating a reproducible code cache

The main problem with the pkg approach is that the code cache was never intended to be or designed as a portable "intermediate" code format, but as a cache for running the code inside the exact same env (arch/platform/v8 version), with the source script always available to fall back to

@faulpeltz
Copy link
Author

@robertsLando you might want to remove the node 18 CI test from the PR checks

@faulpeltz
Copy link
Author

faulpeltz commented Sep 1, 2025

Also regarding the Node patches:

The two changes I made for better cross-platform determinism could be fixed upstream

  1. the read-only-serializer fix should imho be in V8 upstream
  2. the HOLE_CHECK macro thing I am unsure why this even causes the filename string to land in the snapshot..

@robertsLando
Copy link
Member

The main problem with the pkg approach is that the code cache was never intended to be or designed as a portable "intermediate" code format, but as a cache for running the code inside the exact same env (arch/platform/v8 version), with the source script always available to fall back to

I'm wondering if this could ever live behind a flag for example, about the VFS I heard they were considering implementing an abstract layer to allow patching it without doing a monkey patch but dunno if there have been some updates on this, I don't have the links to the issues unfortunately...

I think ATM the most advanced framework for single executables is bun: https://bun.com/docs/bundler/executables

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants