Article de reference

Conventions d'appel x86

( Découvrez comment et quand supprimer ce message ) Cet article décrit les conventions d'appel utilisées lors de la programmation des microprocesseurs d'architecture x86 . Les c...

( Découvrez comment et quand supprimer ce message )

Cet article décrit les conventions d'appel utilisées lors de la programmation des microprocesseurs d'architecture x86 .

Les conventions d'appel décrivent l'interface du code appelé :

  • L'ordre dans lequel les paramètres atomiques (scalaires), ou les parties individuelles d'un paramètre complexe, sont alloués
  • Comment les paramètres sont transmis (poussés sur la pile, placés dans des registres ou un mélange des deux)
  • Quels registres la fonction appelée doit conserver pour l'appelant (également appelés : registres sauvegardés par l'appelé ou registres non volatils)
  • Comment la tâche de préparation de la pile pour, et de restauration après, un appel de fonction est divisée entre l'appelant et l'appelé

Ceci est intimement lié à l'attribution de tailles et de formats aux types de langage de programmation. Un autre sujet étroitement lié est la modification des noms , qui détermine la manière dont les noms de symboles dans le code sont mappés aux noms de symboles utilisés par le linker. Les conventions d'appel, les représentations de type et la modification des noms font toutes partie de ce que l'on appelle une interface binaire d'application (ABI).

Il existe des différences subtiles dans la façon dont les différents compilateurs implémentent ces conventions, il est donc souvent difficile d'interfacer du code compilé par différents compilateurs. En revanche, les conventions qui sont utilisées comme norme API (comme stdcall) sont implémentées de manière très uniforme.

Contexte historique

