Share via


Panoramica delle convenzioni ABI ARM64

L'interfaccia ABI (Application Binary Interface) di base per Windows quando viene compilata ed eseguita su processori ARM in modalità a 64 bit (architetture ARMv8 o successive), per la maggior parte segue l'EABI standard di ARM AArch64. In questo articolo vengono evidenziati alcuni dei presupposti chiave e delle modifiche rispetto a quanto documentato nell'EABI. Per informazioni sull'ABI a 32 bit, vedere Panoramica delle convenzioni ABI arm. Per altre informazioni sullo standard ARM EABI, vedere Application Binary Interface (ABI) per l'architettura arm (collegamento esterno).

Definizioni

Con l'introduzione del supporto a 64 bit, ARM ha definito diversi termini:

  • AArch32: architettura del set di istruzioni a 32 bit legacy definita da ARM, inclusa l'esecuzione in modalità Thumb.
  • AArch64 : la nuova architettura del set di istruzioni a 64 bit definita da ARM.
  • ARMv7 : specifica dell'hardware ARM di "seconda generazione", che include solo il supporto per AArch32. Questa versione dell'hardware arm è la prima versione supportata da Windows per ARM.
  • ARMv8 : specifica dell'hardware ARM di "seconda generazione", che include il supporto sia per AArch32 che per AArch64.

Windows usa anche questi termini:

  • ARM : si riferisce all'architettura ARM a 32 bit (AArch32), talvolta denominata WoA (Windows in ARM).
  • ARM32 , come ARM, sopra; usato in questo documento per maggiore chiarezza.
  • ARM64 : fa riferimento all'architettura ARM a 64 bit (AArch64). Non c'è niente di simile a WoA64.

Infine, quando si fa riferimento ai tipi di dati, viene fatto riferimento alle definizioni seguenti di ARM:

  • Short-Vector : tipo di dati direttamente rappresentabile in SIMD, vettore di 8 byte o 16 byte di elementi. È allineato alle dimensioni, ovvero 8 byte o 16 byte, in cui ogni elemento può essere 1, 2, 4 o 8 byte.
  • HFA (Aggregate a virgola mobile omogenea): tipo di dati con 2-4 membri a virgola mobile identici, float o double.
  • HVA (Uniform Short-Vector Aggregate): tipo di dati con 2-4 membri Short-Vector identici.

Requisiti di base

La versione ARM64 di Windows presuppone che sia in esecuzione in un'architettura ARMv8 o successiva in qualsiasi momento. Sia il supporto a virgola mobile che il supporto NEON sono considerati presenti nell'hardware.

La specifica ARMv8 descrive i nuovi codici opcode helper CRC e crypto facoltativi per AArch32 e AArch64. Il supporto è attualmente facoltativo, ma consigliato. Per sfruttare questi codici operativo, le app devono prima eseguire controlli di runtime per verificarne l'esistenza.

Ordine dei byte

Come per la versione ARM32 di Windows, in ARM64 Windows viene eseguito in modalità little-endian. Il passaggio dell'endianness è difficile da ottenere senza il supporto della modalità kernel in AArch64, quindi è più facile da applicare.

Allineamento

Windows in esecuzione su ARM64 consente all'hardware della CPU di gestire in modo trasparente gli accessi non allineati. In un miglioramento di AArch32, questo supporto ora funziona anche per tutti gli accessi integer (inclusi gli accessi a più parole) e per gli accessi a virgola mobile.

Tuttavia, gli accessi alla memoria non memorizzata nella cache (dispositivo) devono essere sempre allineati. Se il codice potrebbe leggere o scrivere dati non allineati dalla memoria non memorizzata nella cache, deve assicurarsi di allineare tutti gli accessi.

Allineamento del layout predefinito per le variabili locali:

Dimensioni in byte Allineamento in byte
1 1
2 2
3, 4 4
> 4 8

Allineamento del layout predefinito per le tabelle globali e statiche:

Dimensioni in byte Allineamento in byte
1 1
2 - 7 4
8 - 63 8
>= 64 16

Registri interi

L'architettura AArch64 supporta 32 registri integer:

