Skip to content

Commit 84ff5a8

Browse files
committed
initial version for arm64 musl support
1 parent 8fb00ef commit 84ff5a8

6 files changed

Lines changed: 191 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ This list of officially supported platforms is available in the Node.js [BUILDIN
3434
* **linux-armv6l**: Linux ARMv6 binaries, cross-compiled on Ubuntu 22.04 with a [custom GCC 12 toolchain](https://github.com/rvagg/rpi-newer-crosstools) (for Node.js 16 and later) in a similar manner to the official linux-armv7l binaries. Binaries are optimized for `armv6zk` which is suitable for Raspberry Pi devices (1, 1+ and Zero in particular). ARMv6 binaries were dropped from Node.js 12 and ARMv6 support is now considered "Experimental".
3535
* **riscv64**: Linux riscv64 (RISC-V), cross compiled on Ubuntu 20.04 with the toolchain which the Adoptium project uses (for now...). Built with --openssl-no-asm (Should be with --with-intl=none but that gets overriden)
3636
* **loong64**: Linux loong64 (LoongArch64), cross compiled on Ubuntu 20.04 with the toolchain.
37+
* **linux-arm64-musl**: Linux arm64 binaries compiled against [musl libc](https://www.musl-libc.org/). Primarily useful for users of Alpine Linux on arm64 hardware (e.g. Apple M1 machines). Linux arm64 with musl is considered "Experimental" by Node.js but the Node.js test infrastructure includes some Alpine test servers so support is generally good.
3738

3839
"Experimental" status for Node.js is defined as:
3940
> Experimental: May not compile or test suite may not pass. The core team does not create releases for these platforms. Test failures on experimental platforms do not block releases. Contributions to improve support for these platforms are welcome.

bin/_config.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ recipes=(
1010
"riscv64"
1111
"loong64"
1212
"x64-debug"
13+
"arm64-musl"
1314
)
1415

1516

recipes/arm64-musl/51256-v16.diff

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
diff --git a/configure.py b/configure.py
2+
index ce41bff4d3f..7dd9d14b431 100755
3+
--- a/configure.py
4+
+++ b/configure.py
5+
@@ -1259,10 +1259,6 @@ def configure_node(o):
6+
7+
o['variables']['want_separate_host_toolset'] = int(cross_compiling)
8+
9+
- # Enable branch protection for arm64
10+
- if target_arch == 'arm64':
11+
- o['cflags']+=['-msign-return-address=all']
12+
-
13+
if options.node_snapshot_main is not None:
14+
if options.shared:
15+
# This should be possible to fix, but we will need to refactor the
16+
diff --git a/node.gyp b/node.gyp
17+
index de409a31f38..86c54a9dac7 100644
18+
--- a/node.gyp
19+
+++ b/node.gyp
20+
@@ -111,6 +111,9 @@
21+
},
22+
23+
'conditions': [
24+
+ ['target_arch=="arm64"', {
25+
+ 'cflags': ['-mbranch-protection=standard'], # Pointer authentication.
26+
+ }],
27+
['OS=="aix"', {
28+
'ldflags': [
29+
'-Wl,-bnoerrmsg',

recipes/arm64-musl/55596.diff

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
diff --git a/deps/v8/src/compiler/wasm-compiler.cc b/deps/v8/src/compiler/wasm-compiler.cc
2+
--- a/deps/v8/src/compiler/wasm-compiler.cc 2024-10-28 21:25:11.000000000 -0400
3+
+++ b/deps/v8/src/compiler/wasm-compiler.cc 2024-11-01 02:02:22.554537121 -0400
4+
@@ -8613,11 +8613,13 @@
5+
'-');
6+
7+
auto compile_with_turboshaft = [&]() {
8+
+ wasm::WrapperCompilationInfo ci;
9+
+ ci.code_kind = CodeKind::WASM_TO_JS_FUNCTION;
10+
+ ci.import_info.import_kind = kind;
11+
+ ci.import_info.expected_arity = expected_arity;
12+
+ ci.import_info.suspend = suspend;
13+
return Pipeline::GenerateCodeForWasmNativeStubFromTurboshaft(
14+
- env->module, sig,
15+
- wasm::WrapperCompilationInfo{
16+
- .code_kind = CodeKind::WASM_TO_JS_FUNCTION,
17+
- .import_info = {kind, expected_arity, suspend}},
18+
+ env->module, sig, ci,
19+
func_name, WasmStubAssemblerOptions(), nullptr);
20+
};
21+
auto compile_with_turbofan = [&]() {
22+
@@ -8774,12 +8776,14 @@
23+
base::VectorOf(name_buffer.get(), kMaxNameLen) + kNamePrefixLen, sig);
24+
25+
auto compile_with_turboshaft = [&]() {
26+
+ wasm::WrapperCompilationInfo ci;
27+
+ ci.code_kind = CodeKind::WASM_TO_JS_FUNCTION;
28+
+ ci.import_info.import_kind = kind;
29+
+ ci.import_info.expected_arity = expected_arity;
30+
+ ci.import_info.suspend = suspend;
31+
std::unique_ptr<turboshaft::TurboshaftCompilationJob> job =
32+
Pipeline::NewWasmTurboshaftWrapperCompilationJob(
33+
- isolate, sig,
34+
- wasm::WrapperCompilationInfo{
35+
- .code_kind = CodeKind::WASM_TO_JS_FUNCTION,
36+
- .import_info = {kind, expected_arity, suspend}},
37+
+ isolate, sig, ci,
38+
nullptr, std::move(name_buffer), WasmAssemblerOptions());
39+
40+
// Compile the wrapper

recipes/arm64-musl/Dockerfile

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
FROM alpine:3.22
2+
3+
ARG GID=1000
4+
ARG UID=1000
5+
6+
RUN addgroup -g $GID node \
7+
&& adduser -u $UID -G node -s /bin/sh -D node
8+
9+
RUN apk add --no-cache \
10+
libstdc++ \
11+
&& apk add --no-cache --virtual .build-deps \
12+
bash \
13+
binutils-gold \
14+
curl \
15+
g++ \
16+
gcc \
17+
gnupg \
18+
libgcc \
19+
linux-headers \
20+
make \
21+
python3 \
22+
ccache \
23+
xz \
24+
patch \
25+
# this is required to support building v16 on python 3.12
26+
py3-packaging
27+
28+
RUN cd /opt \
29+
&& curl -O https://musl.cc/aarch64-linux-musl-cross.tgz \
30+
&& tar xzf aarch64-linux-musl-cross.tgz
31+
32+
COPY --chown=node:node run.sh /home/node/run.sh
33+
COPY --chown=node:node 55596.diff /home/node/55596.diff
34+
COPY --chown=node:node 51256-v16.diff /home/node/51256-v16.diff
35+
36+
VOLUME /home/node/.ccache
37+
VOLUME /out
38+
VOLUME /home/node/node.tar.xz
39+
40+
USER node
41+
42+
ENTRYPOINT [ "/home/node/run.sh" ]

recipes/arm64-musl/run.sh

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#!/usr/bin/env bash
2+
set -e
3+
set -x
4+
5+
release_urlbase="$1"
6+
disttype="$2"
7+
customtag="$3"
8+
datestring="$4"
9+
commit="$5"
10+
fullversion="$6"
11+
source_url="$7"
12+
source_urlbase="$8"
13+
config_flags=""
14+
15+
cd /home/node
16+
17+
tar -xf node.tar.xz
18+
cd "node-${fullversion}"
19+
20+
major=$(echo ${fullversion} | cut -d . -f 1 | tr -d v)
21+
22+
# Patch needed to support cross compilation for arm64 on amd64 for node 16 and 18.
23+
# The PR introducing the issue (https://github.com/nodejs/node/pull/43200) was merged in main released in v20 and backported to v18 (https://github.com/nodejs/node/pull/44353) and v16 (https://github.com/nodejs/node/pull/44886)
24+
# The fix for the issue (https://github.com/nodejs/node/pull/51256) was only merged into v20 (https://github.com/nodejs/node/pull/52793)
25+
# Therefore the fix needs to be re-applied to v16 and v18 if we want to compile those too.
26+
# the initial patch works on v18, for v16 a reroll was required.
27+
# @see https://github.com/nodejs/node/pull/51256
28+
if [ "$major" == "18" ]; then
29+
wget -O /home/node/51256.diff https://patch-diff.githubusercontent.com/raw/nodejs/node/pull/51256.diff
30+
patch -p1 < /home/node/51256.diff || true
31+
elif [ "$major" == "16" ]; then
32+
patch -p1 < /home/node/51256-v16.diff || true
33+
rm /home/node/51256-v16.diff
34+
fi
35+
36+
# Patch needed so we can depend on newer Python versions for older NodeJS versions.
37+
# This allows us to use the same Dockerfile and Alpine version.
38+
if [ "$major" == "18" ] || [ "$major" == "16" ] ; then
39+
wget -O /home/node/50209.diff https://patch-diff.githubusercontent.com/raw/nodejs/node/pull/50209.diff
40+
patch -p1 < /home/node/50209.diff || true
41+
fi
42+
43+
# For v16, and additional patch is needed to replace the dependency on distutils, as it was removed in Python 3.12
44+
# There is both usage of distutils in node itself, and in the gyp dependency.
45+
# This also requires the packaging module, which is installed in the Dockerfile (see py3-packaging)
46+
if [ "$major" == "16" ] ; then
47+
wget -O /home/node/50582.diff https://patch-diff.githubusercontent.com/raw/nodejs/node/pull/50582.diff
48+
patch -p1 < /home/node/50582.diff || true
49+
50+
wget -O /home/node/2888.diff https://patch-diff.githubusercontent.com/raw/nodejs/node-gyp/pull/2888.diff
51+
patch -d tools -p1 < /home/node/2888.diff
52+
fi
53+
54+
# Patch needed to support compilation of Node 22 using musl
55+
# @see https://github.com/nodejs/node/issues/55596#issuecomment-2451411974
56+
# Although the GCC version in Alpine 3.22 is 14, The musl toolchain used is compiled using 11.2 (see https://musl.cc/)
57+
# recompiling musl is a step too far, so we add a simple patch that allows us to compile v22
58+
if [ "$major" == "22" ]; then
59+
patch -p1 < /home/node/55596.diff
60+
fi
61+
62+
export CC_host="ccache gcc"
63+
export CXX_host="ccache g++"
64+
export CC="ccache /opt/aarch64-linux-musl-cross/bin/aarch64-linux-musl-gcc"
65+
export CXX="ccache /opt/aarch64-linux-musl-cross/bin/aarch64-linux-musl-g++ -static-libstdc++"
66+
67+
make -j$(getconf _NPROCESSORS_ONLN) binary V= \
68+
DESTCPU="arm64" \
69+
ARCH="arm64" \
70+
VARIATION="musl" \
71+
DISTTYPE="$disttype" \
72+
CUSTOMTAG="$customtag" \
73+
DATESTRING="$datestring" \
74+
COMMIT="$commit" \
75+
RELEASE_URLBASE="$release_urlbase" \
76+
CONFIG_FLAGS="$config_flags"
77+
78+
mv node-*.tar.?z /out/

0 commit comments

Comments
 (0)