La norme pour les PC compatibles IBM a été définie par les processeurs Intel (8086, 80386) et le matériel livré par IBM. Les extensions matérielles et toutes les normes logicielles (à l'exception d'une convention d'appel du BIOS ) ont été ouvertes à la concurrence du marché.

Une multitude d'éditeurs de logiciels indépendants proposaient des systèmes d'exploitation, des compilateurs pour de nombreux langages de programmation et des applications. De nombreux schémas d'appel différents ont été mis en œuvre par les entreprises, souvent mutuellement exclusifs, en fonction des différentes exigences, des pratiques historiques et de la créativité des programmeurs.

Après la révolution du marché des logiciels compatibles IBM, les systèmes d'exploitation et les outils de programmation Microsoft (avec des conventions différentes) ont prédominé, tandis que les entreprises de second plan comme Borland et Novell , et les projets open source comme GNU Compiler Collection (GCC), conservaient leurs propres normes. Des dispositions d' interopérabilité entre les fournisseurs et les produits ont finalement été adoptées, simplifiant le problème du choix d'une convention viable.

Nettoyage de l'appelant

Dans ces types de conventions d'appel, l'appelant nettoie les arguments de la pile (réinitialise l'état de la pile tel qu'il était avant l'appel de la fonction appelée).

cdécl

La déclaration C cdecl(qui signifie déclaration C ) est une convention d'appel pour le langage de programmation C et est utilisée par de nombreux compilateurs C pour l' architecture x86 . Dans cdecl, les arguments de sous-routine sont passés sur la pile . Si les valeurs de retour sont des valeurs entières ou des adresses mémoire, elles sont placées dans le registre EAX par l'appelé, tandis que les valeurs à virgule flottante sont placées dans le registre x87 ST0 . Les registres EAX, ECX et EDX sont sauvegardés par l'appelant et les autres sont sauvegardés par l'appelé. Les registres à virgule flottante x87 ST0 à ST7 doivent être vides (éjectés ou libérés) lors de l'appel d'une nouvelle fonction, et ST1 à ST7 doivent être vides à la sortie d'une fonction. ST0 doit également être vide lorsqu'il n'est pas utilisé pour renvoyer une valeur.

Dans le contexte du langage C, les arguments de fonction sont poussés sur la pile dans l'ordre de droite à gauche (RTL), c'est-à-dire que le dernier argument est poussé en premier.

Considérez l’extrait de code source C suivant :

int appelant ( int , int , int );
int appelant ( void ) { return callee ( 1 , 2 , 3 ) + 5 ; }

Sur x86 , il peut produire le code assembleur suivant ( syntaxe Intel ) :

appelant : 
; créer un nouveau cadre d'appel ; (certains compilateurs peuvent produire une instruction 'enter' à la place) push ebp ; enregistrer l'ancien cadre d'appel mov ebp , esp ; initialiser un nouveau cadre d'appel ; pousser les arguments d'appel, en sens inverse ; (certains compilateurs peuvent soustraire l'espace requis du pointeur de pile, ; puis écrire chaque argument directement, voir ci-dessous. ; L'instruction 'enter' peut aussi faire quelque chose de similaire) ; sub esp, 12 : l'instruction 'enter' pourrait le faire pour nous ; mov [ebp-4], 3 : ou mov [esp+8], 3 ; mov [ebp-8], 2 : ou mov [esp+4], 2 ; mov [ebp-12], 1 : ou mov [esp], 1 push 3 push 2 push 1 appeler l' appelé ; appeler la sous-routine 'appelé' add esp , 12 ; supprimer les arguments d'appel du cadre add eax , 5 ; modifier le résultat de la sous-routine ; (eax est la valeur de retour de notre appelant, ; nous n'avons donc pas besoin de la déplacer dans une variable locale) ; restaurer l'ancien cadre d'appel ; (certains compilateurs peuvent produire une instruction 'leave' à la place) mov esp , ebp ; la plupart des conventions d'appel imposent que ebp soit sauvegardé par l'appelant, ; c'est-à-dire qu'il est conservé après l'appel de l'appelant. ; il pointe donc toujours vers le début de notre cadre de pile. ; nous devons cependant nous assurer ; que l'appelant ne modifie pas (ou ne restaure pas) ebp, ; nous devons donc nous assurer ; qu'il utilise une convention d'appel qui fait cela pop ebp ; restaurer l'ancien cadre d'appel ret ; retourner

L'appelant nettoie la pile après le retour de l'appel de fonction.

La cdeclconvention d'appel est généralement la convention d'appel par défaut pour les compilateurs C x86 , bien que de nombreux compilateurs fournissent des options pour modifier automatiquement les conventions d'appel utilisées. Pour définir manuellement une fonction à cdecl, certains prennent en charge la syntaxe suivante :

type_de_retour __cdecl nom_fonction ();

Variations

Il existe quelques variations dans l'interprétation de cdecl. Par conséquent, les programmes x86 compilés pour différentes plateformes de systèmes d'exploitation et/ou par différents compilateurs peuvent être incompatibles, même s'ils utilisent tous deux la convention « cdecl » et ne font pas appel à l'environnement sous-jacent.

Certains compilateurs renvoient des structures de données simples d'une longueur de 2 registres ou moins dans la paire de registres EAX:EDX et des structures et objets de classe plus grands nécessitant un traitement spécial par le gestionnaire d'exceptions (par exemple, un constructeur, un destructeur ou une affectation définis) sont renvoyés en mémoire. Pour passer « en mémoire », l'appelant alloue de la mémoire et passe un pointeur vers celle-ci en tant que premier paramètre caché ; l'appelé remplit la mémoire et renvoie le pointeur, faisant apparaître le pointeur caché lors du retour.

Sous Linux , GCC définit la norme de facto pour les conventions d'appel. Depuis la version 4.5 de GCC, la pile doit être alignée sur une limite de 16 octets lors de l'appel d'une fonction (les versions antérieures ne nécessitaient qu'un alignement sur 4 octets).

Une version de cdeclest décrite dans System V ABI pour les systèmes i386.

appel système

Ceci est similaire à cdecl dans le sens où les arguments sont poussés de droite à gauche. Cependant, EAX, ECX et EDX ne sont pas conservés et la taille de la liste de paramètres en mots doubles est transmise dans AL.

Syscall est la convention d'appel standard pour l'API OS/2 32 bits .

lien d'option

Les arguments sont poussés de droite à gauche. Les trois premiers arguments (les plus à gauche) sont transmis dans EAX, EDX et ECX et jusqu'à quatre arguments à virgule flottante sont transmis dans ST0 à ST3, bien que de l'espace leur soit réservé dans la liste d'arguments sur la pile. Les résultats sont renvoyés dans EAX ou ST0. Les registres EBP, EBX, ESI et EDI sont conservés.

Optlink est utilisé par les compilateurs IBM VisualAge .

Nettoyage de la rue

Dans ces conventions, l'appelé nettoie les arguments de la pile. Les fonctions qui utilisent ces conventions sont faciles à reconnaître dans le code ASM car elles déroulent la pile après le retour. L' retinstruction x86 autorise un paramètre 16 bits facultatif qui spécifie le nombre d'octets de pile à libérer après le retour à l'appelant. Un tel code ressemble à ceci :

ret 12

Les conventions nommées fastcallou registern'ont pas été normalisées et ont été implémentées différemment, selon le fournisseur du compilateur. En général, les conventions d'appel basées sur des registres transmettent un ou plusieurs arguments dans des registres, ce qui réduit le nombre d'accès mémoire requis pour l'appel et, par conséquent, les rend généralement plus rapides.

Pascal

Basé sur la convention d'appel du langage Borland Turbo Pascal , les paramètres sont poussés sur la pile dans l'ordre de gauche à droite (LTR) (à l'opposé de cdecl), et l'appelé est responsable de leur suppression de la pile.

Le retour du résultat fonctionne comme suit :

  • Les valeurs ordinales sont renvoyées dans AL ( valeurs 8 bits ), AX (valeurs 16 bits), EAX ( valeurs 32 bits ) ou DX:AX (valeurs 32 bits sur les systèmes 16 bits).
  • Les valeurs réelles sont renvoyées dans DX:BX:AX.
  • Les valeurs à virgule flottante (8087) sont renvoyées dans ST0.
  • Les pointeurs sont renvoyés dans EAX sur les systèmes 32 bits et dans AX sur les systèmes 16 bits.
  • Les chaînes sont renvoyées dans un emplacement temporaire pointé par le symbole @Result.

Cette convention d'appel était courante dans les API 16 bits suivantes : OS/2 1.x, Microsoft Windows 3.x et Borland Delphi version 1.x. Les versions modernes de l'API Windows utilisent stdcall , qui fait toujours en sorte que l'appelé restaure la pile comme dans la convention Pascal, mais les paramètres sont désormais poussés de droite à gauche.

appel standard

La convention d'appel stdcall est une variante de la convention d'appel Pascal dans laquelle l'appelé est responsable du nettoyage de la pile, mais les paramètres sont poussés sur la pile dans l'ordre de droite à gauche, comme dans la convention d'appel _cdecl. Les registres EAX, ECX et EDX sont désignés pour être utilisés dans la fonction. Les valeurs de retour sont stockées dans le registre EAX.

stdcall est la convention d'appel standard pour l' API Microsoft Win32 et pour Open Watcom C++ .

Appel rapide Microsoft

La convention Microsoft __fastcall (alias __msfastcall ) transmet les deux premiers arguments (évalués de gauche à droite) qui conviennent à ECX et EDX. Les arguments restants sont poussés sur la pile de droite à gauche. Lorsque le compilateur compile pour IA64 ou AMD64 , il ignore le mot-clé __fastcall (ou tout autre mot-clé de convention d'appel en dehors de __vectorcall) et utilise à la place la convention d'appel 64 bits par défaut de Microsoft .

D'autres compilateurs comme GCC , Clang , et ICC fournissent des conventions d'appel « fastcall » similaires, bien qu'elles ne soient pas nécessairement compatibles entre elles ou avec Microsoft fastcall.

Considérez l’extrait de code C suivant :

__attribute__ (( fastcall )) void printnums ( int num1 , int num2 , int num3 ){ printf ( "Les numéros que vous avez envoyés sont : %d %d %d" , num1 , num2 , num3 ); }
int main (){ printnums ( 1 , 2 , 3 ); return 0 ; }

La décompilation x86 de la fonction principale ressemblera à ceci (dans la syntaxe Intel) :

main: 
; configuration de la pile push ebp mov ebp , esp push 3 ; immédiat 3 (le troisième argument est poussé vers la pile) mov edx , 0x2 ; immédiat 2 (deuxième argument) est copié dans le registre edx. mov ecx , 0x1 ; immédiat 1 (premier argument) est copié dans le registre ecx. call printnums mov eax , 0 ; return 0 leave retn

Les deux premiers arguments sont passés dans l'ordre de gauche à droite, et le troisième argument est poussé sur la pile. Il n'y a pas de nettoyage de la pile, car le nettoyage de la pile est effectué par l'appelé. Le désassemblage de la fonction appelée est le suivant :

printnums : 
; configuration de la pile push ebp mov ebp , esp sub esp , 0x08 mov [ ebp-0x04 ], ecx ; dans x86, ecx = premier argument. mov [ ebp-0x08 ], edx ; arg2 push [ ebp + 0x08 ] ; arg3 est poussé vers la pile. push [ ebp-0x08 ] ; arg2 est poussé push [ ebp-0x04 ] ; arg1 est poussé push 0x8065d67 ; "Les numéros que vous avez envoyés sont %d %d %d" appel printf ; nettoyage de la pile add esp , 0x10 nop leave retn 0x04

Comme les deux arguments ont été transmis via les registres et qu'un seul paramètre a été poussé dans la pile, la valeur poussée est effacée par l'instruction retn, car int a une taille de 4 octets dans les systèmes x86.

Appel vectoriel Microsoft

Dans Visual Studio 2013, Microsoft a introduit la convention d'appel __vectorcall en réponse aux préoccupations d'efficacité des développeurs de jeux, de graphiques, de vidéo/audio et de codecs. Le schéma permet de transmettre des types de vecteurs plus volumineux ( float , double , __m128 , __m256 ) dans des registres plutôt que sur la pile.

Pour le code IA-32 et x64, __vectorcall est similaire à __fastcall et aux conventions d'appel x64 originales, mais les étend pour prendre en charge le passage d'arguments vectoriels à l'aide de registres SIMD . Dans IA-32, les valeurs entières sont passées comme d'habitude, et les six premiers registres SIMD ( XMM / YMM 0-5) contiennent jusqu'à six valeurs à virgule flottante, vectorielles ou HVA séquentiellement de gauche à droite, quelles que soient les positions réelles causées par, par exemple, un argument int apparaissant entre eux. Dans x64, cependant, la règle de la convention x64 originale s'applique toujours, de sorte que XMM/YMM0-5 ne contient que des arguments à virgule flottante, vectoriels ou HVA lorsqu'ils se trouvent être du premier au sixième.

__vectorcall ajoute la prise en charge de la transmission de valeurs d'agrégats vectoriels homogènes (HVA), qui sont des types composites (structures) constitués uniquement de quatre types vectoriels identiques maximum, en utilisant les mêmes six registres. Une fois les registres alloués aux arguments de type vectoriel, les registres inutilisés sont alloués aux arguments HVA de gauche à droite. Les règles de positionnement s'appliquent toujours. Le type vectoriel et les valeurs HVA résultants sont renvoyés à l'aide des quatre premiers registres XMM/YMM.

Le compilateur Clang et le compilateur Intel C++ implémentent également vectorcall. ICC a une convention antérieure similaire appelée __regcall ; elle est également prise en charge par Clang.

Registre Borland

En évaluant les arguments de gauche à droite, il passe trois arguments via EAX, EDX, ECX. Les arguments restants sont poussés sur la pile, également de gauche à droite. C'est la convention d'appel par défaut du compilateur 32 bits de Delphi , où elle est connue sous le nom de register . Cette convention d'appel est également utilisée par C++Builder d'Embarcadero, où elle est appelée __fastcall . Dans ce compilateur, le fastcall de Microsoft peut être utilisé comme __msfastcall .

GCC et Clang peuvent être amenés à utiliser une convention d'appel similaire en utilisant __stdcalll' regparmattribut function ou le -mregparm=3switch. (L'ordre de la pile est inversé.) Il est également possible de produire une variante de nettoyage de l'appelant en utilisant cdeclou d'étendre cela pour utiliser également les registres SSE. Une cdeclversion basée sur est utilisée par le noyau Linux sur i386 depuis la version 2.6.20 (sortie en février 2007).

Registre Watcom

Watcom ne prend pas en charge le mot-clé __fastcall, sauf pour lui attribuer un alias null. La convention d'appel du registre peut être sélectionnée par le biais d'un commutateur de ligne de commande.

Jusqu'à 4 registres sont assignés aux arguments dans l'ordre EAX, EDX, EBX, ECX. Les arguments sont assignés aux registres de gauche à droite. Si un argument ne peut pas être assigné à un registre (par exemple s'il est trop grand), celui-ci et tous les arguments suivants sont assignés à la pile. Les arguments assignés à la pile sont poussés de droite à gauche. Les noms sont déformés en ajoutant un trait de soulignement suffixé.

Les fonctions variadiques reviennent à la convention d'appel basée sur la pile Watcom.

Le compilateur Watcom C/C++ utilise également la directive #pragma aux qui permet à l'utilisateur de spécifier sa propre convention d'appel. Comme l'indique son manuel, « très peu d'utilisateurs sont susceptibles d'avoir besoin de cette méthode, mais si elle est nécessaire, elle peut s'avérer très utile ».

TopSpeed, Clarion, JPI

Les quatre premiers paramètres entiers sont transmis dans les registres eax, ebx, ecx et edx. Les paramètres à virgule flottante sont transmis sur la pile à virgule flottante – registres st0, st1, st2, st3, st4, st5 et st6. Les paramètres de structure sont toujours transmis sur la pile. Les paramètres ajoutés sont transmis sur la pile une fois les registres épuisés. Les valeurs entières sont renvoyées dans eax, les pointeurs dans edx et les types à virgule flottante dans st0.

appel sécurisé

Dans Delphi et Free Pascal sur Microsoft Windows , la convention d'appel safecall encapsule la gestion des erreurs COM ( Component Object Model ), ainsi les exceptions ne sont pas divulguées à l'appelant, mais sont signalées dans la valeur de retour HRESULT , comme l'exige COM/OLE. Lors de l'appel d'une fonction safecall à partir du code Delphi, Delphi vérifie également automatiquement le HRESULT renvoyé et génère une exception si nécessaire.

La convention d'appel safecall est la même que la convention d'appel stdcall, sauf que les exceptions sont renvoyées à l'appelant dans EAX sous forme de HResult (au lieu de FS:[0]), tandis que le résultat de la fonction est transmis par référence sur la pile comme s'il s'agissait d'un paramètre final "out". Lors de l'appel d'une fonction Delphi depuis Delphi, cette convention d'appel apparaîtra comme n'importe quelle autre convention d'appel, car bien que les exceptions soient renvoyées dans EAX, elles sont automatiquement reconverties en exceptions appropriées par l'appelant. Lors de l'utilisation d'objets COM créés dans d'autres langages, les HResults seront automatiquement générés en tant qu'exceptions, et le résultat des fonctions Get se trouve dans le résultat plutôt que dans un paramètre. Lors de la création d'objets COM dans Delphi avec safecall, il n'y a pas besoin de s'inquiéter des HResults, car les exceptions peuvent être générées normalement mais seront considérées comme des HResults dans d'autres langages.

fonction nom_fonction ( a : DWORD ) : DWORD ; safecall ;

Renvoie un résultat et génère des exceptions comme une fonction Delphi normale, mais elle transmet des valeurs et des exceptions comme si c'était le cas :

fonction nom_fonction ( a : DWORD ; out Résultat : DWORD ) : HResult ; stdcall ;

Nettoyage de l'appelant ou de l'appelé

cet appel

Cette convention d'appel est utilisée pour appeler des fonctions membres non statiques C++. Il existe deux versions principales de thiscallcette convention, selon le compilateur et selon que la fonction utilise ou non un nombre variable d'arguments.

Pour le compilateur GCC, thiscallc'est presque identique à cdecl: l'appelant nettoie la pile et les paramètres sont passés dans l'ordre de droite à gauche. La différence est l'ajout du thispointeur , qui est poussé sur la pile en dernier, comme s'il s'agissait du premier paramètre du prototype de fonction.

Sur le compilateur Microsoft Visual C++, le thispointeur est passé dans ECX et c'est l' appelant qui nettoie la pile, reflétant la stdcallconvention utilisée en C pour ce compilateur et dans les fonctions API Windows. Lorsque les fonctions utilisent un nombre variable d'arguments, c'est l'appelant qui nettoie la pile (cf. cdecl).

La thiscallconvention d'appel ne peut être spécifiée explicitement que sur Microsoft Visual C++ 2005 et versions ultérieures. Sur tout autre compilateur, thiscall n'est pas un mot-clé. (Cependant, les désassembleurs, tels que IDA , doivent le spécifier. IDA utilise donc le mot-clé __thiscall pour cela.)

Conservation du registre

Une autre partie d'une convention d'appel est de déterminer quels registres sont assurés de conserver leurs valeurs après un appel de sous-programme. Ce comportement est connu sous le nom de préservation des registres.

Registres enregistrés par l'appelant (volatils)

Selon l'ABI Intel à laquelle se conforme la grande majorité des compilateurs, les EAX, EDX et ECX doivent être libres d'utilisation dans une procédure ou une fonction et n'ont pas besoin d'être conservés.

Comme leur nom l'indique, ces registres à usage général contiennent généralement des informations temporaires (volatiles), qui peuvent être écrasées par n'importe quelle sous-routine.

Il est donc de la responsabilité de l'appelant de pousser chacun de ces registres sur la pile, s'il souhaite restaurer leurs valeurs après un appel de sous-routine.

Registres (non volatiles) enregistrés par l'appelé

Les autres registres sont utilisés pour conserver des valeurs à longue durée de vie (non volatiles), qui doivent être conservées entre les appels.

En d’autres termes, lorsque l’appelant effectue un appel de procédure, il peut s’attendre à ce que ces registres contiennent la même valeur après le retour de l’appelé.

Ainsi, il incombe à l'appelé de les sauvegarder (push au démarrage) et de les restaurer (pop en conséquence) avant de les renvoyer à l'appelant. Comme dans le cas précédent, cette pratique ne doit être effectuée que sur les registres que l'appelé modifie.

Conventions d'appel x86-64

Les conventions d'appel x86-64 tirent parti de l'espace de registre supplémentaire pour passer plus d'arguments dans les registres. De plus, le nombre de conventions d'appel incompatibles a été réduit. Il en existe deux couramment utilisées.

Convention d'appel Microsoft x64

La convention d'appel Microsoft x64 est suivie sur Windows et UEFI pré-démarrage (pour le mode long sur x86-64 ). Les quatre premiers arguments sont placés sur les registres. Cela signifie RCX, RDX, R8, R9 (dans cet ordre) pour les arguments entiers, struct ou pointeur, et XMM0, XMM1, XMM2, XMM3 pour les arguments à virgule flottante. Les arguments ajoutés sont poussés sur la pile (de droite à gauche). Les valeurs de retour entières (similaires à x86) sont renvoyées dans RAX si elles sont de 64 bits ou moins. Les valeurs de retour à virgule flottante sont renvoyées dans XMM0. Les paramètres de moins de 64 bits ne sont pas étendus à zéro ; les bits de poids fort ne sont pas mis à zéro.

Les structures et les unions dont les tailles correspondent à des entiers sont transmises et renvoyées comme s'il s'agissait d'entiers. Sinon, elles sont remplacées par un pointeur lorsqu'elles sont utilisées comme argument. Lorsqu'un retour d'une structure surdimensionnée est nécessaire, un autre pointeur vers un espace fourni par l'appelant est ajouté comme premier argument, décalant tous les autres arguments vers la droite d'une place.

Lors de la compilation pour l'architecture x64 dans un contexte Windows (que vous utilisiez des outils Microsoft ou non Microsoft), stdcall, thiscall, cdecl et fastcall se résolvent tous à l'aide de cette convention.

Dans la convention d'appel Microsoft x64, il incombe à l'appelant d'allouer 32 octets d'« espace fantôme » sur la pile juste avant d'appeler la fonction (quel que soit le nombre réel de paramètres utilisés) et de vider la pile après l'appel. L'espace fantôme est utilisé pour déverser RCX, RDX, R8 et R9, mais doit être mis à disposition de toutes les fonctions, même celles qui ont moins de quatre paramètres.

Les registres RAX, RCX, RDX, R8, R9, R10, R11 sont considérés comme volatils (enregistrés par l'appelant).

Les registres RBX, RBP, RDI, RSI, RSP, R12, R13, R14 et R15 sont considérés comme non volatils (enregistrés par l'appelé).

Par exemple, une fonction prenant 5 arguments entiers prendra le premier au quatrième dans les registres, et le cinquième sera poussé au-dessus de l'espace fantôme. Ainsi, lorsque la fonction appelée est saisie, la pile sera composée (dans l'ordre croissant) de l'adresse de retour, suivie de l'espace fantôme (32 octets) suivi du cinquième paramètre.

Dans x86-64 , Visual Studio 2008 stocke les nombres à virgule flottante dans XMM6 et XMM7 (ainsi que dans XMM8 à XMM15) ; par conséquent, pour x86-64 , les routines en langage assembleur écrites par l'utilisateur doivent conserver XMM6 et XMM7 (par rapport à x86 où les routines en langage assembleur écrites par l'utilisateur n'avaient pas besoin de conserver XMM6 et XMM7). En d'autres termes, les routines en langage assembleur écrites par l'utilisateur doivent être mises à jour pour enregistrer/restaurer XMM6 et XMM7 avant/après la fonction lors du portage de x86 vers x86-64 .

À partir de Visual Studio 2013, Microsoft a introduit la convention d’appel __vectorcall qui étend la convention x64.

Système V AMD64 ABI

La convention d'appel de l' ABI System V AMD64 est suivie sur Solaris , Linux , FreeBSD , macOS , et est la norme de facto parmi les systèmes d'exploitation Unix et de type Unix. La norme d'appel OpenVMS sur x86-64 est basée sur l'ABI System V avec quelques extensions nécessaires à la compatibilité ascendante. Les six premiers arguments entiers ou pointeurs sont passés dans les registres RDI, RSI, RDX, RCX, R8, R9 (R10 est utilisé comme pointeur de chaîne statique dans le cas de fonctions imbriquées ), tandis que XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6 et XMM7 sont utilisés pour les premiers arguments à virgule flottante. Comme dans la convention d'appel Microsoft x64, les arguments ajoutés sont passés sur la pile. Les valeurs de retour entières jusqu'à 64 bits sont stockées dans RAX tandis que les valeurs jusqu'à 128 bits sont stockées dans RAX et RDX. Les valeurs de retour à virgule flottante sont stockées de la même manière dans XMM0 et XMM1. Les registres YMM et ZMM plus larges sont utilisés pour transmettre et renvoyer des valeurs plus larges à la place de XMM lorsqu'elles existent.

Les paramètres de structure et d'union avec des tailles de deux pointeurs (huit dans le cas de champs SSE uniquement) ou moins qui sont alignés sur des limites de 64 bits sont décomposés en « huit octets » et chacun est classé et passé en tant que paramètre séparé. Sinon, ils sont remplacés par un pointeur lorsqu'ils sont utilisés comme argument. Les types de retour de structure et d'union avec des tailles de deux pointeurs ou moins sont renvoyés dans RAX et RDX (ou XMM0 et XMM1). Lorsqu'un retour de structure surdimensionné est nécessaire, un autre pointeur vers un espace fourni par l'appelant est ajouté comme premier argument, décalant tous les autres arguments vers la droite d'une place, et la valeur de ce pointeur est renvoyée dans RAX.

Si l'appelé souhaite utiliser les registres RBX, RSP, RBP et R12–R15, il doit restaurer leurs valeurs d'origine avant de rendre le contrôle à l'appelant. Tous les autres registres doivent être sauvegardés par l'appelant s'il souhaite conserver leurs valeurs.

Pour les fonctions de type leaf-node (fonctions qui n'appellent aucune autre fonction), un espace de 128 octets est stocké juste en dessous du pointeur de pile de la fonction. L'espace est appelé la zone rouge . Cette zone ne sera pas écrasée par aucun signal ou gestionnaire d'interruption. Les compilateurs peuvent donc utiliser cette zone pour sauvegarder des variables locales. Les compilateurs peuvent omettre certaines instructions au démarrage de la fonction (ajustement de RSP, RBP) en utilisant cette zone. Cependant, d'autres fonctions peuvent écraser cette zone. Par conséquent, cette zone ne doit être utilisée que pour les fonctions de type leaf-node. gccet clangpropose l' -mno-red-zoneoption permettant de désactiver les optimisations de la zone rouge.

Si l'appelé est une fonction variadique , le nombre d'arguments à virgule flottante passés à la fonction dans les registres vectoriels doit être fourni par l'appelant dans le registre AL.

Contrairement à la convention d'appel Microsoft, aucun espace fantôme n'est fourni ; lors de l'entrée de la fonction, l'adresse de retour est adjacente au septième argument entier de la pile.

Liste des conventions d'appel x86

Il s'agit d'une liste de conventions d'appel x86. Il s'agit de conventions principalement destinées aux compilateurs C/C++ (en particulier la partie 64 bits ci-dessous), et donc en grande partie à des cas particuliers. D'autres langages peuvent utiliser d'autres formats et conventions dans leurs implémentations.

Notes de bas de page

Autres sources

  • Interface binaire d'application System V : Supplément pour processeur d'architecture Intel386 (PDF) (4e éd.). The Santa Cruz Operation, Inc. 1997-03-19.
  • Trifunovic, Nemanja (2001-07-22). Ewington, Sean (éd.). « Les conventions d'appel démystifiées ». Le projet Code .
  • Friedl, Stephen J. « Conventions d'appel de fonction Intel x86 – Vue assemblage ». Conseils techniques Unixwiz.net de Steve Friedl .
  • « Visual Studio 2010 – Convention d'appel de Visual C++ ». Bibliothèque MSDN . Microsoft. 2010.
  • Jonsson, Andreas (13/02/2005). « Conventions d'appel sur la plate-forme x86 ».
  • Chen, Raymond (2004-01-02). "L'histoire des conventions d'appel, partie 1". The Old New Thing .
  • Chen, Raymond (2004-01-07). "L'histoire des conventions d'appel, partie 2". La vieille chose nouvelle .
  • Chen, Raymond (2004-01-08). "L'histoire des conventions d'appel, partie 3". The Old New Thing .
  • Chen, Raymond (13/01/2004). « L'histoire des conventions d'appel, partie 4 : ia64 ». La vieille chose nouvelle .
  • Chen, Raymond (14/01/2004). « L'histoire des conventions d'appel, partie 5 ; amd64 ». La vieille chose nouvelle .

Lectures complémentaires

  • de Boyne Pollard, Jonathan (2010). "La génération sur les conventions d'appel de fonctions". Réponses fréquemment données .
  • Irvine, Kip R. (2011). « Procédures avancées (chapitre 8) ». Langage d'assemblage pour processeurs x86 (6e éd.). Prentice Hall. ISBN 978-0-13-602212-1.
  • Guide de l'utilisateur de Borland C/C++ version 3.1 (PDF) . Borland. 1992. pp. 158, 189–191.
  • Lauer, Thomas (1995). « La nouvelle séquence d'appel __stdcall ». Portage vers Win32 : Guide pour préparer vos applications à l'avenir 32 bits de Windows . Springer. ISBN 978-0-387-94572-9.
Sujets d'assemblage x86
Original text
Rate this translation
Your feedback will be used to help improve Google Translate