nasm-know-hows

nasm assembly related stuff

View on GitHub

πŸ“˜ Topic 9: Calling Conventions

Master the rules for passing parameters and calling functions across different platforms and ABIs.


Overview

A calling convention is a standardized protocol that defines:

Different conventions exist for different architectures, operating systems, and languages.


Part 1: Why Calling Conventions Matter

The Problem

; Without conventions, this is ambiguous:
call my_function

; Questions:
; - Where are the arguments?
; - Where does the result go?
; - Which registers can the function modify?
; - Who cleans the stack?

The Solution

Calling conventions provide standard answers so code from different sources can work together.

Benefits:


Part 2: System V AMD64 ABI (Linux/Unix x86-64)

This is the standard for Linux, macOS, BSD on 64-bit systems.

Parameter Passing

First 6 integer/pointer arguments go in registers:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Arg 1  β”‚  Arg 2   β”‚  Arg 3   β”‚  Arg 4   β”‚  Arg 5   β”‚  Arg 6   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚   RDI   β”‚   RSI    β”‚   RDX    β”‚   RCX    β”‚   R8     β”‚   R9     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Additional arguments (7th, 8th, …) go on the stack (right-to-left).

Floating-point arguments use XMM0-XMM7 (8 registers).

Return Values

Integer/Pointer: RAX
Floating-point:  XMM0
128-bit:         RDX:RAX (high:low)

Register Usage

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Register         β”‚ Usage                                 β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ RAX              β”‚ Return value, temp, syscalls          β”‚
β”‚ RBX, RBP         β”‚ Callee-saved (must preserve)          β”‚
β”‚ R12, R13, R14,   β”‚ Callee-saved (must preserve)          β”‚
β”‚ R15              β”‚                                       β”‚
β”‚ RDI, RSI, RDX,   β”‚ Argument passing, temp (caller-saved) β”‚
β”‚ RCX, R8, R9      β”‚                                       β”‚
β”‚ R10, R11         β”‚ Temp, caller-saved                    β”‚
β”‚ RSP              β”‚ Stack pointer (must preserve)         β”‚
β”‚ RIP              β”‚ Instruction pointer (hardware)        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Stack Alignment

RSP must be 16-byte aligned before call instruction.


Part 3: System V Examples

Example 1: Simple Function Call

C Equivalent:

int add(int a, int b) {
    return a + b;
}

int main() {
    int result = add(5, 10);
    return result;
}

Assembly:

section .text
    global main

; int add(int a, int b)
add:
    ; a is in EDI, b is in ESI (32-bit ints use lower 32 bits)
    lea eax, [rdi + rsi]        ; result = a + b
    ret                         ; return in EAX

main:
    ; Call add(5, 10)
    mov edi, 5                  ; arg1 = 5
    mov esi, 10                 ; arg2 = 10
    call add
    
    ; RAX now contains 15
    mov rdi, rax                ; exit code = result
    mov rax, 60                 ; sys_exit
    syscall

Example 2: Six Arguments

C Equivalent:

long sum6(long a, long b, long c, long d, long e, long f) {
    return a + b + c + d + e + f;
}

int main() {
    long result = sum6(1, 2, 3, 4, 5, 6);
    return result;
}

Assembly:

section .text
    global main

; long sum6(long a, long b, long c, long d, long e, long f)
sum6:
    ; a=RDI, b=RSI, c=RDX, d=RCX, e=R8, f=R9
    mov rax, rdi
    add rax, rsi
    add rax, rdx
    add rax, rcx
    add rax, r8
    add rax, r9
    ret                         ; return sum in RAX

main:
    mov rdi, 1                  ; arg1
    mov rsi, 2                  ; arg2
    mov rdx, 3                  ; arg3
    mov rcx, 4                  ; arg4
    mov r8, 5                   ; arg5
    mov r9, 6                   ; arg6
    call sum6
    
    ; RAX = 21
    mov rdi, rax
    mov rax, 60
    syscall

Example 3: More Than Six Arguments (Stack)

