5.1.16 · D4 · HinglishC Programming

ExercisesStack frames — how function calls work at the memory level

2,583 words12 min read↑ Read in English

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 push SP 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
  1. Us call ki Local variables.
  2. Saved BP (caller ka frame reference, taaki usse restore kiya ja sake).
  3. Return address (wapas kahan jump karna hai) — x86 ke liye stack par; ARM/RISC-V ke liye pehle link register mein.
  4. 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
  1. push old BPWHY: BP shared hardware hai; caller ka save karo taaki caller ka frame na khoye.
  2. BP = SPWHY: is frame ki base mark karo; ab locals ko BP se constant offsets milenge.
  3. SP -= NWHY: locals ke liye N bytes carved karo (yahan, s ke 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 (cdecl right-to-left push karta hai, toh pehle 3 phir 2).
  • retaddr — x86 par CALL instruction khud push karta hai, taaki add ko pata ho wapas kahan jaana hai.
  • add ka prologue abhi nahi chala, toh is snapshot mein stack par koi saved BP / koi s nahi 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) calls fact(3) calls fact(2) calls fact(1). Jis waqt fact(1) chal raha hai, koi bhi return nahi hua hai.
  • Toh n=4, 3, 2, 1 char 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 n ek 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).

  • local bad ke 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 bad ki nahi — agla call use overwrite kar dega.
  • Return ke bilkul baad luck se abhi bhi 42 read ho sakta hai, jo exactly is bug ko sneaky banata hai.
  • Fix: return local; (by value) ya heap par malloc karo 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, BL return 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 LR karta 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 = SP ke baad carved.
  • BP ke upar (add): saved BP, return address, phir teen args.
  • Right-to-left push matlab c pehle gaya (highest address), a aakhir 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
  • buf BP - 8 par start hota hai. Return address BP + 4 par start hota hai.
  • Distance = (BP + 4) - (BP - 8) = 12 bytes.
  • 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 buf index 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 liye BL fact execute 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 instant fact(1) ka apna prologue (ya koi bhi nested BL) 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 epilogue BX LR se 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.