Chuyện gì vừa xảy ra
Bạn tải về một binary, biên dịch một chương trình, hoặc sao chép một file thực thi từ máy khác — rồi chạy nó và nhận được thông báo này:
bash: ./app: cannot execute binary file: Exec format error
Shell đã tìm thấy file. Nó xác nhận bit thực thi đã được bật. Nhưng kernel từ chối chạy nó hoàn toàn. Sự từ chối này hầu như luôn chỉ đến một trong ba nguyên nhân: binary nhắm đến kiến trúc CPU khác, file thực ra không phải là file thực thi, hoặc ELF interpreter bị thiếu. Mỗi nguyên nhân cần một cách sửa khác nhau. Bỏ qua bước chẩn đoán và bạn sẽ tốn hai mươi phút vào sai hướng.
Bước 1 — Kiểm tra file thực sự là gì
Chạy lệnh file trên binary. Đây là cách nhanh nhất để xem nó được build cho kiến trúc và định dạng nào:
file ./app
Ví dụ về kết quả đầu ra và ý nghĩa của chúng:
# Binary ARM trên máy x86_64 — không khớp kiến trúc
./app: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV)
# Binary x86_64 — có thể chạy trên hầu hết các máy Linux hiện đại
./app: ELF 64-bit LSB executable, x86-64, version 1 (SYSV)
# Không phải ELF — có thể là file .exe Windows bị đổi tên
./app: PE32+ executable (console) x86-64, for MS Windows
# Shell script không có shebang
./app: ASCII text
Bây giờ kiểm tra kiến trúc của máy bạn:
uname -m
# x86_64 → Intel/AMD 64-bit
# aarch64 → ARM 64-bit (Apple Silicon, Raspberry Pi 4, AWS Graviton)
# armv7l → ARM 32-bit (Raspberry Pi đời cũ)
Không khớp giữa kết quả lệnh file và uname -m? Đó là thủ phạm.
Bước 2 — Xác định nguyên nhân chính xác
Nguyên nhân A: Không khớp kiến trúc (phổ biến nhất)
Bạn đang chạy binary ARM trên máy x86_64, hoặc ngược lại. Bốn tình huống thường xuyên gây ra điều này:
- Bạn tải binary dựng sẵn từ internet mà không kiểm tra nhãn kiến trúc
- Bạn build trên Mac Apple Silicon (aarch64) rồi sao chép binary lên server x86_64
- Bạn đang ở trong Docker container có base image nhắm đến kiến trúc khác với máy host
- Bạn cross-compile nhưng không đặt target đúng
Nguyên nhân B: Định dạng file sai (không phải ELF)
File thực thi PE của Windows (.exe), binary Mach-O của macOS, và các script văn bản thuần không có shebang đều gây ra cùng một lỗi. Phần mở rộng file không quan trọng — Linux dùng magic bytes, không phải tên file.
Nguyên nhân C: Binary 32-bit trên hệ thống 64-bit thiếu thư viện 32-bit
Ngay cả binary đúng kiến trúc cũng có thể thất bại ở đây. Một ELF x86 32-bit trên hệ thống Linux 64-bit cần cài các gói lib32:
file ./app
# ./app: ELF 32-bit LSB executable, Intel 80386
Bước 3 — Áp dụng cách sửa đúng
Cách sửa A: Tải binary đúng cho kiến trúc của bạn
Đang tải bản phát hành dựng sẵn? Lấy đúng archive cho nền tảng của bạn. Hầu hết các dự án dùng cách đặt tên có thể đoán trước:
# x86_64 / amd64
app-linux-amd64
app-linux-x86_64
# ARM 64-bit
app-linux-arm64
app-linux-aarch64
# ARM 32-bit
app-linux-armv7
app-linux-arm
Tải về, xác minh bằng file, rồi chạy:
curl -LO https://example.com/releases/app-linux-amd64
chmod +x app-linux-amd64
file ./app-linux-amd64
./app-linux-amd64
Cách sửa B: Biên dịch lại cho kiến trúc đích
Có mã nguồn? Biên dịch trực tiếp trên máy đích, hoặc cross-compile với flag target đúng.
Go làm cho cross-compilation trở nên đơn giản — chỉ cần hai biến môi trường là đủ:
# Biên dịch cho Linux x86_64
GOOS=linux GOARCH=amd64 go build -o app-linux-amd64 .
# Biên dịch cho Linux ARM64
GOOS=linux GOARCH=arm64 go build -o app-linux-arm64 .
Với C dùng GCC, cài bộ công cụ cross-compiler trước:
# Cài cross-compiler ARM
sudo apt install gcc-aarch64-linux-gnu
# Biên dịch cho ARM64
aarch64-linux-gnu-gcc -o app-arm64 main.c
Cách sửa C: Thêm QEMU emulation (chạy binary kiến trúc khác mà không cần biên dịch lại)
Đôi khi bạn không thể biên dịch lại — binary là mã nguồn đóng, hoặc bạn chỉ cần test nhanh. QEMU user-mode emulation cho phép chạy binary kiến trúc khác mà không cần động đến mã nguồn:
# Cài QEMU user-mode emulation
sudo apt install qemu-user qemu-user-static
# Chạy binary ARM64 trên x86_64
qemu-aarch64 ./app-arm64
# Đăng ký binfmt_misc handlers để kernel tự gọi QEMU
sudo apt install binfmt-support
sudo update-binfmts --enable qemu-aarch64
Sau khi đăng ký binfmt handlers, bạn không cần tiền tố nữa:
./app-arm64 # chạy tự động qua QEMU
Cách sửa D: Cài hỗ trợ 32-bit cho binary ELF 32-bit
# Debian/Ubuntu
sudo apt install lib32z1 lib32stdc++6
# CentOS/RHEL
sudo yum install glibc.i686 libstdc++.i686
Cách sửa E: Thêm shebang nếu đây là script
Kết quả văn bản thuần từ lệnh file nghĩa là file đang thiếu dòng interpreter. Kiểm tra dòng đầu tiên:
head -1 ./app
Không có #! ở đầu? Thêm vào:
#!/usr/bin/env bash
# phần còn lại của script...
Rồi đặt lại bit thực thi và chạy:
chmod +x ./app
./app
Tình huống đặc thù với Docker
Docker container là nơi lỗi này hay gây bất ngờ nhất. Kéo image arm64 trên host x86_64 — hoặc build trên Mac M1 rồi deploy lên cloud VM — âm thầm tạo ra kiến trúc sai. Kiểm tra trước khi triển khai:
# Kiểm tra kiến trúc của image
docker inspect --format='{{.Architecture}}' myimage
# Kéo image cho nền tảng cụ thể
docker pull --platform linux/amd64 myimage
# Build cho nền tảng cụ thể
docker buildx build --platform linux/amd64 -t myimage .
Triển khai lên server ARM như AWS Graviton hay Raspberry Pi? Đặt --platform linux/arm64 trong bước build và đưa nó vào CI pipeline, đừng để đến lúc deploy mới nghĩ đến.
Xác minh cách sửa đã có tác dụng
# 1. Xác nhận kiến trúc khớp với máy của bạn
file ./app
uname -m
# 2. Chạy binary
./app
# 3. Kiểm tra exit code — 0 là thành công
echo $?
Vẫn lỗi sau khi sửa kiến trúc? Thư viện dùng chung bị thiếu là nghi can tiếp theo:
ldd ./app
# Tìm các dòng như:
# libssl.so.1.1 => not found
Mỗi mục not found tương ứng với một gói bạn cần cài.
Tóm tắt nhanh
- Không khớp kiến trúc → tải binary đúng hoặc biên dịch lại cho kiến trúc đích
- Cần chạy binary kiến trúc khác → cài QEMU user-mode + binfmt_misc
- Binary 32-bit trên hệ thống 64-bit → cài các gói
lib32 - Script không có shebang → thêm
#!/usr/bin/env bashlàm dòng đầu tiên - Binary Windows/macOS → bạn cần bản build cho Linux, không có cách nào khác
Chạy file ./app trước tiên. Luôn luôn. Hai giây kết quả đầu ra cho bạn biết chính xác cách sửa nào trong năm cách trên cần áp dụng.