C Equivalent:

long sum8(long a, long b, long c, long d, long e, long f, long g, long h) {
    return a + b + c + d + e + f + g + h;
}

int main() {
    long result = sum8(1, 2, 3, 4, 5, 6, 7, 8);
    return result;
}

Assembly:

section .text
    global main

; long sum8(long a, long b, long c, long d, long e, long f, long g, long h)
sum8:
    ; Args 1-6 in registers: RDI, RSI, RDX, RCX, R8, R9
    ; Args 7-8 on stack: [rsp+8] = g, [rsp+16] = h
    
    mov rax, rdi                ; sum = a
    add rax, rsi                ; sum += b
    add rax, rdx                ; sum += c
    add rax, rcx                ; sum += d
    add rax, r8                 ; sum += e
    add rax, r9                 ; sum += f
    add rax, [rsp + 8]          ; sum += g (7th arg)
    add rax, [rsp + 16]         ; sum += h (8th arg)
    ret

main:
    ; Push args 8, 7 (right-to-left) for stack alignment
    push 8                      ; arg8 (h)
    push 7                      ; arg7 (g)
    
    ; Register args 1-6
    mov rdi, 1                  ; arg1
    mov rsi, 2                  ; arg2
    mov rdx, 3                  ; arg3
    mov rcx, 4                  ; arg4
    mov r8, 5                   ; arg5
    mov r9, 6                   ; arg6
    call sum8
    
    ; Clean up stack (2 pushes Γ— 8 bytes = 16 bytes)
    add rsp, 16
    
    ; RAX = 36
    mov rdi, rax
    mov rax, 60
    syscall

Example 4: Callee-Saved Registers

C Equivalent:

int helper(int x) {
    // Uses registers that must be preserved
    int temp1 = x * 2;      // Uses RBX
    int temp2 = x * 3;      // Uses R12
    return temp1 + temp2;
}

int main() {
    int result = helper(10);
    return result;
}

Assembly:

section .text
    global main

; int helper(int x)
helper:
    ; Must save callee-saved registers before using them
    push rbx
    push r12
    
    ; x is in EDI
    mov ebx, edi
    shl ebx, 1                  ; temp1 = x * 2 (in RBX)
    
    lea r12d, [rdi + rdi*2]     ; temp2 = x * 3 (in R12)
    
    lea eax, [rbx + r12]        ; result = temp1 + temp2
    
    ; Restore callee-saved registers
    pop r12
    pop rbx
    ret

main:
    mov edi, 10
    call helper
    
    ; RAX = 50
    mov rdi, rax
    mov rax, 60
    syscall

Part 4: Microsoft x64 Calling Convention (Windows)

Parameter Passing

First 4 arguments go in registers (different from System V!):

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Arg 1  β”‚  Arg 2   β”‚  Arg 3   β”‚  Arg 4   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚   RCX   β”‚   RDX    β”‚   R8     β”‚   R9     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

All additional arguments go on the stack.

Shadow Space: Caller must allocate 32 bytes (4 Γ— 8) on stack for register args (even if not used).

Return Values

Integer/Pointer: RAX
Floating-point:  XMM0

Register Usage

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Register         β”‚ Usage                                 β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ RAX              β”‚ Return value, temp                    β”‚
β”‚ RBX, RBP, RDI,   β”‚ Callee-saved (must preserve)          β”‚
β”‚ RSI, R12-R15     β”‚                                       β”‚
β”‚ RCX, RDX, R8, R9 β”‚ Argument passing, temp                β”‚
β”‚ R10, R11         β”‚ Temp, caller-saved                    β”‚
β”‚ RSP              β”‚ Stack pointer                         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Stack Alignment

RSP must be 16-byte aligned before call.


Part 5: 32-bit Conventions (cdecl, stdcall, fastcall)

cdecl (C Declaration)

Default for C on 32-bit x86.

; int add(int a, int b)
; C: add(5, 10)

; Caller:
push 10                     ; arg2 (right-to-left)
push 5                      ; arg1
call add
add esp, 8                  ; Caller cleans stack (2 args Γ— 4 bytes)

