When a binary behaves strangely and you have no source, llvm-objdump-18 lets you read it anyway. You work through headers, sections, symbols and a bounded disassembly, none of it touching the file. The examples use /bin/true, but the same commands work with any binary you built or received for analysis. Allow about 15 minutes for a first pass. Everything here is an ordinary, unprivileged read operation.
This guide targets the llvm-18 package and its installed LLVM 18.1.3 command on Ubuntu. The local manual is for llvm-objdump 18, while the package revision is 1:18.1.3-1ubuntu1. Check your own path and version before copying output into a report:
$ command -v llvm-objdump-18
/usr/bin/llvm-objdump-18
$ llvm-objdump-18 --version
Ubuntu LLVM version 18.1.3
...
Choose a real input file. Do not use an untrusted binary as a command: llvm-objdump-18 reads it as data, but running that binary would be a separate and potentially unsafe action entirely.
$ file /bin/true
/bin/true: ELF 64-bit LSB pie executable, x86-64, ...
$ test -r /bin/true && echo readable
readable
Start with --file-headers, also available as -f. It reports the file format, architecture and entry point, telling you whether the input is the kind of object you meant to inspect, and giving you the address execution actually starts at.
$ llvm-objdump-18 --file-headers /bin/true
/bin/true: file format elf64-x86-64
architecture: x86_64
start address: 0x00000000000019f0
If the command rejects the file, stop and check its type rather than guessing at options. The tool understands other object formats too, but format-specific options have boundaries: Mach-O options require --macho, for instance, and several Mach-O reports only apply to Mach-O files at all.
Use --section-headers, or its short alias -h, to see section names, sizes, virtual addresses and types. The section address matters later, when you limit a disassembly to a range.
$ llvm-objdump-18 --section-headers /bin/true | head -12
/bin/true: file format elf64-x86-64
Sections:
Idx Name Size VMA Type
0 00000000 0000000000000000
1 .interp 0000001c 0000000000000318 DATA
2 .note.gnu.property 00000030 0000000000000338
3 .note.gnu.build-id 00000024 0000000000000368
4 .note.ABI-tag 00000020 000000000000038c
5 .gnu.hash 00000024 00000000000003b0
Do not treat every listed region as executable code. In a normal ELF file, .text is the section you usually want to disassemble, while sections such as .rodata hold plain data. The exact list depends entirely on how the binary was linked.
Run --syms, or -t, to request the symbol table. On a stripped executable the output can be sparse, a property of the file, not evidence the command failed.
$ llvm-objdump-18 --syms /bin/true | head -12
/bin/true: file format elf64-x86-64
SYMBOL TABLE:
...
For a more useful report on a binary that has symbols, combine --demangle with the symbol listing so C++ names appear in human-readable form. The short alias for demangling is -C. Do not assume demangling can recover names that were removed when the binary was stripped in the first place.
--disassemble, or -d, disassembles executable sections. Add --section=.text to keep the request focused. The tool normally prints raw instruction bytes alongside the assembly; --no-show-raw-insn removes those bytes. For stable log output, disable disassembler colour, since the default mode assumes a terminal.
$ llvm-objdump-18 --disassemble --section=.text \
--disassembler-color=off --no-show-raw-insn \
--start-address=0x1480 --stop-address=0x14a0 /bin/true
/bin/true: file format elf64-x86-64
Disassembly of section .text:
0000000000001480 <.text>:
1480: callq 0x1260 <.plt.sec>
1485: nopw %cs:(%rax,%rax)
148f: nop
1490: endbr64
1494: pushq %rbp
1495: movq %rsp, %rbp
The address range is an inspection boundary, not a source-level function selector. Use section headers to choose plausible addresses first. If the range misses a section entirely, the installed tool warns that no section overlaps it and hands you no useful instructions at all.
Use --full-contents, or -s, to dump section contents. Restrict it with --section=.text when you only need the code bytes:
$ llvm-objdump-18 --full-contents --section=.text /bin/true | head -8
/bin/true: file format elf64-x86-64
Contents of section .text:
1480 e8dbfdff ff662e0f 1f840000 00000090 .....f..........
1490 f30f1efa 554889e5 41564155 41545348 ....UH..AVAUATSH
This earns its keep when an assembly line looks surprising: the left side is the byte representation, the right side a printable preview. Avoid dumping a large section straight into a terminal if you do not need to. Redirect reports to a new file, and remember shell redirection with > truncates an existing destination before the command even starts.
None of these commands change the input, and none need sudo unless your chosen file or directory is deliberately unreadable to your account. If you save output, use a new destination and check both the exit status and the file size:
$ llvm-objdump-18 --all-headers /path/to/program > program-headers.txt
$ test -s program-headers.txt && echo report-written
report-written
If you accidentally pick the wrong output name, stop before overwriting anything else. Remove only the report you created, and only after checking its path. The original binary stays unchanged regardless, there is no llvm-objdump undo operation because it never edits the input in the first place.