What does call and ret actually do internally?
call pushes the address of the next instruction onto the stack and jumps to the target; ret pops that address and jumps back to it.
* call ≡ push return-addr ; jmp target (%rsp −= 8). ret ≡ pop → %rip ; %rsp += 8. The value at (%rsp) on entry to a callee is its return address. *
call <function>
is equivalent to:
push %rip
jmp <function>
(Pushes the address of the instruction after call, then jumps)
ret
is equivalent to:
pop %rip
(Pops the return address from the stack into the program counter)
Key consequence: After call, %rsp is 8 bytes lower. The value at (%rsp) inside the called function is the return address.
Note: This is why functions that call other functions must keep the stack 16-byte aligned — call pushes 8 bytes, so the function prologue often does sub $8, %rsp or push %rbx to re-align.
Go deeper:
CALL instruction (felixcloutier) — Confirms call saves procedure-linking info (the return address) on the stack and branches to the target.
RET instruction (felixcloutier) — Confirms ret transfers control to the return address at the top of the stack.
Call stack (Wikipedia) — Big-picture view of how return addresses on the stack let nested calls resume correctly.