Registrazione Volatilità Ruolo
x0-x8 Volatile Registri dei file scratch dei parametri/risultati
x9-x15 Volatile Registri scratch
x16-x17 Volatile Registri scratch intra-procedure-call
x18 N/D Registro della piattaforma riservata: in modalità kernel, punta a KPICR per il processore corrente; In modalità utente punta a TEB
x19-x28 Non volatile Registri scratch
x29/fp Non volatile Puntatori ai frame
x30/lr Entrambe Registro collegamenti: la funzione chiamato deve conservarla per la propria restituzione, ma il valore del chiamante andrà perso.

È possibile accedere a ogni registro come valore a 64 bit completo (tramite x0-x30) o come valore a 32 bit (tramite w0-w30). Le operazioni a 32 bit estendono i risultati fino a 64 bit.

Per informazioni dettagliate sull'uso dei registri dei parametri, vedere la sezione Passaggio di parametri.

A differenza di AArch32, il contatore del programma (PC) e il puntatore dello stack (SP) non sono registri indicizzati. Sono limitati nel modo in cui possono essere accessibili. Si noti anche che non è presente alcun registro x31. Tale codifica viene utilizzata per scopi speciali.

Il puntatore al frame (x29) è necessario per garantire la compatibilità con l'esecuzione rapida dello stack usata da ETW e da altri servizi. Deve puntare alla coppia {x29, x30} precedente nello stack.

Registri a virgola mobile/SIMD

L'architettura AArch64 supporta anche 32 registri a virgola mobile/SIMD, riepilogati di seguito:

Registrazione Volatilità Ruolo
v0-v7 Volatile Registri dei file scratch dei parametri/risultati
v8-v15 Entrambe I 64 bit bassi sono non volatili. I 64 bit alti sono volatili.
v16-v31 Volatile Registri scratch

È possibile accedere a ogni registro come valore completo a 128 bit (tramite v0-v31 o q0-q31). È possibile accedervi come valore a 64 bit (tramite d0-d31), come valore a 32 bit (tramite s0-s31), come valore a 16 bit (tramite h0-h31) o come valore a 8 bit (tramite b0-b31). Gli accessi inferiori a 128 bit accedono solo ai bit inferiori del registro completo a 128 bit. Lasciano invariati i bit rimanenti, se non diversamente specificato. AArch64 è diverso da AArch32, in cui i registri più piccoli sono stati compressi sopra i registri più grandi.

Il registro di controllo a virgola mobile (FPCR) presenta determinati requisiti per i vari campi di bit al suo interno:

BITS Significato Volatilità Ruolo
26 AHP Non volatile Controllo a metà precisione alternativo.
25 DN Non volatile Controllo della modalità NaN predefinito.
24 FZ Non volatile Controllo in modalità scaricamento su zero.
23-22 RMode Non volatile Controllo modalità arrotondamento.
15,12-8 IDE/IXE/etc Non volatile La trap di eccezione abilita i bit, deve essere sempre 0.

Registri di sistema

Come AArch32, la specifica AArch64 fornisce tre registri "ID thread" controllati dal sistema:

Registrazione Ruolo
TPIDR_EL0 Riservato.
TPIDRRO_EL0 Contiene il numero di CPU per il processore corrente.
TPIDR_EL1 Punta alla struttura KPICR per il processore corrente.

Eccezioni a virgola mobile

Il supporto per le eccezioni A virgola mobile I edizione Enterprise E è facoltativo nei sistemi AArch64. Per le varianti del processore con eccezioni a virgola mobile hardware, il kernel di Windows intercetta automaticamente le eccezioni e le disabilita in modo implicito nel registro FPCR. Questa trap garantisce il comportamento normalizzato tra le varianti del processore. In caso contrario, il codice sviluppato in una piattaforma senza supporto eccezioni può rilevare eccezioni impreviste durante l'esecuzione in una piattaforma con supporto.

Passaggio dei parametri

Per le funzioni non variadic, Windows ABI segue le regole specificate da ARM per il passaggio dei parametri. Queste regole vengono estratti direttamente dallo standard di chiamata di procedura per l'architettura AArch64:

Fase A - Inizializzazione

Questa fase viene eseguita esattamente una volta, prima dell'inizio dell'elaborazione degli argomenti.

  1. Il numero registro per utilizzo generico successivo (NGRN) è impostato su zero.

  2. Il numero di registrazione a virgola mobile e SIMD successivo (NSRN) è impostato su zero.

  3. L'indirizzo dell'argomento in pila successivo (NSAA) viene impostato sul valore corrente del puntatore dello stack (SP).

