Sdílet prostřednictvím


Přehled konvencí ARM64 ABI

Základní binární rozhraní aplikace (ABI) pro Windows při kompilaci a spuštění na procesorech ARM v 64bitovém režimu (ARMv8 nebo novějších architekturách) většinou následuje standardní AArch64 EABI ARM. Tento článek popisuje některé klíčové předpoklady a změny z toho, co je zdokumentované v EABI. Informace o 32bitové verzi ABI najdete v tématu Přehled konvencí ABI ARM. Další informace o standardním ARM EABI najdete v tématu ABI (Application Binary Interface) pro architekturu ARM (externí odkaz).

Definice

Se zavedením 64bitové podpory definoval ARM několik termínů:

  • AArch32 – starší 32bitová architektura instrukční sady (ISA) definovaná ARM, včetně provádění v režimu thumb.
  • AArch64 – nová 64bitová architektura sady instrukcí (ISA) definovaná ARM.
  • ARMv7 – specifikace hardwaru ARM "7. generace", která zahrnuje pouze podporu pro AArch32. Tato verze hardwaru ARM je první verze Windows pro ARM podporovaná.
  • ARMv8 – specifikace hardwaru ARM "8. generace", která zahrnuje podporu pro AArch32 i AArch64.

Windows také používá tyto termíny:

  • ARM – označuje 32bitovou architekturu ARM (AArch32), někdy označovanou jako WoA (Windows v ARM).
  • ARM32 – stejné jako ARM, výše, použité v tomto dokumentu pro přehlednost.
  • ARM64 – označuje 64bitovou architekturu ARM (AArch64). Nic takového jako WoA64 neexistuje.

Při odkazování na datové typy se na tyto definice z ARM odkazuje:

  • Short-Vector – datový typ přímo reprezentovatelný v SIMD, vektor 8 bajtů nebo 16 bajtů hodnot prvků. Zarovná se k jeho velikosti, buď 8 bajtů, nebo 16 bajtů, kde každý prvek může být 1, 2, 4 nebo 8 bajtů.
  • HFA (homogenní agregace s plovoucí desetinou čárkou) – datový typ s 2 až 4 identickými členy s plovoucí desetinou čárkou, plovoucí desetinou čárkou nebo dvojitou čárkou.
  • HVA (homogenní short-vector aggregate) – datový typ s 2 až 4 identickými členy short-vector.

Základní požadavky

Verze ARM64 windows je předem spuštěná v architektuře ARMv8 nebo novější. Předpokládá se, že podpora s plovoucí desetinou čárkou i NEON se předpokládá v hardwaru.

Specifikace ARMv8 popisuje nové volitelné kryptografické opcode a pomocné kódy CRC pro AArch32 i AArch64. Podpora pro ně je aktuálně volitelná, ale doporučuje se. Aby aplikace tyto opkódy využily, musí nejprve provést kontroly jejich existence za běhu.

Endianness

Stejně jako u verze ARM32 windows se v SYSTÉMU WINDOWS ARM64 spouští v režimu little-endian. Přepínání endianness je obtížné dosáhnout bez podpory režimu jádra v AArch64, takže je jednodušší vynutit.

Zarovnání

Windows běžící na ARM64 umožňuje hardwaru procesoru transparentně zpracovávat nesprávně zarovnaný přístup. V rámci vylepšení AArch32 teď tato podpora funguje také pro všechna celočíselná přístupová oprávnění (včetně přístupů s více slovy) a pro přístup s plovoucí desetinou čárkou.

Přístup k paměti bez mezipaměti (zařízení) však musí být vždy zarovnaný. Pokud by kód mohl číst nebo zapisovat nesprávně zarovnaná data z paměti bez mezipaměti, musí zajistit zarovnání všech přístupů.

Výchozí zarovnání rozložení pro místní hodnoty:

Velikost v bajtech Zarovnání v bajtech
1 0
2 2
3, 4 4
> 4 8

Výchozí zarovnání rozložení pro globální a statické objekty:

Velikost v bajtech Zarovnání v bajtech
1 1
2 - 7 4
8 - 63 8
>= 64 16

Celočíselné registry

Architektura AArch64 podporuje 32 celých registrů:

