Inspect ELF Binaries Safely with llvm-readobj-18
You will finish with a small, repeatable set of inspections for an ELF executable: its file header, sections, dynamic libraries and machine-readable JSON. The examples use Ubuntu LLVM 18.1.3 and the llvm-readobj-18 binary installed by the llvm-18 package.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about ten minutes. You need a shell and a file you are allowed to read. Every command here is read-only and runs without elevated privileges. Replace /path/to/program with a real object file, shared library or executable on your system.
1. Confirm the tool and input
Check the executable before interpreting its output:
$ command -v llvm-readobj-18
/usr/bin/llvm-readobj-18
$ llvm-readobj-18 --version
Ubuntu LLVM version 18.1.3
Optimized build.
The installed manpage is dated 27 May 2024 and identifies the tool as LLVM 15 in its generated header, but the executable on this machine reports LLVM 18.1.3. Trust the binary's --version output when recording what produced a report.
Checkpoint: make sure the input is the file you intend to inspect, not a command or a directory:
$ file /path/to/program
/path/to/program: ELF 64-bit LSB pie executable, x86-64, ...
2. Read the ELF file header
Start with --file-header, or its short form -h:
$ llvm-readobj-18 --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)
FileVersion: 1
}
Type: SharedObject (0x3)
Machine: EM_X86_64 (0x3E)
...
}
The magic bytes confirm ELF, while the format, architecture and address size tell you how to read the rest of the report. The file type can be surprising: a position-independent executable may be reported as an ELF shared object because it uses the ELF ET_DYN type.
If the header is the first check in a script, test the exit status rather than matching a text line:
if llvm-readobj-18 --file-header /path/to/program >/tmp/readobj-header.txt; then
printf '%s\n' 'header read successfully'
else
status=$?
printf 'llvm-readobj-18 failed with status %s\n' "$status" >&2
exit "$status"
fi
3. Map sections and inspect selected content
Use --sections, also available as --section-headers or -S, to list the sections. This is useful when you need to find a section name before dumping it:
$ llvm-readobj-18 --sections /bin/true
File: /bin/true
Format: elf64-x86-64
...
Sections [
Section {
Index: 0
Name: (0)
Type: SHT_NULL (0x0)
}
Section {
Index: 1
Name: .interp (11)
...
}
]
Dump a named section as bytes with --hex-dump or -x, and as strings with --string-dump or -p:
$ llvm-readobj-18 --hex-dump=.rodata /path/to/program
$ llvm-readobj-18 --string-dump=.rodata /path/to/program
Section names are file data, not guaranteed input. If the requested name does not exist, expect a non-zero status and an error. Copy a name from the section listing instead of guessing it. --decompress can be added to either dump operation when compressed section content needs to be shown after decompression.
4. Check symbols and shared-library dependencies
Display the symbol table with --symbols, --syms or -s. The short option is easy to misremember: in llvm-readobj-18, -s means symbols, while -S means sections. Add --demangle or -C when C++ names should be readable.
$ llvm-readobj-18 --symbols --demangle /path/to/program
File: /path/to/program
Format: elf64-x86-64
...
Symbols [
Symbol {
Name: ...
...
}
]
For an ELF file's runtime library requirements, use the ELF-specific --needed-libs option:
$ llvm-readobj-18 --needed-libs /bin/true
File: /bin/true
Format: elf64-x86-64
...
NeededLibraries [
libc.so.6
]
This reports names recorded in the binary. It does not prove that the dynamic loader can find a compatible library on another machine. Resolve that separate deployment question with the target system's loader and package configuration.
5. Produce JSON for automation
For ELF input, select JSON explicitly with --elf-output-style=JSON. The default output style is LLVM's expanded structured text. JSON is intended for machine consumption:
$ llvm-readobj-18 --elf-output-style=JSON --file-header /bin/true
[{"FileSummary":{"File":"/bin/true","Format":"elf64-x86-64","Arch":"x86_64","AddressSize":"64bit",...}}]
Do not parse the default human-readable layout with brittle field matching if a JSON consumer is available. Validate the complete stream before using it, because a command can emit diagnostics and return a failure status for a bad input. Add --pretty-print when a person needs readable JSON; leave it out for compact transport.
llvm-readobj-18 --elf-output-style=JSON --headers /path/to/program \
| jq '.[0].FileSummary'
The jq command is optional and is not part of LLVM. If it is absent, save the output and pass it to another JSON parser. Do not treat an empty or partial file as a valid report.
6. Read from standard input and handle failures
Use a single hyphen as the input name to read from standard input. This helps when an earlier, trusted pipeline stage supplies the object data:
$ cat /bin/true | llvm-readobj-18 --file-header -
File: <stdin>
Format: elf64-x86-64
Arch: x86_64
AddressSize: 64bit
...
Keep the input source clear. A pipeline can hide which file was inspected, and the tool labels it <stdin> rather than preserving the original path.
Errors return a non-zero status. For example, a missing file produces an error on standard error and status 1:
$ llvm-readobj-18 --file-header /definitely/missing
llvm-readobj-18: error: '/definitely/missing': No such file or directory
$ printf '%s\n' "$?"
1
Capture $? immediately if you need it. Do not add a diagnostic command first. No command in this guide changes the inspected file, the loader, section data or system configuration, so there is no state to undo.
Done means
- You confirmed the installed LLVM version and the exact input path.
- You can read the ELF header and list sections before selecting one to dump.
- You know that
-smeans symbols and-Smeans sections here. - You can inspect dependencies with
--needed-libswithout mistaking names for proof of deployment. - Your automation selects JSON, checks the command status and validates the complete output.
- You can distinguish a missing or invalid input from a successful inspection.