process.dlopen() should throw a clear Error (not segfault) when addon's libnode.so ABI mismatches the running Node
Chưa có ai nhận issue này.
- Ngôn ngữ chính
- JavaScript
- Star
- 122k
- Fork
- 37.3k
- Merge trung bình
- 4 ngày 2 giờ
- Pull request đã merge (30 ngày)
- 283
Mô tả
Summary
When a Node.js native addon is loaded into a process where the addon's linked libnode.so.{} ABI version does not match the running node binary's process.config.variables.node_module_version, the addon segfaults during napi_register_module_v1 instead of producing a useful error.
This is a common, easily-misdiagnosed failure mode (I personally misdiagnosed it as a WSL2/libuv bug for hours before finding the real cause with a 10-line reproducer). Node could detect the mismatch and throw a clear Error before the segfault, saving users significant debugging time.
The Failure (What Currently Happens)
On a system with a stale libnode.so.109 from an old libnode109 package, plus Node 22 (ABI 127), building any NAPI addon with default node-gyp flags produces a .node file that segfaults on load:
/* minimal.c — 10-line NAPI addon, no SIMD, no threading */
#include <node_api.h>
napi_value Hello(napi_env env, napi_callback_info info) {
napi_value result;
napi_create_string_utf8(env, "hello from native", NAPI_AUTO_LENGTH, &result);
return result;
}
NAPI_MODULE_INIT() {
napi_value fn;
napi_create_function(env, "hello", NAPI_AUTO_LENGTH, Hello, NULL, &fn);
napi_set_named_property(env, exports, "hello", fn);
return exports;
}
# Build with default node-gyp flags (-lnode) — segfaults
$ gcc -shared -fPIC -I/usr/include/node minimal.c -lnode -o minimal_lnode.node
$ node -e "require('./minimal_lnode.node')"
Segmentation fault (core dumped)
# Build without -lnode (rare in practice, since node-gyp adds it) — works
$ gcc -shared -fPIC -I/usr/include/node minimal.c -o minimal.node
$ node -e "require('./minimal.node')"
# works fine
The ldd difference:
$ ldd minimal.node
libc.so.6
(no libnode)
$ ldd minimal_lnode.node
libnode.so.109 => /lib/x86_64-linux-gnu/libnode.so.109
libuv.so.1 => /lib/x86_64-linux-gnu/libuv.so.1
...
The system has libnode.so.109 (from Node 21) at the standard library path. node-gyp defaults to -lnode, so the addon's ELF needs that exact file. Node 22 (ABI 127) maps its own internal libnode.so.127 into the same address space, and the two libraries disagree on V8/libuv global layouts. The first call that touches shared state (napi_register_module_v1) segfaults.
ldd /usr/bin/node on the same system shows Node 22 itself linked to libnode.so.109, which is its own can of worms — but the addon-level segfault is what the user actually sees.
The Request
In process.dlopen() (or wherever the addon is mapped and napi_register_module_v1 is invoked), detect a libnode.so ABI mismatch and throw a clear Error instead of segfaulting.
Concretely, something like:
Error: Cannot load native addon './minimal_lnode.node':
the addon was linked against libnode.so.109, but this Node.js
process is built against libnode.so.127 (Node.js 22.22.2).
This usually means the system has an outdated libnode package
(e.g. libnode109 on Ubuntu/Debian). Try:
sudo apt remove libnode109 libnode-dev
or rebuild the addon with the correct headers for this Node version.
at Object.Module._extensions..node (node:internal/modules/cjs/loader:1429:18)
at Module.load (node:internal/modules/cjs/loader:1034:32)
...
This is a one-time check at dlopen() time and would be invaluable for debugging. The cost is negligible; the gain is a much friendlier onboarding / debugging experience for users hitting this trap.
Suggested Implementation Sketch
After dlopen() succeeds, walk the addon's DT_NEEDED entries (via dl_iterate_phdr or by parsing DT_NEEDED directly from the addon's own .dynamic section) and check whether any of them look like libnode.so.<number>. If found, compare that number to process.config.variables.node_module_version (or equivalent) and throw before calling napi_register_module_v1.
Approximate location: src/node_binding.cc / dlopen in lib/internal/modules/cjs/loader.js, around the call to napi_register_module_v1.
A non-fatal variant could just emit process.emitWarning(...) rather than throw, if maintainers prefer non-breaking behaviour. But throwing is more discoverable.
Why This Matters
-
The segfault is silent and unactionable. Users see "Segmentation fault" and assume Node, WSL2, their CPU, or the addon is broken. They try downgrades, rebuilds, BIOS settings — none of which help. The real fix is a one-liner on the system admin side.
-
The error is easy to detect.
dl_iterate_phdris already in use elsewhere in Node. A small extension would catch this. -
It avoids duplicate issues. Searching the issue tracker, this class of bug appears repeatedly under different framings (WSL2, libuv, Hyper-V, CPU, etc.). A clear error message at load time would let users self-diagnose in seconds instead of opening duplicate bug reports.
-
I have the minimal reproducer and a 100%-confirmed root cause. I was the original reporter; I can supply additional diagnostics, machine state, or test cases on request.
Related / Similar Reports
These may all be the same root cause with different symptom descriptions:
- nodejs/node#58690 — WSL2 segfault on Node 24.2.0
- microsoft/Foundry-Local#626 — Native lib segfault on WSL2 during
catalog.getModel() - bun#11882 — Bun segfaults on WSL2 (worked on v1.0.9)
If any of those reporters can share their ldd output for the affected binary, I'd bet they show the same libnode.so.<wrong-version> linkage. Adding a friendly error in Node would at minimum give Bun/Foundry-Local users a head start.
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- 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.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Hướng nghiên cứu
Bắt đầu với trình tái hiện minimal.c và so sánh đầu ra của ldd đối với addon hoạt động và addon bị crash. Đọc src/node_binding.cc và đường dẫn dlopen trong lib/internal/modules/cjs/loader.js, sau đó xác định cách kiểm tra dependency libnode.so của addon trước napi_register_module_v1. Hoàn thành khi một lỗi không tương thích ABI tạo ra một Error rõ ràng thay vì segfault.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Đánh giá
- Công nghệ
- c, cpp, javascript, linux, node.js
- Lĩnh vực
- backend, operating-systems
- Loại issue
- Lỗi
- Độ khó
- 5/5
- Thời gian dự kiến
- Hơn một tuần
- 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
- 42/100