Registrovat Nestálost Role
x0-x8 Nestálý Pomocné registry parametrů a výsledků
x9-x15 Nestálý Pomocné registry
x16-x17 Nestálý Pomocné registry volání uvnitř procedury
x18 Registr rezervované platformy: v režimu jádra odkazuje na KPCR pro aktuální procesor; V uživatelském režimu odkazuje na TEB.
x19-x28 Nestálé Pomocné registry
x29/fp Nestálé Ukazatel rámce
x30/lr Oba Link Register: Volaná funkce ji musí zachovat pro vlastní vrácení, ale hodnota volajícího bude ztracena.

Každý registr může být přístupný jako úplná 64bitová hodnota (prostřednictvím x0-x30) nebo jako 32bitová hodnota (prostřednictvím w0-w30). 32bitové operace nula rozšiřují výsledky až na 64 bitů.

Podrobnosti o použití registrů parametrů najdete v části Předávání parametrů.

Na rozdíl od AArch32 se čítač programu (PC) a ukazatel zásobníku (SP) neindexují. Jsou omezené tím, jak se k nim může přistupovat. Všimněte si také, že neexistuje žádný registr x31. Toto kódování se používá pro speciální účely.

Ukazatel rámce (x29) se vyžaduje kvůli kompatibilitě s rychlým procházením zásobníků používanými pro Windows a dalšími službami. Musí odkazovat na předchozí dvojici {x29, x30} v zásobníku.

Registrace s plovoucí desetinou čárkou nebo SIMD

Architektura AArch64 také podporuje 32 registrů s plovoucí desetinou čárkou nebo SIMD, které jsou shrnuté níže:

Registrovat Nestálost Role
v0-v7 Nestálý Pomocné registry parametrů a výsledků
v8-v15 Oba Nízkých 64 bitů jsou nestálé. Vysoký počet 64 bitů je nestálý.
v16-v31 Nestálý Pomocné registry

Každý registr může být přístupný jako úplná 128bitová hodnota (prostřednictvím v0-v31 nebo q0-q31). Může být přístupný jako 64bitová hodnota (prostřednictvím d0-d31), jako 32bitová hodnota (přes s0-s31), jako 16bitová hodnota (prostřednictvím h0-h31) nebo jako 8bitová hodnota (přes b0-b31). Přístup k menším než 128 bitům má přístup pouze k nižším bitům úplného 128bitového registru. Zbývající bity zůstanou nedotčené, pokud není uvedeno jinak. (AArch64 se liší od AArch32, kde menší registry byly zabalené nad většími registry.)

Řídicí registr s plovoucí deseti čárkou (FPCR) má určité požadavky na různá bitová pole v něm:

Bity Význam Nestálost Role
26 AHP Nestálé Alternativní polopřesnostní ovládání.
25 DN Nestálé Výchozí ovládací prvek režimu NaN.
24 FZ Nestálé Řízení režimu vyprazdněte na nulu.
23-22 RMode Nestálé Ovládací prvek režimu zaokrouhlování
15,12-8 IDE/IXE/etc Nestálé Soutisk výjimek musí obsahovat bity, vždy musí být 0.

Systémové registry

Podobně jako AArch32 poskytuje specifikace AArch64 tři systémové "ID vlákna":

Registrovat Role
TPIDR_EL0 Vyhrazeno.
TPIDRRO_EL0 Obsahuje číslo procesoru pro aktuální procesor.
TPIDR_EL1 Odkazuje na strukturu KPCR pro aktuální procesor.

Výjimky s plovoucí desetinou čárkou

Podpora výjimek s plovoucí desetinou čárkou IEEE je v systémech AArch64 volitelná. U variant procesoru, které mají výjimky s plovoucí desetinou čárkou hardwaru, jádro Systému Windows bezobslužně zachytí výjimky a implicitně je zakáže v registru FPCR. Tato past zajišťuje normalizované chování napříč variantami procesoru. Jinak se kód vyvinutý na platformě bez podpory výjimek může při spuštění na platformě s podporou setkat s neočekávanými výjimkami.

Předávání parametrů

V případě jiných než variadických funkcí se ABI systému Windows řídí pravidly určenými arm pro předávání parametrů. Tato pravidla jsou vyjmutá přímo ze standardu volání procedur pro architekturu AArch64:

Fáze A – inicializace

