Ошибки exec format error, bad ELF interpreter и cannot execute binary file в Debian и Ubuntu выглядят тревожно, но в большинстве случаев сводятся к нескольким типовым причинам. Чаще всего это запуск бинарника не той архитектуры, например сборки под arm64 на хосте amd64, или наоборот. На втором месте — отсутствие нужного динамического загрузчика, когда ELF-файл формально подходит, но система не может найти требуемый интерпретатор.
Отдельный класс проблем возникает в контейнерах: образ собран под одну платформу, а запускается на другой. Ещё одна частая история — перенос бинарника между Alpine и Debian/Ubuntu без учёта различий между musl и glibc. Внешне симптомы похожи, но исправление у каждого случая своё.
Хорошая новость в том, что такие сбои диагностируются быстро, если не гадать, а идти по шагам: определить архитектуру хоста, посмотреть, что именно представляет собой файл, проверить путь к dynamic linker и только потом разбираться с библиотеками, Docker или shebang.
Что означают эти ошибки
Формулировки похожи, но смысл у них немного разный.
cannot execute binary file— оболочка не может запустить файл как исполняемый бинарник. Часто это неверная архитектура, повреждённый файл или попытка выполнить текстовый файл как бинарный.exec format error— ядро не распознало формат исполняемого файла для текущей среды. Классический случай: бинарник собран под другую CPU-архитектуру.bad ELF interpreter— ELF-файл распознан, но указанный внутри интерпретатор отсутствует по нужному пути.
Проще говоря, первые две ошибки чаще говорят: «это не тот бинарник для этой машины», а третья — «бинарник подходит, но не хватает runtime-компонента для старта».
Если нужен быстрый порядок проверки: сначала архитектура ELF, затем битность, затем путь к dynamic linker и только после этого зависимости и контейнерное окружение.
Быстрый чек-лист диагностики
Когда вы видите такую ошибку, не стоит сразу ставить пакеты наугад. Сначала соберите базовые факты о системе и файле.
uname -m
getconf LONG_BIT
file ./mybin
readelf -h ./mybin
readelf -l ./mybin | grep 'interpreter'
ldd ./mybin
Этого набора обычно достаточно для первичной диагностики:
uname -mпоказывает архитектуру хоста, напримерx86_64илиaarch64.getconf LONG_BITбыстро показывает 32- или 64-битную среду.fileопределяет формат ELF, архитектуру и тип линковки.readelf -hпоказывает заголовок ELF: класс, machine type и ABI.readelf -lпозволяет увидеть ожидаемый интерпретатор.lddпомогает понять, каких библиотек не хватает, если файл вообще удаётся разобрать.
Дополнительно полезно посмотреть код возврата и сообщения ядра:
./mybin
echo $?
dmesg | tail -n 20
Если вы часто разворачиваете приложения в изолированной среде, на практике удобнее сразу иметь отдельный сервер или стенд на VDS, где можно быстро сверить архитектуру, libc и поведение контейнеров без влияния соседних сервисов.
Сценарий №1: неправильная архитектура — amd64 против arm64
Это самый частый случай. Бинарник выглядит нормальным, права на исполнение стоят, но Debian или Ubuntu отвечает cannot execute binary file либо exec format error. Обычно это значит, что файл собран не под ту архитектуру.
Типичный пример: сервер работает на amd64, а вы скачали релиз для arm64. Или локально сборка сделана на ARM-машине, а деплой идёт на обычный x86_64-хост. Особенно часто это всплывает в CI и Docker.
Как проверить архитектуру системы
uname -m
dpkg --print-architecture
lscpu | grep Architecture
Полезно помнить типичные соответствия:
x86_64иamd64— одна и та же 64-битная архитектура x86.aarch64иarm64— одна и та же 64-битная ARM-архитектура.i386иi686— 32-битный x86.armhfиarmv7— 32-битный ARM.
Как проверить архитектуру бинарника
file ./mybin
readelf -h ./mybin | grep -E 'Class|Machine'
Если хост показывает x86_64, а file сообщает, что это ELF для ARM aarch64, причина найдена. Такой файл нативно не запустится.
Исправление простое: скачать корректную сборку или пересобрать приложение под нужную платформу. Если вы работаете со смешанной инфраструктурой, полезно держать отдельные артефакты и не полагаться только на имя архива. Для серверов на ARM может пригодиться и статья про сценарии, где ARM VDS действительно выгоден для PHP и Node.js.

