guides / ldd-not-a-dynamic-executable troubleshootingupdated 2026-11-14 readelf -h ./app

ldd: not a dynamic executable — what it means (nothing is broken)

2026-11-14 By luckyray Debian 12 · static Go binary (x86-64)

Short answer

ldd reads the dynamic section of an ELF file. A statically linked binary has none, so ldd reports not a dynamic executable and exits 1. The binary is not broken. It simply does not need a loader at runtime.

This is the most common false alarm in Linux binary troubleshooting: people read it as "the executable is invalid" and start looking for a missing library that does not exist.

Confirm it in three commands

Take any static binary — a Go or Rust build, or busybox from a static image — and run:

$ ldd ./hello-static
        not a dynamic executable
$ echo $?
1
$ file ./hello-static
./hello-static: ELF 64-bit LSB executable, x86-64, version 1 (SYSV),
statically linked, Go BuildID=rLXBd7VvG-TozsbigJ3a/..., stripped
$ readelf -d ./hello-static
There is no dynamic section in this file.

file says statically linked and readelf -d says no dynamic section. Both agree with ldd. Nothing is missing.

What the message actually means

Tool Says Because
ldd not a dynamic executable no DT_NEEDED, no PT_INTERP
file statically linked same reason, different wording
readelf -d There is no dynamic section in this file. the .dynamic section is absent
readelf -l \| grep -c INTERP 0 no program interpreter to start

Contrast with a dynamically linked binary on the same host:

$ readelf -l ./hello-new | grep -A1 INTERP
  INTERP         0x0000000000000318 0x0000000000000318 0x0000000000000318
$ readelf -d ./hello-new | grep NEEDED
 0x0000000000000001 (NEEDED)   Shared library: [libstdc++.so.6]
 0x0000000000000001 (NEEDED)   Shared library: [libm.so.6]
 0x0000000000000001 (NEEDED)   Shared library: [libgcc_s.so.1]
 0x0000000000000001 (NEEDED)   Shared library: [libc.so.6]

An INTERP entry plus a list of NEEDED libraries is what ldd needs to do its job. A static binary has neither.

Where this shows up in practice

Go and Rust builds. CGO_ENABLED=0 Go builds and musl-targeted Rust builds are static by default. Running ldd on them is a category error, not a defect.

Distroless and scratch images. There is no shell, no binutils, sometimes no ldd.

Static busybox. busybox --install links applets into a single static binary; every applet reports this message.

Jump hosts without binutils. A slim debian:12 image has neither file nor readelf:

$ file ./hello-static
sh: 1: file: not found
$ readelf -h ./hello-static
sh: 1: readelf: not found

That is exactly the situation the ELF Inspector exists for — it parses in your browser, so the target host needs no tooling and the binary is not uploaded.

When not a dynamic executable is a real problem

Rarely, but it matters:

  1. You expected a dynamic binary. If a build system silently produced a static binary, dlopen() will fail at runtime because there is no dynamic loader in the process.
  2. glibc NSS breaks. A fully static glibc binary cannot load NSS modules (getpwnam, DNS via getaddrinfo can behave oddly). Use -static with care, or prefer static musl.
  3. The binary was corrupted mid-transfer. Combined with a truncated file, ldd can also report this. Check the size and a checksum if the file came over the network.

In every other case the message is informational.

What to use instead

# what kind of file is it at all
file ./binary

# architecture, word size, endianness, type
readelf -h ./binary | head -20

# any dynamic dependencies (empty output = static)
readelf -d ./binary | grep NEEDED

# interpreter present?
readelf -l ./binary | grep INTERP

# the same information without installing binutils
# -> open the binary in the browser ELF Inspector

The browser tool reports the same fields and adds the verdicts that are easy to get wrong by hand: PIE, static vs shared object, full RELRO, stripped, and the required GLIBC_* / GLIBCXX_* symbol versions.

One safety note about ldd

For dynamically linked binaries, ldd is not a pure reader. Most implementations execute the program's own loader (LD_TRACE_LOADED_OBJECTS) to get the resolution result. On an untrusted binary that means running its interpreter.

Static binaries are not affected — that is the one case where ldd genuinely cannot execute anything. For untrusted files, prefer readelf -d or the browser tool, which only parse data.

FAQ

Does not a dynamic executable mean the binary is corrupted? No. It means there is no dynamic section to inspect. A corrupted file usually gives a different error (cannot execute binary file, invalid ELF header).

Why does ldd exit 1 then? Because it could not perform the requested inspection. Exit status reflects the tool's success, not the binary's validity.

How do I know whether a binary is static or dynamic without ldd? file prints statically linked or dynamically linked; readelf -d prints the dynamic section or says there is none.

Can a static binary still fail at runtime? Yes — on a kernel that does not support its architecture, on a filesystem mounted noexec, or when it needs files it expects to find (glibc static + NSS). Those are different errors than this one.

Related

guides
  1. glibc and libstdc++ versions by distribution — measured, not remembered
  2. version `GLIBCXX_3.4.29' not found — and why copying libstdc++ does not fix it