Tato fáze se provádí přesně jednou před zahájením zpracování argumentů.

  1. Číslo registru pro obecné účely (NGRN) je nastaveno na nulu.

  2. Číslo registru s plovoucí desetinou čárkou (Next SIMD a NSRN) je nastavené na nulu.

  3. Další skládaná adresa argumentu (NSAA) je nastavena na aktuální hodnotu ukazatele zásobníku (SP).

Fáze B – předsazení a rozšíření argumentů

Pro každý argument v seznamu se použije první odpovídající pravidlo z následujícího seznamu. Pokud žádné pravidlo neodpovídá, použije se argument beze změny.

  1. Pokud je typ argumentu složený typ, jehož velikost nemůže být staticky určena volajícím i volaným, argument se zkopíruje do paměti a argument se nahradí ukazatelem na kopii. (V jazyce C/C++ neexistují žádné takové typy, ale existují v jiných jazycích nebo v jazykových rozšířeních).

  2. Pokud je typ argumentu HFA nebo HVA, použije se argument beze změny.

  3. Pokud je typ argumentu složený typ větší než 16 bajtů, zkopíruje se argument do paměti přidělené volajícím a argument se nahradí ukazatelem na kopii.

  4. Pokud je typ argumentu složený typ, zaokrouhlí se velikost argumentu nahoru na nejbližší násobek 8 bajtů.

Fáze C – přiřazení argumentů k registrům a zásobníku

Pro každý argument v seznamu se použijí následující pravidla, dokud nebude argument přidělen. Pokud je argument přiřazen k registru, všechny nepoužité bity v registru mají nezadanou hodnotu. Pokud je argument přiřazený ke slotu zásobníku, všechny nepoužité bajty odsazení mají nezadanou hodnotu.

  1. Pokud je argument poloviční, jednoduchý, dvojitý nebo čtyřúhelníkový typ s plovoucí desetinnou čárkou nebo krátkým vektorem a NSRN je menší než 8, pak je argument přidělen nejméně významným bitům registru v[NSRN]. NSRN se zvýší o jeden. Argument byl nyní přidělen.

  2. Pokud je argument HFA nebo HVA a existuje dostatek nepřidělených registrů SIMD a plovoucí desetiny (NSRN + počet členů ≤ 8), pak je argument přidělen simd a registrům s plovoucí desetinou čárkou, jeden registr na členA HFA nebo HVA. NSRN se zvýší o počet použitých registrů. Argument byl nyní přidělen.

  3. Pokud je argument HFA nebo HVA, hodnota NSRN je nastavená na hodnotu 8 a velikost argumentu se zaokrouhlí nahoru na nejbližší násobek 8 bajtů.

  4. Pokud je argumentem HFA, HVA, čtyřúhelník s plovoucí desetinnou čárkou nebo krátkým vektorovým typem, zaokrouhlí se NSAA nahoru na větší z 8 nebo přirozené zarovnání typu argumentu.

  5. Pokud je argument typu Plovoucí desetinná čárka s poloviční nebo jednoduchou přesností, je velikost argumentu nastavena na 8 bajtů. Účinek je stejný, jako kdyby byl argument zkopírován do nejméně významných bitů 64bitového registru a zbývající bity vyplněné nezadanou hodnotou.

  6. Pokud je argumentem HFA, HVA, Half-, Single-, Double-nebo Quad-precision Floating-point nebo Short Vector Type, pak je argument zkopírován do paměti v upravené NSAA. NSAA se zvýší o velikost argumentu. Argument byl nyní přidělen.

  7. Pokud je argument celočíselným typem nebo typem ukazatele, je velikost argumentu menší nebo rovna 8 bajtům a hodnota NGRN je menší než 8, argument se zkopíruje do nejméně významných bitů v argumentu x[NGRN]. NGRN se zvýší o jeden. Argument byl nyní přidělen.

  8. Pokud má argument zarovnání 16, zaokrouhlí se číslo NGRN nahoru na další sudé číslo.

  9. Pokud je argument celočíselným typem, je velikost argumentu rovna 16 a hodnota NGRN je menší než 7, argument se zkopíruje do x[NGRN] a x[NGRN+1]. x[NGRN] musí obsahovat nižší adresované dvojité slovo reprezentace paměti argumentu. NGRN se zvýší o dvě. Argument byl nyní přidělen.

  10. Pokud je argument složeným typem a velikost argumentu není větší než 8 minus NGRN, pak se argument zkopíruje do po sobě jdoucích registrů pro obecné účely počínaje x[NGRN]. Argument se předá, jako kdyby byl načten do registrů z adresy zarovnané s dvojitým slovem, s odpovídající sekvencí instrukcí LDR, které načítají po sobě jdoucí registry z paměti. Obsah všech nepoužívaných částí registrů není určen touto normou. Hodnota NGRN se zvýší o počet použitých registrů. Argument byl nyní přidělen.

  11. Hodnota NGRN je nastavená na hodnotu 8.

  12. NSAA se zaokrouhlí nahoru na větší z 8 nebo přirozené zarovnání typu argumentu.

  13. Pokud je argument složený typ, pak je argument zkopírován do paměti v upravené NSAA. NSAA se zvýší o velikost argumentu. Argument byl nyní přidělen.

  14. Pokud je velikost argumentu menší než 8 bajtů, je velikost argumentu nastavena na 8 bajtů. Účinek je stejný, jako kdyby se argument zkopíroval do nejméně významných bitů 64bitového registru a zbývající bity byly vyplněny nespecifikovanými hodnotami.

  15. Argument se zkopíruje do paměti v upravené NSAA. NSAA se zvýší o velikost argumentu. Argument byl nyní přidělen.

