Articulo de referencia

convenciones de llamada x86

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 ...

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 ; devolver

El 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 .

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 12

Las 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 @Result sí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))voidprintnums(intnum1,intnum2,intnum3){printf("The numbers you sent are: %d %d %d",num1,num2,num3);}intmain(){printnums(1,2,3);return0;}

x86 decompilation of the main function will look like (in Intel syntax):

main:; stack setuppushebpmovebp,esppush3; immediate 3 (third argument is pushed to the stack)movedx,0x2; immediate 2 (second argument) is copied to edx register.movecx,0x1; immediate 1 (first argument) is copied to ecx register.callprintnumsmoveax,0; return 0leaveretn

The first two arguments are passed in the left to right order, and the third argument is pushed on the stack. There is no stack cleanup, as stack cleanup is performed by the callee. The disassembly of the callee function is:

printnums:; stack setuppushebpmovebp,espsubesp,0x08mov[ebp-0x04],ecx; in x86, ecx = first argument.mov[ebp-0x08],edx; arg2push[ebp+0x08]; arg3 is pushed to stack.push[ebp-0x08]; arg2 is pushedpush[ebp-0x04]; arg1 is pushedpush0x8065d67; "The numbers you sent are %d %d %d"callprintf; stack cleanupaddesp,0x10nopleaveretn0x04

As the two arguments were passed through the registers and only one parameter was pushed in the stack, the pushed value is being cleared by the retn instruction, as int is 4 bytes in size in x86 systems.

Microsoft vectorcall

In Visual Studio 2013, Microsoft introduced the __vectorcall calling convention in response to efficiency concerns from game, graphic, video/audio, and codec developers. The scheme allows for larger vector types (float, double, __m128, __m256) to be passed in registers as opposed to on the stack.[10]

__vectorcall adds support for passing homogeneous vector aggregate (HVA) values, which are composite types (structs) consisting solely of up to four identical vector types, using the same six registers. Once the registers have been allocated for vector type arguments, the unused registers are allocated to HVA arguments from left to right. The positioning rules still apply. Resulting vector type and HVA values are returned using the first four XMM/YMM registers.[11]

For IA-32 and x64 code, __vectorcall is similar to __fastcall and the original x64 calling conventions respectively, but extends them to support passing vector arguments using SIMD registers. In IA-32, the integer values are passed as usual, and the first six SIMD (XMM/YMM0-5) registers hold up to six floating-point, vector, or HVA values sequentially from left to right, regardless of actual positions caused by, e.g. an int argument appearing between them. In x64, however, the rule from the original x64 convention still apply, so that XMM/YMM0-5 only hold floating-point, vector, or HVA arguments when they happen to be the first through the sixth.[11]

The Clang compiler and the Intel C++ Compiler also implement vectorcall.[12]

vectorcall only handles the 256-bit (AVX, YMM) view of the vector registers. It does not cover the AVX-512 (ZMM) view.

Intel register

Before __vectorcall, ICC has a similar convention called __regcall, where as many parameters are stuffed into the registers as possible.[13][14] It is also supported by clang.[12]

Intel has a Vector Function ABI based on __regcall not implemented by clang (as of November 2025). It deals with vectors of ordinary types, so most parameters are passed (and the results returned) via [XYZ]MM registers.[15] A variant of this ABI employing instead the x86_64 platform's usual System V calling convention with regard to register use is adopted by glibc's libmvec and supported by GCC, Clang, and 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

Esta convención de llamada se utiliza para invocar funciones miembro no estáticas de C++. Existen dos versiones principales que thiscalldependen del compilador y de si la función utiliza o no un número variable de argumentos.

Para el compilador GCC, thiscalles casi idéntico a cdecl: quien llama limpia la pila y los parámetros se pasan de derecha a izquierda. La diferencia radica en la adición del thispuntero , que se coloca en la pila al final, como si fuera el primer parámetro del prototipo de la función.

En el compilador Microsoft Visual C++, el thispuntero se pasa en ECX y es la función llamada la que limpia la pila, reflejando la stdcallconvención utilizada en C para este compilador y en las funciones de la API de Windows. Cuando las funciones usan un número variable de argumentos, es la función que llama la que limpia la pila (cf. cdecl).

La thiscallconvención de llamada solo se puede especificar explícitamente en Microsoft Visual C++ 2005 y versiones posteriores. En cualquier otro compilador, `thiscall` no es una palabra clave. (Sin embargo, los desensambladores, como IDA , deben especificarla. Por lo tanto, IDA utiliza la palabra clave `__thiscall` para esto).

Conservación del registro

Otro aspecto de una convención de llamada es qué registros tienen garantizado conservar sus valores después de una llamada a una subrutina. Este comportamiento se conoce como preservación de registros.

Registros guardados por el llamador (volátiles)

Según la ABI de Intel , a la que se ajusta la gran mayoría de los compiladores, EAX, EDX y ECX deben poder utilizarse libremente dentro de un procedimiento o función, y no es necesario conservarlas.

Como su nombre indica, estos registros de propósito general suelen contener información temporal (volátil), que puede ser sobrescrita por cualquier subrutina.

Por lo tanto, es responsabilidad de quien realiza la llamada colocar cada uno de estos registros en la pila, si desea restaurar sus valores después de una llamada a una subrutina.

Registros guardados por el destinatario (no volátiles)

Los demás registros se utilizan para almacenar valores de larga duración (no volátiles), que deben conservarse entre llamadas.