Fase B: pre-riempimento ed estensione degli argomenti

Per ogni argomento nell'elenco, viene applicata la prima regola corrispondente dell'elenco seguente. Se nessuna regola corrisponde, l'argomento viene utilizzato senza modifiche.

  1. Se il tipo di argomento è un tipo composito le cui dimensioni non possono essere determinate in modo statico sia dal chiamante che dal chiamato, l'argomento viene copiato in memoria e l'argomento viene sostituito da un puntatore alla copia. Non esistono tipi di questo tipo in C/C++ ma esistono in altri linguaggi o nelle estensioni del linguaggio.

  2. Se il tipo di argomento è un HVA o UN HVA, l'argomento viene utilizzato senza modifiche.

  3. Se il tipo di argomento è un tipo composito maggiore di 16 byte, l'argomento viene copiato nella memoria allocata dal chiamante e l'argomento viene sostituito da un puntatore alla copia.

  4. Se il tipo di argomento è un tipo composito, le dimensioni dell'argomento vengono arrotondate fino al multiplo più vicino di 8 byte.

Fase C: assegnazione di argomenti per la registrazione e lo stack

Per ogni argomento nell'elenco, le regole seguenti vengono applicate a sua volta fino a quando l'argomento non viene allocato. Quando un argomento viene assegnato a un registro, tutti i bit inutilizzati nel registro hanno un valore non specificato. Se un argomento viene assegnato a uno slot dello stack, i byte di spaziatura interna inutilizzati non hanno un valore specificato.

  1. Se l'argomento è un tipo a virgola mobile o a virgola mobile a precisione singola, doppia o a precisione quad e NSRN è minore di 8, l'argomento viene allocato ai bit meno significativi del registro v[NSRN]. L'NSRN viene incrementato di uno. L'argomento è stato allocato.

  2. Se l'argomento è un HVA o UN HVA e sono presenti registri SIMD e a virgola mobile non allocati (NSRN + numero di membri ≤ 8), l'argomento viene allocato ai registri SIMD e a virgola mobile, un registro per membro dell'HVA o DELL'HVA. L'NSRN viene incrementato in base al numero di registri utilizzati. L'argomento è stato allocato.

  3. Se l'argomento è un HVA o UN HVA, la NSRN è impostata su 8 e le dimensioni dell'argomento vengono arrotondate per errotondare al multiplo più vicino di 8 byte.

  4. Se l'argomento è un HVA, un HVA, un tipo a virgola mobile e a virgola mobile a precisione quad o short vector, l'NSAA viene arrotondato per enumerare le dimensioni maggiori di 8 o l'allineamento naturale del tipo dell'argomento.

  5. Se l'argomento è un tipo a virgola mobile a precisione singola o mezzo, la dimensione dell'argomento viene impostata su 8 byte. L'effetto è come se l'argomento fosse stato copiato nei bit meno significativi di un registro a 64 bit e i bit rimanenti riempiti con valori non specificati.

  6. Se l'argomento è un HVA, un HVA, un oggetto Half-, Single-, Double-, o Quad-precision a virgola mobile o Short Vector Type, l'argomento viene copiato in memoria in corrispondenza dell'NSAA rettificata. L'indirizzo NSAA viene incrementato della dimensione dell'argomento. L'argomento è stato allocato.

  7. Se l'argomento è un tipo integrale o puntatore, la dimensione dell'argomento è minore o uguale a 8 byte e NGRN è minore di 8, l'argomento viene copiato nei bit meno significativi in x[NGRN]. La NGRN viene incrementata di uno. L'argomento è stato allocato.

  8. Se l'argomento ha un allineamento pari a 16, la NGRN viene arrotondata fino al numero pari successivo.

  9. Se l'argomento è un tipo integrale, la dimensione dell'argomento è uguale a 16 e NGRN è minore di 7, l'argomento viene copiato in x[NGRN] e x[NGRN+1]. x[NGRN] conterrà la doppia parola in basso indirizzata della rappresentazione di memoria dell'argomento. La NGRN viene incrementata di due. L'argomento è stato allocato.

  10. Se l'argomento è un tipo composito e le dimensioni in parole doppie dell'argomento non sono più di 8 meno NGRN, l'argomento viene copiato in registri consecutivi per utilizzo generico, a partire da x[NGRN]. L'argomento viene passato come se fosse stato caricato nei registri da un indirizzo allineato a due parole, con una sequenza appropriata di istruzioni LDR che caricano registri consecutivi dalla memoria. Il contenuto di tutte le parti inutilizzate dei registri non è specificato da questo standard. La NGRN viene incrementata in base al numero di registri utilizzati. L'argomento è stato allocato.

  11. La NGRN è impostata su 8.

  12. L'NSAA viene arrotondato fino all'elemento più grande di 8 o l'allineamento naturale del tipo dell'argomento.

  13. Se l'argomento è un tipo composito, l'argomento viene copiato in memoria nella NSAA modificata. L'indirizzo NSAA viene incrementato della dimensione dell'argomento. L'argomento è stato allocato.

  14. Se le dimensioni dell'argomento sono inferiori a 8 byte, le dimensioni dell'argomento vengono impostate su 8 byte. L'effetto è come se l'argomento fosse stato copiato nei bit meno significativi di un registro a 64 bit e i bit rimanenti sono stati riempiti con valori non specificati.

  15. L'argomento viene copiato in memoria nella NSAA modificata. L'indirizzo NSAA viene incrementato della dimensione dell'argomento. L'argomento è stato allocato.

