nodejs / nodejs/node

sea: `--build-sea` produces a segfaulting executable on macOS x64 (v26.7.0), while the blob + postject path works on the same runtime

Đang mở
#65,479 1 bình luận 0 reaction 0 người được giao Xem trên GitHub

Chưa có ai nhận issue này.

Ngôn ngữ chính
JavaScript
Star
122k
Fork
37.4k
Merge trung bình
4 ngày 3 giờ
Pull request đã merge (30 ngày)
272

Mô tả

Version

v26.7.0

Platform
Darwin 24.6.0 Darwin Kernel Version 24.6.0 x86_64
macOS 15.7.3 (24G419), Intel Core i5-7500
Subsystem

sea

What steps will reproduce the bug?

On macOS x64, an executable produced by node --build-sea segfaults on launch — including
for a one-line hello world. The same runtime, given the same script through
--experimental-sea-config + postject, produces an executable that runs.

$ echo 'console.log("hello");' > hello.js

# --build-sea
$ printf '{"main":"hello.js","output":"a-build-sea","disableExperimentalSEAWarning":true}' > a.json
$ node --build-sea a.json
Generated single executable .../bin/node + a.json -> a-build-sea
$ codesign --remove-signature a-build-sea && codesign --sign - a-build-sea
$ ./a-build-sea
Segmentation fault: 11        # exit 139

# the blob path, same node, same script
$ printf '{"main":"hello.js","output":"hello.blob","disableExperimentalSEAWarning":true}' > blob.json
$ node --experimental-sea-config blob.json
Wrote single executable preparation blob to hello.blob
$ cp "$(command -v node)" d-postject && chmod 755 d-postject
$ npx postject d-postject NODE_SEA_BLOB hello.blob \
    --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 \
    --macho-segment-name NODE_SEA --overwrite
$ codesign --remove-signature d-postject && codesign --sign - d-postject
$ ./d-postject
hello                          # exit 0
How often does it reproduce? Is there a required condition?

Every time, on this host. Not conditional on anything in the config:

variant result
--build-sea, CommonJS (no mainFormat) SIGSEGV
--build-sea, "mainFormat": "module" SIGSEGV
--build-sea, no codesign step at all SIGSEGV
--experimental-sea-config + postject, same node runs

So it is neither the module format nor the ad-hoc signature.

What is the expected behavior? Why is that the expected behavior?

node --build-sea should produce a runnable executable — it is documented as the
single-command replacement for the blob + postject procedure, and the procedure it
replaces works on this exact runtime.

What do you see instead?

SIGSEGV before main, while dyld is running the binary's static initializers:

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x0000000000000001

thread #1, stop reason = EXC_BAD_ACCESS (code=1, address=0x1)
  frame #0: 0x000000010000c8f0 a-build-sea`__cxx_global_var_init
  dyld  invocation function for block in dyld3::MachOAnalyzer::forEachInitializer(...)
  dyld  mach_o::Header::forEachSection(...)
  dyld  dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const
  dyld  dyld4::JustInTimeLoader::runInitializers(dyld4::RuntimeState&) const
  dyld  dyld4::Loader::runInitializersBottomUp(...)
  dyld  dyld4::APIs::runAllInitializersForMain()

The faulting address is 0x1, which reads like an initializer entry that was not
relocated rather than a fault inside any particular initializer. (The frame #0 symbol
is a nearest-symbol guess and is probably not meaningful — the appended blob shifts
the symbolication.)

Additional information

Comparing the two outputs, the segment layout is structurally identical but
--build-sea places __DATA_CONST (and everything after it) one page higher than the
postject output does:

$ otool -l a-build-sea | grep -A4 LC_SEGMENT_64 | grep -E 'segname|vmaddr'
  segname __TEXT
   vmaddr 0x0000000100000000
  segname __DATA_CONST
   vmaddr 0x00000001061e1000
  segname __DATA
   vmaddr 0x0000000106394000
  segname NODE_SEA
   vmaddr 0x000000010640f000
  segname __LINKEDIT
   vmaddr 0x0000000106410000

$ otool -l d-postject | grep -A4 LC_SEGMENT_64 | grep -E 'segname|vmaddr'
  segname __TEXT
   vmaddr 0x0000000100000000
  segname __DATA_CONST
   vmaddr 0x00000001061e0000        # one page lower
  segname __DATA
   vmaddr 0x0000000106393000
  segname NODE_SEA
   vmaddr 0x000000010640e000
  segname __LINKEDIT
   vmaddr 0x000000010640f000

Both binaries carry __init_offsets and report identical Mach-O headers (ncmds 22,
sizeofcmds 2728), so the load commands themselves are not obviously malformed — but
__DATA_CONST moving without the initializer offsets following it would produce exactly
the observed 0x1. I have not confirmed that causally; it is where I would look first.

This is the same class of problem as #61483 (SEA build corrupting .gnu.hash on Linux
arm64) — the build step rewriting the container in a way the loader then rejects — on a
different platform and a different section. #61504 already skips --build-sea tests on
platforms where SEA is flaky; macOS x64 may belong in that set until this is understood.

Node was installed via nvm from the official tarball
(process.release.sourceUrl = https://nodejs.org/download/release/v26.7.0/node-v26.7.0.tar.gz),
so this is an official x64 build, not a self-compiled or Rosetta one.

Practical impact: a project shipping standalone executables cannot rely on --build-sea
on macOS x64 and has to keep the postject fallback alive, detecting the failure by
running the produced binary before trusting it.

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Hướng nghiên cứu

Bắt đầu bằng cách tái hiện lỗi trên macOS x64 với lệnh node --build-sea được ghi trong tài liệu, sau đó so sánh bố cục segment Mach-O và __init_offsets của nó với đường dẫn hoạt động sử dụng --experimental-sea-config cùng postject. Đọc phần coverage hiện có của --build-sea được tham chiếu trong #61504; được xem là hoàn tất khi một tệp thực thi SEA được tạo có thể khởi chạy thành công trên host này mà không cần fallback của postject.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
javascript, node.js
Lĩnh vực
build-system, operating-systems
Loại issue
Lỗi
Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức độ hoạt động
Sôi nổi
Độ rõ ràng
Khá rõ ràng
Mức phù hợp với người mới
52/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.