Condividi tramite


Panoramica delle convenzioni ABI di ARM64EC

ARM64EC è un'interfaccia ABI (Application Binary Interface) che consente l'esecuzione dei file binari ARM64 in modo nativo e interoperabile con codice x64. In particolare, il ARM64EC ABI segue convenzioni software x64, tra cui convenzione di chiamata, utilizzo dello stack e allineamento dei dati, rendendo ARM64EC e x64 codice interoperabile. Il sistema operativo emula la parte x64 del file binario. (L'EC in ARM64EC è l'acronimo di emulation compatible.)

Per altre informazioni sulle convenzioni ABI x64 e ARM64, vedere Panoramica delle convenzioni ABI x64 e Panoramica delle convenzioni ABI ARM64.

ARM64EC non risolve le differenze tra le architetture basate su ARM e x64. Per altre informazioni, vedere Problemi comuni di migrazione arm di Visual C++.

Definizioni

  • ARM64 : flusso di codice per i processi ARM64 che contiene il codice ARM64 tradizionale.
  • ARM64EC: flusso di codice che usa un subset del set di registri ARM64 per garantire l'interoperabilità con il codice x64.

Registrare il mapping

I processi x64 possono avere thread che eseguono ARM64EC codice. È quindi sempre possibile recuperare un contesto di registro x64, ARM64EC usa un subset dei registri di base ARM64 che eseguono il mapping 1:1 ai registri x64 emulati. Importante, ARM64EC non usa mai registri all'esterno di questo subset, tranne per leggere l'indirizzo TEB (Thread Environment Block) da x18.

I processi ARM64 nativi non devono regredire le prestazioni quando alcune o più funzioni vengono ricompilate come ARM64EC. Per mantenere le prestazioni, l'ABI segue questi principi:

  • Il subset di registrazione ARM64EC include tutti i registri che fanno parte della convenzione di chiamata di funzione ARM64.

  • La convenzione di chiamata ARM64EC esegue direttamente il mapping alla convenzione di chiamata ARM64.

Routine helper speciali come __chkstk_arm64ec l'uso di convenzioni di chiamata personalizzate e registri. Questi registri sono inclusi anche nel sottoinsieme di registri ARM64EC.

Registrare il mapping per i registri integer

