SingleLocalVariable.c — interactive single-stepper

OST2 Arch1001 · adding a single local variable · func finally makes a stack frame
RIP = 00000001`40001020 — no instruction yet executed
☒executed instruction
♍modified value
⌘start value
RIP▶next instruction

C source

Disassembly

⤴ execution continues at 00000001`40001349 — the code that called main()

Registers

rax
rsp

Stack (each row = 8 bytes)

↑higher addresses
lower addresses (stack grows down)↓
rsp
this time func DOES make/use a stack frame — 0x18 bytes for its local i!

←/→ step · Space play/pause · F frames · Home/End jump

Pop Quiz — fill in the stack 📝

Straight from the course: if we step through line 0000000140001004 (the mov dword ptr [rsp], 5CA1AB1Eh in func()), what does the stack look like? This table shows every 8-byte slot — including the five rows the stepper above collapses into "…". Fill in each value, then check yourself. (That state is Step 4 up top. Also: the original slide lists 0014FED0 — a typo for 0014FDD0, fixed here.)
rsp points at:

Mystery Listery 🕵️

Why sub rsp, 18h — 24 bytes for a single 4-byte int?

Two ingredients: allocation granularity + alignment.

MSVC hands out local-variable storage in 16-byte chunks, so one 4-byte int rounds up to 0x10. Then 8 more bytes are added so that the frame keeps rsp 16-byte aligned: the call into func pushed an 8-byte return address (rsp ended in 8), and 8 + 0x18 = 0x20 — a multiple of 16. Hence 0x18.

Compare with main, which has no locals but calls func: its sub rsp, 28h is 32 bytes of shadow space + 8 alignment. func is a leaf (calls nobody), so it needs no shadow space — just local storage + alignment.

Why does i live in memory at all? Why not just mov eax, 5CA1AB1Eh?

This is an unoptimized build. At -O0 / MSVC debug, every local variable gets a real stack slot, and every read/write of it becomes a real memory access — that's what lets a debugger show and modify i at any moment.

An optimizing compiler would collapse func to mov eax, 5CA1AB1Eh; ret — or inline the whole thing and have main return the constant directly. No frame, no store, no load.

Why does the assembly say dword ptr?

mov [rsp], 5CA1AB1Eh alone would be ambiguous — should the assembler store 1, 2, 4, or 8 bytes at that address? The immediate doesn't carry a size, and neither does a bare memory operand. dword ptr pins it to 4 bytes, matching int.

And that's exactly why the upper half of the stack slot stays garbage: a dword store touches exactly 4 bytes. Zero-extension is a register-destination rule only.

What's 0x5CA1AB1E?

Hexspeak: 5CA1AB1E ≈ "SCALABLE". Same family as 0xBEEF and 0xF00D from CallASubroutine1, or the classics 0xDEADBEEF and 0xCAFEBABE. Course tradition: if a constant looks pronounceable, it probably is.