Skip to content

If your Build fails on Termux: cannot locate symbol "ggml_norm_affine" — fix: -DBUILD_SHARED_LIBS=OFF, only if you also have whisper-cli #137

@Manamama

Description

@Manamama

Build fails on Termux: cannot locate symbol "ggml_norm_affine" — fix: -DBUILD_SHARED_LIBS=OFF

Environment

  • Platform: Android / Termux
  • Architecture: aarch64
  • Termux packages installed: whisper-cli (or any package that ships its own libggml.so)

Symptom

Build reaches the test-linking stage and fails:

CANNOT LINK EXECUTABLE "build/bin/test-flash-attn-defaults": cannot locate symbol
"ggml_norm_affine" referenced by "build/bin/test-flash-attn-defaults"...

CMake Error at ...catch2.../CatchAddTests.cmake:81 (message):
  Error running test executable '.../test-flash-attn-defaults':
    Result: 1

Multiple test binaries fail with the same error (test-paraformer, test-flash-attn-defaults, etc.).

Root Cause

Termux installs system-wide packages (e.g. whisper-cli) that ship their own libggml.so
into /data/data/com.termux/files/usr/lib/. This system library is an older version that
does not export ggml_norm_affine.

CrispASR builds a newer libggml locally under build/ggml/src/, which does export the
symbol. However, the dynamic linker finds the system library first (it appears first in
RUNPATH) and loads that instead — so the symbol is missing at runtime.

The locally built library is correct; the wrong one is being loaded.

Fix

Add -DBUILD_SHARED_LIBS=OFF to your cmake invocation:

cmake -DCMAKE_INSTALL_PREFIX=$PREFIX -DBUILD_SHARED_LIBS=OFF ..
make -j$(nproc)
make install

This switches from shared (.so) to static (.a) libraries. The linker copies all ggml
code directly into each executable at compile time. At runtime there is no .so to look
up, so the system library conflict cannot occur.

Trade-offs

The only cost is binary size. Each installed binary is self-contained and larger than
it would be in a shared build — roughly 2-5× bigger before stripping. For a full
CrispASR install this amounts to approximately 200-300 MB extra vs. a shared build.

You can recover roughly half of that by stripping debug symbols after install:

strip $PREFIX/bin/crispasr*

Note: the build configuration RelWithDebInfo (the default) includes debug symbols, so
unstripped binaries are significantly larger than they need to be in production.

Benefits on Termux:

  • No LD_LIBRARY_PATH manipulation needed
  • No RPATH configuration needed
  • Immune to future Termux package upgrades that bump the system libggml version
  • Binaries are portable to other aarch64 Android devices that have Termux installed

What does NOT work

Setting CMAKE_INSTALL_RPATH / CMAKE_BUILD_WITH_INSTALL_RPATH to point at the local
build directory does not reliably fix this on Termux, because Termux prepends its own
$PREFIX/lib to RUNPATH regardless, and that directory contains the conflicting system
library.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions