ldd and Shared Libraries: Why Your Binary Says No Such File

ldd and Shared Libraries: Why Your Binary Says No Such File

You download a binary, mark it executable, run it, and get:

bash: ./myapp: No such file or directory

The file is right there. The message is not about the file you named; it is about something the file needs, usually the dynamic loader or a shared library.

What dynamic linking is doing

Most Linux binaries do not contain the code they use for common tasks. Instead they record that they need libc.so.6 and libssl.so.3, and at launch the dynamic loader finds those files, maps them into memory, and resolves the references.

The upside is that one copy of a library serves every program using it, and a security fix in that library benefits all of them without rebuilding anything. The downside is that a program is not self-contained, and moving it to a machine with different libraries can break it.

ldd

ldd /usr/bin/curl
	linux-vdso.so.1 (0x00007ffd...)
	libcurl.so.4 => /lib/x86_64-linux-gnu/libcurl.so.4 (0x00007f...)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f...)
	/lib64/ld-linux-x86-64.so.2 (0x00007f...)

Each line is a dependency and where it resolved to. The one to look for:

	libfoo.so.2 => not found

That is your problem.

linux-vdso.so.1 having no path is normal; it is provided by the kernel rather than a file.

The safety caveat

ldd may run the binary to resolve dependencies, which is a bad idea for anything you do not trust. Use tools that only parse the file:

objdump -p ./binary | grep NEEDED
readelf -d ./binary | grep NEEDED

These list the recorded dependencies without executing anything.

Finding the package that provides a library

Once you know what is missing:

# Debian and Ubuntu
apt-file search libfoo.so.2
dpkg -S libfoo.so.2              # if already installed somewhere

# Fedora
dnf provides '*/libfoo.so.2'

# Arch
pacman -F libfoo.so.2

dnf provides is particularly good at this, and it is worth remembering because the package name rarely matches the library name.

The search path

The loader looks in a specific order: paths recorded in the binary itself as RPATH or RUNPATH, then LD_LIBRARY_PATH, then the cache built from /etc/ld.so.conf and /etc/ld.so.conf.d/, then the default directories.

# What is in the cache
ldconfig -p | grep libssl

# Add a directory permanently
echo "/opt/myapp/lib" | sudo tee /etc/ld.so.conf.d/myapp.conf
sudo ldconfig

# Temporarily, for testing
LD_LIBRARY_PATH=/opt/myapp/lib ./myapp

LD_LIBRARY_PATH is fine for a one-off test and a poor permanent answer, because it applies to every child process and can cause a program to pick up the wrong version of something.

Version numbers and sonames

The number after .so is the soname version, and it signals binary compatibility.

libssl.so.1.1     OpenSSL 1.1
libssl.so.3       OpenSSL 3.x

These are different, incompatible libraries. Symlinking one to the other to satisfy a dependency appears to work and then crashes at runtime when the program calls a function whose signature changed. Install the correct version instead; distributions frequently ship both.

The confusing error, explained

bash: ./myapp: No such file or directory

If the binary exists and is executable, this almost always means the loader it names is missing. Check what it wants:

file ./myapp
./myapp: ELF 64-bit LSB executable, x86-64, dynamically linked,
  interpreter /lib/ld-musl-x86_64.so.1, ...

That interpreter line is the loader. ld-musl on a glibc system means the binary was built against musl, typically inside an Alpine container, and will not run on Debian or Fedora without the musl loader installed.

Other variants of the same problem: a 32-bit binary on a system without 32-bit libraries, or an ARM binary on x86.

Static binaries

file ./mytool
# statically linked
ldd ./mytool
# not a dynamic executable

Statically linked programs carry everything they need. Go produces these by default, which is why Go tools are so easy to distribute and why restic, jq, and similar utilities are single files you can copy anywhere.

The tradeoff is size and updates: a vulnerability in a bundled library requires rebuilding the program, whereas a dynamically linked one is fixed by updating the shared library.

Checking for a specific symbol

Sometimes the library is present but too old:

nm -D /lib/x86_64-linux-gnu/libc.so.6 | grep somefunction
objdump -T /lib/x86_64-linux-gnu/libc.so.6 | grep GLIBC_2.3

The classic version of this is a binary built on a newer distribution failing on an older one with a message about GLIBC_2.34 not found. glibc is backward compatible but not forward compatible, so build on the oldest system you need to support.

Frequently Asked Questions

What does ldd do?

It lists the shared libraries a binary depends on and shows which file each one resolves to. Entries marked not found are the missing dependencies stopping the program from starting.

Why does my program say No such file or directory when the file clearly exists?

The message usually refers to the dynamic loader or a required library, not the program itself. A binary built for musl running on a glibc system produces exactly this, which is why it confuses people so reliably.

Is it safe to run ldd on an untrusted binary?

Not entirely. ldd may execute the binary in some cases to resolve dependencies. Use objdump -p or readelf -d instead when inspecting something you do not trust, since those only parse the file.

What is the difference between a static and a dynamic binary?

A dynamic binary loads shared libraries at runtime, so it is small and benefits from library security updates. A static binary contains everything it needs, so it runs anywhere but must be rebuilt to pick up a fix in a bundled library.

How do I add a directory to the library search path?

Create a file in /etc/ld.so.conf.d/ containing the path and run ldconfig. Setting LD_LIBRARY_PATH works for one process and is fine for testing, but it is a poor permanent solution and can break other programs.

What is a soname and why do version numbers matter?

The soname is the versioned name a binary records, such as libssl.so.3. It signals binary compatibility, so libssl.so.3 and libssl.so.1.1 are different incompatible libraries despite the similar names, and symlinking one to the other will crash things.