← SEARCHABLE ARCHIVE
Assembly · 2026-08-15

Llamadas a función y ABI de x86-64 en la práctica

Argumentos, registros preservados, alineación de pila y unwinding en System V y Windows x64

INTERACTIVE READING MODE →

Llamadas a función y ABI de x86-64 en la práctica

Estos artículos del blog se han creado con fines didácticos. Este artículo compara System V AMD64 y Microsoft x64. Kernel, syscalls, vectorcall y extensiones del toolchain requieren documentación específica del objetivo.

La instrucción call de la CPU solo guarda una dirección de retorno y transfiere el control. La ABI define dónde viven argumentos y resultados, quién conserva registros, cómo se alinea la pila y cómo se hace unwind de un marco.

ISA, convención de llamada y ABI

  • La ISA define instrucciones y comportamiento de registros.
  • La convención de llamada define argumentos, resultados, estado preservado y responsabilidad de pila.
  • La ABI incluye además layout de tipos, formato de objeto, símbolos, relocations y reglas de unwind.

extern "C" controla el language linkage y name mangling de C++; no transforma una función Windows en System V.

Comparación

ElementoSystem V AMD64Microsoft x64
Argumentos enterosrdi,rsi,rdx,rcx,r8,r9posiciones 1–4: rcx,rdx,r8,r9
Argumentos FPprincipalmente xmm0–xmm7posiciones 1–4: xmm0–xmm3
Retorno enteroraxrax
Retorno FPxmm0xmm0
Zona del callersin shadow space32 bytes de shadow space
Red zone128 bytes para ciertas leaf de usuarioninguna
GPR nonvolatile principalesrbx,rbp,r12–r15,rspademás rdi,rsi
XMM nonvolatileninguno128 bits bajos de xmm6–xmm15

Aggregates, vectores, funciones variádicas y punteros de retorno ocultos necesitan clasificación adicional.

Caller-saved y callee-saved

Un registro volatile puede cambiar tras una llamada; el caller guarda un valor vivo si lo necesita. Si el callee usa un registro nonvolatile, debe restaurarlo.

push rbx
mov rbx, rcx
; use rbx
pop rbx
ret

Preservar no obliga a usar push. Un registro no usado no se guarda, y también puede emplearse un stack slot con mov.

Llamada System V y Windows

System V:

mov edi, 10
mov esi, 20
mov edx, 30
call combine
; resultado en rax

Windows x64:

sub rsp, 40
mov ecx, 10
mov edx, 20
mov r8d, 30
call combine
add rsp, 40

Los 32 bytes son el shadow space que aporta el caller. Los 8 restantes forman una disposición típica de alineación teniendo en cuenta el retorno insertado por call. Se reserva incluso con menos de cuatro argumentos.

El long de C suele medir 64 bits en System V LP64, pero 32 bits en Windows LLP64. x64 no impone una anchura idéntica a todos los tipos del lenguaje.

La alineación necesita un instante

Distinga:

  1. caller justo antes de call;
  2. entrada del callee tras apilar el retorno;
  3. después del prólogo;
  4. justo antes de un nested call.

Aplicar la regla módulo de un instante a otro causa la habitual confusión de ocho bytes. Windows x64 exige rsp alineado a 16 bytes fuera del prólogo y epílogo.

Red zone y unwind

Ciertas funciones leaf de usuario System V pueden usar 128 bytes bajo rsp. La suposición no vale tras una llamada anidada, en Windows, en kernel ni con la red zone desactivada.

Una función non-leaf de Windows debe hacer coincidir su prólogo real con los metadatos de unwind.

Work PROC FRAME
    push rbx
    .pushreg rbx
    sub rsp, 32
    .allocstack 32
    .endprolog
    ; body
    add rsp, 32
    pop rbx
    ret
Work ENDP

Las directivas describen instrucciones, no las ejecutan. Una discrepancia puede superar pruebas de retorno normal y romper excepciones, profiler y stack walking.

Argumentos de pila y FP

Windows pasa en pila las posiciones desde la quinta. El offset cambia según se mida en la entrada o después del prólogo; [rsp+40] no significa siempre “quinto argumento” sin indicar el instante.

void f(int a, double b, int c, float d);

En Windows se obtiene aproximadamente a -> rcx, b -> xmm1, c -> r8, d -> xmm3. Se cuentan posiciones, no solo argumentos FP.

Variádicas, estructuras y parámetros ocultos

Las llamadas como printf añaden reglas:

  • System V comunica en al información relacionada con argumentos en registros vectoriales.
  • Microsoft x64 duplica un FP variádico en el registro entero de la misma posición.
  • Las promociones por defecto de C convierten float en double.

System V clasifica aggregates por eightbytes y puede dividirlos entre registros enteros y SSE. Windows pasa ciertos tamaños por valor y otros mediante un puntero a un temporal del caller. Una frontera ASM manual debe preferir escalares de anchura fija, funciones libres extern "C", assertions de sizeof, alignof, offsetof, e inspección de caller y callee.

Un resultado estructurado grande puede añadir un puntero de retorno oculto y desplazar aparentemente todos los argumentos visibles.

Tail call

wrapper:
    ; preparar argumentos de target
    jmp target

Si las condiciones ABI lo permiten, el compilador cambia un call final seguido de ret por jmp, y target retorna directamente al caller original.

Orden de depuración

  1. ¿Caller y callee usan la misma arquitectura y ABI?
  2. ¿Coinciden símbolo y language linkage?
  3. ¿Están bits y tipos en el lugar correcto justo antes de call?
  4. ¿A qué instante de rsp se refiere cada offset?
  5. ¿Son correctos alineación y shadow space de Windows?
  6. ¿Se restauran todos los nonvolatile modificados en cada camino?
  7. ¿Coinciden las directivas Windows con el prólogo real?
  8. ¿Se lee el resultado con la anchura correcta de rax o desde xmm0?

Resumen

call y ret son solo el esqueleto. System V usa seis registros enteros principales y una red zone; Microsoft x64 usa cuatro slots por posición, 32 bytes de shadow space, XMM nonvolatile y reglas estrictas de unwind. Demuestre el contrato con desensamblado de caller y callee, pruebas de límites y stack walking.

Fuentes oficiales