Repository navigation
Closed
Labels
alpine-muslitems relating to the Alpine/musl buildsitems relating to the Alpine/musl builds
Description
Description
SEA built using musl builds result in segmentation faults when executed, while the same process works correctly on the official node:alpine image.
Reproduction
#!/bin/sh
# sea-test-official.sh
set -eu
echo "console.log('Hello from SEA')" > hello.js
echo '{"main":"hello.js","output":"sea-prep.blob"}' > sea-config.json
node --experimental-sea-config sea-config.json
cp "$(which node)" hello
npx --yes postject hello NODE_SEA_BLOB sea-prep.blob --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2
chmod +x hello
./hello || echo "Error code: $?"$ docker run -it --rm node:25.6.0-alpine sh -c "$(cat sea-test-official.sh)"
Wrote single executable preparation blob to sea-prep.blob
Start injection of NODE_SEA_BLOB in hello...
warning: Can't find string offset for section name '.note'
💉 Injection done!
npm notice
npm notice New minor version of npm available! 11.8.0 -> 11.9.0
npm notice Changelog: https://github.com/npm/cli/releases/tag/v11.9.0
npm notice To update run: npm install -g [email protected]
npm notice
Hello from SEA
(node:40) ExperimentalWarning: Single executable application is an experimental feature and might change at any time
(Use `hello --trace-warnings ...` to show where the warning was created)#!/bin/sh
# sea-test-unofficial.sh
set -eu
apk add --no-cache curl xz libgcc libstdc++
curl -fsSL https://unofficial-builds.nodejs.org/download/release/v25.6.0/node-v25.6.0-linux-arm64-musl.tar.xz | tar -xJ
export PATH="$PWD/node-v25.6.0-linux-arm64-musl/bin:$PATH"
echo "console.log('Hello from SEA')" > hello.js
echo '{"main":"hello.js","output":"sea-prep.blob"}' > sea-config.json
node --experimental-sea-config sea-config.json
cp "$(which node)" hello
npx --yes postject hello NODE_SEA_BLOB sea-prep.blob --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2
chmod +x hello
./hello || echo "Error code: $?"$ docker run -it --rm alpine sh -c "$(cat sea-test-unofficial.sh)"
( 1/14) Installing brotli-libs (1.2.0-r0)
( 2/14) Installing c-ares (1.34.6-r0)
( 3/14) Installing libunistring (1.4.1-r0)
( 4/14) Installing libidn2 (2.3.8-r0)
( 5/14) Installing nghttp2-libs (1.68.0-r0)
( 6/14) Installing nghttp3 (1.13.1-r0)
( 7/14) Installing libpsl (0.21.5-r3)
( 8/14) Installing zstd-libs (1.5.7-r2)
( 9/14) Installing libcurl (8.17.0-r1)
(10/14) Installing curl (8.17.0-r1)
(11/14) Installing libgcc (15.2.0-r2)
(12/14) Installing libstdc++ (15.2.0-r2)
(13/14) Installing xz-libs (5.8.2-r0)
(14/14) Installing xz (5.8.2-r0)
Executing busybox-1.37.0-r30.trigger
OK: 17.0 MiB in 30 packages
Wrote single executable preparation blob to sea-prep.blob
Start injection of NODE_SEA_BLOB in hello...
💉 Injection done!
npm notice
npm notice New minor version of npm available! 11.8.0 -> 11.9.0
npm notice Changelog: https://github.com/npm/cli/releases/tag/v11.9.0
npm notice To update run: npm install -g [email protected]
npm notice
Segmentation fault (core dumped)
Error code: 139Metadata
Metadata
Assignees
Labels
alpine-muslitems relating to the Alpine/musl buildsitems relating to the Alpine/musl builds
Activity
rvagg commentedon Feb 9, 2026
Someone's going to need to investigate this further, perhaps that's you? You could also look through git history here to figure out who's interested in arm64 musl. It looks like a cross-compile artifact to me. The Alpine Docker containers for arm64 build natively inside the container (unlike the x64 ones which pull from here). We cross-compile here because we're running on x64 hardware. So there might be a cross compilation flag that's missing or some other change that's needed to the pipeline here to make it work properly. It's going to either need some deeper expertise here or some patience to tinker.
sxa commentedon Feb 9, 2026
Other than for x64 the Alpine docker images that are published are built in a completely different way from the builds on unofficial builds server and, in fact, are not even built on node.js infrastructure (It's not cross-compiled for a start so as rvagg suggests above that could be the difference) I'll be honest I'm 100% in favour of not publishing those unofficial container images at all, but as @rvagg says it will need a volunteer to look at this if there is any desire to make this experimental feature on an experimental platform work reliably :-)
FYI @RafaelGSS @MikeMcC399 (Not because I expect them to volunteer, but because I've been discussing the Alpine docker images with them both recently and this is the sort of inconsistency we can see from the current process :-)
[-]Single Executable Application segfaults on musl builds but works on official node:alpine[/-][+]Single Executable Application segfaults on musl builds but works on official node:alpine on arm64[/+]sxa commentedon Apr 21, 2026
Edited title to clarify architecture as this works ok on x64
awbx commentedon May 20, 2026
Reproducing this on Apple Silicon (native arm64) with Docker Desktop on both
v25.6.0andv26.1.0of the unofficial-buildslinux-arm64-musltarball. The unmodified binary boots fine; only the postject-modified binary crashes.The 5 SIGILLs aren't the bug. Under
strace -e signal=allyou can see OpenSSL's ARM crypto-extension probes firing (_armv8_sm3_probe,_armv8_sha256_probe, etc.) and being caught by OpenSSL's installed SIGILL handler. That's normal CPU-feature detection. The actual death is a NULL-derefmemcpyafter OpenSSL init completes:postject_find_resource("NODE_SEA_BLOB", ...)returns NULL →BlobDeserializer::ReadArithmeticdoesmemcpy(dst, NULL, sizeof(uint32_t))→ SEGV.Why
postject_find_resource()returns NULL — the unofficial arm64-musl binary has noPT_NOTEprogram header at all:The unofficial build strips
.note*sections and emits noPT_NOTEprogram header. When postject (via LIEF) adds a.note.NODE_SEA_BLOBsection, there's noPT_NOTEsegment to extend, so the note is unreachable todl_iterate_phdrat runtime.Fix locations, cleanest first:
.note*from arm64-musl builds. The officialnode:alpinekeeps.note.gnu.build-id; the unofficial cross-compile shouldn't be more aggressive. Likely a-sorstrip --strip-allinvocation that's specific to this arch/libc combination.PT_NOTEprogram header instead of silently producing a non-functional output.BlobDeserializer—CHECK_NOT_NULLthe resource pointer beforememcpyand emit a clean error like "SEA fuse is set but no NODE_SEA_BLOB found".Reproduces on
v25.6.0andv26.1.0of the unofficial-builds arm64-musl tarball. Doesn't reproduce with the officialnode:alpineDocker image (which has.note.gnu.build-id+PT_NOTE). cc @rvagg @sxaMikeMcC399 commentedon May 20, 2026
On https://unofficial-builds.nodejs.org/logs/202605201309-v26.2.0/ you can see that arm64-musl.log shows the arm64 build hasn't finished building yet.
awbx commentedon May 20, 2026
awbx commentedon May 20, 2026
Located the precise fix — and walking back my earlier guess about a
stripflag.The unofficial arm64-musl tarball is the only unofficial build missing notes. The other cross-compile recipes are all fine:
.note.gnu.build-id?arm64-muslrichfelker/musl-cross-make(commit3635262)loong64g++-14-loongarch64-linux-gnupackageriscv64/riscv64-pointer-compressiong++-14-riscv64-linux-gnupackagemusl(x64-musl)g++Not a strip step. Distro-packaged cross-compilers (Ubuntu's
gcc-X-arch-linux-gnu, Alpine's native gcc) ship with specs files that inject-Wl,--build-idinto every link. The musl-cross-make-built toolchain has no such specs, and binutils'lddoesn't emit.note.gnu.build-idby default. So the binary just never gets the note rather than getting one and being stripped.A one-line fix in
recipes/arm64-musl/run.sh:export CC="ccache /opt/aarch64-linux-musl-cross/bin/aarch64-linux-musl-gcc" export CXX="ccache /opt/aarch64-linux-musl-cross/bin/aarch64-linux-musl-g++ -static-libstdc++" +export LDFLAGS="-Wl,--build-id=sha1"awbx commentedon May 20, 2026
PR for the fix: #233
fix: emit build-id in arm64-musl cross-compiled binaries (#233)