ARM64EC registro Registro x64 convenzione di chiamata ARM64EC Convenzione di chiamata ARM64 Convenzione di chiamata x64
x0 rcx volatile volatile volatile
x1 rdx volatile volatile volatile
x2 r8 volatile volatile volatile
x3 r9 volatile volatile volatile
x4 r10 volatile volatile volatile
x5 r11 volatile volatile volatile
x6 mm1 (basso 64 bit di registro x87 R1 ) volatile volatile volatile
x7 mm2 (basso 64 bit di registro x87 R2 ) volatile volatile volatile
x8 rax volatile volatile volatile
x9 mm3 (basso 64 bit di registro x87 R3 ) volatile volatile volatile
x10 mm4 (basso 64 bit di registro x87 R4 ) volatile volatile volatile
x11 mm5 (basso 64 bit di registro x87 R5 ) volatile volatile volatile
x12 mm6 (basso 64 bit di registro x87 R6 ) volatile volatile volatile
x13 N/D Consentita volatile N/D
x14 N/D Consentita volatile N/D
x15 mm7 (basso 64 bit di registro x87 R7 ) volatile volatile volatile
x16 Alto 16 bit di ognuno dei registri x87 R0-R3 volatile(xip0) volatile(xip0) volatile
x17 Alto 16 bit di ognuno dei registri x87 R4-R7 volatile(xip1) volatile(xip1) volatile
x18 GS.base fixed(TEB) fixed(TEB) fixed(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 N/D Consentita non volatile N/D
x24 N/D Consentita non volatile N/D
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 N/D Consentita Consentita N/D
fp rbp non volatile non volatile non volatile
lr mm0 (basso 64 bit di registro x87 R0 ) Entrambi Entrambi Entrambi
sp rsp non volatile non volatile non volatile
pc rip puntatore all'istruzione puntatore all'istruzione puntatore all'istruzione
PSTATEsubset: NZSS/C//V/1, 2 RFLAGS sottoinsieme: SF/ZF/CF/OF/TF volatile volatile volatile
N/D RFLAGS sottoinsieme: PF/AF N/D N/D volatile
N/D RFLAGS sottoinsieme: DF N/D N/D non volatile

1 Evitare di leggere, scrivere o calcolare direttamente i mapping tra PSTATE e RFLAGS. Questi bit possono essere usati in futuro e sono soggetti a modifiche.

2 La bandiera C porta ARM64EC è l'inverso della bandiera CF di trasporto x64 per le operazioni di sottrazione. Non esiste una gestione speciale, perché il flag è volatile e pertanto viene cestinato durante la transizione tra le funzioni (ARM64EC e x64).

Registrare il mapping per i registri vettoriali

ARM64EC registro Registro x64 convenzione di chiamata ARM64EC Convenzione di chiamata ARM64 Convenzione di chiamata x64
v0-v5 xmm0-xmm5 volatile volatile volatile
v6-v7 xmm6-xmm7 volatile volatile non volatile
v8-v15 xmm8-xmm15 volatile 1 volatile 1 non volatile
v16-v31 xmm16-xmm31 Consentita volatile non consentito (l'emulatore x64 non supporta AVX-512)
FPCR 2 MXCSR[15:6] non volatile non volatile non volatile
FPSR 2 MXCSR[5:0] volatile volatile volatile

1 Questi registri ARM64 sono speciali in quanto i 64 bit inferiori sono non volatili, ma i 64 bit superiori sono volatili. Dal punto di vista di un chiamante x64, sono effettivamente volatili perché il chiamato avrebbe spazzatura i dati.

2 Evitare di leggere, scrivere o calcolare direttamente mapping di FPCR e FPSR. Questi bit possono essere usati in futuro e sono soggetti a modifiche.

Compressione degli struct

ARM64EC segue le stesse regole di compressione degli struct usate per x64 per garantire l'interoperabilità tra codice ARM64EC e codice x64. Per altre informazioni ed esempi di compressione degli struct x64, vedere Panoramica delle convenzioni ABI x64.

Routine ABI dell'helper di emulazione

ARM64EC codice e i python usano routine helper di emulazione per passare tra le funzioni x64 e ARM64EC.

La tabella seguente descrive ogni routine ABI speciale e registra gli usi di ABI. Le routine non modificano i registri conservati elencati nella colonna ABI. Non devono essere fatte ipotesi sui registri non elencati. Su disco, i puntatori di routine ABI sono Null. In fase di caricamento, il caricatore aggiorna i puntatori in modo che puntino alle routine dell'emulatore x64.

Nome Descrizione ABI
__os_arm64x_dispatch_call_no_redirect Chiamato da un threadk di uscita per chiamare una destinazione x64 (una funzione x64 o una sequenza di inoltro rapido x64). La routine esegue il push dell'indirizzo restituito ARM64EC (nel LR registro) seguito dall'indirizzo dell'istruzione che ha esito positivo su un'istruzione blr x16 che richiama l'emulatore x64. Esegue quindi l'istruzione blr x16 valore restituito in x8 (rax)
__os_arm64x_dispatch_ret Chiamato da un entry thunk per tornare al suo chiamante x64. Viene visualizzato l'indirizzo restituito x64 dallo stack e richiama l'emulatore x64 per passare a esso N/D
__os_arm64x_check_call Chiamato da ARM64EC codice con un puntatore a un thread di uscita e l'indirizzo di destinazione ARM64EC indiretto da eseguire. La destinazione ARM64EC è considerata patchable e l'esecuzione torna sempre al chiamante con gli stessi dati con cui è stato chiamato o con dati modificati Argomenti:
x9: indirizzo di destinazione
x10: l'indirizzo dell'uscita del threadk
x11: l'indirizzo della sequenza di inoltro rapido

Cambio:
x9: se la funzione di destinazione è stata deviazionata, contiene l'indirizzo della sequenza di inoltro rapido
x10: l'indirizzo dell'uscita del threadk
x11: se la funzione è stata deviazionata, contiene l'indirizzo di uscita del threadk. In caso contrario, l'indirizzo di destinazione è saltato a

Registri conservati: x0-x8, x15 ().chkstk e q0-q7
__os_arm64x_check_icall Chiamato da ARM64EC codice, con un puntatore a un thread di uscita, per gestire un passaggio a un indirizzo di destinazione x64 o ARM64EC. Se la destinazione è x64 e il codice x64 non è stato sottoposto a patch, la routine imposta il registro degli indirizzi di destinazione. Punta alla versione ARM64EC della funzione, se presente. In caso contrario, imposta il registro in modo che punti al thread di uscita che passa alla destinazione x64. Viene quindi restituito al codice ARM64EC chiamante, che quindi passa all'indirizzo nel registro. Questa routine è una versione non ottimizzata di , in cui l'indirizzo di destinazione non è noto in fase di __os_arm64x_check_callcompilazione

Usato in un sito di chiamata di una chiamata indiretta
Argomenti:
x9: indirizzo di destinazione
x10: l'indirizzo dell'uscita del threadk
x11: l'indirizzo della sequenza di inoltro rapido

Cambio:
x9: se la funzione di destinazione è stata deviazionata, contiene l'indirizzo della sequenza di inoltro rapido
x10: l'indirizzo dell'uscita del threadk
x11: se la funzione è stata deviazionata, contiene l'indirizzo di uscita del threadk. In caso contrario, l'indirizzo di destinazione è saltato a

Registri conservati: x0-x8, x15 (chkstk) e q0-q7
__os_arm64x_check_icall_cfg __os_arm64x_check_icall Uguale a ma controlla anche che l'indirizzo specificato sia una destinazione di chiamata indiretta di Control Flow Graph valida Argomenti:
x10: l'indirizzo dell'uscita del threadk
x11: indirizzo della funzione di destinazione

Cambio:
x9: se la destinazione è x64, l'indirizzo della funzione. In caso contrario, non definito
x10: l'indirizzo dell'uscita del threadk
x11: se la destinazione è x64, contiene l'indirizzo del thread di uscita. In caso contrario, l'indirizzo della funzione

Registri conservati: x0-x8, x15 (chkstk) e q0-q7
__os_arm64x_get_x64_information Ottiene la parte richiesta del contesto del registro live x64 _Function_class_(ARM64X_GET_X64_INFORMATION) NTSTATUS LdrpGetX64Information(_In_ ULONG Type, _Out_ PVOID Output, _In_ PVOID ExtraInfo)
__os_arm64x_set_x64_information Imposta la parte richiesta del contesto del registro live x64 _Function_class_(ARM64X_SET_X64_INFORMATION) NTSTATUS LdrpSetX64Information(_In_ ULONG Type,_In_ PVOID Input, _In_ PVOID ExtraInfo)
__os_arm64x_x64_jump Usato nel regolatore senza firma e in altri batch che inoltrano direttamente (jmp) una chiamata a un'altra funzione che può avere qualsiasi firma, rinviando l'applicazione potenziale del diritto del montenegrok al vero obiettivo Argomenti:
x9: destinazione per passare a

Tutti i registri dei parametri mantenuti (inoltrati)

Thunk

I batch sono i meccanismi di basso livello per supportare le funzioni ARM64EC e x64 che si chiamano tra loro. Esistono due tipi: i batch di ingresso per l'immissione di funzioni di ARM64EC e i thread di uscita per chiamare le funzioni x64.

Entry kubernetes e entry kubernetes intrinseci: da x64 a ARM64EC chiamata di funzione

Per supportare i chiamanti x64 quando una funzione C/C++ viene compilata come ARM64EC, la toolchain genera un'unica voce basata su ARM64EC codice computer. Gli intrinseci hanno un entry thunk proprio. Tutte le altre funzioni condividono unavocea con tutte le funzioni che dispongono di una convenzione di chiamata corrispondente, parametri e tipo restituito. Il contenuto del threadk dipende dalla convenzione di chiamata della funzione C/C++.

Oltre a gestire i parametri e l'indirizzo restituito, il bridgek collega le differenze nella volatilità tra i registri vettoriali ARM64EC e x64 causati dal mapping dei registri vettoriali ARM64EC:

ARM64EC registro Registro x64 convenzione di chiamata ARM64EC Convenzione di chiamata ARM64 Convenzione di chiamata x64
v6-v15 xmm6-xmm15 volatile, ma salvato/ripristinato nellavocea (x64 a ARM64EC) volatile o parzialmente volatile superiore a 64 bit non volatile

La voce thunk esegue le azioni seguenti:

Numero parametro Uso dello stack
0-4 Archivia ARM64EC v6 e v7 nello spazio home allocato dal chiamante

Poiché il chiamato è ARM64EC, che non ha la nozione di spazio domestico, i valori archiviati non sono clobbered.

Alloca 128 byte aggiuntivi nello stack e archivia ARM64EC v8 tramite v15.
5-8 x4 = 5° parametro dallo stack
x5 = 6° parametro dallo stack
x6 = 7° parametro dallo stack
x7 = 8° parametro dallo stack

Se il parametro è SIMD, vengono invece usati i v4-v7 registri
+9 AlignUp(NumParams - 8 , 2) * 8 Alloca byte nello stack. *

Copia i 9 e i parametri rimanenti in questa area

* Allineamento del valore a un numero pari garantisce che lo stack rimanga allineato a 16 byte

Se la funzione accetta un parametro integer a 32 bit, è consentito eseguire il push solo di 32 bit anziché dei 64 bit completi del registro padre.

Successivamente, il thunk usa un'istruzione ARM64 bl per chiamare la funzione ARM64EC. Dopo la restituzione della funzione, il thunk:

  1. Annulla le allocazioni dello stack
  2. Chiama l'helper dell'emulatore __os_arm64x_dispatch_ret per visualizzare l'indirizzo restituito x64 e riprendere l'emulazione x64.

Uscita da thunk: ARM64EC alla chiamata di funzione x64

Per ogni chiamata eseguita da una funzione C/C++ ARM64EC al codice x64 potenziale, la toolchain MSVC genera un thread di uscita. Il contenuto del threadk dipende dai parametri del chiamato x64 e dal fatto che il chiamato usi la convenzione di chiamata standard o __vectorcall. Il compilatore ottiene queste informazioni da una dichiarazione di funzione per il chiamato.

Prima di tutto, il csvk inserisce l'indirizzo restituito presente nel registro ARM64EC lr e un valore fittizio a 8 byte per garantire che lo stack sia allineato a 16 byte. In secondo luogo, il thunk gestisce i parametri:

Numero parametro Uso dello stack
0-4 Alloca 32 byte di spazio home nello stack
5-8 AlignUp(NumParams - 4, 2) * 8 Alloca più byte in alto nello stack. *

Copia il 5° e i parametri successivi da ARM64EC x4-x7 a questo spazio aggiuntivo
+9 Copia il 9° e i parametri rimanenti nello spazio aggiuntivo

* L'allineamento del valore a un numero pari garantisce che lo stack rimanga allineato a 16 byte.

In terzo luogo, il helper dell'emulatore chiama l'helper __os_arm64x_dispatch_call_no_redirect dell'emulatore x64 per eseguire la funzione x64. La chiamata deve essere un'istruzione blr x16 (convenientemente, x16 è un registro volatile). È necessaria un'istruzione blr x16 perché l'emulatore x64 analizza questa istruzione come hint.

La funzione x64 tenta in genere di tornare all'helper dell'emulatore usando un'istruzione x64 ret . A questo punto, l'emulatore x64 rileva che si trova nel codice ARM64EC. Legge quindi l'hint a 4 byte precedente che si verifica come istruzione ARM64 blr x16 . Poiché questo hint indica che l'indirizzo restituito si trova in questo helper, l'emulatore passa direttamente a questo indirizzo.

La funzione x64 può tornare all'helper dell'emulatore usando qualsiasi istruzione di ramo, tra cui x64 jmp e call. L'emulatore gestisce anche questi scenari.

Quando l'helper torna poi al thunk, il thunk:

  1. Annulla qualsiasi allocazione dello stack
  2. Visualizza il registro ARM64EC lr
  3. Esegue un'istruzione ARM64 ret lr .

ARM64EC decorazione del nome della funzione

Un nome di funzione ARM64EC ha una decorazione secondaria applicata dopo qualsiasi decorazione specifica del linguaggio. Per le funzioni con collegamento C (compilato come C o tramite extern "C"), un # oggetto viene anteporto al nome. Per le funzioni decorate in C++, viene inserito un $$h tag nel nome.

foo         => #foo
?foo@@YAHXZ => ?foo@@$$hYAHXZ

__vectorcall

La toolchain ARM64EC attualmente non supporta __vectorcall. Il compilatore genera un errore quando rileva __vectorcall l'utilizzo con ARM64EC.

Vedi anche

Informazioni ARM64EC codice ABI e assembly
Problemi comuni di migrazione arm di Visual C++
Nomi decorati