; Callee:
add:
    push ebp
    mov ebp, esp
    mov eax, [ebp + 8]      ; arg1
    add eax, [ebp + 12]     ; arg2
    pop ebp
    ret                     ; No stack cleanup

Characteristics:


stdcall (Standard Call)

Used by Windows API.

; int add(int a, int b)

; Caller:
push 10
push 5
call add
; Note: NO stack cleanup by caller

; Callee:
add:
    push ebp
    mov ebp, esp
    mov eax, [ebp + 8]
    add eax, [ebp + 12]
    pop ebp
    ret 8                   ; Callee cleans stack (return and pop 8 bytes)

Characteristics:


fastcall

Uses registers for first few arguments.

; int add(int a, int b)

; Caller:
mov ecx, 5                  ; arg1 in ECX
mov edx, 10                 ; arg2 in EDX
call add

; Callee:
add:
    lea eax, [ecx + edx]    ; result = arg1 + arg2
    ret

Characteristics:


Part 6: Comparison Table

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Convention   β”‚ Architecture  β”‚ Arg Order    β”‚ Stack Cleanupβ”‚ Registers   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ System V     β”‚ x64 Linux/Mac β”‚ Reg then Stk β”‚ Caller       β”‚ RDI RSI RDX  β”‚
β”‚ AMD64        β”‚               β”‚              β”‚              β”‚ RCX R8 R9    β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Microsoft    β”‚ x64 Windows   β”‚ Reg then Stk β”‚ Caller       β”‚ RCX RDX R8   β”‚
β”‚ x64          β”‚               β”‚              β”‚ (shadow!)    β”‚ R9           β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ cdecl        β”‚ x86 (32-bit)  β”‚ Right-to-leftβ”‚ Caller       β”‚ None         β”‚
β”‚              β”‚               β”‚ on stack     β”‚              β”‚              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ stdcall      β”‚ x86 (32-bit)  β”‚ Right-to-leftβ”‚ Callee       β”‚ None         β”‚
β”‚              β”‚ Windows       β”‚ on stack     β”‚              β”‚              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ fastcall     β”‚ x86 (32-bit)  β”‚ ECX, EDX,    β”‚ Callee       β”‚ ECX, EDX     β”‚
β”‚              β”‚               β”‚ then stack   β”‚              β”‚              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Part 7: Practical Example - Mixed Convention

Calling C from Assembly (System V)

C Code (mylib.c):

#include <stdio.h>

int multiply(int a, int b) {
    return a * b;
}

void print_result(int result) {
    printf("Result: %d\n", result);
}

Assembly (main.asm):

section .text
    extern multiply
    extern print_result
    global main

main:
    ; Call multiply(6, 7)
    mov edi, 6                  ; arg1 = 6
    mov esi, 7                  ; arg2 = 7
    call multiply               ; result in EAX
    
    ; Call print_result(result)
    mov edi, eax                ; arg1 = result
    call print_result
    
    ; Exit
    xor eax, eax                ; return 0
    ret

Compile and Link:

nasm -f elf64 main.asm -o main.o
gcc -c mylib.c -o mylib.o
gcc main.o mylib.o -o program
./program
# Output: Result: 42

Part 8: Stack Frame Layout

System V AMD64 Stack Frame

Higher Addresses
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Arg 8         β”‚ ← [rbp + 24]
β”‚   Arg 7         β”‚ ← [rbp + 16]
β”‚ Return Address  β”‚ ← [rbp + 8] (pushed by call)
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Saved RBP       β”‚ ← RBP (current frame base)
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Local Var 1     β”‚ ← [rbp - 8]
β”‚ Local Var 2     β”‚ ← [rbp - 16]
β”‚ ...             β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Saved Regs      β”‚ (if needed)
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Alignment       β”‚ (padding to 16 bytes)
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ← RSP (stack top)
Lower Addresses

Part 9: Common Mistakes

❌ Mistake 1: Wrong Argument Order

