Worked examples — Disassembly — objdump, reading assembly
5.3.17 · D3· Coding › Build Systems & Toolchain › Disassembly — objdump, reading assembly
Prerequisites jinpe hum depend karte hain: Calling Conventions (System V x86-64), Stack Frames & The Stack Pointer, Compiler Optimization Levels, aur parent Disassembly.
Scenario matrix
Is topic ke har possible case, aur unhe cover karne wala example:
| Cell | Scenario class | Covered by |
|---|---|---|
| C1 | Signed comparison, positive branch (>) |
Example 1 |
| C2 | Signed comparison, negative operand / opposite sign (<) |
Example 2 |
| C3 | Unsigned comparison — sign ka trap | Example 3 |
| C4 | Zero / degenerate input — empty function | Example 4 |
| C5 | Loop (neeche zero ki taraf count karna, back-edge jump) | Example 5 |
| C6 | Byte-level decode by hand (variable length) | Example 6 |
| C7 | Limiting case — optimizer sab kuch delete kar deta hai (-O2) |
Example 7 |
| C8 | Real-world word problem — crash address → source line | Example 8 |
| C9 | Exam twist — same mnemonic, do meanings (lea vs mov) |
Example 9 |
| C10 | Tail call — jmp jahan call expect karte the |
Example 10 |
Upar se neeche padho; har cell pichle wale pe build karta hai.
C1 — Signed comparison, positive branch
Steps
test edi, edicompute karta haiedi & edi. Yeh step kyun? Ek number ko khud se AND karne se kuch change nahi hota lekin flags zaroor set hote hain —cmp edi, 0se sasta hai. Sign flag (SF) end meinake top bit ke barabar hota hai, zero flag (ZF) = 1 agara == 0.jle 0xbka matlab hai "jump if less-than-or-equal (signed)". Yeh step kyun? Source kehta haireturn 1tab karo jab a > 0; assembly ise invert karkereturn 0block ki taraf jump karti hai jaba <= 0ho. Inversion normal pattern hai (parent kaifexample).a = 5ke liye:5 <= 0false hai → jump nahi →mov eax, 1. Result 1.a = 0ke liye:0 <= 0true hai →0xbpar jump →mov eax, 0. Result 0.a = -3ke liye:-3 <= 0true → jump → 0.
Recall
jle kyun aur jbe kyun nahi?
jle signed version hai. jbe ("below or equal") unsigned version hai.
C type int (signed) tha, isliye compiler ko signed jump chunnna padta hai. Yahi choice
C3 ka pura subject hai.
Verify: C function return karta hai a > 0 ? 1 : 0. {5, 0, -3} ke liye yeh hoga {1, 0, 0} —
steps 3-5 se match karta hai. ✔
C2 — Negative operand, < branch
Steps
0xfffffffcdecode karo. Yeh step kyun? 32-bit two's complement mein, top bit sign hai.0xfffffffc = 4294967292unsigned mein, lekin signedintmein yeh hai . Compiler ne-4is tarah likha.cmp [rbp-0x4], -4compute karta haix - (-4) = x + 4aur flags set karta hai. Yeh step kyun?cmpkoi result store nahi karta; sirf flags ko agle jump ke liye set karta hai (parent ka rule).jge 0xf= "jump if greater-or-equal (signed)". Source chahta haiy=1jabx < -4; assembly else (y=2) ki taraf jump karta hai jabx >= -4ho.x = -10ke liye: kya-10 >= -4? Nahi. Toh hum jump nahi karte → fall through karkey = 1.
Verify: -10 < -4 true hai, toh y = 1 — step 4 se match karta hai. Aur immediate: -4 ko unsigned mein store karo toh . ✔
C3 — Unsigned trap
Steps
- Same bit pattern, do readings. Yeh step kyun?
0xffffffffsignedintmein-1hai, lekinunsignedmein4294967295. Bits bilkul same hain; sirf compiler ka chuna hua instruction decide karta hai kaise compare hoga. setgsigned semantics use karta hai. Tohfpoochta hai: kya-1 > 3? Nahi →al = 0.setaunsigned semantics use karta hai. Tohgpoochta hai: kya4294967295 > 3? Haan →al = 1.- Asm padhne ke liye yeh kyun zaroori hai? Mnemonic tumhe C type bata deta hai.
g/a-family = unsigned,l/ge-family = signed. Tum signedness recover kar sakte ho jo raw bytes se chhupi rehti hai.
Verify: signed -1 > 3 = 0, unsigned 4294967295 > 3 = 1. ✔
C4 — Degenerate input: empty function
Steps
- Kuch nahi karte huye bhi,
-O0phir bhi prologue (push rbp; mov rbp, rsp) aur epilogue (pop rbp; ret) emit karta hai. Yeh step kyun?-O0mechanical hai: yeh frame set up karta hai locals ke liye jo shayad chahiye hon, phir tod deta hai. Dekho Stack Frames & The Stack Pointer. - Akela
90eknop(no-operation) hai. Yeh step kyun? Yeh (empty) function body ko mark karta hai aur alignment mein help kar sakta hai. Koi state nahi badlata. - Registers par net effect:
rbppush hua phir pop hua → unchanged;rspwapas jahan tha.
Verify: bytes gino: 55 (1) + 48 89 e5 (3) + 90 (1) + 5d (1) + c3 (1) = 7
bytes, aur last address 6 hai 1-byte ret ke liye, toh function 0x0..0x6
inclusive span karta hai = 7 bytes. ✔ Register net change = zero. ✔
C5 — Ek loop jo neeche count karta hai (back-edge jump)

