Este artículo describe las convenciones de llamada utilizadas al programar microprocesadores con arquitectura x86 .
Las convenciones de llamada describen la interfaz del código llamado:
- El orden en que se asignan los parámetros atómicos (escalares) o las partes individuales de un parámetro complejo.
- Cómo se pasan los parámetros (se apilan, se almacenan en registros o se combina ambos).
- ¿Qué registros debe conservar la función llamada para quien la llama (también conocidos como: registros guardados por la función llamada o registros no volátiles)?
- Cómo se divide la tarea de preparar la pila para una llamada a una función y restaurarla después de la misma entre quien llama y quien es llamado.
Esto está íntimamente relacionado con la asignación de tamaños y formatos a los tipos de lenguajes de programación. Otro tema estrechamente relacionado es la modificación de nombres , que determina cómo se asignan los nombres de los símbolos en el código a los nombres de símbolos utilizados por el enlazador. Las convenciones de llamada, las representaciones de tipos y la modificación de nombres forman parte de lo que se conoce como interfaz binaria de aplicación (ABI).
Existen sutiles diferencias en la forma en que los distintos compiladores implementan estas convenciones, por lo que a menudo resulta difícil integrar código compilado por diferentes compiladores. Por otro lado, las convenciones que se utilizan como estándar de API (como stdcall) se implementan de forma muy uniforme.
Antecedentes históricos
El estándar para los ordenadores compatibles con IBM PC fue definido por los procesadores Intel (8086, 80386) y el hardware que IBM distribuía. Las extensiones de hardware y todos los estándares de software (excepto una convención de llamada de BIOS ) quedaron sujetos a la competencia del mercado.
Numerosas empresas de software independientes ofrecían sistemas operativos, compiladores para muchos lenguajes de programación y aplicaciones. Estas empresas implementaban diversos esquemas de llamadas, a menudo mutuamente excluyentes, basados en diferentes requisitos, prácticas históricas y la creatividad de los programadores.
Tras la reestructuración del mercado de sistemas compatibles con IBM, predominaron los sistemas operativos y las herramientas de programación de Microsoft (con convenciones diferentes), mientras que empresas de segundo nivel como Borland y Novell , y proyectos de código abierto como GNU Compiler Collection (GCC), mantuvieron sus propios estándares. Finalmente, se adoptaron disposiciones para la interoperabilidad entre proveedores y productos, lo que simplificó el problema de elegir una convención viable. [ 1 ]
Limpieza de llamadas
En este tipo de convenciones de llamada, quien realiza la llamada limpia los argumentos de la pila (restablece el estado de la pila tal como estaba antes de que se llamara a la función llamada).
cdecl
La cdecl(que significa C declaration ) es una convención de llamada para el lenguaje de programación C y es utilizada por muchos compiladores de C para la arquitectura x86 . [ 1 ] En cdecl, los argumentos de subrutina se pasan en la pila . Si los valores de retorno son valores enteros o direcciones de memoria, son colocados en el registro EAX por la llamada, mientras que los valores de punto flotante se colocan en el registro x87 ST0 . Los registros EAX, ECX y EDX son guardados por la llamada, y el resto son guardados por la llamada. Los registros de punto flotante x87 ST0 a ST7 deben estar vacíos (retirados o liberados) cuando se llama a una nueva función, y ST1 a ST7 deben estar vacíos al salir de una función. ST0 también debe estar vacío cuando no se utiliza para devolver un valor.
En el contexto del lenguaje C, los argumentos de las funciones se colocan en la pila en orden de derecha a izquierda (RTL), es decir, el último argumento se coloca primero.
Considere el siguiente fragmento de código fuente en C:
entero llamado ( int , int , int );int llamador ( void ) { return llamador ( 1 , 2 , 3 ) + 5 ; }En x86 , podría producir el siguiente código ensamblador ( sintaxis de Intel ):
llamador: ; crear nuevo marco de llamada ; (algunos compiladores pueden producir una instrucción 'enter' en su lugar) push ebp ; guardar el marco de llamada antiguo mov ebp , esp ; inicializar nuevo marco de llamada ; push argumentos de llamada, en orden inverso ; (algunos compiladores pueden restar el espacio requerido del puntero de pila, ; luego escribir cada argumento directamente, ver más abajo. ; La instrucción 'enter' también puede hacer algo similar) ; sub esp, 12 : la instrucción 'enter' podría hacer esto por nosotros ; mov [ebp-4], 3 : o mov [esp+8], 3 ; mov [ebp-8], 2 : o mov [esp+4], 2 ; mov [ebp-12], 1 : o mov [esp], 1 push 3 push 2 push 1 call callee ; llamar a la subrutina 'callee' add esp , 12 ; eliminar argumentos de llamada del marco agregar eax , 5 ; modificar el resultado de la subrutina ; (eax es el valor de retorno de nuestra llamada, ; por lo que no tenemos que moverlo a una variable local) ; restaurar el marco de llamada anterior ; (algunos compiladores pueden producir una instrucción 'leave' en su lugar) ; la mayoría de las convenciones de llamada dictan que ebp se guarde en la llamada, ; es decir, se conserva después de llamar a la llamada. ; por lo tanto, todavía apunta al inicio de nuestro marco de pila. ; sin embargo, debemos asegurarnos de que ; la llamada no modifique (o restaure) ebp, ; por lo que debemos asegurarnos de que ; utilice una convención de llamada que haga esto pop ebp ; restaurar el marco de llamada anterior ret ; devolverEl llamador limpia la pila después de que la llamada a la función finaliza.
La cdeclconvención de llamada suele ser la convención de llamada predeterminada para los compiladores C de x86 , aunque muchos compiladores ofrecen opciones para cambiar automáticamente las convenciones de llamada utilizadas. Para definir manualmente una función como cdecl, algunos admiten la siguiente sintaxis:
return_type __cdecl func_name ();Variaciones
Existen algunas variaciones en la interpretación de cdecl. Como resultado, los programas x86 compilados para diferentes plataformas de sistemas operativos o con diferentes compiladores pueden ser incompatibles, incluso si ambos utilizan la convención "cdecl" y no hacen referencia al entorno subyacente.
Algunos compiladores devuelven estructuras de datos simples con una longitud de 2 registros o menos en el par de registros EAX:EDX, y las estructuras más grandes y los objetos de clase que requieren un tratamiento especial por parte del manejador de excepciones (por ejemplo, un constructor, destructor o asignación definidos) se devuelven en memoria. Para pasar "en memoria", quien llama asigna memoria y pasa un puntero a ella como primer parámetro oculto; quien es llamado llena la memoria y devuelve el puntero, eliminando el puntero oculto al regresar. [ 2 ]
En los sistemas tipo Unix, la convención de llamada estándar se define en la ABI del sistema V. Hay documentos separados para i386, [ 3 ] x86-64, K1OM , etc.
En Linux , GCC establece el estándar de facto para las convenciones de llamada. Desde la versión 4.5 de GCC, la pila debe estar alineada a un límite de 16 bytes al llamar a una función (las versiones anteriores solo requerían una alineación de 4 bytes, según la ABI de System V) debido al uso de instrucciones de carga alineada SSE . [ 1 ] [ 4 ]
llamada al sistema
Esto es similar a cdecl en que los argumentos se pasan de derecha a izquierda. Sin embargo, EAX, ECX y EDX no se conservan, y el tamaño de la lista de parámetros en palabras dobles se pasa en AL.
Syscall es la convención de llamada estándar para la API de OS/2 de 32 bits .
optlink
Los argumentos se procesan de derecha a izquierda. Los tres primeros argumentos (los de la izquierda) se pasan por EAX, EDX y ECX, y hasta cuatro argumentos de coma flotante se pasan por ST0 a ST3, aunque se reserva espacio para ellos en la lista de argumentos de la pila. Los resultados se devuelven por EAX o ST0. Los registros EBP, EBX, ESI y EDI se conservan.
Optlink es utilizado por los compiladores IBM VisualAge .
Limpieza de Callee
En estas convenciones, la función llamada limpia los argumentos de la pila. Las funciones que utilizan estas convenciones son fáciles de reconocer en el código ensamblador porque desenrollan la pila después de regresar. La instrucción x86 retpermite un parámetro opcional de 16 bits que especifica la cantidad de bytes de la pila que se liberarán después de regresar a la función que la llamó. Dicho código se ve así:
ret 12Las convenciones, algunas estandarizadas fastcally otras registerno, se han implementado de forma diferente según el proveedor del compilador. [ 1 ] Normalmente, las convenciones de llamada basadas en registros pasan uno o más argumentos en registros, lo que reduce el número de accesos a memoria necesarios para la llamada y, por lo tanto, suele hacerlas más rápidas.
Pascal
Según la convención de llamada del lenguaje Borland Turbo Pascal , los parámetros se insertan en la pila de izquierda a derecha (LTR) (lo opuesto a cdecl), y la función llamada es responsable de eliminarlos de la pila.
La devolución del resultado funciona de la siguiente manera:
- Los valores ordinales se devuelven en formato AL ( valores de 8 bits ), AX (valores de 16 bits), EAX ( valores de 32 bits ) o DX:AX (valores de 32 bits en sistemas de 16 bits).
- Los valores reales se devuelven en DX:BX:AX.
- Los valores de punto flotante (8087) se devuelven en ST0.
- En los sistemas de 32 bits, los punteros se devuelven en EAX, y en los sistemas de 16 bits, en AX.
- Las cadenas se devuelven en una ubicación temporal a la que apunta el
@Resultsímbolo.
Esta convención de llamada era común en las siguientes API de 16 bits: OS/2 1.x, Microsoft Windows 3.x y Borland Delphi versión 1.x. Las versiones modernas de la API de Windows usan stdcall , que todavía hace que la función llamada restaure la pila como en la convención de Pascal, pero ahora los parámetros se mueven de derecha a izquierda.
llamada estándar
La convención de llamada stdcall [ 5 ] es una variación de la convención de llamada de Pascal en la que la función llamada es responsable de limpiar la pila, pero los parámetros se insertan en la pila de derecha a izquierda, como en la convención de llamada _cdecl. Los registros EAX, ECX y EDX están designados para su uso dentro de la función. Los valores de retorno se almacenan en el registro EAX.
stdcall es la convención de llamada estándar para la API de Microsoft Win32 y para Open Watcom C++ .
Llamada rápida de Microsoft
La convención __fastcall de Microsoft pasa los dos primeros argumentos que caben (evaluados de izquierda a derecha) a ECX y EDX. [ 6 ] Los argumentos restantes se colocan en la pila de derecha a izquierda. Cuando el compilador compila para IA64 o AMD64 , ignora la palabra clave __fastcall (o cualquier otra palabra clave de convención de llamada que no sea __vectorcall ) y utiliza la convención de llamada predeterminada de 64 bits de Microsoft .
Otros compiladores como GCC [ 7 ] y Clang [ 8 ] proporcionan convenciones de llamada "fastcall" similares, aunque no son necesariamente compatibles entre sí ni con fastcall de Microsoft. [ 9 ]
Considere el siguiente fragmento de código C:
__attribute__ (( fastcall )) void printnums ( int num1 , int num2 , int num3 ){ printf ( "Los números que enviaste son: %d %d %d" , num1 , num2 , num3 ); }int main (){ printnums ( 1 , 2 , 3 ); return 0 ; }La descompilación x86 de la función principal se verá así (en sintaxis Intel):
principal: ; configuración de la pila push ebp mov ebp , esp push 3 ; inmediato 3 (el tercer argumento se inserta en la pila) mov edx , 0x2 ; inmediato 2 (el segundo argumento) se copia al registro edx. mov ecx , 0x1 ; inmediato 1 (el primer argumento) se copia al registro ecx. llamar a printnums mov eax , 0 ; devolver 0 salir retnLos dos primeros argumentos se pasan de izquierda a derecha, y el tercer argumento se coloca en la pila. No se realiza ninguna limpieza de la pila, ya que esta la realiza la función llamada. El desensamblaje de la función llamada es:
printnums: ; configuración de la pila push ebp mov ebp , esp sub esp , 0x08 mov [ ebp-0x04 ], ecx ; en x86, ecx = primer argumento. mov [ ebp-0x08 ], edx ; arg2 push [ ebp + 0x08 ] ; arg3 se inserta en la pila. push [ ebp-0x08 ] ; arg2 se inserta push [ ebp-0x04 ] ; arg1 se inserta push 0x8065d67 ; "Los números que enviaste son %d %d %d" call printf ; limpieza de la pila add esp , 0x10 nop leave retn 0x04Como los dos argumentos se pasaron a través de los registros y solo un parámetro se colocó en la pila, el valor colocado se borra mediante la instrucción retn, ya que int tiene un tamaño de 4 bytes en los sistemas x86.
Llamada vectorial de Microsoft
En Visual Studio 2013, Microsoft introdujo la convención de llamada __vectorcall en respuesta a las preocupaciones de eficiencia de los desarrolladores de juegos, gráficos, vídeo/audio y códecs. Este esquema permite que los tipos de vectores más grandes ( float , double , __m128 , __m256 ) se pasen en registros en lugar de en la pila. [ 10 ]
__vectorcall añade soporte para pasar valores agregados de vectores homogéneos (HVA), que son tipos compuestos (estructuras) formados únicamente por hasta cuatro tipos de vectores idénticos, utilizando los mismos seis registros. Una vez que los registros se han asignado a los argumentos de tipo vector, los registros no utilizados se asignan a los argumentos HVA de izquierda a derecha. Las reglas de posicionamiento siguen aplicándose. Los valores de tipo vector y HVA resultantes se devuelven utilizando los primeros cuatro registros XMM/YMM. [ 11 ]
Para el código IA-32 y x64, __vectorcall es similar a __fastcall y a las convenciones de llamada originales de x64 , respectivamente, pero las extiende para admitir el paso de argumentos vectoriales mediante registros SIMD . En IA-32, los valores enteros se pasan como de costumbre, y los primeros seis registros SIMD ( XMM / YMM 0-5) almacenan hasta seis valores de punto flotante, vectoriales o HVA secuencialmente de izquierda a derecha, independientemente de las posiciones reales causadas, por ejemplo, por un argumento int que aparezca entre ellos. En x64, sin embargo, la regla de la convención original de x64 sigue aplicándose, de modo que XMM/YMM0-5 solo almacenan argumentos de punto flotante, vectoriales o HVA cuando resultan ser del primero al sexto. [ 11 ]
El compilador Clang y el compilador Intel C++ también implementan vectorcall. [ 12 ]
La función vectorcall solo admite la vista de 256 bits (AVX, YMM) de los registros vectoriales. No abarca la vista AVX-512 (ZMM).
Registro de Intel
Antes de __vectorcall, ICC tenía una convención similar llamada __regcall , donde se colocaban tantos parámetros como fuera posible en los registros. [ 13 ] [ 14 ] También es compatible con clang. [ 12 ]
Intel tiene una ABI de función vectorial basada en __regcall que no está implementada por clang (a fecha de noviembre de 2025). Trabaja con vectores de tipos ordinarios, por lo que la mayoría de los parámetros se pasan (y los resultados se devuelven) a través de los registros [XYZ]MM. [ 15 ] Una variante de esta ABI que emplea la convención de llamada System V habitual de la plataforma x86_64 con respecto al uso de registros es adoptada por libmvec de glibc y es compatible con GCC, Clang e ICC. [ 16 ]
Modificadores de GNU y clang
En la arquitectura x86 de 32 bits, GNU cuenta con un atributo `regparm` que permite pasar hasta tres parámetros no variádicos en EAX, EDX y ECX. No se trata tanto de una convención de llamada propia, sino más bien de una modificación que puede aplicarse a las convenciones de llamada existentes. [ 12 ]
Clang tiene modificadores de convención similares en forma de preserve_all , preserve_none , preserve_most , ampliamente aplicables a muchas arquitecturas. [ 12 ] GCC lo admite en x86 y x86-64. [ 17 ]preserve_none
Registro de Borland
Evaluando los argumentos de izquierda a derecha, pasa tres argumentos a través de EAX, EDX, ECX. Los argumentos restantes se colocan en la pila, también de izquierda a derecha. [ 18 ] Es la convención de llamada predeterminada del compilador de 32 bits de Delphi , donde se conoce como registro . Esta convención de llamada también la utiliza C++Builder de Embarcadero , donde se llama __fastcall . [ 19 ] En este compilador, fastcall de Microsoft se puede usar como __msfastcall . [ 20 ]
GCC y Clang pueden hacerse usar una convención de llamada similar usando __stdcallel regparmatributo de función o el -mregparm=3interruptor. (El orden de la pila se invierte). También es posible producir una variante de limpieza del llamador usando cdeclo extender esto para usar también registros SSE. [ 21 ] Una cdeclversión basada en es utilizada por el kernel de Linux en i386 desde la versión 2.6.20 (lanzada en febrero de 2007). [ 22 ]
Registro de Watcom
Watcom no admite la palabra clave __fastcall, excepto para asignarle un alias nulo. La convención de llamada de registros se puede seleccionar mediante un parámetro de línea de comandos.
Se asignan hasta 4 registros a los argumentos en el orden EAX, EDX, EBX, ECX. Los argumentos se asignan a los registros de izquierda a derecha. Si algún argumento no puede asignarse a un registro (por ejemplo, si es demasiado grande), este, junto con todos los argumentos subsiguientes, se asigna a la pila. Los argumentos asignados a la pila se insertan de derecha a izquierda. Los nombres se modifican añadiendo un guion bajo al final. La función que realiza la llamada limpia la pila. [ 23 ]
Las funciones variádicas recurren a la convención de llamada basada en la pila de Watcom.
El compilador Watcom C/C++ también utiliza la directiva #pragma aux [ 24 ] que permite al usuario especificar su propia convención de llamada. Como indica su manual, "Es probable que muy pocos usuarios necesiten este método, pero si se necesita, puede ser de gran ayuda".
TopSpeed, Clarion, JPI
Los primeros cuatro parámetros enteros se pasan a través de los registros EAX, EBX, ECX y EDX. Los parámetros de coma flotante se pasan a través de la pila de coma flotante (registros ST0, ST1, ST2, ST3, ST4, ST5 y ST6). Los parámetros de estructura siempre se pasan a través de la pila. Los parámetros adicionales se pasan a través de la pila una vez agotados los registros. Los valores enteros se devuelven en EAX, los punteros en EDX y los tipos de coma flotante en ST0.
llamada segura
En Delphi y Free Pascal para Microsoft Windows , la convención de llamada safecall encapsula el manejo de errores de COM ( Component Object Model ), por lo que las excepciones no se filtran al llamador, sino que se informan en el valor de retorno HRESULT , como lo requiere COM/OLE. Al llamar a una función safecall desde código Delphi, Delphi también verifica automáticamente el HRESULT devuelto y genera una excepción si es necesario.
La convención de llamada safecall es la misma que la convención de llamada stdcall, excepto que las excepciones se devuelven al llamador en EAX como un HResult (en lugar de en FS:[0]), mientras que el resultado de la función se pasa por referencia en la pila como si fuera un parámetro de salida final. Al llamar a una función de Delphi desde Delphi, esta convención de llamada aparecerá como cualquier otra, ya que, aunque las excepciones se devuelven en EAX, el llamador las convierte automáticamente en excepciones adecuadas. Al usar objetos COM creados en otros lenguajes, los HResult se generarán automáticamente como excepciones, y el resultado de las funciones Get estará en el resultado en lugar de en un parámetro. Al crear objetos COM en Delphi con safecall, no es necesario preocuparse por los HResult, ya que las excepciones se pueden generar normalmente, pero se verán como HResult en otros lenguajes.
función nombre_función ( a : DWORD ) : DWORD ; llamada segura ;Devuelve un resultado y genera excepciones como una función Delphi normal, pero pasa valores y excepciones como si fuera:
función nombre_función ( a : DWORD ; salida Resultado : DWORD ) : HResult ; stdcall ;Limpieza tanto para quien llama como para quien recibe la llamada
esta llamada
This calling convention is used for calling C++ non-static member functions. There are two primary versions of thiscall used depending on the compiler and whether or not the function uses a variable number of arguments.
For the GCC compiler, thiscall is almost identical to cdecl: The caller cleans the stack, and the parameters are passed in right-to-left order. The difference is the addition of the this pointer, which is pushed onto the stack last, as if it were the first parameter in the function prototype.
On the Microsoft Visual C++ compiler, the this pointer is passed in ECX and it is the callee that cleans the stack, mirroring the stdcall convention used in C for this compiler and in Windows API functions. When functions use a variable number of arguments, it is the caller that cleans the stack (cf. cdecl).
The thiscall calling convention can only be explicitly specified on Microsoft Visual C++ 2005 and later. On any other compiler thiscall is not a keyword. (However, disassemblers, such as IDA, must specify it. So IDA uses keyword __thiscall for this.)
Register preservation
Another part of a calling convention is which registers are guaranteed to retain their values after a subroutine call. This behavior is known as register preservation.
Caller-saved (volatile) registers
According to the Intel ABI to which the vast majority of compilers conform, EAX, EDX, and ECX are to be free for use within a procedure or function, and need not be preserved.
As the name implies, these general-purpose registers usually hold temporary (volatile) information, that can be overwritten by any subroutine.
Therefore, it is the caller's responsibility to push each of these registers onto the stack, if it would like to restore their values after a subroutine call.
Callee-saved (non-volatile) registers
The other registers are used to hold long-lived values (non-volatile), that should be preserved across calls.
In other words, when the caller makes a procedure call, it can expect that those registers will hold the same value after the callee returns.
Thus, making it the callee's responsibility to both save (push at the start) and restore (pop accordingly) them before returning to the caller. As in the prior case, this practice should only be done on registers that the callee changes.
x86-64 calling conventions
x86-64 calling conventions take advantage of the added register space to pass more arguments in registers. Also, the number of incompatible calling conventions has been reduced. There are two in common use.
Microsoft x64 calling convention
La convención de llamada Microsoft x64 [ 25 ] [ 26 ] se sigue en Windows y UEFI de prearranque (para el modo largo en x86-64 ). Los primeros cuatro argumentos se colocan en los registros. Esto significa RCX, RDX, R8 y R9 (en ese orden) para argumentos enteros, de estructura o de puntero, y XMM0, XMM1, XMM2 y XMM3 para argumentos de punto flotante. Los argumentos añadidos se insertan en la pila (de derecha a izquierda). Los valores de retorno enteros (similares a x86) se devuelven en RAX si son de 64 bits o menos. Los valores de retorno de punto flotante se devuelven en XMM0. Los parámetros de menos de 64 bits no se extienden a cero; los bits superiores no se ponen a cero. [ 27 ]
Las estructuras y uniones con tamaños que coinciden con enteros se pasan y devuelven como si fueran enteros. De lo contrario, se reemplazan por un puntero cuando se usan como argumento. Cuando se necesita devolver una estructura de tamaño excesivo, se antepone otro puntero a un espacio proporcionado por quien llama como primer argumento, desplazando todos los demás argumentos una posición a la derecha. Este puntero también se convierte en el valor de retorno en RAX. [ 28 ]
Al compilar para la arquitectura x64 en un contexto de Windows (ya sea utilizando herramientas de Microsoft o de terceros), stdcall, thiscall, cdecl y fastcall se resuelven utilizando esta convención.
En la convención de llamada de Microsoft x64, es responsabilidad del llamador asignar 32 bytes de "espacio de sombra" en la pila justo antes de llamar a la función (independientemente del número real de parámetros utilizados) y extraer los bytes de la pila después de la llamada. El espacio de sombra se utiliza para desbordar RCX, RDX, R8 y R9, [ 29 ] pero debe estar disponible para todas las funciones, incluso aquellas con menos de cuatro parámetros.
Los registros RAX, RCX, RDX, R8, R9, R10 y R11 se consideran volátiles (guardados por quien los llama). [ 30 ]
Los registros RBX, RBP, RDI, RSI, RSP, R12, R13, R14 y R15 se consideran no volátiles (guardados por el destinatario). [ 30 ]
Por ejemplo, una función que recibe 5 argumentos enteros almacenará del primero al cuarto en los registros, y el quinto se colocará en la parte superior del espacio de memoria. Así, cuando se accede a la función llamada, la pila estará compuesta (en orden ascendente) por la dirección de retorno, seguida del espacio de memoria (32 bytes) y, finalmente, el quinto parámetro.
El marco de pila está alineado a 16 bytes. [ 31 ]
Visual Studio 2008 almacena los números de coma flotante en XMM6 y XMM7, además de XMM8 a XMM15.
La convención de llamada no utiliza las partes YMM y ZMM de los registros vectoriales para el paso de argumentos y resultados.
ABI del sistema V AMD64
La convención de llamada de la ABI System V AMD64 se sigue en Solaris , Linux , FreeBSD , macOS , [ 32 ] y es el estándar de facto entre los sistemas operativos Unix y similares. El estándar de llamada OpenVMS en x86-64 se basa en la ABI System V con algunas extensiones necesarias para la retrocompatibilidad. [ 33 ]
Los primeros seis argumentos enteros o de puntero se pasan en los registros RDI, RSI, RDX, RCX, R8, R9 (R10 se usa como un puntero de cadena estático en caso de funciones anidadas [ 34 ] : 21 ), mientras que XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6 y XMM7 se usan para los primeros argumentos de punto flotante. [ 34 ] : 22 Como en la convención de llamada de Microsoft x64, los argumentos añadidos se pasan en la pila. [ 34 ] : 22 Los valores de retorno enteros de hasta 64 bits de tamaño se almacenan en RAX, mientras que los valores de hasta 128 bits se almacenan en RAX y RDX. Los valores de retorno de punto flotante se almacenan de manera similar en XMM0 y XMM1. [ 34 ] : 25 Los registros más amplios YMM y ZMM se usan para pasar y devolver valores más amplios en lugar de XMM cuando existen. [ 34 ] : 26, 55
Los parámetros de estructura y unión con tamaños de dos (ocho en el caso de campos SSE únicamente) punteros o menos alineados en límites de 64 bits se descomponen en "ocho bytes" y cada uno se clasifica y pasa como un parámetro separado, siempre que haya suficientes registros disponibles. [ 34 ] : 24 De lo contrario, se pasan en memoria. Los tipos de retorno de estructura y unión con tamaños de dos punteros o menos se devuelven en RAX y RDX (o XMM0 y XMM1). Cuando se necesita un retorno de estructura de tamaño excesivo, se antepone otro puntero a un espacio proporcionado por el llamador como primer argumento, desplazando todos los demás argumentos una posición a la derecha, y el valor de este puntero se devuelve en RAX. [ 34 ] : 27
Si la función llamada desea utilizar los registros RBX, RSP, RBP y R12–R15, debe restaurar sus valores originales antes de devolver el control a la función que la llamó. Todos los demás registros deben ser guardados por la función que llama si desea conservar sus valores. [ 34 ] : 16
Para las funciones de nodo hoja (funciones que no llaman a ninguna otra función), se almacena un espacio de 128 bytes justo debajo del puntero de pila de la función. Este espacio se denomina zona roja . Esta zona no será sobrescrita por ningún controlador de señal o interrupción. Por lo tanto, los compiladores pueden usar esta zona para guardar variables locales. Los compiladores pueden omitir algunas instrucciones al inicio de la función (ajuste de RSP, RBP) utilizando esta zona. Sin embargo, otras funciones pueden sobrescribir esta zona. Por consiguiente, esta zona solo debe usarse para funciones de nodo hoja. gccy clangofrece la -mno-red-zonebandera para deshabilitar las optimizaciones de la zona roja.
Si la función llamada es una función variádica , entonces el número de argumentos de punto flotante que se pasan a la función en los registros vectoriales debe ser proporcionado por quien realiza la llamada en el registro AL. [ 34 ] : 55
El marco de pila se alinea a un límite de 16 bytes por defecto. Si __m256se pasa un tipo (AVX) en la pila, debe estar alineado a 32 bytes; si __m512se pasa un tipo (AVX512), debe estar alineado a 64 bytes. [ 34 ] : 22
A diferencia de la convención de llamada de Microsoft, no se proporciona un espacio de sombra; al entrar en la función, la dirección de retorno es adyacente al séptimo argumento entero en la pila.
Convenciones de llamada especiales para x64
- A partir de Visual Studio 2013, Microsoft introdujo la convención de llamada __vectorcall , que extiende la convención x64. Para una descripción completa, consulte la sección § Microsoft vectorcall más arriba.
- Registro de Intel (ver más arriba).
Lista de convenciones de llamada x86
Esta es una lista de convenciones de llamada x86. [ 1 ] Estas convenciones están destinadas principalmente a compiladores de C/C++ (especialmente la parte de 64 bits que se describe a continuación) y, por lo tanto, son casos especiales. Otros lenguajes pueden usar otros formatos y convenciones en sus implementaciones.
Nota:
Referencias
Notas a pie de página
- 1 2 3 4 5 Fog, Agner (2010-02-16). Convenciones de llamada para diferentes compiladores de C++ y sistemas operativos (PDF) .
- ↑ Pollard, Jonathan de Boyne (2010). "The gen on function calling conventions" . Respuestas frecuentes .
- ↑ "Interfaz binaria de aplicación del sistema V: suplemento del procesador con arquitectura Intel 386" (PDF) (4.ª ed.).
- ↑ "GCC Bugzilla – Error 40838 - gcc no debería asumir que la pila está alineada" . 2009.
- ↑ "__stdcall (C++)" . MSDN . Microsoft. Archivado del original el 10 de abril de 2008. Consultado el 13 de febrero de 2019 .
- ↑ "__fastcall" . MSDN . Consultado el 26 de septiembre de 2013 .
- ↑ Ohse, Uwe. "Descripción general de los atributos de gcc: llamada rápida a la función" . ohse.de. Consultado el 27 de septiembre de 2010 .
- ↑ "Atributos en Clang: fastcall" . Documentación de Clang . 2022. Consultado el 15 de diciembre de 2022 .
- ↑ Patocka, Mikulas (11 de agosto de 2009). "La convención de llamadas Fastcall es incompatible con Windows" . Recuperado el 15 de diciembre de 2022 .
- ↑ "Presentamos la 'Convención de Llamada Vectorial'"" . MSDN. 11 de julio de 2013 . Consultado el 31 de diciembre de 2014 .
- 1 2 3 4 "__vectorcall" . MSDN . Consultado el 31/12/2014 .
- 1 2 3 4 5 "Atributos en Clang: vectorcall" . Documentación de Clang . 2022. Consultado el 15 de diciembre de 2022 .
- 1 2 3 "Convenciones de llamada de C/C++" . 16 de diciembre de 2022. Consultado el 15 de diciembre de 2022 .
- ↑ "_vectorcall y __regcall desmitificadas" (PDF) . software.intel.com . 7 de junio de 2017.
- ↑ "Interfaz binaria de aplicación de funciones vectoriales versión 0.9.8" . 6 de abril de 2016.
- ↑ "libmvec" . sourceware.org .(Ver VectorABI.txt)
- ↑ "Atributos de funciones x86 (Usando la colección de compiladores GNU (GCC))" . gcc.gnu.org .
- ↑ "Control de programas: Convención de registros" . docwiki.embarcadero.com. 1 de junio de 2010. Consultado el 27 de septiembre de 2010 .
- ↑ "_fastcall, __fastcall" . docwiki.embarcadero.com.
- ↑ "__msfastcall" . docwiki.embarcadero.com.
- ↑ "Atributos de función x86" . Uso de la colección de compiladores GNU (GCC) .
- ↑ "i386: habilitar siempre regparm" .
- 1 2 3 "Guía del usuario de Open Watcom 2.0 C/C++" . open-watcom.github.io . Consultado el 27 de junio de 2026 .
- ↑ "Convenciones de llamada: Especificando las convenciones de llamada al estilo Wattcom" . openwatcom.org. 27 de abril de 2010. Archivado del original el 8 de marzo de 2021. Consultado el 31 de agosto de 2018 .
- 1 2 "Convenciones de software x64: Convenciones de llamadas" . msdn.microsoft.com. 2010. Recuperado el 27 de septiembre de 2010 .
- ↑ "Arquitectura x64" . msdn.microsoft.com. 6 de enero de 2023.
- ↑ "Convención de llamada x64: Paso de parámetros" . learn.microsoft.com . Consultado el 24 de junio de 2026 .
- ↑ "Convención de llamada x64: Valores de retorno" . docs.microsoft.com . Consultado el 17 de enero de 2020 .
- ↑ "Convenciones de software x64 - Asignación de pila" . Microsoft . Consultado el 31 de marzo de 2010 .
- 1 2 "Registros guardados del emisor/receptor" . Microsoft Docs . Microsoft. 18 de mayo de 2022.
- ↑ "Uso de la pila x64" . learn.microsoft.com .
- ↑ "Modelo de código x86-64" . Biblioteca para desarrolladores de Mac . Apple Inc. Archivado del original el 10 de marzo de 2016. Consultado el 6 de abril de 2016.
El entorno x86-64 en OS X tiene un único modelo de código para el código de espacio de usuario. Es muy similar al modelo PIC pequeño definido por la ABI System V x86-64.
- ↑ "Estándar de llamadas VSI OpenVMS" (PDF) . vmssoftware.com . Mayo de 2020. Consultado el 21 de diciembre de 2020 .
- 1 2 3 4 5 6 7 8 9 10 11 Lu, HJ; Matz, Michael; Girkar, Milind; Hubička, Jan; Jaeger, Andreas; Mitchell, Mark, eds. (2023-05-23). "Interfaz binaria de aplicación System V: Suplemento del procesador de arquitectura AMD64 (con modelos de programación LP64 e ILP32) Versión 1.0" (PDF) . GitLab . 1.0.
- ↑ Guía del usuario de Borland C/C++ versión 3.1 (PDF) . Borland. 1992. págs. 158, 189–191 .
- ↑ "Convenciones de llamada de Linux de 32 bits" . Consultado el 22 de abril de 2024 .
- ↑ "Uso del registro" . Microsoft Docs . Microsoft. Archivado del original el 15 de septiembre de 2017. Consultado el 15 de septiembre de 2017 .
- ↑ "Convenciones de llamada de Linux de 64 bits" . Consultado el 22 de abril de 2024 .
Otras fuentes
- Interfaz binaria de aplicaciones System V: Suplemento del procesador con arquitectura Intel386 (PDF) (4.ª ed.). The Santa Cruz Operation, Inc. 1997-03-19.
- Trifunovic, Nemanja (22 de julio de 2001). Ewington, Sean (ed.). "Desmitificando las convenciones de llamadas" . The Code Project .
- Friedl, Stephen J. "Convenciones de llamada a funciones Intel x86 – Vista de ensamblador" . Consejos técnicos de Steve Friedl en Unixwiz.net .
- "Visual Studio 2010 – Convención de llamadas de Visual C++" . Biblioteca MSDN . Microsoft. 2010.
- Jonsson, Andreas (13 de febrero de 2005). "Convenciones de llamada en la plataforma x86" .
- Chen, Raymond (2004-01-02). "La historia de las convenciones de llamadas, parte 1" . The Old New Thing .
- Chen, Raymond (2004-01-07). "La historia de las convenciones de llamadas, parte 2" . The Old New Thing .
- Chen, Raymond (2004-01-08). "La historia de las convenciones de llamadas, parte 3" . The Old New Thing .
- Chen, Raymond (13 de enero de 2004). "La historia de las convenciones de llamadas, parte 4: ia64" . The Old New Thing .
- Chen, Raymond (14 de enero de 2004). "La historia de las convenciones de llamadas, parte 5; amd64" . The Old New Thing .
Lecturas adicionales
- de Boyne Pollard, Jonathan (2010). "The gen on function calling conventions" . Respuestas frecuentes .
- Irvine, Kip R. (2011). «Procedimientos avanzados (Capítulo 8)». Lenguaje ensamblador para procesadores x86 (6.ª ed.). Prentice Hall. ISBN 978-0-13-602212-1.
- Guía del usuario de Borland C/C++ versión 3.1 (PDF) . Borland. 1992. págs. 158, 189–191 .
- Lauer, Thomas (1995). "La nueva secuencia de llamadas __stdcall". Porting to Win32: A Guide to Making Your Applications Ready for the 32-Bit Future of Windows . Springer. ISBN 978-0-387-94572-9.
- Arquitectura X86
- Subrutinas