Skip to content

fix(sea): skip mach-O __LINKEDIT patch for SEA binaries#247

Open
robertsLando wants to merge 2 commits intomainfrom
fix/sea-macos-signing
Open

fix(sea): skip mach-O __LINKEDIT patch for SEA binaries#247
robertsLando wants to merge 2 commits intomainfrom
fix/sea-macos-signing

Conversation

@robertsLando
Copy link
Copy Markdown
Member

Summary

  • SEA binaries on macOS arm64 were segfaulting at startup (v8::ToLocalChecked Empty MaybeLocal inside node::sea::LoadSingleExecutableApplication) when the payload was non-trivial, e.g. a NestJS app bundled via enhanced SEA (~9 MB blob). Reported in discussion Did anyone successfully package a NestJS app with pkg and SEA? #236.
  • Root cause: signMacOSIfNeeded in lib/sea.ts calls patchMachOExecutable before codesign. That helper exists for the classic pkg flow (VFS payload appended to the end of the binary, __LINKEDIT extended so codesign hashes cover it). For SEA, postject already inserts NODE_SEA as a proper LC_SEGMENT_64 — patching __LINKEDIT on top of that corrupts the SEA blob on macOS arm64.
  • Fix: split the ad-hoc-sign path used by SEA into signMacOSSeaIfNeeded, which matches the Node.js SEA docscodesign --sign -, no __LINKEDIT patching. The non-SEA producer path keeps signMacOSIfNeeded unchanged.

Test plan

Notes

Vanilla --sea (no bootstrap) remains incompatible with NestJS regardless of this fix: Node's built-in SEA require() only resolves builtin modules, so require("@nestjs/common") inside a webpack bundle throws ERR_UNKNOWN_BUILTIN_MODULE. Enhanced SEA (pkg --sea .) is the right path for framework apps like NestJS.

Refs: discussion #236

The classic pkg flow appends the VFS payload to the end of the binary
and uses patchMachOExecutable to extend __LINKEDIT so codesign hashes
cover the payload. For SEA, postject creates a dedicated NODE_SEA
segment — patching __LINKEDIT on top of that corrupts the SEA blob on
macOS arm64 once the payload is non-trivial (reported for NestJS apps
in discussion #236, 9 MB enhanced-SEA blob → segfault at
LoadSingleExecutableApplication).

Split the ad-hoc-sign path used by SEA into signMacOSSeaIfNeeded,
which matches the Node.js SEA docs: just codesign, no LINKEDIT patch.
signMacOSIfNeeded still patches for the non-SEA producer path.

Refs: #236
Per review on #247 — a dedicated second function was more code than the
branch deserved. Replace it with a single isSea parameter that skips the
__LINKEDIT patch when true. Call sites read clearer too.
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.

1 participant