Steps
- Initialize
eax=0(sum),edx=3(i). Yeh step kyun? Compiler dono ko tight loop ke liye registers mein rakhta hai — koi stack spill ki zaroorat nahi. add eax, edxphirsub edx, 0x1. Yeh reordered order kyun? Compiler ne pehle body run ki, decrement baad mein ki, taaki loop ek singlejnepar end ho josubke flags check kare — ek branch per iteration.jne 0xa= "jump if not equal" —subka zero flag padhta hai. Jab taki0 tak nahi pahuncha, loop karte hain. Yeh step kyun? Zero tak neeche count karna sasta hai: CPUsubpar ZF free mein set karta hai, toh alagcmpki zaroorat nahi.- Loop kaisa dikhta hai (inline describe kiya, agar figure load na ho): chaar
instructions
add → sub → jneek seedhi vertical column mein hain, lekinjneaddress0xfpar address0xako target karta hai — ek address jo khud se pehle ka hai. Yeh upward jump back-edge hai (figure mein right side ki thick red curved arrow ke roop mein drawn): yeh control ko body ke bottom seadd eax, edxke top par wapas loop karta hai. Jabifinally 0 par pahunchta hai toh zero flag set hota hai,jnetaken nahi hota, aur control seedharetpar fall through karta hai — loop se bahar nikal jaata hai. - Trace:
sum=0,i=3→ add→sum=3, sub→i=2, back-edge taken. → add→sum=5,i=1, taken. → add→sum=6,i=0, not taken → fall through →ret.
Verify: . Final eax = 6. ✔
C6 — Raw bytes haath se decode karo (variable length)
Steps
- Pehla byte
48ek REX prefix hai. Yeh step kyun? Range0x40–0x4fmein,48matlab "yeh operation 64-bit operands use karta hai" (parent ne note kiya tha48= 64-bit). Yeh ek prefix hai, standalone op nahi — real opcode aage aata hai. - Agla byte
83= opcode "arithmetic with an 8-bit immediate" ek 32/64-bit register par. Yeh step kyun?0x83compact form hai jo tab use hota hai jab constant ek signed byte mein fit ho. c0ModR/M byte hai jo operation select karta hai (/0field = ADD) aur register (rax).05immediate =5hai.- Meaning assemble karo:
add rax, 0x5, total length 4 bytes. Yeh kyun matter karta hai: tum x86 ko RISC ki tarah fixed 4-byte words mein nahi kaad sakte — dekho CISC vs RISC. Tumhe chalana hoga prefix → opcode → ModR/M → immediate.
Verify: instruction length yahan hai prefix(1) + opcode(1) + modrm(1) + imm8(1) =
4 bytes, aur 0x05 decimal 5 decode hota hai. ✔
C7 — Limiting case: optimizer sab kuch delete kar deta hai
Steps
- Sab kuch constant hai. Yeh step kyun? Optimizer constant folding karta hai: yeh
2*3+4 = 10, phir10*10 = 100compile time par evaluate karta hai. Dekho Compiler Optimization Levels. 0x64wahi folded constant hai. Yeh step kyun? . Pura function collapse ho jaata hai "100 load karo, return karo" mein.- Yeh optimization ka limiting behaviour hai: jab ek result runtime par kisi cheez par depend nahi karta, runtime work bilkul gayab ho jaata hai.
Verify: , , aur . ✔
C8 — Real-world: crash address → source line
Steps
- Bug division by zero hai. Yeh step kyun?
esi = 0ke saathidiv esiek#DE(divide error) CPU exception trigger karta hai — ek hardware trap, memory segfault nahi, lekin OS ise tumhare process ko surface karta hai. idivse pehlecdq. Yeh step kyun?idivedx:eaxmein 64-bit value ko divide karta hai.cdqeaxka sign bitedxke pura across copy karta hai taaki signed divide sahi ho.- Faulting instruction
0x401133(idiv) par hai. Crash line0x40113akyun print hua? Bilkul:#DEfault par CPU faultingripkhud (0x401133) kernel stack par push karta hai — saved instruction pointeridivpar point karta hai, usse aage nahi. Lekin tumhare program ke apne signal/crash reporter ne woh rawripprint nahi kiya; usne symbol boundary print ki jo usne address resolve ki.0x40113ayahan agle symbol ki start hai — functions commonly 16-byte boundary par pad hote hain, toh linker nedivide(ending at0x401135) aur agle function ke beech0x40113apar gap bytes chhode. Ek naive reporter jo "main kaunse function ke sabse near hoon" upward round karta hai, ya stack se stale return address padhta hai, us padded boundary par land karta hai. - Reliable method: rounded address par kabhi trust mat karo. Sabse bada instruction
address dhundho jo ≤ reported address ho aur kisi real function ke andar ho.
0x401133(idiv) last real instruction hai ≤0x40113adivideke andar, toh wahi culprit hai. LiveripDebugging with GDB mein confirm karo rather than printed line par trust karo.
Verify: idiv 0x401133 par hai; agle symbol tak padding gap
bytes hai (ret ke baad), aur undefined hai → divide error. ✔
C9 — Exam twist: same brackets, do meanings
Steps
[rdi+rsi]ek address expression hai:100 + 4 = 104. Yeh step kyun? Intel syntax mein square brackets matlab "yeh address compute karo".mov eax, [rdi+rsi](A) dereference karta hai: address104par jao, wahan stored value load karo. Yeh step kyun?movbracket source ke saath memory padhta hai. Toheax = 77.lea eax, [rdi+rsi](B) memory touch nahi karta — yeh address itself store karta hai. Yeh step kyun?lea= "load effective address"; yeh tumhe104ek number ke roop mein deta hai. Optimizer ise free adder ke roop mein abuse karta hai (parent ka-O2 addexample). Toheax = 104.- Trap: identical brackets, opposite behaviour — ek RAM padhta hai, ek number compute karta hai.
Verify: (A) memory[104] = 77 load karta hai. (B) 100 + 4 = 104 compute karta hai. ✔
C10 — Tail call: ek jmp jahan call expect karte the
Steps
add edi, 0x1argument prepare karta hai. Yeh step kyun?ediarg 1 hold karta hai (System V convention).helper(a+1)koedimeina+1chahiye, toh compiler ise in-place increment karta hai.- Last instruction
jmp helperhai,call helpernahi. Yeh step kyun? Yeh ek tail call hai:wrapperka ek hi kaam bacha — jo bhihelperreturn kare use return karna.call helper(jo ek return address push karta) phirretkarne ki bajaye, compilerwrapperka apna return address reuse karta hai — woh seedhahelpermeinjmpkarta hai. Jabhelperapnaretrun karta hai, woh seedha wrapper ke caller ko return karta hai,wrapperko skip karke. - Yahan
retkyun nahi?helperreturn hone ke baad kuch karna bacha hi nahi, tohwrapperka koi epilogue hi nahi. Stack frame count flat rehta hai — yahi essence hai tail-call optimization ki. - Reading rule: ek function ke end mein kisi aur function ke label par
jmpna loop hai na bug — yeh tail call hai. Return value untouched flow karta hai.
Verify: wrapper(a) return karta hai helper(a+1). a = 7 ke liye, edi 8 ban jaata hai, aur
result jo bhi helper(8) yield kare — wrapper kuch extra nahi add karta. Toh observable
result helper(8) ke barabar hai, yaani +1 hi ek maatra transformation hai. ✔
Question — setg on 0xffffffff (as signed int > 3)?
-1 > 3 false hai.Question — seta on 0xffffffff (as unsigned > 3)?
4294967295 > 3 true hai.Question — down-counting loop 3+2+1 ka final eax?
6.Question — -O2 constant-folded compute mein 0x64 decode karo?
Question — mov/lea mein se [rdi+rsi] par kaun RAM padhta hai?
mov RAM padhta hai; lea sirf address number compute karta hai.Question — ek function mein ret ke bina end mein jmp helper?
helper mein jump karta hai, jo seedha original caller ko return karta hai.Connections
- 5.3.17 Disassembly — objdump, reading assembly (Hinglish) — parent topic, saath mein padho.
- Calling Conventions (System V x86-64) — kyun args
edi/esimein aur resultseaxmein hote hain. - Stack Frames & The Stack Pointer — C4 mein prologue/epilogue aur C10 mein tail call.
- Compiler Optimization Levels — C7 mein constant folding, C9 mein
leareuse, C10 mein tail calls. - Debugging with GDB — C8 crash mein live
ripconfirm karo. - CISC vs RISC — kyun C6 ke bytes variable length hain.