Сценарий №2: 32-битный бинарник на 64-битной системе
64-битная Debian или Ubuntu не всегда автоматически запускает старые 32-битные приложения. Это возможно только при наличии нужных 32-битных runtime-компонентов. Когда их нет, нередко появляется именно bad ELF interpreter.
Классический симптом: бинарник определяется как 32-битный ELF для Intel 80386, а система жалуется на отсутствие /lib/ld-linux.so.2 или похожего загрузчика.
Как увидеть, какой linker нужен
readelf -l ./legacy-app | grep interpreter
file ./legacy-app
Если в выводе фигурирует /lib/ld-linux.so.2, приложению нужен 32-битный загрузчик glibc. На чистом x86_64-хосте его может не быть.
Как включить multiarch и поставить нужные пакеты
sudo dpkg --add-architecture i386
sudo apt update
sudo apt install libc6:i386 libstdc++6:i386
После установки снова проверьте зависимости:
ldd ./legacy-app
Если ldd показывает not found, ставьте недостающие библиотеки точечно. Так проще поддерживать систему и понимать, что именно нужно приложению.
Сценарий №3: бинарник собран под musl, а система ждёт glibc
Иногда архитектура совпадает, но проблема не в CPU, а в libc. Например, вы берёте исполняемый файл из Alpine и пытаетесь запустить его в Debian или Ubuntu. Формально ELF может быть правильной архитектуры, но путь к интерпретатору и ABI будут другими.
Для Debian/Ubuntu обычно ожидается glibc-loader вида /lib64/ld-linux-x86-64.so.2 на x86_64. Для Alpine часто используется musl, и интерпретатор будет другим. В итоге система пишет, что файл не запускается или отсутствует interpreter.
Как распознать проблему libc
file ./mybin
readelf -l ./mybin | grep interpreter
strings ./mybin | grep -Ei 'glibc|musl' | head
В такой ситуации надёжнее пересобрать приложение под целевую систему, чем пытаться вручную совмещать чужие рантаймы. Особенно это важно для production-окружений.
Сценарий №4: проблема не в ELF, а в shebang
Иногда сообщение cannot execute binary file сбивает с толку, хотя файл вообще не бинарный. Это может быть скрипт с неправильной первой строкой: указан несуществующий интерпретатор, либо файл сохранён с Windows-окончаниями строк.
Сначала проверьте, что вы вообще запускаете:
file ./runme
head -n 1 ./runme
sed -n '1,3p' ./runme
Если там указан #!/bin/bash, а в контейнере есть только /bin/sh, запуск закончится ошибкой. Если shebang содержит скрытый \r, система будет искать несуществующий путь.
Проверить это можно так:
sed -n '1p' ./runme | cat -vet
Если в конце строки виден ^M, переведите файл в Unix-формат:
sed -i 's/\r$//' ./runme
Сценарий №5: Docker-образ собран не под ту платформу
В Docker проблема встречается особенно часто. Образ может успешно скачаться, но на старте контейнера вы получите exec format error, потому что entrypoint или базовый слой рассчитан на другую архитектуру.
Типовой сценарий: образ собран на ARM-ноутбуке, затем публикуется и запускается на Debian/Ubuntu-сервере с amd64. Внешне Dockerfile может быть корректным, но платформа артефакта не совпадает с платформой хоста.
Что проверить в Docker
docker image inspect myimage --format '{{.Architecture}} {{.Os}}'
docker inspect mycontainer --format '{{.Platform}}'
uname -m
Если архитектура образа не совпадает с архитектурой сервера, причина очевидна. Для multi-platform-сборок лучше явно задавать платформу на этапе сборки:
docker buildx build --platform linux/amd64 -t myimage:amd64 .
docker buildx build --platform linux/arm64 -t myimage:arm64 .
Для быстрой проверки гипотезы можно явно указать платформу и при запуске:
docker run --platform linux/amd64 --rm myimage:latest
Но это не универсальное решение: без поддержки эмуляции или без корректного multi-arch-образа контейнер всё равно не заработает. Если вы дополнительно разбираете сетевые проблемы контейнеров, полезно держать под рукой разбор взаимодействия Docker с iptables и nftables, потому что часть симптомов на старте контейнера админы ошибочно принимают за проблемы сети, а не платформы.

