Exercises — Stack frames — how function calls work at the memory level
5.1.16 · D4· Coding › C Programming › Stack frames — how function calls work at the memory level
Yeh page ek self-test ladder hai. Har rung pichle se mushkil hai: sirf frame ke parts ko pehchanne se lekar apna layout banane aur security ke baare mein sochne tak. Har problem ka ek complete solution ek collapsible [!recall]- callout ke andar chhupa hua hai — pehle khud try karo, phir reveal karo.
Agar koi bhi word yahan unfamiliar lage, toh pehle Stack frames — how function calls work at the memory level par wapas jao; neeche sab kuch usi note ki foundation pe assume karta hai. Related ideas: Recursion, Pointers and addresses in C, Heap vs Stack memory, Calling conventions (cdecl, stdcall), Buffer overflow & return-address smashing, Assembly: push, pop, call, ret, BL.
Level 1 — Recognition
Goal: kya tum parts ko naam de sakte ho aur stack ki picture padh sakte ho?
L1·Q1
Kaun sa register hamesha call stack ke top (nayi end) ki taraf point karta hai: SP ya BP?
Recall Solution
SP (the stack pointer).
- WHAT: SP stack ke top ka address hold karta hai — use mein aane wala nayi byte.
- WHY not BP: BP (base/frame pointer) current frame ke andar ek fixed reference point hai; yeh function ke chalte hue move nahi karta, isliye locals ko BP se constant offsets par dhundha ja sakta hai. SP har push/pop par move karta hai.
- Picture: upar diye figure mein, SP woh arrow hai jo bilkul neeche hai (sabse chhota address, kyunki stack neeche ki taraf grow karta hai).
L1·Q2
Downward-growing stack par, "top of stack" currently use ho raha highest address hai ya lowest address?
Recall Solution
Sabse chhota (lowest) address.
- Har
pushSP ko decrease karta hai, toh nayi data sabse chhote address par rehti hai. - "Top" word recency ke baare mein hai, page par height ke baare mein nahi. Nayi frame = sabse chhota address = "top".
L1·Q3
Ek typical stack frame mein jo chaar cheezein hoti hain, unhe naam do.
Recall Solution
- Us call ki Local variables.
- Saved BP (caller ka frame reference, taaki usse restore kiya ja sake).
- Return address (wapas kahan jump karna hai) — x86 ke liye stack par; ARM/RISC-V ke liye pehle link register mein.
- Iske arguments (aksar — calling convention par depend karta hai).
Level 2 — Application
Goal: concrete code par prologue/epilogue steps apply karo.
L2·Q1
int add(int a, int b) { int s = a + b; return s; } ke liye, callee ke prologue ke operations entry par (conceptual x86-style) order mein list karo.
Recall Solution
push old BP— WHY: BP shared hardware hai; caller ka save karo taaki caller ka frame na khoye.BP = SP— WHY: is frame ki base mark karo; ab locals ko BP se constant offsets milenge.SP -= N— WHY: locals ke liyeNbytes carved karo (yahan,ske liye jagah).
Epilogue ise reverse mein undo karta hai: SP = BP (locals drop karo) → pop old BP (caller restore karo) → RET (wapas jump karo).
L2·Q2
main calls add(2, 3). CALL add execute hone ke theek baad stack ka content do (top last likha), x86-style. Assume karo main ke paas pehle se local x hai.
Recall Solution
[ x ][ 3 ][ 2 ][ retaddr ]
x— main ka local, sabse purana.3,2— caller ne push kiye arguments (cdeclright-to-left push karta hai, toh pehle3phir2).retaddr— x86 parCALLinstruction khud push karta hai, taakiaddko pata ho wapas kahan jaana hai.addka prologue abhi nahi chala, toh is snapshot mein stack par koi saved BP / koisnahi hai.
L2·Q3
addr(local_k) = BP - offset_k use karte hue, ek local BP - 8 par hai aur BP mein address 0x7ffe_ff40 hai. Local kis address par hai? Kya yeh memory mein BP se upar hai ya neeche?
Recall Solution
0x7ffeff40 - 8 = 0x7ffeff38.- Yeh BP se chhote address par hai → memory mein BP se neeche. Locals BP set hone ke baad push hote hain, aur pushing neeche ki taraf move karta hai, isliye woh BP ke neeche rehte hain → hum subtract karte hain.
Level 3 — Analysis
Goal: kitne frames alive hain, aur kya tootta hai, iske baare mein sochna.
L3·Q1
int fact(int n) (recursive factorial) ke liye, jab fact(4) call karo toh sabse gehre point par kitne frames ek saath alive hain?
Recall Solution
4 frames.
fact(4)callsfact(3)callsfact(2)callsfact(1). Jis waqtfact(1)chal raha hai, koi bhi return nahi hua hai.- Toh
n=4, 3, 2, 1char alag desks occupy karte hain.fact(1)(sabse gehra) pehle return karta hai — strict LIFO — phir 2, phir 3, phir 4. - Dekho Recursion — kyun har
nek independent memory slot hai.
L3·Q2
int *bad(void){ int local = 42; return &local; }. bad return karne ke baad, kya returned pointer valid hai? SP ke terms mein explain karo.
Recall Solution
Nahi — yeh ek dangling pointer hai (undefined behaviour).
localbadke frame mein rehta hai. Return par, epilogue SP ko wapas upar le jaata hai, logically us frame ko destroy kar deta hai.- Pointer abhi bhi purana address hold karta hai, lekin woh memory ab
badki nahi — agla call use overwrite kar dega. - Return ke bilkul baad luck se abhi bhi
42read ho sakta hai, jo exactly is bug ko sneaky banata hai. - Fix:
return local;(by value) ya heap parmallockaro agar yeh call se zyada time tak rehna chahiye.
L3·Q3
Ek function mein koi locals nahi hain aur aage koi call nahi karta. ARM par, kya iska return address kabhi stack ko touch karta hai?
Recall Solution
Nahi.
- ARM par,
BLreturn address link register (LR) mein store karta hai, stack mein nahi. - LR tabhi stack par spill hota hai jab function doosre function ko call kare (jo LR overwrite kar dega). Ek leaf function jo calls nahi karta, apna return address purely LR mein rakhta hai aur return karne ke liye bas
BX LRkarta hai. Dekho Assembly: push, pop, call, ret, BL.
Level 4 — Synthesis
Goal: khud scratch se ek layout / trace build karo.
L4·Q1
int f(int a, int b, int c) ke liye frame design karo jismein do locals int p, q hain. 32-bit x86 layout par (har ek 4 bytes; saved BP [BP] par, return address [BP+4] par), har argument aur local ka BP se relative offset do. Assume karo args right-to-left push hue.
Recall Solution
| Item | Offset from BP | Reason |
|---|---|---|
q |
BP - 8 |
doosra local, sabse gehra |
p |
BP - 4 |
pehla local |
| saved BP | BP + 0 |
prologue ne purana BP push kiya, phir yahan BP set kiya |
| return address | BP + 4 |
CALL ne push kiya, saved BP ke bilkul upar |
a |
BP + 8 |
pehla argument (aakhir mein push hua → sabse nazdik upar) |
b |
BP + 12 |
doosra argument |
c |
BP + 16 |
teesra argument (pehle push hua → sabse door) |
- BP ke neeche (subtract): do locals
p, q,BP = SPke baad carved. - BP ke upar (add): saved BP, return address, phir teen args.
- Right-to-left push matlab
cpehle gaya (highest address),aaakhir mein (return address ke sabse nazdik).
L4·Q2
fact(3) ko frame-by-frame trace karo. Har frame se return ki gayi value aur final answer dikhao.
Recall Solution
Build-up (push):
fact(3):n=3, base case nahi →3 * fact(2)chahiye, wait karta hai.fact(2):n=2, base case nahi →2 * fact(1)chahiye, wait karta hai.fact(1):n=1, base case → returns 1.
Unwind (pop, LIFO):
fact(2)resume karta hai:2 * 1 = 2→ returns 2.fact(3)resume karta hai:3 * 2 = 6→ returns 6.
Final answer: fact(3) = 6. Sabse gehre point par teen frames alive the.
Level 5 — Mastery
Goal: layout ko security aur cross-architecture behaviour se connect karo.
L5·Q1
Ek local char buf[8] BP - 8 par hai, saved BP BP + 0 par, return address BP + 4 par (32-bit). Attacker ko return address tak pahunchne aur use overwrite karne ke liye buf ke start se kitne bytes likhne padenge? Yeh kaun sa classic attack hai?
Recall Solution
bufBP - 8par start hota hai. Return addressBP + 4par start hota hai.- Distance =
(BP + 4) - (BP - 8) = 12bytes. - Toh 12 bytes likhne se return address ki start tak pahuncha jaata hai; 13th–16th bytes use overwrite karte hain.
- Yeh ek stack buffer overflow / return-address smashing hai — kyunki
bufindex mein upar ki taraf badhta hai higher addresses ki taraf (saved BP aur return address mein), 8 bytes se zyada likhna control data ko clobber karta hai. Dekho Buffer overflow & return-address smashing.
L5·Q2
ARM par, ek recursive fact obviously aur calls karta hai. Precisely explain karo ki fact(2) ka fact(1) ko call ka return address kahan rehta hai jab fact(1) chal raha hai, aur kyun LR akela insufficient hai.
Recall Solution
- Jab
fact(2)fact(1)ko call karne ke liyeBL factexecute karta hai,fact(2)mein wapas jaane ka return address LR mein rakha jaata hai. - Lekin
fact(1)khud bhi conceptually non-leaf hai / same LR share karta hai — jis instantfact(1)ka apna prologue (ya koi bhi nestedBL) chalta hai, LR overwrite ho jaata. - Isliye
fact(2)ka prologue call karne se pehle LR ko apne stack frame mein spill karta hai, aur epilogueBX LRse pehle LR reload karta hai. - LR akela kyun fail karta hai: LR ek single register hai — ek waqt mein exactly ek return address hold kar sakta hai. Recursion ko bahut saare ek saath chahiye, toh unhe stack par park karna padta hai. Yeh register-file wajah hai ki ARM par bhi recursion ko stack chahiye.
L5·Q3 (Synthesis)
Do threads ek hi waqt mein same recursive function run karte hain. Ek crisp sentence mein explain karo ki unke locals kabhi collide kyun nahi karte, stack se tied.
Recall Solution
Har thread ka apna alag call stack hota hai, isliye har call ka frame — aur therefore har local ki har copy — ek private address par rehti hai; identical code, independent desks.
- Ek local ka naam/offset compile time par fix hota hai, lekin storage har call har stack ke liye re-create hoti hai, isliye do live frames kabhi bytes share nahi karte.
Recall Final self-check (sabhi levels ke baad reveal karo)
Zor se jawab do: (1) Kaun si end "top" hai? sabse chhota address. (2) Locals: BP se add karein ya subtract? subtract. (3) Args: add karein ya subtract? add. (4) fact(4) ke liye kitne frames? chaar. (5) ARM pehle return address kahan rakhta hai? link register (LR) mein. Agar koi bhi shaky laga, woh level dobara karo.