Addendum: Funzioni variadic

Le funzioni che accettano un numero variabile di argomenti vengono gestite in modo diverso da quello precedente, come indicato di seguito:

  1. Tutti i compositi vengono trattati allo stesso modo; nessun trattamento speciale di HFC o HVA.

  2. I registri SIMD e a virgola mobile non vengono usati.

In effetti, è uguale alle regole C.12–C.15 per allocare argomenti a uno stack immaginario, in cui i primi 64 byte dello stack vengono caricati in x0-x7 e tutti gli argomenti dello stack rimanenti vengono posizionati normalmente.

Valori restituiti

I valori integrali vengono restituiti in x0.

I valori a virgola mobile vengono restituiti in s0, d0 o v0, in base alle esigenze.

Un tipo è considerato come un HVA o UN HVA SE tutto il blocco seguente:

  • Non è vuoto,
  • Non dispone di costruttori predefiniti o di copia non semplici, distruttori o operatori di assegnazione,
  • Tutti i suoi membri hanno lo stesso tipo DI HVA o HVA o sono tipi float, double o neon che corrispondono ai tipi HVA o HFA degli altri membri.

I valori HVA con quattro o meno elementi vengono restituiti in s0-s3, d0-d3 o v0-v3, in base alle esigenze.

I tipi restituiti da value vengono gestiti in modo diverso a seconda che abbiano determinate proprietà e se la funzione è una funzione membro non statica. Tipi con tutte queste proprietà,

  • sono aggregati dalla definizione standard C++14, ovvero non dispongono di costruttori forniti dall'utente, di membri dati non privati o protetti non statici, di classi di base e di funzioni virtuali e
  • hanno un operatore di assegnazione di copia semplice e
  • hanno un distruttore semplice,

e vengono restituiti da funzioni membro non membro o funzioni membro statiche, usare lo stile restituito seguente:

  • I tipi che sono HFC con quattro o meno elementi vengono restituiti in s0-s3, d0-d3 o v0-v3, in base alle esigenze.
  • I tipi minori o uguali a 8 byte vengono restituiti in x0.
  • I tipi minori o uguali a 16 byte vengono restituiti in x0 e x1, con x0 contenente gli 8 byte di ordine inferiore.
  • Per altri tipi di aggregazione, il chiamante riserva un blocco di memoria di dimensioni e allineamento sufficienti per contenere il risultato. L'indirizzo del blocco di memoria deve essere passato come argomento aggiuntivo alla funzione in x8. Il chiamato può modificare il blocco di memoria dei risultati in qualsiasi momento durante l'esecuzione della subroutine. Il chiamato non è necessario per mantenere il valore archiviato in x8.

