Calling Conventions
arm64ARM64 Calling Convention (AAPCS64)
How AArch64 passes arguments and return values: x0–x7 for the first eight integer args, x8 for indirect results, and the link register x30 for the return address.
AArch64 follows AAPCS64. With 31 general-purpose registers it passes far more
in registers than the stack-heavy x86 ABIs, so reading argument flow is usually
just a matter of reading x0–x7.
Integer / pointer arguments and return
| Register(s) | Role |
|---|---|
x0–x7 | First eight integer/pointer arguments |
x0 (x1) | Return value (x1 too for 128-bit returns) |
x8 | Indirect result location (address of a large struct return) |
| further args | passed on the stack |
Floating-point and SIMD arguments use v0–v7 the same way, with v0 holding
the FP return value. See ARM64 registers for the
w/x 32/64-bit aliasing.
Saved registers and the frame
- Callee-saved:
x19–x28(andv8–v15). A function must preserve these if it uses them — you'll see them spilled in the prologue. - Caller-saved (temporaries):
x9–x15. x29= frame pointer (FP),x30= link register (LR),sp= stack pointer (must stay 16-byte aligned).
The link register changes everything
There is no x86-style call/ret that pushes the return address to the stack.
Instead BL/BLR put the return address in x30 (LR), and RET jumps to
whatever x30 holds.
; typical leaf-ish prologue / epilogue
stp x29, x30, [sp, #-16]! ; save FP and LR, pre-decrement sp
mov x29, sp ; set up frame pointer
; ... body uses x0..x7 as incoming args ...
ldp x29, x30, [sp], #16 ; restore FP and LR, post-increment sp
ret ; return to address in x30 (LR)A leaf function (one that calls nothing) need not save x30 at all, since
nothing overwrites it — its absence in the prologue tells you the function makes
no calls.
Reverse-engineering notes
- Arguments are almost always in
x0–x7at function entry; map them positionally to reconstruct the signature.x0is both the first argument and the return value. stp x29, x30, [sp, #-N]!is the canonical non-leaf prologue — spotting it (and the matchingldp … ; ret) is the fastest way to find function boundaries in stripped AArch64 code.- A function that immediately uses
x8is returning a large struct by value: the caller passed the destination address inx8. - Because
x30is just a register, it can be saved, overwritten and restored — enabling tail calls (b targetinstead ofbl/ret) and making return-address corruption look different from x86's stack-based scheme.
See also: x64 Calling Conventions · x86 (32-bit) Calling Conventions · ARM64 registers · ARM64 common instructions.