5.3.17Build Systems & Toolchain

Disassembly — objdump, reading assembly

1,940 words9 min readdifficulty · medium

WHY does disassembly exist?

WHY you care (the 80/20):

  • A crash gives you a faulting address, not a line of C. Disassembly bridges that gap.
  • Optimizers reorder/delete your code. The only ground truth is the emitted instructions.
  • Security/reverse engineering: you often have no source, only the binary.

The full pipeline:

.ccompile.s (asm)assemble.o (machine code)linkexecutable\text{.c} \xrightarrow{\text{compile}} \text{.s (asm)} \xrightarrow{\text{assemble}} \text{.o (machine code)} \xrightarrow{\text{link}} \text{executable}

Disassembly runs that arrow backwards from the .o or executable.


HOW to invoke objdump

WHY two syntaxes exist: historical. GNU defaults to AT&T (mov %eax, %ebx means ebx = eax, src→dst, registers prefixed %, immediates $). Intel (mov ebx, eax means ebx = eax, dst←src) reads like math assignment. We use Intel because it matches how you think: dst = src.


WHAT a disassembly line looks like

Figure — Disassembly — objdump, reading assembly

A single line has four columns:

 1149:   48 83 ec 08             sub    rsp, 0x8
  ^addr   ^raw bytes              ^mnemonic ^operands
  • address 1149: offset/virtual address of this instruction.
  • raw bytes 48 83 ec 08: the actual machine code (variable length on x86).
  • mnemonic sub: the operation.
  • operands rsp, 0x8: what it operates on.

Reading a real function from scratch (DERIVATION)

Source:

int add(int a, int b) {
    return a + b;
}

Compile: gcc -O0 -g -c add.c then objdump -d -M intel add.o:

0000000000000000 <add>:
   0:  55                 push   rbp           ; save caller's frame ptr
   1:  48 89 e5           mov    rbp, rsp      ; set up new frame
   4:  89 7d fc           mov    [rbp-0x4], edi ; spill arg a (edi) to stack
   7:  89 75 f8           mov    [rbp-0x8], esi ; spill arg b (esi) to stack
   a:  8b 55 fc           mov    edx, [rbp-0x4] ; load a
   d:  8b 45 f8           mov    eax, [rbp-0x8] ; load b
  10:  01 d0              add    eax, edx       ; eax = b + a
  12:  5d                 pop    rbp            ; restore frame
  13:  c3                 ret                   ; return (result in eax)

Reading it line-by-line — Why this step?

  • push rbp / mov rbp,rspWhy? Standard function prologue: saves the old base pointer and establishes a stable reference frame for locals.
  • mov [rbp-0x4], ediWhy? The System V x86-64 calling convention passes arg 1 in edi, arg 2 in esi. At -O0 the compiler immediately spills them to stack.
  • add eax, edxWhy eax? The convention returns integers in ==rax/eax==. So the compiler arranges the result there.
  • ret — pops the return address and jumps to it.

Control flow: how if/loops look


Common mistakes


Flashcards

What does objdump -d do?
Disassembles the executable (.text) sections of an object file/binary into assembly.
What does the -M intel flag change?
Switches output from default AT&T syntax to Intel syntax (operand order dst, src; no %/$ sigils).
In Intel syntax, what does mov rbp, rsp mean?
rbp = rsp (destination first, source second).
What are the 4 columns of an objdump line?
Address, raw machine-code bytes, mnemonic, operands.
Why are x86 instructions variable length?
x86 is CISC; encodings range from 1 byte to 15, with prefixes (e.g. REX 48 = 64-bit operand).
Where is an integer return value placed (System V x86-64)?
In rax/eax.
Which registers hold the first two integer args (System V x86-64)?
rdi (arg1) and rsi (arg2); 32-bit views edi, esi.
What is the standard function prologue?
push rbp then mov rbp, rsp to save/establish the stack frame.
What does cmp a, b actually do?
Computes a - b, discards the result, but sets the CPU flags so a following conditional jump can use them.
Why does lea eax, [rdi+rsi] appear instead of an add?
The optimizer reuses lea's address arithmetic to add two registers in one instruction with no flag side effects.
What flag interleaves C source with assembly?
-S (requires the binary compiled with -g debug info).
Difference between objdump -d and -D?
-d disassembles only executable sections; -D disassembles ALL sections (data may show as garbage instructions).

Recall Feynman: explain to a 12-year-old

A recipe written in English (your code) gets turned into a secret number-code that the robot chef (CPU) understands. Once it's numbers, you can't read it anymore. Disassembly is a decoder ring: it turns those numbers back into short English-ish words like "add", "jump", "copy" so you can check the robot is cooking what you wanted — not pizza when you asked for cake. objdump is that decoder ring.


Connections

  • Compilation Pipeline — disassembly is the reverse of the assemble step.
  • Calling Conventions (System V x86-64) — explains rdi, rsi, rax usage.
  • Stack Frames & The Stack Pointer — prologue/epilogue push rbp/sub rsp.
  • Compiler Optimization Levels-O0 vs -O2 and what the asm reveals.
  • Debugging with GDBdisassemble and layout asm use the same decoding.
  • ELF Object Files & Sections.text, .data, relocations that -r shows.
  • CISC vs RISC — why x86 encodings are variable length.

Concept Map

compile and assemble

inverts

produces

performs

controlled by

prints

has

has

has

explains

used for

maps fault to

.c source for humans

machine code bytes

assembly mnemonics

Disassembly

objdump -d

Intel vs AT&T syntax

disasm line four columns

address

raw bytes variable length

x86 CISC encoding

debug crashes and optimizers

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Dekho, jab tum .c file likhte ho, woh sirf humans ke liye hai. CPU ko sirf bytes (machine code) samajh aata hai. Compiler tumhare code ko in bytes me badal deta hai. Ab problem ye hai ki bytes ko padhna impossible hai. Yahan disassembly kaam aata hai — ye ulta kaam karta hai, bytes ko wapas mov, add, jump jaise readable words me convert kar deta hai. Aur jo tool ye karta hai usko bolte hai objdump. Command simple hai: objdump -d -M intel ./a.out. -d matlab disassemble, aur -M intel matlab Intel syntax (jisme mov dst, src matlab dst = src, bilkul maths jaisa).

Har line ke 4 parts hote hai — yaad rakho A-B-M-O: Address, Bytes, Mnemonic, Operands. Jaise 1149: 48 83 ec 08 sub rsp, 0x8. Yahan 1149 address hai, 48 83 ec 08 actual bytes hai, sub operation hai, aur rsp, 0x8 operands. x86 me instructions ki length fixed nahi hoti (CISC hai), isliye kabhi 1 byte kabhi 15 byte — ye ARM se alag hai.

Isko padhna kyun important hai? Kyunki jab tumhara program crash hota hai, tumhe ek address milta hai, line number nahi. Disassembly us gap ko bridge karti hai. Aur sabse mazedaar baat — jab -O2 optimization on karte ho, compiler tumhare a+b ko poora stack frame banaye bina sirf ek lea eax, [rdi+rsi] me kar deta hai. Ye dekh ke tumhe pata chalta hai compiler ne actually kya kiya. Galti se mat sochna ki "zyada instructions = slow" — ek div 30 cycle le sakta hai, dus mov sirf 3. Count nahi, kya chal raha hai woh dekho, phir profile karo.

Go deeper — visual, from zero

Test yourself — Build Systems & Toolchain

Connections