En otras palabras, cuando quien realiza la llamada a un procedimiento, puede esperar que esos registros mantengan el mismo valor después de que la función llamada regrese.

De este modo, recae sobre la entidad llamada la responsabilidad de guardar (almacenar al inicio) y restaurar (descargar según corresponda) los datos antes de devolverlos a la entidad que los llamó. Al igual que en el caso anterior, esta práctica solo debe realizarse en los registros que la entidad llamada modifique.

convenciones de llamada x86-64

Las convenciones de llamada x86-64 aprovechan el espacio de registro adicional para pasar más argumentos en los registros. Además, se ha reducido el número de convenciones de llamada incompatibles. Actualmente, se utilizan dos de forma común.

Convención de llamadas de Microsoft x64

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:

  • GCC, clang y, en menor medida, ICC pueden usar cualquier convención de llamada en cualquier plataforma si se les pide que lo hagan usando atributos como ms_abi, sysv_abi, y fastcall. [ 12 ] [ 13 ] Esto es útil para crear software que cruza límites ABI como wine .

Referencias

Notas a pie de página

  1. 1 2 3 4 5 Fog, Agner (2010-02-16). Convenciones de llamada para diferentes compiladores de C++ y sistemas operativos (PDF) .
  2. Pollard, Jonathan de Boyne (2010). "The gen on function calling conventions" . Respuestas frecuentes .
  3. "Interfaz binaria de aplicación System V: Suplemento del procesador con arquitectura Intel 386" (PDF) (4.ª ed.). 
  4. "GCC Bugzilla – Error 40838 - gcc no debería asumir que la pila está alineada" . 2009.
  5. "__stdcall (C++)" . MSDN . Microsoft. Archivado del original el 10 de abril de 2008. Consultado el 13 de febrero de 2019 .
  6. "__fastcall" . MSDN . Consultado el 26 de septiembre de 2013 .
  7. 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 .
  8. "Atributos en Clang: fastcall" . Documentación de Clang . 2022. Consultado el 15 de diciembre de 2022 .
  9. Patocka, Mikulas (11 de agosto de 2009). "La convención de llamadas Fastcall es incompatible con Windows" . Recuperado el 15 de diciembre de 2022 .
  10. "Presentamos la 'Convención de Llamada Vectorial'"" . MSDN. 11 de julio de 2013 . Consultado el 31 de diciembre de 2014 .
  11. 1 2 3 4 "__vectorcall" . MSDN . Consultado el 31/12/2014 .
  12. 1 2 3 4 5 "Atributos en Clang: vectorcall" . Documentación de Clang . 2022. Consultado el 15 de diciembre de 2022 .
  13. 1 2 3 "Convenciones de llamada de C/C++" . 16 de diciembre de 2022. Consultado el 15 de diciembre de 2022 .
  14. "_vectorcall y __regcall desmitificadas" (PDF) . software.intel.com . 7 de junio de 2017.
  15. "Interfaz binaria de aplicación de funciones vectoriales versión 0.9.8" . 6 de abril de 2016.
  16. "libmvec" . sourceware.org .(Ver VectorABI.txt)
  17. "Atributos de funciones x86 (usando la colección de compiladores GNU (GCC))" . gcc.gnu.org .
  18. "Control de programas: Convención de registros" . docwiki.embarcadero.com. 1 de junio de 2010. Consultado el 27 de septiembre de 2010 .
  19. "_fastcall, __fastcall" . docwiki.embarcadero.com.
  20. "__msfastcall" . docwiki.embarcadero.com.
  21. "Atributos de función x86" . Uso de la colección de compiladores GNU (GCC) .
  22. "i386: habilitar siempre regparm" .
  23. 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 .
  24. "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 .
  25. 1 2 "Convenciones de software x64: Convenciones de llamadas" . msdn.microsoft.com. 2010. Recuperado el 27 de septiembre de 2010 .
  26. "Arquitectura x64" . msdn.microsoft.com. 6 de enero de 2023.
  27. "Convención de llamada x64: Paso de parámetros" . learn.microsoft.com . Consultado el 24 de junio de 2026 .
  28. "Convención de llamada x64: Valores de retorno" . docs.microsoft.com . Consultado el 17 de enero de 2020 .
  29. "Convenciones de software x64 - Asignación de pila" . Microsoft . Consultado el 31 de marzo de 2010 .
  30. 1 2 "Registros guardados del emisor/receptor" . Microsoft Docs . Microsoft. 18 de mayo de 2022.
  31. "Uso de la pila x64" . learn.microsoft.com .
  32. "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.
  33. "Estándar de llamadas VSI OpenVMS" (PDF) . vmssoftware.com . Mayo de 2020. Consultado el 21 de diciembre de 2020 .
  34. 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.
  35. Guía del usuario de Borland C/C++ versión 3.1 (PDF) . Borland. 1992. págs. 158, 189–191 . 
  36. "Convenciones de llamada de Linux de 32 bits" . Consultado el 22 de abril de 2024 .
  37. "Uso del registro" . Microsoft Docs . Microsoft. Archivado del original el 15 de septiembre de 2017. Consultado el 15 de septiembre de 2017 .
  38. "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". Portabilidad a Win32: Guía para preparar sus aplicaciones para el futuro de Windows de 32 bits . Springer. ISBN 978-0-387-94572-9.
Obtenido de " https://en.wikipedia.org/w/index.php?title=X86_calling_conventions&oldid=1361342415#optlink "