Dodatek: Variadické funkce

Funkce, které přebírají proměnlivý počet argumentů, se zpracovávají jinak než výše:

  1. Všechny složené materiály jsou považovány za stejné; žádné zvláštní zacházení s HFA nebo HVA.

  2. Simd a registrace s plovoucí desetinou čárkou se nepoužívají.

V podstatě je to stejné jako v následujících pravidlech C.12–C.15 pro přidělení argumentů imaginárnímu zásobníku, kde se prvních 64 bajtů zásobníku načtou do x0-x7 a všechny zbývající argumenty zásobníku se obvykle umístí.

Vrácené hodnoty

Celočíselné hodnoty jsou vráceny v x0.

Hodnoty s plovoucí desetinou čárkou se vrátí v s0, d0 nebo v0 podle potřeby.

Typ se považuje za HFA nebo HVA, pokud se jedná o všechny následující blokování:

  • Je to neprázdné,
  • Nemá žádné jiné než triviální výchozí konstruktory, destruktory ani operátory přiřazení.
  • Všichni jeho členové mají stejný typ HFA nebo HVA, nebo jsou plovoucí, dvojité nebo neonové typy, které odpovídají HFA nebo HVA ostatních členů.

Hodnoty HVA se čtyřmi nebo méně prvky se vrátí v s0-s3, d0-d3 nebo v0-v3 podle potřeby.

Typy vrácené hodnotou se zpracovávají odlišně v závislosti na tom, jestli mají určité vlastnosti a jestli je funkce nestatická členová funkce. Typy, které mají všechny tyto vlastnosti,

  • Agregují se standardní definicí jazyka C++14, tedy nemají žádné konstruktory poskytované uživatelem, žádné soukromé ani chráněné nestatické datové členy, žádné základní třídy a žádné virtuální funkce a
  • mají triviální operátor přiřazení kopírování a
  • mají triviální destruktor,

a vrací se nečlenovými funkcemi nebo statickými členskými funkcemi, použijte následující návratový styl:

  • V případě potřeby se vrátí typy HFA se čtyřmi nebo méně prvky v s0-s3, d0-d3 nebo v0-v3.
  • Typy menší nebo rovné 8 bajtů se vrátí v x0.
  • Typy menší nebo rovno 16 bajtům se vrátí v x0 a x1 s x0 obsahujícími 8 bajtů nižšího řádu.
  • U jiných agregačních typů si volající vyhrazuje blok paměti s dostatečnou velikostí a zarovnáním pro uložení výsledku. Adresa bloku paměti se předá jako další argument funkce v x8. Volaný může změnit blok paměti výsledku v jakémkoli okamžiku během provádění podprogramu. Volaný není nutný k zachování hodnoty uložené v x8.

Všechny ostatní typy používají tuto konvenci:

  • Volající si zarezervuje blok paměti s dostatečnou velikostí a zarovnáním pro uložení výsledku. Adresa bloku paměti se předá jako další argument funkce v x0 nebo x1, pokud je $this předána v x0. Volaný může změnit blok paměti výsledku v jakémkoli okamžiku během provádění podprogramu. Volaný vrátí adresu bloku paměti v x0.

