How to check whether a binary is 32-bit or 64-bit (and for which CPU)
The question sounds trivial until you are holding a binary that will not start. Word size and instruction set are two separate facts recorded in the ELF header, and mixing them up sends people installing multilib packages for a problem that is actually a CPU mismatch.
The one command that answers both
$ file ./agent
./agent: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked,
interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, stripped
Read it left to right and you have everything:
| Fragment | Means |
|---|---|
ELF 64-bit | EI_CLASS — pointer and offset width |
LSB | little endian (MSB would be big endian) |
executable | e_type — ET_EXEC; shared object or pie executable would appear otherwise |
ARM aarch64 | e_machine — the instruction set |
dynamically linked | has PT_INTERP and DT_NEEDED |
interpreter /lib/ld-linux-aarch64.so.1 | which libc family the loader expects |
stripped | no .symtab |
If file is not installed, or you want the raw fields, readelf -h is the authoritative source:
$ readelf -h ./agent | grep -E 'Class|Data|OS/ABI|Type|Machine|Flags'
Class: ELF64
Data: 2's complement, little endian
OS/ABI: UNIX - System V
Type: DYN (Position-Independent Executable file)
Machine: AArch64
Flags: 0x0
What “64-bit” does not tell you
Class and machine are independent. A 64-bit file can be EM_X86_64, EM_AARCH64,
EM_RISCV, EM_PPC64 or several others. Two binaries that both report ELF 64-bit will refuse to
run on each other’s machines.
The mapping that matters in practice:
file says | e_machine | Runs on |
|---|---|---|
x86-64 | 62 | Intel/AMD 64-bit hosts |
ARM aarch64 | 183 | ARM 64-bit (Pi 4/5, Graviton, Apple Silicon VMs) |
Intel 80386 | 3 | 32-bit x86, or 64-bit hosts with multilib |
ARM, EABI5 | 40 | 32-bit ARM (Pi 2/3 in 32-bit mode, many embedded boards) |
RISC-V | 243 | RISC-V 64 |
IBM S/390 | 22 | s390x mainframes — and this one is big endian |
That last row is the trap: comparing uname -m output without checking endianness gives a confident
and wrong answer.
Compare against the host correctly
$ uname -m
x86_64
$ getconf LONG_BIT
64
uname -m reports the kernel’s architecture. getconf LONG_BIT reports the userland’s word
size. On a 64-bit kernel with a 32-bit userland they disagree, and that disagreement explains why a
64-bit binary can fail on a machine whose uname -m says x86_64.
For containers there is a third place to look:
$ docker image inspect myapp:latest --format '{{.Architecture}} / {{.Os}}'
arm64 / linux
If that disagrees with your host, you have the cause of the failure before you ever run the binary.
When the answers disagree
| Binary | Host | Result |
|---|---|---|
| ELF32 i386 | x86_64 + multilib | runs |
| ELF32 i386 | x86_64, no multilib | missing ld-linux.so.2 |
| ELF64 AArch64 | x86_64 | Exec format error |
| ELF64 s390x | x86_64 | Exec format error — wrong endianness and machine |
| ELF64 x86-64 | x86_64 | runs, subject to GLIBC symbol versions |
A one-liner for a directory full of binaries
When you are triaging a release tarball rather than a single file:
find . -type f -exec file {} + | grep -E 'ELF (32|64)-bit' | sort | uniq -c | sort -rn
That collapses to something like “12 × ELF 64-bit x86-64, 3 × ELF 64-bit AArch64” in one line, which is usually the answer to “did the build pipeline emit the wrong architecture again”.
Doing it without a shell
If the machine with the problem is the one you cannot install binutils on — a hardened jump host, a
scratch container, a customer VPN — the ELF Inspector reads class,
endianness and e_machine in a browser tab and reports them in the same vocabulary as readelf.
The Compatibility Checker goes one step further and compares those
fields against a target platform you select, including the GLIBC symbol versions the binary requires.
Related: Exec format error for the failure this diagnosis usually explains, and wrong ELF class for the 32/64 mix-up at link time.
Every command in this guide was run against the distribution named in the output. If your output differs, the mechanism section explains why — and the ELF Inspector shows the same fields without needing a shell.