Skip to content

fix(npm): build linux-gnu prebuilds against a glibc 2.17 floor - #469

Merged
crowecawcaw merged 2 commits into
mainfrom
fix/462-npm-glibc-floor
Oct 21, 2026
Merged

crowecawcaw merged 2 commits into
mainfrom
fix/462-npm-glibc-floor

Conversation

@crowecawcaw

Copy link
Copy Markdown
Contributor

Fixes #462.

Change

  • Build: both linux-gnu npm targets now build with napi-rs --use-napi-cross, which uses a GCC toolchain with a glibc 2.17 sysroot. That matches the manylinux2014 floor the Python wheels get from manylinux: auto.
    • libxkbcommon is exposed to the linker from a directory that holds only that one library. Adding /usr/lib/<multiarch> instead would put the runner's libc.so on the search path and undo the floor.
    • The old aarch64 linker/CC environment is removed: napi-cross only sets those variables when they're unset, so the old values would have won.
  • Guard: .github/scripts/check_glibc_floor.py --max 2.17 parses the ELF version tables in pure Python and fails on any GLIBC_* version above the floor. On failure it names the symbols responsible. It also fails on GLIBC_PRIVATE, on a file with no GLIBC references, and on input that isn't ELF. It has 12 pytest tests.
  • CI: the Linux js job builds the same way, runs the guard, and loads the .node in an almalinux:9 container (glibc 2.34). The guard's self-tests run in the python job and in cargo xtask test-harness.
  • RUSTFLAGS is appended to rather than replaced, so the workflow-level -Dwarnings still applies.

Tests

  • Red: the current ubuntu-24.04 build fails the guard: it needs GLIBC_2.39 (pidfd_getpid, pidfd_spawnp).
  • Green: the local cross builds pass. x64 needs at most GLIBC_2.15 and arm64 GLIBC_2.17.
  • npm test passes, 152/152, against the floor-built binary.

Only verifiable in CI or at release

  • The AlmaLinux 9 load step; there was no Docker locally.
  • The arm64 publish job.

Not addressed

The binary still has a hard NEEDED libxkbcommon.so.0, as before. Loading it at runtime with xkbcommon-dl would be a follow-up.


Generated by Claude Code

The `*-linux-*-gnu` npm prebuilds were linked on bare ubuntu-latest and
inherited its glibc: the shipped `.node` required GLIBC_2.39 (Rust std's
weak `pidfd_spawnp` / `pidfd_getpid`), so it failed to load on RHEL 9,
AL2023 and Debian 12.

Build both linux-gnu targets with napi-rs's `--use-napi-cross`, whose GCC
toolchain links against a glibc 2.17 sysroot. That is the floor the Python
wheels already get from maturin-action's `manylinux: auto`
(manylinux2014). libxkbcommon is the only system library xa11y-linux
links, and the cross sysroot lacks it, so it is exposed to the linker from
a directory holding only that one `.so`; putting the runner's whole
multiarch libdir on `-L` would let its libc.so shadow the sysroot's.
The aarch64 job's gcc-aarch64-linux-gnu linker env is removed:
`--use-napi-cross` only sets the linker when it is unset, so leaving it
would silently restore the bug.

Guard it so it cannot regress:

- `.github/scripts/check_glibc_floor.py` reads `.gnu.version_r` straight
  from the ELF (no binutils, works on the cross-built aarch64 object) and
  fails, naming the offending symbols, when any GLIBC_* need exceeds
  `--max`. It also fails on an object with no GLIBC_* needs at all, so a
  parser bug or wrong input cannot pass. Self-tests on synthesized
  ELF32/ELF64, LE/BE objects plus the running interpreter run in the CI
  `python` job and `cargo xtask test-harness`.
- publish.yml runs it after each linux-gnu build, before upload.
- The CI `js` job on Linux now builds the binding the same way, runs the
  guard, loads the `.node` in an AlmaLinux 9 container, and runs the unit
  tests against that binary, so a regression fails on the PR rather than
  at release time.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01DqmE11zWLkMi4JgzFdRP8a
Writing RUSTFLAGS to GITHUB_ENV replaced the workflow-level -Dwarnings
for every later step in the job.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01DqmE11zWLkMi4JgzFdRP8a
@crowecawcaw
crowecawcaw merged commit e11fbdd into main Oct 21, 2026
37 checks passed
@crowecawcaw
crowecawcaw deleted the fix/462-npm-glibc-floor branch October 21, 2026 04:53
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.

npm Linux bindings require glibc 2.39 (built on ubuntu-latest); fail to load on RHEL 9 / AL2023 / Debian 12

2 participants