Tutti gli altri tipi usano questa convenzione:

  • Il chiamante riserva un blocco di memoria di dimensioni e allineamento sufficienti per contenere il risultato. L'indirizzo del blocco di memoria deve essere passato come argomento aggiuntivo alla funzione in x0 o x1 se $this viene passato in x0. Il chiamato può modificare il blocco di memoria dei risultati in qualsiasi momento durante l'esecuzione della subroutine. Il chiamato restituisce l'indirizzo del blocco di memoria in x0.

Stack

Dopo l'interfaccia ABI inserita da ARM, lo stack deve rimanere sempre allineato a 16 byte. AArch64 contiene una funzionalità hardware che genera errori di allineamento dello stack ogni volta che sp non è allineato a 16 byte e viene eseguito un carico o un archivio relativo a SP. Windows viene eseguito con questa funzionalità abilitata in qualsiasi momento.

Le funzioni che allocano 4k o più dello stack devono garantire che ogni pagina prima della pagina finale venga toccata in ordine. Questa azione garantisce che nessun codice possa "superare" le pagine di protezione usate da Windows per espandere lo stack. In genere il tocco viene eseguito dall'helper __chkstk , che ha una convenzione di chiamata personalizzata che supera l'allocazione totale dello stack diviso per 16 in x15.

Zona rossa

L'area a 16 byte immediatamente sotto il puntatore dello stack corrente è riservata per l'uso da scenari di analisi e applicazione dinamica di patch. Questa area consente l'inserimento di codice generato con attenzione che archivia due registri in [sp, #-16] e li usa temporaneamente per scopi arbitrari. Il kernel di Windows garantisce che questi 16 byte non vengano sovrascritti se viene eseguita un'eccezione o un interrupt, sia in modalità utente che in modalità kernel.

Stack del kernel

Lo stack di modalità kernel predefinito in Windows è di sei pagine (24.000). Prestare particolare attenzione alle funzioni con buffer dello stack di grandi dimensioni in modalità kernel. Un interrupt mal timed potrebbe venire in con poco spazio e creare un controllo di bug di panico dello stack.

Camminare in pila

Il codice in Windows viene compilato con puntatori a fotogrammi abilitati (/Oy-) per abilitare l'esecuzione rapida dello stack. In genere, x29 (fp) punta al collegamento successivo nella catena, ovvero una coppia {fp, lr} che indica il puntatore al frame precedente nello stack e l'indirizzo restituito. Il codice di terze parti è incoraggiato ad abilitare anche i puntatori ai fotogrammi, per consentire una migliore profilatura e traccia.

Rimozione delle eccezioni

La rimozione durante la gestione delle eccezioni viene assistita tramite l'uso di codici di rimozione. I codici di rimozione sono una sequenza di byte archiviati nella sezione xdata del file eseguibile. Descrivono l'operazione del prologo e dell'epilogo in modo astratto, in modo che gli effetti del prologo di una funzione possano essere annullati in preparazione al backup dello stack frame del chiamante. Per altre informazioni sui codici di rimozione, vedere Gestione delle eccezioni ARM64.

Arm EABI specifica anche un modello di rimozione delle eccezioni che usa codici di rimozione. Tuttavia, la specifica presentata non è sufficiente per la rimozione in Windows, che deve gestire i casi in cui il PC si trova al centro di un prologo o di un epilogo di funzione.

Il codice generato in modo dinamico deve essere descritto con tabelle di funzioni dinamiche tramite RtlAddFunctionTable e funzioni associate, in modo che il codice generato possa partecipare alla gestione delle eccezioni.

Contatore ciclo

Tutte le CPU ARMv8 sono necessarie per supportare un registro dei contatori del ciclo, un registro a 64 bit configurato da Windows per essere leggibile a qualsiasi livello di eccezione, inclusa la modalità utente. È possibile accedervi tramite lo speciale registro PMCCNTR_EL0, usando il codice operativo MSR nel codice assembly o l'intrinseco _ReadStatusReg nel codice C/C++.

Il contatore del ciclo qui è un vero contatore del ciclo, non un orologio a parete. La frequenza di conteggio varia con la frequenza del processore. Se si ritiene che sia necessario conoscere la frequenza del contatore del ciclo, non è consigliabile usare il contatore del ciclo. Si vuole invece misurare l'ora dell'orologio a parete, per cui è consigliabile usare QueryPerformanceCounter.

Vedi anche

Problemi comuni relativi alla migrazione di Visual C++ ARM
Gestione delle eccezioni ARM64