Как читать ELF без гадания
Команды file часто достаточно, но в спорных случаях полезно понимать, что именно смотреть в ELF-заголовке.
Class— 32- или 64-битный формат.Machine— архитектура процессора.OS/ABI— ABI-совместимость.interpreterв program headers — путь к dynamic linker.
readelf -h ./mybin
readelf -l ./mybin
Если Machine не совпадает с хостом, это почти наверняка неправильная архитектура. Если совпадает, но нужный interpreter отсутствует, перед вами сценарий с bad ELF interpreter. Если бинарник статический, секции с интерпретатором может не быть вовсе.
Что делать, если файл повреждён или скачан не полностью
Хотя это более редкий сценарий, повреждённый файл тоже может давать ошибки формата. Артефакт мог обрезаться в CI, скачаться не полностью, распаковаться с ошибкой или вообще оказаться HTML-страницей вместо бинарника.
file ./mybin
sha256sum ./mybin
ls -lh ./mybin
Если file показывает HTML document или ASCII text там, где вы ждёте ELF, значит проблема вообще не в интерпретаторе, а в источнике артефакта.
Пошаговый runbook для Debian/Ubuntu
Если нужен короткий рабочий алгоритм, используйте такой порядок:
- Проверьте архитектуру хоста через
uname -mиdpkg --print-architecture. - Проверьте файл через
fileиreadelf -h. - Если архитектура не совпадает, скачайте или пересоберите правильный бинарник.
- Если архитектура совпадает, посмотрите
readelf -lи найдитеinterpreter. - При отсутствии loader установите нужные runtime-пакеты и при необходимости включите
multiarch. - Если это контейнер, сравните платформу хоста, образа и
entrypoint. - Если это скрипт, проверьте shebang и окончания строк.
- Если картина не сходится, проверьте целостность файла и источник сборки.
Типичные примеры и их смысл
Пример 1: x86_64-сервер, ARM-бинарник
$ uname -m
x86_64
$ file ./app
./app: ELF 64-bit LSB pie executable, ARM aarch64, dynamically linked
$ ./app
bash: ./app: cannot execute binary file: Exec format error
Здесь всё просто: бинарник для arm64, а система — amd64.
Пример 2: 32-битное приложение на Ubuntu amd64
$ file ./legacy-app
./legacy-app: ELF 32-bit LSB executable, Intel 80386, dynamically linked
$ ./legacy-app
bash: ./legacy-app: /lib/ld-linux.so.2: bad ELF interpreter: No such file or directory
Здесь не хватает 32-битного загрузчика и, вероятно, библиотек i386.
Пример 3: Docker-образ другой платформы
$ docker image inspect myimage --format '{{.Architecture}}'
arm64
$ uname -m
x86_64
$ docker run --rm myimage
exec /usr/local/bin/app: exec format error
Проблема не в коде приложения, а в несовпадении платформы образа и хоста.
Как предотвратить проблему в CI/CD
Лучшее решение — не искать причину уже после деплоя, а проверять артефакты заранее.
- Проверяйте итоговый бинарник через
file. - Явно маркируйте артефакты архитектурой в имени файла.
- Для Docker используйте multi-arch manifests и явный
--platform. - Тестируйте запуск
entrypointв целевой среде. - Не переносите бинарники из Alpine в Debian/Ubuntu без понимания различий в libc.
Итог
Ошибки exec format error, bad ELF interpreter и cannot execute binary file в Debian/Ubuntu почти всегда имеют конкретную и проверяемую причину. Обычно это либо неверная архитектура, либо отсутствующий dynamic linker, либо несовпадение платформы в Docker.
Главное правило простое: не лечите вслепую. Сначала проверьте архитектуру хоста, затем формат ELF, потом интерпретатор и зависимости. Такой порядок почти всегда приводит к точному ответу за несколько минут.
Если у вас несколько сред и смешанные платформы, имеет смысл встроить эти проверки в CI/CD и runbook дежурной команды. Тогда проблема обнаружится до выката, а не ночью после релиза.