; WRONG - System V uses RDI, RSI, not RCX, RDX
mov rcx, 5                  ; ❌ Wrong register!
mov rdx, 10
call my_function

; CORRECT
mov rdi, 5                  ; βœ“ RDI for 1st arg (System V)
mov rsi, 10                 ; βœ“ RSI for 2nd arg
call my_function

❌ Mistake 2: Not Preserving Callee-Saved Registers

; WRONG
my_function:
    mov rbx, rdi            ; ❌ Destroyed RBX without saving!
    ; ... use rbx ...
    ret

; CORRECT
my_function:
    push rbx                ; βœ“ Save RBX
    mov rbx, rdi
    ; ... use rbx ...
    pop rbx                 ; βœ“ Restore RBX
    ret

❌ Mistake 3: Forgetting Stack Cleanup

; cdecl convention - caller cleans
push 10
push 5
call add
; ❌ MISSING: add esp, 8

; CORRECT
push 10
push 5
call add
add esp, 8                  ; βœ“ Caller cleans stack

❌ Mistake 4: Stack Misalignment

; WRONG - RSP not 16-byte aligned
main:
    push rax                ; RSP -= 8 (now misaligned!)
    call function           ; ❌ Function expects aligned stack
    
; CORRECT
main:
    push rax                ; RSP -= 8
    sub rsp, 8              ; Align to 16 bytes
    call function           ; βœ“ Stack aligned
    add rsp, 8
    pop rax

Part 10: Debugging Tips

Check Calling Convention

# Check how GCC compiles a function
gcc -S -O2 test.c -o test.s
cat test.s

# Check what convention is used
objdump -d program | less

GDB Tips

gdb ./program

# View registers before call
(gdb) info registers

# Check stack
(gdb) x/10xg $rsp

# Step into function
(gdb) si

# View arguments (System V)
(gdb) print $rdi
(gdb) print $rsi

βœ… Practice Exercises

Exercise 1: Convert to Assembly

Write assembly for this C function using System V:

int average(int a, int b, int c) {
    return (a + b + c) / 3;
}
Solution ```nasm ; int average(int a, int b, int c) average: ; a=EDI, b=ESI, c=EDX lea eax, [rdi + rsi] ; sum = a + b add eax, edx ; sum += c mov ecx, 3 cdq ; Sign-extend EAX to EDX:EAX idiv ecx ; EAX = sum / 3 ret ```

Exercise 2: Call from Assembly

Write assembly that calls this C function:

int max(int x, int y);
Solution ```nasm section .text extern max global main main: mov edi, 42 ; arg1 = 42 mov esi, 17 ; arg2 = 17 call max ; RAX contains max(42, 17) = 42 mov rdi, rax ; exit code mov rax, 60 syscall ```

Exercise 3: Fix the Bug

What’s wrong with this code?

my_function:
    mov r12, rdi            ; Use R12 for something
    ; ... more code ...
    ret
Solution R12 is callee-saved and must be preserved! ```nasm my_function: push r12 ; Save R12 mov r12, rdi ; ... more code ... pop r12 ; Restore R12 ret ```

πŸ“‹ Quick Reference Card

System V AMD64 (Linux/macOS)

Args (int):  RDI, RSI, RDX, RCX, R8, R9, then stack
Return:      RAX
Callee-save: RBX, RBP, R12-R15
Alignment:   16-byte before call

Microsoft x64 (Windows)

Args (int):  RCX, RDX, R8, R9, then stack
Return:      RAX
Shadow:      32 bytes
Callee-save: RBX, RBP, RDI, RSI, R12-R15
Alignment:   16-byte before call

32-bit cdecl

Args:        Stack (right-to-left)
Return:      EAX
Cleanup:     Caller
Callee-save: EBX, ESI, EDI, EBP

🎯 Knowledge Check

Before moving to Topic 10, verify you understand:


πŸŽ‰ Excellent! You now understand calling conventions!

Next: Topic 10: Procedures


← Previous Topic Back to Main Next Topic β†’