Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
ARM64EC est une interface ABI (Application Binary Interface) qui permet aux binaires ARM64 de s’exécuter de façon native et interopérable avec du code x64. Plus précisément, l’interface ABI ARM64EC suit les conventions logicielles x64, notamment la convention d’appel, l’utilisation de piles et l’alignement de données, ce qui rend ARM64EC et le code x64 interopérables. Le système d’exploitation émule la partie x64 du binaire. (EC dans ARM64EC est la contraction d’émulation compatible.)
Pour plus d’informations sur les interfaces ABI x64 et ARM64, consultez Vue d’ensemble des conventions de l’interface ABI x64 et Vue d’ensemble des conventions de l’interface ABI ARM64.
ARM64EC ne résout pas les différences de modèle de mémoire entre les architectures x64 et ARM. Pour plus d’informations, consultez Problèmes courants de migration ARM Microsoft C++.
Définitions
- ARM64 : flux de code pour les processus ARM64 qui contiennent du code ARM64 traditionnel.
- ARM64EC : flux de code qui utilise un sous-ensemble du jeu de registres ARM64 pour assurer l’interopérabilité avec le code x64.
Mappage de registres
Les processus x64 peuvent avoir des threads qui exécutent du code ARM64EC. Donc, il est toujours possible de récupérer un contexte de registre x64. ARM64EC utilise un sous-ensemble des registres de base ARM64 qui correspondent aux registres x64 émulés, un pour un. Il est important de noter qu’ARM64EC n’utilise jamais de registres en dehors de ce sous-ensemble, sauf pour lire l’adresse TEB (Thread Environment Block) de x18.
Les processus ARM64 natifs ne doivent pas régresser en performances quand des fonctions, aussi nombreuses soient-elles, sont recompilées sous la forme ARM64EC. Pour maintenir les performances, l’interface ABI suit ces principes :
Le sous-ensemble de registres ARM64EC englobe tous les registres qui font partie de la convention d’appel de fonction ARM64.
La convention d'appel ARM64EC correspond directement à la convention d'appel ARM64.
Des routines d’assistance spéciales telles que __chkstk_arm64ec utilisent des conventions d’appel personnalisées et des registres. Ces registres sont également inclus dans le sous-ensemble ARM64EC des registres.
Mappage de registres pour les registres d’entiers
| Registre ARM64EC | Registre x64 | Convention d’appel ARM64EC | Convention d’appel ARM64 | Convention d’appel x64 |
|---|---|---|---|---|
x0 |
rcx |
volatil | volatil | volatil |
x1 |
rdx |
volatil | volatil | volatil |
x2 |
r8 |
volatil | volatil | volatil |
x3 |
r9 |
volatil | volatil | volatil |
x4 |
r10 |
volatil | volatil | volatil |
x5 |
r11 |
volatil | volatil | volatil |
x6 |
mm1 (64 bits bas du registre R1 x87) |
volatil | volatil | volatil |
x7 |
mm2 (64 bits bas du registre R2 x87) |
volatil | volatil | volatil |
x8 |
rax |
volatil | volatil | volatil |
x9 |
mm3 (64 bits inférieurs du registre R3 x87) |
volatil | volatil | volatil |
x10 |
mm4 (partie basse de 64 bits du registre R4 x87) |
volatil | volatil | volatil |
x11 |
mm5 (64 bits inférieurs du registre x87 R5) |
volatil | volatil | volatil |
x12 |
mm6 (les 64 bits inférieurs du registre R6 x87) |
volatil | volatil | volatil |
x13 |
Non applicable | non autorisée | volatil | N/A |
x14 |
Non applicable | non autorisé | volatil | S/O |
x15 |
mm7 (64 bits bas du registre R7 x87) |
volatil | volatil | volatil |
x16 |
16 bits supérieurs de chaque registre x87 R0-R3 |
volatile(xip0) |
volatile(xip0) |
volatil |
x17 |
16 bits hauts de chacun des registres R4-R7 x87 |
volatile(xip1) |
volatile(xip1) |
volatil |
x18 |
GS.base | fixé(TEB) | fixé(TEB) | fixé(TEB) |
x19 |
r12 |
non volatile | non volatile | non volatile |
x20 |
r13 |
non volatile | non volatile | non volatile |
x21 |
r14 |
non volatile | non volatile | non volatile |
x22 |
r15 |
non volatile | non volatile | non volatile |
x23 |
S/O | non autorisé | non volatile | S/O |
x24 |
S/O | non autorisé | non volatile | S/O |
x25 |
rsi |
non volatile | non volatile | non volatile |
x26 |
rdi |
non volatile | non volatile | non volatile |
x27 |
rbx |
non volatile | non volatile | non volatile |
x28 |
S/O | non autorisé | non autorisé | S/O |
fp |
rbp |
non volatile | non volatile | non volatile |
lr |
mm0 (64 bits bas du registre R0 x87) |
Les deux | Les deux | Les deux |
sp |
rsp |
non volatile | non volatile | non volatile |
pc |
rip |
pointeur d’instruction | pointeur d’instruction | pointeur d’instruction |
PSTATE sous-ensemble : N/Z/C/V/SS1, 2 |
RFLAGS sous-ensemble : SF/ZF/CF/OF/TF |
volatil | volatil | volatil |
| S/O |
RFLAGS sous-ensemble : PF/AF |
S/O | S/O | volatil |
| S/O |
RFLAGS sous-ensemble : DF |
S/O | Sans Objet | non volatile |
1 Évitez de lire, écrire ou calculer directement des mappages entre PSTATE et RFLAGS. Ces bits pourraient être utilisés à l'avenir et sont susceptibles de changer.
2 L’indicateur de retenue ARM64EC C est l’inverse de l’indicateur de retenue x64 CF pour les opérations de soustraction. Aucun traitement particulier n’est nécessaire, car l’indicateur est volatil et donc jeté lors de la transition d’une fonction à l’autre (ARM64EC et x64).
Mappage de registres pour les registres vectoriels
| Registre ARM64EC | Registre x64 | Convention d’appel ARM64EC | Convention d’appel ARM64 | Convention d’appel x64 |
|---|---|---|---|---|
v0-v5 |
xmm0-xmm5 |
volatil | volatil | volatil |
v6-v7 |
xmm6-xmm7 |
volatil | volatil | non volatile |
v8-v15 |
xmm8-xmm15 |
volatile 1 | volatile 1 | non volatile |
v16-v31 |
xmm16-xmm31 |
non autorisé | volatil | non autorisée (l’émulateur x64 ne prend pas en charge AVX-512) |
FPCR
2 |
MXCSR[15:6] |
non volatile | non volatile | non volatile |
FPSR
2 |
MXCSR[5:0] |
volatil | volatil | volatil |
1 Ces registres ARM64 sont particuliers dans le sens où les 64 bits inférieurs ne sont pas volatils alors que les 64 bits supérieurs le sont. Du point de vue d’un appelant x64, ils sont effectivement volatils parce que l’appelé jette les données.
2 Évitez de lire, écrire ou calculer directement des mappages de FPCR et de FPSR. Ces bits pourraient être utilisés à l'avenir et sont susceptibles de changer.
Emballage de structures
ARM64EC suit les mêmes règles de compression de structs que celles utilisées pour x64 afin de garantir l’interopérabilité entre le code ARM64EC et le code x64. Pour plus d’informations et des exemples de compression de structs x64, consultez Vue d’ensemble des conventions de l’interface ABI x64.
Exceptions à virgule flottante
Pour déterminer si un processeur ARM prend en charge les exceptions, écrivez une valeur qui active les exceptions dans le registre FPCR, puis lisez-la. Si l’UC prend en charge les exceptions à virgule flottante, les bits correspondant aux exceptions prises en charge restent définis, tandis que l’UC réinitialise les bits pour les exceptions non prises en charge.
Sur ARM64EC, Windows intercepte les exceptions à virgule flottante du processeur et les désactive dans le registre FPCR. Cela garantit un comportement cohérent entre différentes variantes de processeur.
Routines ABI auxiliaires d’émulation
Le code ARM64EC et les thunks utilisent des routines d’assistance d’émulation pour passer d’une fonction x64 à une fonction ARM64EC.
Le tableau suivant décrit chaque routine ABI spéciale et les registres que l’interface ABI utilise. Les routines ne modifient pas les registres conservés listés sous la colonne ABI. Aucune hypothèse ne doit être établie sur les registres non listés. Sur disque, les pointeurs de routine ABI sont nuls. Au moment du chargement, le chargeur met à jour les pointeurs pour les faire pointer vers les routines de l’émulateur x64.
| Nom | Descriptif | ABI |
|---|---|---|
__os_arm64x_dispatch_call_no_redirect |
Appelée par un thunk de sortie pour invoquer une cible x64 (soit une fonction x64, soit une séquence accélérée x64). La routine pousse l'adresse de retour ARM64EC (dans le registre LR) suivie de l'adresse de l'instruction qui succède à une instruction blr x16 qui invoque l'émulateur x64. Elle exécute ensuite l’instruction blr x16 |
valeur de retour en x8 (rax) |
__os_arm64x_dispatch_ret |
Appelée par un thunk d’entrée pour revenir à son appelant x64. Elle affiche l’adresse de retour x64 de la pile et appelle l’émulateur x64 pour y accéder | S/O |
__os_arm64x_check_call |
Appelée par le code ARM64EC avec un pointeur vers un thunk de sortie et l’adresse cible ARM64EC indirecte à exécuter. La cible ARM64EC est considérée comme corrigeable et l’exécution revient toujours à l’appelant avec les mêmes données avec lesquelles elle a été appelée, ou avec des données modifiées | Arguments :x9 : Adresse ciblex10 : Adresse du thunk de sortiex11 : Adresse de la séquence d'avance rapideSortie : x9 : Si la fonction cible a été déviée, elle contient l’adresse de la séquence fast-forwardx10 : Adresse du thunk de sortiex11 : Si la fonction a été déviée, elle contient l’adresse du thunk de sortie. Sinon, l’adresse cible est redirigée versRegistres conservés : x0-x8, x15 (chkstk). et q0-q7 |
__os_arm64x_check_icall |
Appelée par le code ARM64EC, avec un pointeur vers le thunk de sortie, pour gérer un saut vers une adresse cible qui est soit x64 soit ARM64EC. Si la cible est x64 et que le code x64 n’a pas été corrigé, la routine définit le registre d’adresses cibles. Elle pointe vers la version ARM64EC de la fonction s’il en existe une. Sinon, il définit le registre pour qu'il pointe vers le "thunk" de sortie qui passe à la cible x64. Ensuite, elle retourne au code ARM64EC de l’appelant, qui accède ensuite à l’adresse dans le registre. Cette routine est une version non optimisée de __os_arm64x_check_call, où l’adresse cible n’est pas connue au moment de la compilationUtilisée à un point d'appel pour un appel indirect |
Arguments :x9 : Adresse ciblex10 : L'adresse de sortie du thunkx11 : Adresse de la séquence d'avance rapideSortie : x9 : Si la fonction cible a été déviée, elle contient l’adresse de la séquence fast-forwardx10 : Adresse du thunk de sortiex11 : Si la fonction a été déviée, elle contient l’adresse du thunk de sortie. Sinon, l’adresse cible est redirigée versRegistres conservés : x0-x8, x15 (chkstk) et q0-q7 |
__os_arm64x_check_icall_cfg |
Identique à __os_arm64x_check_icall, mais vérifie également que l’adresse spécifiée est une cible d’appel indirect de graphe de flux de contrôle valide |
Arguments :x10 : Adresse du thunk de sortiex11 : Adresse de la fonction cibleSortie : x9 : Si la cible est x64, adresse de la fonction. Sinon, non définiex10 : Adresse du thunk de sortiex11 : Si la cible est x64, elle contient l’adresse du thunk de sortie. Sinon, adresse de la fonctionRegistres conservés : x0-x8, x15 (chkstk) et q0-q7 |
__os_arm64x_get_x64_information |
Obtient la section demandée du contexte du registre x64 en cours d'exécution | _Function_class_(ARM64X_GET_X64_INFORMATION) NTSTATUS LdrpGetX64Information(_In_ ULONG Type, _Out_ PVOID Output, _In_ PVOID ExtraInfo) |
__os_arm64x_set_x64_information |
Définit la partie demandée du contexte actuel de registre x64 | _Function_class_(ARM64X_SET_X64_INFORMATION) NTSTATUS LdrpSetX64Information(_In_ ULONG Type,_In_ PVOID Input, _In_ PVOID ExtraInfo) |
__os_arm64x_x64_jump |
Utilisée dans un ajustement sans signature et d'autres thunks qui transfèrent directement (jmp) un appel à une autre fonction pouvant avoir n'importe quelle signature, tout en reportant l'application potentielle du thunk approprié à la cible réelle. |
Arguments :x9 : cible à atteindreTous les registres de paramètres conservés (transférés) |
Thunks
Les thunks sont les mécanismes de bas niveau permettant de prendre en charge les fonctions ARM64EC et x64 qui s’appellent mutuellement. Il en existe deux types : les thunks d’entrée pour entrer des fonctions ARM64EC et les thunks de sortie pour appeler des fonctions x64.
Thunk d’entrée et thunks intrinsèques d’entrée : appel de fonction x64 vers ARM64EC
Pour prendre en charge les appelants x64 lorsqu’une fonction C/C++ est compilée en tant qu’ARM64EC, la chaîne d’outils génère un seul thunk d’entrée constitué de code machine ARM64EC. Les intrinsèques ont un thunk d’entrée à eux. Toutes les autres fonctions partagent un thunk d’entrée avec toutes les fonctions qui ont une convention d’appel, des paramètres et un type de retour correspondants. Le contenu du thunk dépend de la convention d’appel de la fonction C/C++.
En plus de la gestion des paramètres et de l’adresse de retour, le thunk réduit les différences de volatilité entre les registres vectoriels ARM64EC et x64 qu’entraîne le mappage de registres vectoriels ARM64EC :
| Registre ARM64EC | Registre x64 | Convention d’appel ARM64EC | Convention d’appel ARM64 | Convention d’appel x64 |
|---|---|---|---|---|
v6-v15 |
xmm6-xmm15 |
volatile, mais enregistrée/restaurée dans le thunk d’entrée (x64 à ARM64EC) | volatile ou partiellement volatile pour les 64 bits supérieurs | non volatile |
Le thunk d’entrée effectue les actions suivantes :
| Nombre de paramètres | Utilisation de la pile |
|---|---|
| 0-4 | Stocke ARM64EC v6 et v7 dans l’espace d’accueil alloué par l’appelantÉtant donné que le destinataire est ARM64EC, qui n’a pas la notion de "home space", les valeurs stockées ne sont pas écrasées. Alloue 128 octets supplémentaires sur la pile et stocke ARM64EC v8 via v15. |
| 5-8 |
x4 = 5ème paramètre de la pilex5 = 6ème paramètre de la pilex6 = 7ème paramètre de la pilex7 = 8ème paramètre de la pileSi le paramètre est SIMD, les registres v4-v7 sont utilisés à la place |
| 9+ | Alloue les octets AlignUp(NumParams - 8 , 2) * 8 sur la pile. *Copie le 9ème paramètre et les paramètres restants dans cette zone. |
* L’alignement de la valeur sur un nombre pair garantit que la pile reste alignée sur 16 octets
Si la fonction accepte un paramètre entier de 32 bits, le thunk est autorisé à envoyer (push) uniquement 32 bits au lieu des 64 bits du registre parent.
Ensuite, le thunk utilise une instruction ARM64 bl pour appeler la fonction ARM64EC. Une fois la fonction retournée, le thunk :
- Annule toutes les allocations de la pile
- Appelle la fonction d’assistance de l’émulateur
__os_arm64x_dispatch_retpour afficher l’adresse de retour x64 et reprendre l’émulation x64.
Thunk de sortie : appel de fonction de ARM64EC à x64
Pour chaque appel qu’une fonction ARM64EC C/C++ effectue au code x64 potentiel, la chaîne d’outils MSVC génère un thunk de sortie. Le contenu du thunk dépend des paramètres de l’appelant x64 et de l’utilisation par celui-ci de la convention d’appel standard ou de __vectorcall. Le compilateur obtient ces informations d’une déclaration de fonction pour l’appelé.
Premièrement, le thunk envoie (push) l’adresse de retour qui se trouve dans le registre ARM64EC lr et une valeur factice de 8 octets pour garantir que la pile est alignée sur 16 octets. Deuxièmement, le thunk gère les paramètres :
| Nombre de paramètres | Utilisation de la pile |
|---|---|
| 0-4 | Alloue 32 octets d’espace d’accueil sur la pile |
| 5-8 | Alloue AlignUp(NumParams - 4, 2) * 8 octets supplémentaires plus haut dans la pile. * Copie le 5ème paramètre et les suivants du x4-x7 d’ARM64EC vers cet espace supplémentaire |
| 9+ | Copie le 9ème paramètre et tous ceux qui restent dans l’espace supplémentaire |
* L’alignement de la valeur sur un nombre pair garantit que la pile reste alignée sur 16 octets.
Troisièmement, le thunk appelle la fonction d’assistance de l’émulateur __os_arm64x_dispatch_call_no_redirect pour appeler l’émulateur x64 afin d’exécuter la fonction x64. L’appel doit être une instruction blr x16 (x16 est un registre volatil, ce qui est pratique). Une instruction blr x16 est requise, car l’émulateur x64 analyse cette instruction en tant qu’indicateur.
La fonction x64 tente généralement de revenir à la fonction d’assistance de l’émulateur à l’aide d’une instruction ret x64. À ce stade, l’émulateur x64 détecte qu’il se trouve dans du code ARM64EC. Il lit ensuite l’indicateur de 4 octets précédent qui se trouve être l’instruction ARM64 blr x16. Étant donné que cet indicateur indique que l’adresse de retour se trouve dans cette fonction d’assistance, l’émulateur accède directement à cette adresse.
La fonction x64 est autorisée à revenir à l'assistant de l'émulateur en utilisant n'importe quelle instruction de branchement, y compris x64 jmp et call. L’émulateur gère également ces scénarios.
Lorsque l'assistant revient ensuite au thunk, celui-ci :
- Annule toute allocation de la pile
- Fait apparaître le registre ARM64EC
lr - Exécute une instruction ARM64
ret lr.
Décoration de noms de fonction ARM64EC
Un nom de fonction ARM64EC a une décoration secondaire appliquée après toute décoration spécifique au langage. Pour les fonctions avec liaison C (compilées en tant que C ou à l’aide de extern "C"), un #précède le nom. Pour les fonctions C++ décorées, une balise $$h est insérée dans le nom.
foo => #foo
?foo@@YAHXZ => ?foo@@$$hYAHXZ
__vectorcall
La chaîne d’outils ARM64EC ne prend pas en charge __vectorcall actuellement. Le compilateur émet une erreur lorsqu’il détecte l’utilisation de __vectorcall avec ARM64EC.
Voir aussi
Présentation de l’interface ABI ARM64EC et du code d’assembly
Microsoft C++ ARM problèmes courants de migration
Noms décorés