Zásobník

V souladu s ABI podle ARM musí zásobník zůstat vždy zarovnaný na 16 bajtů. AArch64 obsahuje hardwarovou funkci, která generuje chyby zarovnání zásobníku vždy, když sp není 16bajtů zarovnaný a provádí se relativní zatížení nebo úložiště SP. Windows běží s touto funkcí zapnutou vždy.

Funkce, které přidělují 4k nebo více hodnot zásobníku, musí zajistit, aby se každá stránka před poslední stránkou dotýkala v pořadí. Tato akce zajistí, že žádný kód nebude moct přeskočit ochranné stránky, které systém Windows používá k rozšíření zásobníku. Dotykové ovládání obvykle provádí __chkstk pomocník, který má vlastní konvenci volání, která předává celkové přidělení zásobníku dělené číslem 16 v x15.

Červená zóna

Oblast 16 bajtů bezprostředně pod aktuálním ukazatelem zásobníku je vyhrazená pro použití ve scénářích analýzy a dynamických oprav. Tato oblast umožňuje pečlivě vygenerovaný kód vložit, který ukládá dva registry na [sp, #-16] a dočasně je používá pro libovolné účely. Jádro Windows zaručuje, že se těchto 16 bajtů nepřepíše, pokud dojde k výjimce nebo přerušení v uživatelském i režimu jádra.

Zásobník jádra

Výchozí zásobník režimu jádra ve Windows je šest stránek (24 tisíc). Věnujte zvláštní pozornost funkcím s velkými vyrovnávacími paměťmi zásobníku v režimu jádra. Špatně načasované přerušení by mohlo přijít s malou místností a vytvořit zásobník pro kontrolu chyby panice.

Stack walking

Kód v systému Windows je zkompilován s povolenými ukazateli snímků (/Oy-), aby bylo možné rychle procházet zásobníky. Obecně platí, že x29 (fp) odkazuje na další propojení v řetězci, což je dvojice {fp, lr} označující ukazatel na předchozí rámec na zásobníku a zpáteční adresu. Doporučujeme také kód třetí strany, který umožňuje povolit také ukazatele rámců, aby bylo možné zlepšit profilaci a trasování.

Odvíjení výjimek

S odvíjením během zpracování výjimek se pomáhá používat kódy odvíjení. Kódy odvíjení jsou posloupnost bajtů uložených v sekci .xdata spustitelného souboru. Popisují operaci prologue a epilogue abstraktním způsobem, aby účinky prologu funkce mohly být vráceny v rámci přípravy na zálohování do zásobníku volajícího. Další informace o kódech odvíjení najdete v tématu Zpracování výjimek ARM64.

ARM EABI také určuje model odvíjení výjimek, který používá kódy odvíjení. Specifikace, jak je uvedeno, však není dostatečná pro odvíjení ve Windows, což musí zvládnout případy, kdy je počítač uprostřed funkce prologue nebo epilogue.

Kód, který se dynamicky generuje, by měl být popsán pomocí dynamických tabulek funkcí prostřednictvím RtlAddFunctionTable a přidružených funkcí, aby se vygenerovaný kód mohl účastnit zpracování výjimek.

Čítač cyklu

Všechny procesory ARMv8 se vyžadují k podpoře registru čítačů cyklu, 64bitového registru, který systém Windows nakonfiguruje tak, aby byl čitelný na jakékoli úrovni výjimky, včetně uživatelského režimu. Lze k němu přistupovat prostřednictvím speciálního PMCCNTR_EL0 registru, pomocí opcode MSR v kódu sestavení nebo _ReadStatusReg vnitřní v kódu C/C++.

Čítač cyklu zde je skutečný čítač cyklu, ne nástěnné hodiny. Četnost počítání se bude lišit s frekvencí procesoru. Pokud máte pocit, že musíte znát frekvenci čítače cyklu, neměli byste používat čítač cyklu. Místo toho chcete měřit čas nástěnných hodin, pro který byste měli použít QueryPerformanceCounter.

Viz také

Běžné problémy s migrací ARM v prostředí Visual C++
Zpracování výjimek ARM64