Skip to content

nodejs: statically link libstdc++/libgcc on Linux for portable .node#11

Closed
ligon wants to merge 1 commit intoLadybugDB:mainfrom
ligon:portable-linux-binary
Closed

nodejs: statically link libstdc++/libgcc on Linux for portable .node#11
ligon wants to merge 1 commit intoLadybugDB:mainfrom
ligon:portable-linux-binary

Conversation

@ligon
Copy link
Copy Markdown

@ligon ligon commented Apr 26, 2026

Summary

  • Statically link libstdc++ and libgcc into the Linux .node addon by default,
    behind an opt-out CMake option (LBUG_NODEJS_STATIC_LIBSTDCXX).

Why

The published lbugjs.node for Linux dynamically links libstdc++, requiring
at least GLIBCXX_3.4.31. The package therefore fails to load on every
stable distro whose system libstdc++ predates the GitHub-hosted runner's:
Debian 12 (≤3.4.30), Ubuntu 22.04 (≤3.4.30), RHEL 8/9 family.

Users hit:

Error: /lib/aarch64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.31' not found
(required by .../@ladybugdb/core/lbugjs.node)
code: 'ERR_DLOPEN_FAILED'

The liblbug.a static lib already statically links its own libstdc++ via
the compat (manylinux_2_28 + gcc-toolset-13) build; only the addon shim
was leaking the GLIBCXX dependency.

Result

Locally verified on Debian 12 ARM (linux-aarch64). After this change,
ldd lbugjs.node shows only libc, libm, and ld-linux — no libstdc++
dependency, no GLIBCXX version requirement. Binary grows from ~22 MB to
~36 MB (embedded libstdc++), but loads on any glibc-based Linux.

Test plan

  • CI (nodejs-workflow.yml) passes on linux-x64 and linux-arm64
  • Resulting .node depends only on libc/libm/ld (ldd check)
  • Smoke test on Debian 12 / Ubuntu 22.04

The published lbugjs.node currently dynamically links libstdc++,
imposing a GLIBCXX version requirement on end users determined by the
build host. On stable Linux distros (Debian 12, Ubuntu 22.04, RHEL 8/9
family), the system libstdc++ is older than what the GitHub-hosted
runner provides, so loading the addon fails with:

  Error: /lib/.../libstdc++.so.6: version `GLIBCXX_3.4.31' not found
  (required by .../lbugjs.node)
  code: 'ERR_DLOPEN_FAILED'

Add -static-libstdc++ -static-libgcc to the Linux link flags by
default, gated behind a CMake option (LBUG_NODEJS_STATIC_LIBSTDCXX) so
users who explicitly want a shared-libstdc++ addon can opt out.

The static lib (liblbug.a) already statically links its own libstdc++
via the manylinux_2_28 + gcc-toolset-13 compat build, so this only
affects the small addon shim. Tested locally on Debian 12 ARM: the
resulting lbugjs.node depends only on libc, libm, and ld-linux, and
loads without LD_LIBRARY_PATH or any compatibility shim.
@aheev
Copy link
Copy Markdown
Contributor

aheev commented Apr 26, 2026

maybe we could just use manylinux for building nodejs addon too 🤔

@adsharma
Copy link
Copy Markdown
Contributor

LDFLAGS: "-static-libstdc++"

is key here. Without it, the manylinux builds will also have the same issue.

The static linking fixes the end user headache, but it also opens up a security issue. Every CVE in libstdc++ requires shipping new nodejs binaries. The Linux distro people are against it.

One compromise is to mark these nodjes binaries as compat and configure RPM and DEB packages to turn off static linking.

@adsharma
Copy link
Copy Markdown
Contributor

Also, is -static-libgcc really needed for fixing this issue?

@ligon
Copy link
Copy Markdown
Author

ligon commented Apr 26, 2026 via email

@adsharma
Copy link
Copy Markdown
Contributor

LadybugDB/ladybug#370 already fixed this. Could you please test a recent nightly?

libgcc is statically linked by default. I don't think we need any changes.

The release binaries are built from CI in the main repo. The CI in this repo exists to sanity check the code independently.

@adsharma
Copy link
Copy Markdown
Contributor

Please test the v0.15.4 release that went out today.

@ligon
Copy link
Copy Markdown
Author

ligon commented Apr 28, 2026

Confirmed fixed in v0.15.4 on the original platform (Debian 12 / glibc 2.36 / aarch64).

$ ldd node_modules/@ladybugdb/core/lbugjs.node
        linux-vdso.so.1
        libm.so.6 => /lib/aarch64-linux-gnu/libm.so.6
        libgcc_s.so.1 => /lib/aarch64-linux-gnu/libgcc_s.so.1
        libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6
        /lib/ld-linux-aarch64.so.1

$ strings .../lbugjs.node | grep '^GLIBCXX_'
GLIBCXX_TUNABLES         # tunables string only, no version requirement

No libstdc++.so.6, no GLIBCXX_3.4.31 requirement. Database.getVersion() reports 0.15.4, a trivial RETURN 1+1 round-trips. Binary is 25 MB (vs ~22 MB dynamic / ~36 MB I measured with -static-libstdc++ -static-libgcc), consistent with -static-libstdc++ only — which matches @adsharma's read that -static-libgcc wasn't needed. Closing this PR.

@ligon ligon closed this Apr 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants