CallASubroutine1.c — interactive single-stepper

OST2 Arch1001 · x86-64 · watch call / ret and the stack do their dance
RIP = 00000001`40001010 — no instruction yet executed
☒executed instruction
♍modified value
⌘start value
RIP▶next instruction

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
func never even makes/uses its own stack frame!

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

Simple Stack Diagram 2 — main() → foo() → bar()

the same call/ret machinery, one level up in C — watch frames stack and unwind
stdout
stack bottom (higher addresses)
stack top (grows down) ↓

Mystery Listery 🕵️

What's up with the sub/add rsp, 28h in main()?

0x28 = 40 bytes = 32 bytes of "shadow space" + 8 bytes for alignment.

The Windows x64 ABI requires every caller to reserve 32 bytes of shadow space (a.k.a. home space) directly above the return address, so the callee can spill its four register arguments (rcx, rdx, r8, r9) there if it wants to. main() is about to call func, so it must reserve that space even though func never uses it.

Why the extra 8? Alignment. The ABI says rsp must be 16-byte aligned at the point of a call. Entering main, the call to main pushed an 8-byte return address, so rsp is at 16n−8. Subtracting 0x28 (16n−8 − 40 = 16m) restores 16-byte alignment before the next call. The add rsp, 28h in the epilogue is simply the mirror image, tearing the frame back down.

Why do the GCC/Clang HelloWorlds have balanced Push/Pop but Visual Studio doesn't?

GCC/Clang at -O0 keep a classic frame pointer: push rbp; mov rbp,rsp in the prologue and pop rbp in the epilogue — a balanced pair that saves and restores the caller's rbp.

MSVC here doesn't use rbp as a frame pointer at all (it addresses locals relative to rsp), and this tiny main() doesn't need to preserve any non-volatile registers. Nothing to save ⇒ nothing to push/pop. The only stack adjustment left is the sub/add rsp, 28h pair — which is itself "balanced", just done with arithmetic instead of push/pop.