
FAQ AssembleurConsultez toutes les FAQ
Nombre d'auteurs : 8, nombre de questions : 50, dernière mise à jour : 29 avril 2007
Sommaire→F.A.Q. Assembleur x86 / 64→Questions spécifiques à la programmation- Quels compilateurs pour la programmation x86 64 bits ?
- Visual C++ déclenche une erreur lorsque j'utilise le mot clé _asm, pourquoi ?
- Quelle est la convention d'appel pour les processeurs x86 64 bits ?
- Quels sont les registres utilisés pour les retours de fonctions ?
- Quels sont les registres préservés ou détruits lors d'un appel de fonction ?
- Peut-on utiliser le coprocesseur mathématique (x87) et les MMX sous Windows ?
- Comment est le stack frame avant et/ou après l'appel de fonction ?
- Quel est le problème avec l'alignement de la pile en mode 64 bits avant d'appeler une API ?
Windows :
- GoAsm : http://www.jorgon.freeserve.co.uk/
- MASM (64) : fait partie du SDK de Windows (exécutable ml64.exe)
Cross plate-forme (au minimum Linux & Windows) :
- FASM : http://flatassembler.net/
- YASM : http://www.tortall.net/projects/yasm/ (syntaxe NASM)
- Gas : (au travers de GCC par exemple) http://www.gnu.org/software/binutils/
(merci à Gamera pour le lien vers FASM)
Visual C++ ne permet plus l'utilisation de l'inlining Assembleur (Assembleur incorporé dans un programme en C ou C++).
Pour pallier à ce problème vous pouvez :
- Utiliser les intrinsics pour x64 : http://msdn2.microsoft.com/en-us/library/26td21ds(VS.80).aspx
- Utiliser un fichier .asm séparé du code en langage de haut niveau.
Il n'existe plus qu'une seule convention d'appel en mode 64 bits.
Cette dernière est semblable au type d'appel _fastcall, ce qui implique que les arguments, lors d'un appel de fonction, sont d'abord passés au travers des registres.
Pour Windows :
- Lorsqu'il y a 4 arguments entiers (ou moins), ceux-ci sont passés par les registres suivants, dans cet ordre précis : RCX, RDX, R8 et R9.
- Lorsqu'il y a plus de 4 arguments entiers, ceux-ci sont poussés sur la pile.
- Les arguments à virgule flottante (double ou float) sont pris en charge par les registres XMM (dans cet ordre précis) : XMM0, XMM1, XMM2, XMM3.
- Les arguments supérieurs à 64 bits (par ex. les doubles quadword [128 bits]) poussent leurs adresses sur la pile.
Ref. : http://msdn2.microsoft.com/en-us/library/ms235286(VS.80).aspx
Ref. : http://msdn2.microsoft.com/en-us/library/zthk2dkh(VS.80).aspx
Ref. : https://www.microsoft.com/france/msdn/visualc/introduction-convention-appel-64bits.mspx
Pour Linux :
- Lorsqu'il y a 6 arguments entiers (ou moins), ceux-ci sont passés par les registres suivants, dans cet ordre précis : RDI, RSI, RDX, RCX, R8, R9.
- Lorsqu'il y a plus de 6 arguments entiers, ceux-ci sont poussés sur la pile.
- Les arguments à virgule flottante (double ou float) sont pris en charge par les registres XMM (dans cet ordre précis) : XMM0 à XMM7.
- Les arguments supérieurs à 64 bits (par ex. les doubles quadword [128 bits]) poussent leurs adresses sur la pile.
Ref. : AMD64 ABI.
Pour Windows :
La valeur renvoyée par une fonction est placée dans le registre RAX, à moins que le résultat soit un type en virgule flottante, qui est alors renvoyé dans XMM0.
Pour Linux :
La valeur renvoyée par une fonction est placée dans le registre RAX et/ou RDX, à moins que le résultat soit un type en virgule flottante, qui est alors renvoyé dans XMM0 et/ou XMM1.
Le retour de fonction peut aussi s'effectuer sous ST(0) ou ST(1) si la fonction manipule des données via le coprocesseur mathématique à virgule flottante (x87).
Pour Windows :
- D'un appel à l'autre, les registres suivants doivent être préservés : RBX, RBP, RDI, RSI, R12 à R15, XMM6 à XMM15.
- Les registres suivants sont volatiles et peuvent donc être détruits dans la fonction appelée : RAX, RCX, RDX, R8 à R11, ST(0) à ST(7), XMM0 à XMM5.
Ref. : http://msdn2.microsoft.com/en-us/library/9z1stfyw(VS.80).aspx
Ref. : http://msdn2.microsoft.com/en-us/library/6t169e9c(VS.80).aspx
Pour Linux :
- D'un appel à l'autre, les registres suivants doivent être préservés : RBX, RBP, R12 à R15.
- Les registres suivants sont volatiles et peuvent donc être détruits dans la fonction appelée : RAX, RCX, RDX, RSI, RDI, R8 à R11, ST(0) à ST(7), XMM0 à XMM15.
Contrairement à ce qu'avait annoncé dans un premier temps Microsoft la réponse finale et courte est : oui, sauf dans les drivers.
Pour préciser cette réponse, Microsoft annonçait que la commutation de contexte (context switch) effaçait les registres ST(x) et XMM.
Toutefois cette information s'est révélée fausse et a été corrigé par un responsable du programme Visual C++ et un programmeur du groupe Kernel chez Microsoft :
"Let them know that the OS does preserve state of x87 and MMX registers on context switches."
[...]
"For user threads the state of legacy floating point is preserved at context switch. But it is not true for kernel threads. Kernel mode drivers can not use legacy floating point instructions."
Ref. : http://www.planetamd64.com/index.php?showtopic=3458&view=findpost&p=68756
La procédure (ou fonction) appelante est responsable de l'allocation de l'espace nécessaire sur la pile pour la procédure appelée.
Elle (la procédure appelante) doit toujours allouer suffisamment d'espace pour les 4 registres en paramètres même si la procédure appelée ne nécessite pas autant d'arguments.
La question est assez complexe; pour plus d'informations voir la référence ci-après :
Ref. : http://msdn2.microsoft.com/en-us/library/ew5tede7(VS.80).aspx
Bien que la pile utilise des quadruples mots (8 octets soit 64 bits), le pointeur de pile (RSP) doit toujours être aligné sur un multiple de 16 avant d'appeler une API.
Ref. : AMD64 ABI
Ref. : http://msdn2.microsoft.com/en-us/library/ew5tede7(VS.80).aspx
Pour se prémunir de ce problème, on peut utiliser un code comme celui-ci :
PUSH RSP ; sauvegarde la position courante de RSP sur la pile
PUSH [RSP] ; garde une autre copie sur la pile
AND SPL,0F0h ; ajuste RSP pour aligner la pile au cas où elle ne le serait pas.
;
; paramètre(s) de l'API s'il y a lieu.
;
SUB RSP, 32d ; ajuste RSP pour la sauvegarde des paramètres (32d = 0x20)
CALL API
ADD RSP, xx ; nettoie la pile (avec xx = 32d + (nombre de paramètre poussés * 8))
POP RSP ; restaure RSP à sa valeur originale.
N.B : Certaines APIs supportent une pile mal alignée sans déclencher d'exception. Certaines déclenchent une exception en interne et rétablissent (toujours en interne) une pile alignée. D'autres encore ne supportent aucunement une pile mal alignée. Dans le doute : alignez la pile avant l'appel.



