Skip to content

fix(linux): stop the packages needing a glibc newer than the target distro - #322

Merged
EtienneLescot merged 1 commit into
mainfrom
fix/linux-glibc-floor
Aug 9, 2026
Merged

fix(linux): stop the packages needing a glibc newer than the target distro#322
EtienneLescot merged 1 commit into
mainfrom
fix/linux-glibc-floor

Conversation

@EtienneLescot

@EtienneLescot EtienneLescot commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator

The Linux twin of #321. Same bug class: a runtime dependency that is present on every build machine and absent on the target, so it works in CI and in local testing and dies on a user's box.

What was wrong

The linker binds each symbol to the newest version its build machine offers, so ubuntu-latest silently decided the oldest distro these packages could run on. Nothing in the source asked for any of it:

  • __isoc23_strtol / strtoll / strtoul / strtoull — glibc 2.38's C23 redirect of strtol
  • _ZSt21ios_base_library_initv — what GCC 13.2+ emits into every TU that includes <iostream>
  • hypotf@GLIBC_2.35 — Rust's f32::hypot taking the newest version of a symbol that has existed since 2.2.5

Measured on the shipped 1.9.0 packages (the .deb; the AppImage carries byte-identical binaries, sha256-matched), reading .gnu.version_r:

binary max GLIBC max GLIBCXX
whisper-stt-server 2.38 3.4.32
libggml-{base,cpu,vulkan} 2.38 3.4.29 – 3.4.32
compositor_view.node 2.35
openscreen-pipewire-helper 2.34
bundled ffmpeg (BtbN) 2.28
openscreen (Electron) 2.25

So on Ubuntu 22.04 LTS (2.35), Debian 12 (2.36) and RHEL/Rocky/Alma 9 (2.34), whisper-stt-server and the ggml backends died in ld.so before main() — transcription and captions fail with a developer error shown to end users — and on RHEL 9 compositor_view.node failed require() too, so the preview rendered nothing and every export fell back to the no-op compositor.

The app still launches on all of them, because Electron itself only needs 2.25. That is what makes this read as a broken app rather than a broken package, with nothing in any log to say otherwise.

No package format catches it. The deb/rpm/pacman depends lists are hand-written in electron-builder.json5, and electron-builder passes fpm none of --rpm-autoreq* — so not even dnf generates the libc.so.6(GLIBC_2.38) requirement that would have refused the install. README names Ubuntu 22.04 as the PipeWire baseline and offers an .rpm for RHEL/CentOS.

The fix

Pin both jobs that produce Linux natives to ubuntu-22.04 — the oldest distro the README claims, and the binding one against Debian 12 — and enforce the resulting ceiling on the built payload in before-pack.cjs so bumping a runner image cannot quietly lower the reach again.

patchelf gets its own step: 22.04 carries 0.14.3 and --rename-dynamic-symbols only arrived in 0.18.0. 0.18.0 is also exactly what the 24.04 image provided, so the compositor's symbol renaming is unchanged.

The guard reads .gnu.version_r and deliberately not .gnu.version_d — libc and libstdc++ define every version they ever shipped, so reading definitions would report a bundled library as needing itself. It parses ELF in Node for the same reason importedDlls() does not use dumpbin: binutils is not installed on every machine that packages this. It carries the same "read nothing ⇒ the parser broke" assertion as the Windows guard.

Verification

Both workflows run green on this branch, and the resulting packages were downloaded and re-measured the same way the bug was found:

binary before after
whisper-stt-server 2.38 / 3.4.32 2.34 / 3.4.30
libggml-base 2.38 / 3.4.29 2.29 / 3.4.29
libggml-cpu 2.38 / 3.4.29 2.34 / 3.4.29
libggml-vulkan 2.38 / 3.4.32 2.34 / 3.4.30
libwhisper 2.32 / 3.4.31 2.32 / 3.4.29
libparakeet 2.29 / 3.4.31 2.29 / 3.4.29
compositor_view.node 2.35 2.35 (hypotf)
openscreen-pipewire-helper 2.34 2.34

Floor: GLIBC 2.38 → 2.35, GLIBCXX 3.4.32 → 3.4.30. Every __isoc23_* reference is gone. Ubuntu 22.04 LTS and Debian 12 now run the whole app.

The ELF parser was also checked against readelf -V on the real payload — 23/23 files agree — and the guard was confirmed to reject the old 1.9.0 payload, naming all 16 offending files.

Not addressed here

RHEL/Rocky/Alma 9 is still out of range, on two counts: hypotf@GLIBC_2.35 in the compositor addon (2.34 available) and GLIBCXX_3.4.30 in whisper-stt-server and libggml-vulkan (3.4.29 available). Closing that means building in a container (Rocky 9 or Debian 11), not a symbol tweak — a bigger decision, and a separate one. The README's .rpm section still implies RHEL support; correcting that wording is a product call left out of this PR.

ubuntu-22.04 is still a published runner image but on a slower cadence than 24/26 — worth revisiting before it retires, at which point a container is the successor.

