Prebuilt shared libnode.so for OpenHarmony — the embeddable Node.js runtime
used by electerm-harmony to run an
on-device backend inside the app.
For each Node.js version we publish a GitHub release tagged
ohos-node-shared-v<version> containing a real shared library
(libnode-arm64.so for arm64-v8a devices, libnode-x64.so for the x86_64
emulator) — not a PIE executable.
electerm-harmony (the OpenHarmony port of the electerm terminal / SSH client)
runs an on-device Node.js backend. The ArkWeb front-end talks to a Node.js server
bound to 127.0.0.1:5577. To ship Node.js inside the app we dlopen() a
libnode.so from the HAP's native libs directory at runtime — which requires a
true shared library, not a standalone executable.
This repository provides:
- The prebuilt artifacts (GitHub releases).
- The exact, reproducible build scripts (Docker + OpenHarmony SDK + LLVM-19).
- A
prepare-node.shconsumer script that CI / other builds use to pull the right artifact and verify its shape.
hqzing/ohos-node is the well-known
third-party Node.js build for OpenHarmony. The two projects target different
deployment models:
| ohos-node-shared (this repo) | hqzing/ohos-node |
|
|---|---|---|
| Goal | Embed Node.js inside an app (ArkUI / ArkWeb) via dlopen() |
Run the node CLI as a standalone process on a device |
| Output | A real shared library libnode.so (--shared build) |
A node executable tarball (--partly-static) |
libnode.so form |
True ET_DYN shared object, no PT_INTERP |
PIE executable (ET_DYN + PT_INTERP) |
| Architectures | arm64-v8a (device) and x86_64 (emulator) | primarily arm64 (now also xz artifacts) |
| Use when | You need to host Node.js inside your own process | You want to run node script.js on OpenHarmony |
hqzing/ohos-node (and a naive --partly-static build) produce libnode.so
as a PIE executable — an ET_DYN with a PT_INTERP program header. If you
try to dlopen() that inside a HarmonyOS app, the dynamic loader maps the PIE's
thread-local storage using the same %fs base as the host app, because the
linker wrote local-exec TLS offsets. That aliases the host app's TLS block, so
V8's
thread_local current_per_thread_assert_datareads garbage, and the release assertion
AllowHeapAllocationInRelease::IsAllowed()fires on the very first heap allocation at Isolate::Initialize. The app crashes
before any JS runs.
This repo builds Node.js with the --shared configure flag, which makes gyp
emit a genuine ET_DYN shared object with a SONAME (libnode.so.<vernum>)
and dynamic TLS (not local-exec). That form dlopen()s cleanly and V8
works.
prepare-node.sh even rejects a PIE artifact at download time (it checks
e_type == ET_DYN && !PT_INTERP) so a bad build can never reach the HAP.
Everything is reproducible from scripts/. You need Docker — the cross-compile
runs in an Ubuntu 24.04 container that downloads the OpenHarmony SDK + LLVM-19 on
first run (~3 GB, then cached).
# 1. Build one ABI at a time (output -> scripts/output/libnode.so)
./scripts/build-node-ohos.sh v24.2.0 2 arm64
./scripts/build-node-ohos.sh v24.2.0 2 x64
# 2. (Optional) Publish as a GitHub release — one asset per ABI
RELEASE_REPO=electerm/ohos-node-shared ./scripts/publish-node-release.sh v24.2.0 arm64 x64What build-node-ohos-inner.sh does inside the container:
- Installs build deps (
build-essential,curl,git,jq,unzip,python3,libxml2). - Downloads the OpenHarmony SDK (
ohos-sdk-public) and LLVM-19 from the daily build API; extractstoolchainsand the sysroot. - Sets the OHOS clang toolchain as
CC/CXXwith-fno-emulated-tls. - Downloads the Node.js v24.2.0 source tarball.
- Applies
0001-fix-argument-list-too-long.patch(needed for v24.2.0). - Patches V8's
maglev-assembler.hsoset_allow_*is declared outside#ifdef DEBUG(release-build fix). - Configures with
--dest-cpu=<arch> --dest-os=openharmony --cross-compiling --openssl-no-asm --shared. make -j2thenmake install.- Locates the real
libnode.so*, strips it, and self-signs it with the OHOSbinary-sign-tool.jar(required for the HAP to load it). - Copies the result to
scripts/output/libnode.so.
The
--sharedflag is the whole point: it is what makes gyp produce a real shared library instead of a PIE executable.
In a project that embeds Node.js the same way electerm-harmony does:
# downloads libnode-<arch>.so into entry/libs/<abi>/libnode.so
RELEASE_REPO=electerm/ohos-node-shared ./scripts/prepare-node.sh arm64
RELEASE_REPO=electerm/ohos-node-shared ./scripts/prepare-node.sh x64Or grab the binaries directly from the releases page.
For electerm-harmony itself, scripts/prepare-node.sh already defaults to this
repo, so a plain ./scripts/prepare-node.sh arm64 is enough.
| File | Arch | SHA-256 |
|---|---|---|
libnode-arm64.so |
arm64-v8a (device) | 3019bf5f9a279d98a87606fc13c53b0e797b6a50cf174f8a1243d880c71151a1 |
libnode-x64.so |
x86_64 (emulator) | d001ec8b34db459d45ce5f1b8952b49fc55ac0b5e0a5d2b77193f6a1a3608990 |
scripts/
build-node-ohos.sh # Docker driver — builds one ABI's libnode.so
build-node-ohos-inner.sh # runs inside the container
publish-node-release.sh # publishes the GitHub release (one asset per ABI)
prepare-node.sh # consumer: download + verify + install
0001-fix-argument-list-too-long.patch
Maintained by 赵旭东 (ZHAO Xudong) — GitHub @zxdong262, zxdong@gmail.com.
The produced libnode.so is covered by the Node.js license (see
https://github.com/nodejs/node). The build scripts in this repo are MIT.