weichengyi.com

Why a binary says GLIBC_2.34 not found

18 March 2025 · toolchain

glibc versions its exported symbols. Every symbol a binary imports is recorded together with the version node it was resolved against, and the dynamic loader refuses to start the program when the local library exports nothing at that version. The message is blunt:

./program: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found

To find the floor a binary actually requires, read its dynamic symbol table:

objdump -T ./program | grep -o 'GLIBC_[0-9.]*' | sort -uV | tail -1

2.34 shows up constantly, and there is a specific reason. That release folded libpthread, libdl, librt and libanl into libc.so.6. Anything that touches threads and is built against 2.34 or newer inherits the floor, which in practice means almost everything.

What does not help

Stripping does nothing: version requirements live in .gnu.version_r, part of the dynamic linking information the loader needs, not in the debug sections strip removes. Static linking against glibc does not fully help either, because glibc's own NSS machinery still dlopens shared modules at runtime.

What does

Build where you deploy, or lower than you deploy. A container image matching the oldest target distribution is the least surprising option. Failing that, target musl and link statically: x86_64-unknown-linux-musl produces a binary with no glibc dependency at all, at the cost of a different allocator and measurably slower DNS resolution in some workloads.

The one thing you cannot do is fix it after the fact. By the time the binary exists, the floor is already baked into it.