Skip to content

Fix Linux whisper.cpp archives missing libwhisper.so and versioned SONAMEs - #8

Merged
niksedk merged 1 commit into
masterfrom
fix/linux-whisper-shared-libs
Aug 16, 2026
Merged

Fix Linux whisper.cpp archives missing libwhisper.so and versioned SONAMEs#8
niksedk merged 1 commit into
masterfrom
fix/linux-whisper-shared-libs

Conversation

@niksedk

@niksedk niksedk commented Aug 16, 2026

Copy link
Copy Markdown
Member

Fixes the Linux packaging bug behind SubtitleEdit#13680: whisper.cpp speech-to-text has been dead on every Linux install since whispercpp-184 with

whisper-cli: error while loading shared libraries: libwhisper.so.1: cannot open shared object file

What was wrong

The Linux staging step was:

cp build/bin/libggml*.so staging/ 2>/dev/null || cp build/lib/libggml*.so staging/

Three defects, all confirmed against the published whisper-vulkan-linux64.zip and whisper-cuda-linux64.zip:

whisper-cli DT_NEEDED what shipped
libwhisper.so.1 absent — the glob never matched libwhisper.so*
libggml.so.0 shipped as libggml.so (SONAME says libggml.so.0)
libggml-base.so.0 shipped as libggml-base.so

cp dereferences cmake's symlink chain (libggml.so -> libggml.so.0 -> libggml.so.0.9.4), so each library landed under its unversioned alias while the loader looks it up by SONAME. On top of that the binaries carry RUNPATH=/home/runner/work/support-files/support-files/build/bin, which does not exist on a user's machine.

v1.8.3 and earlier shipped a statically linked whisper-cli, which is why nothing broke until the build moved to BUILD_SHARED_LIBS=ON for the GGML_CPU_ALL_VARIANTS plugin set.

What changed

  • Each library is staged as a real file named after its SONAME. Shipping the symlink chain would not work either: Subtitle Edit unpacks these with .NET's ZipArchive, whose zip path writes a symlink entry out as an ordinary file containing the target path.
  • Every ELF gets patchelf --set-rpath '$ORIGIN'. The libraries need this as much as the executable does — DT_RUNPATH is not inherited by transitive dependencies, so whisper-cli's RPATH does not help libggml.so.0 find libggml-base.so.0.
  • New verification step: copies the staged payload elsewhere, runs the loader from an unrelated working directory with LD_LIBRARY_PATH cleared, and fails the build if any bundled whisper/ggml library does not resolve. Libraries we intentionally do not ship — libvulkan from the GPU driver, libcuda/libcudart/libcublas from the user's CUDA install — stay out of scope. This is the guard that was missing: the build machine had the libraries in build/bin the whole time, so nothing ever noticed.
  • Both Linux jobs now share the staging and verification scripts instead of carrying their own copy of the step, so they cannot drift apart again.

Follow-up in the main repo

Publishes to a new tag whispercpp-191-r2; the two Linux URLs and the LinuxVulkan / LinuxCuda hash lists in WhisperDownloadService / DownloadHashManager need to point at it. Windows and macOS keep pointing at whispercpp-191 — both were unaffected (macOS ships a self-contained universal binary; Windows resolves DLLs from the exe directory).

🤖 Generated with Claude Code

The Linux staging step copied only `build/bin/libggml*.so`, which had three
consequences (SubtitleEdit issue #13680):

  - libwhisper.so* was never matched by the glob, so whisper-cli's primary
    dependency was absent from the archive entirely.
  - `cp` dereferenced cmake's symlink chain, staging each library under its
    unversioned alias (libggml.so) while its SONAME - and whisper-cli's
    DT_NEEDED entry - said libggml.so.0. The loader looks up the SONAME, so
    it never matched.
  - The RUNPATH left in the binaries pointed at the CI build directory.

whisper-cli therefore died on startup with "error while loading shared
libraries: libwhisper.so.1" on every Linux install since whispercpp-184.
v1.8.3 and earlier shipped a statically linked binary, which is why this
went unnoticed when the build moved to BUILD_SHARED_LIBS=ON.

Stage each library as a real file named after its SONAME (a symlink chain
would not survive the trip: Subtitle Edit unpacks these with .NET's
ZipArchive, which writes symlink entries out as ordinary files containing
the target path), and rewrite every ELF's RPATH to $ORIGIN.

Add a verification step that copies the staged payload elsewhere and runs
the loader from an unrelated working directory with LD_LIBRARY_PATH
cleared, failing the build if any bundled whisper/ggml library does not
resolve. Libraries we intentionally do not ship - libvulkan from the GPU
driver, libcuda/libcudart/libcublas from the user's CUDA install - stay out
of scope.

Both Linux jobs now share the staging and verification scripts, so they
cannot drift apart again.

Co-Authored-By: Claude Opus 5 <[email protected]>
@niksedk
niksedk merged commit d121b48 into master Aug 16, 2026
7 checks passed
@niksedk
niksedk deleted the fix/linux-whisper-shared-libs branch August 16, 2026 06:27
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