Skip to content

Instructions

x86-64

CALL / RET

CALL pushes the return address and transfers control; RET pops it and jumps back — the backbone of the x86-64 call stack.

CALL and RET implement the x86 subroutine mechanism. Together they maintain the return-address chain that makes nested function calls possible.

Operation

asm
call target    ; PUSH RIP (next instruction) ; JMP target
ret            ; POP RIP  (jump to return address)
ret 0x10       ; POP RIP ; RSP += 0x10  (stdcall stack cleanup)

CALL is effectively push rip; jmp target. The pushed value is the address of the instruction immediately after the call.

RET pops the top of the stack into RIP, resuming execution at the return address. A ret N (far or near with immediate) additionally releases N bytes of stack — used by the Microsoft __stdcall convention.

Call forms

FormExampleNotes
Direct nearcall 0x401234Relative 32-bit displacement
Indirect via registercall raxVirtual dispatch, callbacks
Indirect via memorycall [rax]vtable lookup: call [rax+0x18]

Reverse-engineering notes

  • vtable calls appear as call [rax] or call [rax + offset]; the pointer in memory is the method address. IDA/Ghidra usually resolve these if the type is known.
  • call $+5; pop rax is a classic PIC/shellcode trick to get the current RIP value into a register (the call pushes the next instruction's address).
  • A ret that jumps to an unexpected address is the heart of ROP (Return-Oriented Programming) exploitation.
  • When a tail call optimisation is applied, the compiler replaces call f; ret with just jmp f — so the absence of ret at the end of a function does not always mean a non-returning function.