Inspect ELF Binaries with readelf Without Guessing
You will finish with a small set of readelf commands for answering the usual questions about an ELF executable: what architecture it targets, how it is laid out, which libraries it needs, and which symbols it exposes. The examples use the packaged /usr/bin/readelf from GNU Binutils 2.42, package version 2.42-4ubuntu2.10. The installed manpages are shared by readelf, aarch64-linux-gnu-readelf and x86_64-linux-gnu-readelf.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about fifteen minutes. You need a shell and an ELF file to inspect. The commands only read their input, so they do not need elevated privileges and do not alter the binary. Use an input file you trust when dumping debug or string data: readelf reports bytes from the file, but that output can still contain misleading or sensitive text.
1. Confirm the binary and the tool version
Use an absolute path for a repeatable check. This avoids accidentally calling another readelf earlier in your PATH:
$ command -v /usr/bin/readelf
/usr/bin/readelf
$ /usr/bin/readelf --version
GNU readelf (GNU Binutils for Ubuntu) 2.42
Replace /usr/bin/readelf below if you intentionally need a cross-target alias. The alias changes the target defaults, while the inspection options remain the same. If you want to identify the package that supplied the command, run:
$ dpkg-query -S /usr/bin/readelf
binutils-common:amd64: /usr/bin/readelf
Checkpoint
Record both the executable path and its version before comparing output from another machine. Binutils releases add options and can change formatting.
2. Read the ELF header first
Start with the file header. It answers the questions most likely to prevent a wrong diagnosis:
$ /usr/bin/readelf --file-header /usr/bin/readelf
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 ...
Class: ELF64
Data: 2's complement, little endian
Type: DYN (Position-Independent Executable file)
Machine: Advanced Micro Devices X86-64
The Class is 32-bit or 64-bit, Data gives the byte order, and Machine identifies the target architecture. A DYN type is normal for a position-independent executable on a modern Linux system. It is not the same thing as a shared library, so use the program headers and dynamic section for more context.
Do not infer the architecture from the filename. A file called tool can target a different machine, and a cross-compiled output can be perfectly valid while being unusable by the host kernel.
3. Compare sections with loadable segments
Sections describe the linker's view of an object file. List them with --sections:
$ /usr/bin/readelf --sections /usr/bin/readelf
There are 30 section headers, starting at offset ...
Section Headers:
[Nr] Name Type Address Offset
[ 1] .interp PROGBITS ...
[ 2] .note.gnu.property NOTE ...
Names such as .text, .rodata, .data, .bss and .debug_info tell you what content was placed in the file. Use --section-details when you also need flags and the relationships between sections. It implies --sections.
Segments describe what the loader maps into memory. Show them with --program-headers, also available as --segments:
$ /usr/bin/readelf --program-headers /usr/bin/readelf
Elf file type is DYN (Position-Independent Executable file)
There are 13 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
LOAD ... ... ... R E
This is the useful distinction when investigating loader behaviour: a section can exist in the file without being part of a loadable segment. Look at segment permissions and the requested interpreter, rather than treating a section listing as a memory map.
4. Inspect symbols and shared-library requirements
Use --symbols for the ordinary symbol table and --dyn-syms for the dynamic symbol table used at runtime. Add --wide so long names and columns stay on one line:
$ /usr/bin/readelf --dyn-syms --wide /usr/bin/readelf
Symbol table '.dynsym' contains 104 entries:
Num: Value Size Type Bind Vis Ndx Name
1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __strcat_chk@GLIBC_2.3.4
Names ending in @VERSION carry symbol-version information. A double at sign, such as name@@VERSION, marks the default version used for an unversioned reference. C++ names are deliberately left mangled by default; add --demangle when human-readable function names matter.
Show the dynamic section with --dynamic:
$ /usr/bin/readelf --dynamic /usr/bin/readelf
Dynamic section at offset ... contains 31 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libc.so.6]
The NEEDED entries are the libraries named by the ELF file. They are not a complete guarantee that the program will start: the dynamic loader still has to find compatible files, resolve symbols and satisfy the host architecture.
5. Check notes, relocations and file health
Notes commonly contain build IDs, ABI markers and hardening properties:
$ /usr/bin/readelf --notes /usr/bin/readelf
Displaying notes found in: .note.gnu.build-id
Owner Data size Description
GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring)
Build ID: ...
Use --relocs to display relocation entries, especially when diagnosing an object file or link failure. Use --lint to ask readelf to report possible problems. With no other dumping option, lint examines the file contents and still prints the relevant information:
$ /usr/bin/readelf --lint /usr/bin/readelf >/tmp/readelf-lint.txt
$ printf 'readelf status: %s\n' "$?"
readelf status: 0
An empty lint report with status 0 is a useful result, not proof that a program is safe or correct. A non-zero status or diagnostic needs investigation against the exact file and tool version. Keep the temporary report if you need to share the warning, then remove it with rm -- /tmp/readelf-lint.txt when it no longer contains useful information. That removal is the only state-changing command in this guide, and it is irreversible for that temporary file.
6. Avoid the common output traps
--allis broad, not magic. It combines many report types but does not enable--use-dynamic, so dynamic symbols and relocations are not automatically substituted for their ordinary counterparts.- On 64-bit files, readelf normally wraps long section and segment lines at 80 columns. Add
--widewhen copying output into a ticket or comparing columns. - Dump selectors such as
--hex-dump=.rodataand--string-dump=.rodataaccept either a section number or a name. A name can match more than one section, so confirm the section table first. - Debug dumps can be very large. Begin with a focused selector such as
--debug-dump=info, and remember that linked debug files or debuginfod may be consulted by the installed build. Avoid unexpected network access by using--debug-dump=do-not-use-debuginfodwhere supported.
If an example fails with "Not an ELF file" or a similar diagnostic, check the input with file -- /path/to/input. A shell script, compressed archive or text file needs a different tool. If a file is an archive containing ELF objects, readelf can inspect archives, but use the archive index option, --archive-index, when your immediate question is which symbols the archive advertises.
Done means
- You confirmed the exact readelf executable and version.
- You used the ELF header to verify class, byte order and architecture.
- You separated sections, loadable segments, symbols and dynamic dependencies.
- You used
--widefor stable tabular output and treated warnings as evidence to investigate. - You removed any temporary report that is no longer needed.