Inspect ELF binaries safely with llvm-readobj-20
By the end of this guide you will be able to inspect an ELF executable's headers, sections, symbols and dynamic metadata with llvm-readobj-20, then save a machine-readable report when that is more useful than terminal output. The commands only read their inputs. Nothing here needs sudo, and no command modifies the binary.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about 10 minutes for the first pass. You need a shell, the llvm-20 package and a readable object file such as /bin/true. The examples below were checked with Ubuntu's llvm-20 package, version 20.1.8.
1. Confirm the installed tool
Start by checking which executable is being used and which option set it provides. The version matters because LLVM tools gain and adjust format-specific options over time.
$ command -v llvm-readobj-20
/usr/bin/llvm-readobj-20
$ llvm-readobj-20 --version
Ubuntu LLVM version 20.1.8
Optimized build.
If the command is missing, install the package through your normal system administration process. That is the only step in this guide that may require elevated privileges, and it changes system state, so do not add sudo to an otherwise read-only inspection command.
Checkpoint
Continue when llvm-readobj-20 --version prints a version instead of a shell error.
2. Read the file header
Use --file-header, or its short form -h, for the identity and layout of an object file. The example uses the system's true executable, but replace it with a path you want to investigate.
$ llvm-readobj-20 --file-header /bin/true
File: /bin/true
Format: elf64-x86-64
Arch: x86_64
AddressSize: 64bit
LoadName: <Not found>
ElfHeader {
Ident {
Magic: (7F 45 4C 46)
Class: 64-bit (0x2)
DataEncoding: LittleEndian (0x1)
}
Type: SharedObject (0x3)
Machine: EM_X86_64 (0x3E)
}
The exact fields depend on the file. A confusing detail is that an ELF executable can be reported as SharedObject when it is a position-independent executable. Treat the output as a description of the file format and flags, not as a promise about how the program was launched.
3. List sections and program headers
Sections describe linked contents such as .text, .rodata, .data and symbol tables. Program headers describe the segments the loader maps. Ask for both when you are trying to understand how file contents become process memory.
$ llvm-readobj-20 --sections --program-headers /bin/true
File: /bin/true
Format: elf64-x86-64
...
Sections [
Section {
Index: 1
Name: .interp
Type: SHT_PROGBITS (0x1)
}
Section {
Index: 16
Name: .text
Type: SHT_PROGBITS (0x1)
}
]
ProgramHeaders [
ProgramHeader {
Type: PT_LOAD (0x1)
Flags [ (0x5)
PF_R (0x4)
PF_X (0x1)
]
}
]
The abbreviated output above is representative, not a complete transcript. For a compact ELF overview, --headers combines the file header, program headers and sections:
$ llvm-readobj-20 --headers /bin/true > /tmp/true-headers.txt
$ test -s /tmp/true-headers.txt && echo "header report written"
header report written
Writing a report under /tmp is optional and safe to remove later. Do not redirect output over the input path. A shell redirection is opened before the reader starts, so a command such as llvm-readobj-20 --headers binary > binary can destroy the file before inspection begins.
4. Inspect symbols and demangle names
The symbol table is useful for object files and unstripped binaries. Use --symbols or -s. C++ names can be made easier to read with --demangle or -C; the option applies to ELF and XCOFF.
$ llvm-readobj-20 --symbols --demangle path/to/program
File: path/to/program
Format: elf64-x86-64
...
Symbols [
Symbol {
Name: main
Value: 0x0000000000000000
}
]
Do not treat an empty symbol list as proof that the file contains no functions. Stripped executables can retain dynamic symbols while losing the full regular symbol table. If runtime linkage is the question, inspect the dynamic table and dynamic symbols instead:
$ llvm-readobj-20 --dynamic-table --dyn-symbols /bin/true | sed -n '1,45p'
Keeping the output on screen first makes errors visible. Add a redirect only after you know the input path and output path are different.
5. Choose text, bytes or JSON output
For a named section, --string-dump or -p displays strings, while --hex-dump or -x displays bytes. A section can be selected by name or index.
$ llvm-readobj-20 --string-dump=.rodata /bin/true | sed -n '1,25p'
$ llvm-readobj-20 --hex-dump=.interp /bin/true
These options are inspection views. They do not decompress or rewrite a section. Add --decompress only with a hex or string dump when the section is compressed.
For scripts, request JSON explicitly and validate that the result is parseable:
$ llvm-readobj-20 --elf-output-style=JSON --file-header --sections /bin/true > /tmp/true.json
$ python3 -m json.tool /tmp/true.json > /dev/null && echo "valid JSON"
valid JSON
LLVM-style structured output is the default. GNU style is available with --elf-output-style=GNU when comparing a result with readelf. JSON is intended for machine consumption, but its field names remain part of this LLVM tool's output rather than a universal ELF interchange format.
6. Read relocations and notes when troubleshooting
Use --relocs or -r for relocation entries, --notes or -n for ELF notes, and --dynamic-table or -d for dynamic linking metadata.
$ llvm-readobj-20 --relocs /bin/true | sed -n '1,35p'
$ llvm-readobj-20 --notes /bin/true | sed -n '1,35p'
$ llvm-readobj-20 --dynamic-table /bin/true | sed -n '1,35p'
Large binaries can produce a lot of output. Pipe it to a pager or select a bounded range, but remember that a pipe can hide which command failed unless you check its status. For a complete first survey, --all enables the main display options relevant to the file format. It is often more output than you need, so use focused flags when diagnosing one question.
Common errors and recovery
A non-zero exit status means the tool encountered an error. Check it directly:
$ llvm-readobj-20 --file-header path/to/missing-file
llvm-readobj-20: error: 'path/to/missing-file': No such file or directory
$ printf 'status: %s\n' "$?"
status: 1
Use file path/to/input before choosing ELF-specific options if the format is uncertain. Unsupported format-specific flags may be ignored or produce no useful data, depending on the file type and option. An input of - reads from standard input, so this is also valid:
$ cat /bin/true | llvm-readobj-20 --file-header - | sed -n '1,12p'
There is no persistent change to undo. If you created reports in /tmp, remove only the exact files you created after checking their names. Never use a broad recursive removal command merely to clean up a diagnostic.
Done means
- You confirmed the installed
llvm-readobj-20version. - You can read an ELF file header and distinguish sections from program headers.
- You know when to use regular symbols, dynamic symbols, relocations and notes.
- You can produce JSON and verify it before giving it to another tool.
- You kept the input read-only and avoided redirecting output over it.