Summary by CodeRabbit

  • Bug Fixes

    • Improved Linux package compatibility by detecting unsupported system library requirements before release.
    • Added clearer diagnostics identifying incompatible binaries and required rebuild actions.
    • Standardized Linux build environments for more consistent results.
  • Quality Improvements

    • Strengthened packaging validation and verified required tooling during Linux builds.

…istro

The Linux twin of the Visual C++ Redistributable bug: the linker binds every
symbol to the newest version its BUILD machine offers, so `ubuntu-latest`
silently decided the oldest distro these packages could run on. Nothing in the
source asked for any of it. `__isoc23_strtol` is glibc 2.38's C23 redirect of
`strtol`, `_ZSt21ios_base_library_initv` is what GCC 13.2+ emits into every
translation unit that includes <iostream>, and `hypotf@GLIBC_2.35` is Rust's
f32::hypot taking the newest version of a symbol that has existed since 2.2.5.

Measured on the shipped 1.9.0 packages — the .deb, and the AppImage carries
byte-identical binaries: whisper-stt-server and the libggml backends need
GLIBC_2.38, so they die in ld.so before main() on Ubuntu 22.04, Debian 12 and
RHEL 9 and transcription fails with a developer error shown to end users; and
compositor_view.node needs GLIBC_2.35, so on RHEL 9 the preview renders nothing
and every export falls back to the no-op compositor. The app still LAUNCHES on
all of them, because Electron itself needs only 2.25 — which is exactly what
makes this read as a broken app rather than a broken package.

No package format catches it. The deb/rpm/pacman `depends` lists are
hand-written in electron-builder.json5, and electron-builder passes fpm none of
--rpm-autoreq*, so not even dnf generates the libc.so.6(GLIBC_2.38) requirement
that would have refused the install on a machine that cannot run it.

Pin both jobs that produce Linux natives to ubuntu-22.04 — the oldest distro
the README claims, and the binding one against Debian 12 — and enforce the
resulting ceiling on the built payload in before-pack.cjs, so bumping a runner
image cannot quietly lower the reach again. The guard reads .gnu.version_r and
deliberately not .gnu.version_d: libc and libstdc++ define every version they
ever shipped, and reading definitions would report a bundled library as needing
itself. It is parsed in Node for the same reason importedDlls() does not use
dumpbin — binutils is not installed on every machine that packages this.

patchelf gets its own step because 22.04 carries 0.14.3 and
`--rename-dynamic-symbols` only arrived in 0.18.0. 0.18.0 is also exactly what
the 24.04 image provided, so the compositor's symbol renaming is unchanged.

RHEL/Rocky/Alma 9 (glibc 2.34) stay out of range, by exactly one symbol: the
`hypotf` in compositor_view.node. Everything else in that addon is 2.34 or
older. Closing that is a separate decision from this one.
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4b117447-7110-4d32-8591-eded4d043108

📥 Commits

Reviewing files that changed from the base of the PR and between 0985ddc and 5d8694a.

📒 Files selected for processing (3)
  • .github/workflows/build-whisper-stt.yml
  • .github/workflows/build.yml
  • scripts/before-pack.cjs

📝 Walkthrough

Walkthrough

Linux workflows now use Ubuntu 22.04 and verified patchelf tooling. Linux packaging scans ELF payloads and rejects unsupported GLIBC, GLIBCXX, or CXXABI symbol requirements.

Changes

Linux packaging compatibility

Layer / File(s) Summary
Pinned Linux build environment
.github/workflows/build-whisper-stt.yml, .github/workflows/build.yml
Linux jobs use Ubuntu 22.04. Ninja installation covers Ubuntu runners. patchelf 0.18.0 is checksum-verified and installed separately.
ELF symbol requirement analysis
scripts/before-pack.cjs
The script defines GLIBC, GLIBCXX, and CXXABI ceilings, discovers nested ELF files, and parses .gnu.version_r requirements.
Packaging compatibility gate
scripts/before-pack.cjs
Linux payload validation checks symbol compatibility and reports unsupported requirements or parser failures before packaging.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant LinuxPackaging
  participant NativePayloadValidation
  participant ELFDiscovery
  participant ELFParser
  participant CompatibilityEnforcement
  LinuxPackaging->>NativePayloadValidation: validate native payload
  NativePayloadValidation->>ELFDiscovery: find nested ELF files
  ELFDiscovery->>ELFParser: parse symbol-version requirements
  ELFParser-->>CompatibilityEnforcement: return required runtime versions
  CompatibilityEnforcement-->>LinuxPackaging: allow or reject packaging
Loading

Possibly related PRs

Suggested reviewers: siddharthvaddem

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the Linux packaging fix for preventing unsupported glibc requirements.
Description check ✅ Passed The description is detailed and on-topic, covering the cause, fix, verification, platform impact, and remaining limitations despite omitting template headings.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/linux-glibc-floor

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@EtienneLescot
EtienneLescot merged commit 4a06e7a into main Aug 9, 2026
20 checks passed
@EtienneLescot
EtienneLescot deleted the fix/linux-glibc-floor branch August 9, 2026 20:56
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.

1 participant