Skip to main content
Be Bitwise

Reading objdump output without going cross-eyed

objdump is a workhorse and a wall of text in equal measure. The right flags and a sense of which section answers which question turn it into something you actually use.

#objdump#binary-analysis#tooling

objdump is the binutils Swiss Army knife: ancient, faintly grumpy, and sitting on every Linux machine you've ever logged into. It will dump your binary in fifteen different ways and reward exactly none of them with friendly formatting. The trick is knowing which flag to reach for and which section of the output answers which question. Once you have that mapping, the wall of text turns into something useful.

The four flags you'll use most

bash
objdump -d binary

Disassemble executable sections. AT&T syntax by default, which is half the reason people give up on objdump.

bash
objdump -d -M intel binary

Same, in Intel syntax. mov rax, rbx instead of movq %rbx, %rax. If you've spent more time in Ghidra and IDA than in the GNU toolchain, you'll find the Intel mode dramatically easier on the eyes.

bash
objdump -S binary

Source-interleaved disassembly, when the binary still has DWARF debug info. Each line of source code shows up above the instructions it lowered to, which is the cheapest way to see what an optimiser actually did with your code. Compile with -g -O2 and read the output — you'll learn more about what your compiler can elide than any blog post will teach you.

bash
objdump -r binary.o

Print relocations. Mostly useful on .o files before linking, less useful on stripped executables. When you're debugging linker errors or trying to understand why a symbol resolves the way it does, this is where you look.

bash
objdump -x binary

The kitchen sink: file headers, section headers, symbol table, dynamic symbol table, relocations, version definitions, the lot. Pipe it to less and scroll, or grep for what you want. This is the "dump everything you know" command.

A combination I use constantly:

bash
objdump -d -M intel -S --no-show-raw-insn binary | less

Disassembly, Intel syntax, source-interleaved where possible, suppressing the raw instruction bytes (which are noise unless you're patching the binary). It's the closest objdump gets to comfortable reading.

What the sections of the output mean

Run objdump -d on a small binary and you'll see something like:

text
Disassembly of section .init:
0000000000001000 <_init>:
1000: endbr64
1004: sub $0x8,%rsp
...
Disassembly of section .plt:
0000000000001020 <.plt>:
1020: push QWORD PTR [rip+0x2fda] # 4000 <_GLOBAL_OFFSET_TABLE_+0x8>
...
Disassembly of section .text:
0000000000001140 <main>:
1140: push %rbp
1141: mov %rsp,%rbp
...

A quick map of what each section is for:

  • .init / .fini: runs before main and after main returns. Glue from the C runtime.
  • .plt / .plt.got: procedure linkage table. The trampolines that jump into shared libraries via the GOT. If you see call <foo@plt> from .text, the actual foo lives in libc.
  • .text: your code, plus everything the linker pulled in (constructors, libc startup helpers, etc.).
  • .rodata: read-only data. String literals, switch tables, virtual function tables in C++.
  • .data / .bss: initialised and zero-initialised mutable globals.
  • .got / .got.plt: global offset table entries that the runtime linker patches with the real addresses of imported symbols.
  • .eh_frame / .eh_frame_hdr: exception-handling unwind info. Mostly noise unless you're debugging a stack trace.

Knowing which section contains what means objdump -d -j .text binary (-j selects a section) restricts the output to just your code. Useful when the binary is large.

Finding what you're looking for

Symbol table:

bash
objdump -t binary | grep -i alloc

Dynamic symbol table (imports and exports for shared libraries):

bash
objdump -T binary | head

Strings with addresses (often more useful than strings alone, because you get where the string lives):

bash
objdump -s -j .rodata binary | less

Just the file header (for the entry point address, the architecture, the type):

bash
objdump -f binary

A quick "what does this function look like?" without scrolling:

bash
objdump -d --disassemble=main binary

--disassemble=NAME is the flag I wish I'd known existed for years. It dumps just the named function instead of the whole binary.

When to reach for objdump versus the alternatives

objdump wins when you want one function quickly, when you're on a remote box, when you want grep-friendly output, or when you're inspecting .o files before linking. It's unbeatable for "did the compiler actually inline this?" or "what did -O3 do to my hot loop?". Stick -S on, grep for the function, read.

Ghidra wins when you'll spend more than an hour with the binary. Type propagation, cross-references that update as you rename, a decompiler that turns a thousand instructions into thirty lines of pseudo-C — worth the project-setup overhead the moment your investigation gets serious.

radare2 (or its kinder fork, rizin) wins when you want a scriptable terminal REPL that's strictly more powerful than objdump. Visual graph mode (VV) reads control flow over SSH without forwarding X.

gdb wins when the binary has to actually run, because the answer depends on runtime values, indirect jumps, or input-dependent behaviour static disassembly can't see.

In practice, you'll use several. objdump -d to confirm nothing weird, gdb to step to the interesting function, Ghidra for proper analysis.

A few habits that pay off

Alias your usual flags. alias od='objdump -d -M intel --no-show-raw-insn' saves four flags every time. When chasing an address, pipe through grep -B 5 -A 5 for context. On stripped binaries, --disassemble is useless (no symbols), but --start-address=0xADDR --stop-address=0xADDR works. For ELF headers, readelf -a is sharper than objdump -x; they overlap, but readelf knows ELF and only ELF.

Where this fits

If the assembly itself is what's slowing you down, Module 21: Assembly Language is the foundation: registers, calling conventions, addressing modes, the difference between Intel and AT&T syntax, what every common x86-64 instruction actually does. If the binary is the harder part (sections, symbols, dynamic linking, the things objdump exists to show you), Module 68: Binary Analysis with Ghidra gets you fluent in the file format and the tools that operate on it.