Visual walkthrough — Disassembly — objdump, reading assembly
5.3.17 · D2· Coding › Build Systems & Toolchain › Disassembly — objdump, reading assembly
Hum poore walkthrough mein sirf ek hi function use karenge:
int add(int a, int b) {
return a + b;
}Neeche sab kuch zero se build kiya gaya hai — har box, arrow, aur word ko use karne se pehle define kiya gaya hai.
Step 1 — Do languages, aur unke beech ka bridge
KYA HAI. Ek .c file woh text hai jo ek insaan type karta hai. CPU text nahi padh sakta; woh machine code
padhta hai — numbers ki ek stream. Beech mein hota hai assembler: ek fixed rulebook jo chhote
word-jaisi names (mnemonics) ko un numbers mein convert karta hai. Disassembly us rulebook ko ulta chalti hai.
KYUN. Pehle hum ek bhi assembly line padh sakein, humein yeh agree karna hoga ki ek line hoti kya hai: yeh
ek three-way translation mein ek row hai. Agar hum yeh skip kar dein, toh baad ke har symbol (rbp, edi, 0x8)
bina kisi ghar ke float karta rahega.
PICTURE. Step-1 figure dekho. Orange arrow (left→right) woh hai jo compiler + assembler karta hai. Teal arrow (right→left) woh hai jo objdump karta hai.
Middle column — numbers 55 48 89 e5 ... — woh ek aur sirf ek cheez hai jo CPU kabhi dekhta hai. Assembly
(right column) ek human costume hai jo exactly un bytes ke upar pehna gaya hai.

Step 2 — CPU ke chhote boxes: registers
KYA HAI. Ek register ek chhota named box hai jo CPU ke andar hota hai aur ek number hold karta hai. Yeh
RAM mein nahi hota; yeh wahan hota hai jahan arithmetic hoti hai, isliye yeh sabse fast storage hai. x86-64 par
jo boxes hume yahan care karne chahiye woh hain rax, rbp, rsp, rdi, rsi, rdx.
KYUN. Humari function ko a + b add karna hai. Addition registers ke beech hoti hai. Isliye values add ho sakein,
unhein pehle registers mein arrive karna hoga. Hamari assembly ki har line actually
numbers ko in aur between in boxes mein move karne ke baare mein hai. Tum derivation nahi padh sakte bina
boxes ki mental picture ke — toh yeh rahi, draw ki hue.
PICTURE. Step-2 figure mein chhe boxes hain. Nesting note karo: eax, 64-bit rax ke lower 32 bits hai.
Names r.. (64-bit) aur e.. (32-bit) ek hi box hain jo do widths par dekhe gaye hain. Kyunki hamara int
32 bits ka hai, code e-named views use karega.

Step 3 — Contract: a aur b kahan arrive karte hain?
KYA HAI. Jab koi add(3, 4) call karta hai, toh 3 kahan jaata hai, aur 4 kahan jaata hai?
Yeh koi choice nahi hai jo compiler freely karta hai — yeh ek calling convention follow karta hai, ek fixed
agreement taaki caller aur callee ek doosre ko samjhein.
KYUN. Bina kisi agreed rule ke, caller arguments rdi mein chhod sakta hai jabki function
unhein rsi mein dhundhe — chaos. Rule (System V x86-64, Linux/macOS par use hota hai) kehta hai:
integer arg 1 → rdi, arg 2 → rsi, aur return value rax mein wapas aati hai. Hume yeh zaroor
jaanna chahiye pehle ki disassembly ki line 4 samajh mein aaye.
PICTURE. Step-3 figure: do incoming values edi aur esi (arg slots) mein slide karti hain, aur ek
dashed teal arrow dikhata hai ki result baad mein eax se nikalta hai. Yeh shape yaad rakho — yeh wahi hai
har function ke liye jo tum kabhi bhi disassemble karoge.

Step 4 — Prologue: ek frame banana
KYA HAI. Pehle do instructions -O0 par hamesha same hote hain:
0: 55 push rbp
1: 48 89 e5 mov rbp, rspKYUN. Ek function ko apne local variables dhundhne ke liye ek stable "home base" address chahiye, chahe
stack grow aur shrink hoti rahe. rbp (base pointer) woh home base hai. Lekin rbp abhi
caller ka home base hold karta hai, isliye hum (1) use stack par push karke save karte hain, phir (2)
current stack top (rsp) ko rbp mein copy karke ek fresh base banate hain. Yeh pair function
prologue hai.
PICTURE. Step-4 figure: stack ko boxes ke downward stack ke roop mein draw kiya gaya hai (addresses upar jaane par chhoti hoti hain — yeh x86 convention hai). push rbp old base likhta hai aur rsp ko 8 bytes neeche drop karta hai; phir
rbp naye top par plant kiya jaata hai. Term-by-term:

Step 5 — Arguments ko stack par spill karna
KYA HAI.
4: 89 7d fc mov [rbp-0x4], edi ; store a
7: 89 75 f8 mov [rbp-0x8], esi ; store bKYUN. -O0 ("no optimization") par compiler jaan-boojhkar literal hota hai: woh har
variable ko memory mein ek real ghar deta hai taaki ek debugger use hamesha dhundh sake. Isliye woh a (jo edi mein hai) aur
b (jo esi mein hai) ko registers se stack slots mein copy karta hai. Registers se stack par is copying ko
spill kehte hain.
PICTURE. Step-5 figure: rbp offset 0 par anchor hai; [rbp-0x4] iske 4 bytes neeche hai
(ek int), [rbp-0x8] 8 bytes neeche hai (agla int). Square brackets ka matlab hai "is address par memory",
address khud nahi. Operand padhna:

Step 6 — Wapas load karo aur add karo
KYA HAI.
a: 8b 55 fc mov edx, [rbp-0x4] ; edx = a
d: 8b 45 f8 mov eax, [rbp-0x8] ; eax = b
10: 01 d0 add eax, edx ; eax = eax + edx = b + aKYUN. Arithmetic register-to-register kaam karti hai, isliye jo values abhi spill ki hain unhein wapas load karna hoga
registers mein. Compiler a ko edx mein aur b ko eax mein pull karta hai, phir unhein add karta hai. Usne
b ke liye eax deliberately choose kiya: sum eax mein land karta hai, jo — Step 3 se —
exactly wahan hai jahan return value honi chahiye. Isliye baad mein koi extra move ki zaroorat nahi.
PICTURE. Step-6 figure: do loads (memory → register, teal arrows) phir ek add jo
edx ko eax mein fold karta hai (orange). Add par term-by-term:

Step 7 — Epilogue: tear down aur return
KYA HAI.
12: 5d pop rbp ; caller ka base restore karo
13: c3 ret ; caller ke paas wapas jump karoKYUN. Humne rbp borrow kiya tha; caller ka unhe wapas karna hoga. pop rbp woh value padhta hai jo
humne Step 4 mein save ki thi aur rsp ko wapas upar bump karta hai — prologue ko exactly undo karta hai. Phir ret
return address (original call dwara stack par rakha gaya) ko pop karta hai aur us par jump karta hai. Answer
eax mein pehle se baitha hua hai, Step 3 ke contract ko fulfill karta hua.
PICTURE. Step-7 figure: stack un-wind hoti hai — rsp wapas climb karta hai jahan se shuru hua tha, rbp
caller ke base par wapas aata hai, aur control teal arrow ke saath wapas udta hai eax result carry karte hue.

Step 8 — Degenerate/optimized case: sab collapse ho jaata hai
KYA HAI. -O2 ke saath recompile karo aur poori eight-instruction story sirf do mein simat jaati hai:
0: 8d 04 37 lea eax, [rdi+rsi]
3: c3 retKYUN. Optimization on hone par, compiler prove karta hai ki local variables aur stack frame ki kabhi zaroorat nahi: kuch debug nahi hota, kuch call ke baad nahi bachta. Isliye woh prologue, spills, reloads, aur epilogue delete kar deta hai. Phir woh lea use karta hai — ek instruction jo rdi + rsi jaisa address compute karne ke liye bani hai — ek sneaky one-shot adder ki tarah jo seedha eax mein likhta hai. Yeh woh edge case hai jise tumhe recognize karna hoga: wahi C, radically different bytes.
PICTURE. Step-8 figure: baai taraf lamba -O0 tower, ek arrow labelled "optimizer", aur
daai taraf chhota sa do-line -O2 result — jo sab remove hua uske upar dotted crossings-out ke saath.

Ek-picture summary
Upar sab kuch, ek canvas par: C bilkul baai taraf, do register arg-slots feed karte hue, -O0 ka
stack ke through shuffle, value eax mein land karti hui, aur -O2 shortcut ek
dotted express lane ki tarah draw kiya gaya jo poora detour skip karta hai.

Recall Feynman retelling — poora walkthrough plain words mein
Do numbers edi aur esi naam ke do labelled cubbies mein front door par arrive karte hain.
Function pehle ek chhota signpost (rbp) zameen mein thokta hai taaki woh apna saman dhundh sake,
phir — kyunki humne compiler ko kaha clever mat bano (-O0) — woh carefully dono numbers ko
signpost ke neeche do floor tiles mein copy karta hai. Thodi der baad woh unhe wapas uthata hai, ek
edx mein aur ek eax mein, aur unhein add karta hai. Usne deliberately eax answer ke liye choose kiya kyunki
eax woh "outbox" hai jise sab padhte hain jab function done hota hai. Aakhir mein woh signpost utha leta hai,
zameen ko waise chhod deta hai jaisi thi, aur answer eax mein carry karte hue ghar wapas jump karta hai.
Jab hum clever bano (-O2) ka switch flip karte hain, compiler realize karta hai ki woh saari copying
pointless thi, use throw away karta hai, aur ek single address-math trick (lea) se dono cubbies ko seedha outbox mein add karta hai. Same answer, ek-chauthai instructions.
Connections
- Disassembly — objdump, reading assembly (index 5.3.17) — parent note; yeh page iska central example zero se derive karta hai.
- Calling Conventions (System V x86-64) — Step 3 ka
edi/esi/eaxcontract. - Stack Frames & The Stack Pointer — Steps 4–7 (prologue, spills, epilogue).
- Compiler Optimization Levels — Step 8 ka
-O0vs-O2collapse. - Compilation Pipeline — woh forward arrow jise disassembly reverse karta hai.
- Debugging with GDB — kyun
-O0sab kuch stack par rakhta hai. - ELF Object Files & Sections — woh
.textsection jahan yeh bytes rehte hain. - CISC vs RISC — kyun x86 bytes variable length hote hain aur
leamath kar sakta hai.