Přehled konvencí ARM32 ABI
Binární rozhraní aplikace (ABI) pro kód zkompilovaný pro Windows na procesorech ARM je založené na standardním EABI ARM. Tento článek popisuje hlavní rozdíly mezi Windows v ARM a standardem. Tento dokument popisuje ABI ARM32. Informace o arm64 ABI najdete v tématu Přehled konvencí ARM64 ABI. Další informace o standardním ARM EABI najdete v tématu ABI (Application Binary Interface) pro architekturu ARM (externí odkaz).
Základní požadavky
Windows v ARM vždy předpokládá, že běží v architektuře ARMv7. Podpora s plovoucí desetinou čárkou ve formě VFPv3-D32 nebo novější musí být v hardwaru. VFP musí podporovat jednoduchou přesnost i dvojitou přesnost s plovoucí desetinnou čárkou v hardwaru. Modul runtime Windows nepodporuje emulaci s plovoucí desetinou čárkou, která umožňuje spouštění na hardwaru jiného typu než VFP.
Podpora rozšířených rozšíření SIMD (NEON), včetně celočíselného i plovoucího čísla, musí být také k dispozici v hardwaru. Není k dispozici podpora emulace za běhu.
Celočíselná podpora dělení (UDIV/SDIV) se doporučuje, ale nevyžaduje se. U platforem, které chybí celočíselná podpora dělení, může dojít k penalizaci výkonu, protože tyto operace musí být zachycené a případně opravené.
Endianness
Windows v ARM se spouští v režimu little-endian. Kompilátor MSVC i modul runtime windows vždy očekávají malá endová data. Instrukce SETEND v architektuře sady instrukcí ARM (ISA) umožňuje i kódu v uživatelském režimu změnit aktuální endianitu. Tento postup se ale nedoporučuje, protože je pro aplikaci nebezpečný. Pokud je výjimka generována v režimu big-endian, chování je nepředvídatelné. Může vést k chybě aplikace v uživatelském režimu nebo k kontrole chyb v režimu jádra.
Zarovnání
I když systém Windows umožňuje hardwaru ARM zpracovávat chybně zarovnané celé číslo přístupy transparentně, chyby zarovnání se můžou v některých situacích generovat. Při zarovnání postupujte podle těchto pravidel:
Nemusíte zarovnávat 16bitové a 32bitové celočíselné načítání a úložiště s poloviční velikostí slov. Hardware je zpracovává efektivně a transparentně.
Zatížení s plovoucí desetinou čárkou a úložiště by měla být zarovnaná. Jádro zpracovává nezarovnané zatížení a ukládá transparentně, ale s velkou režií.
Načtení nebo uložení dvojitých operací (LDRD/STRD) a více operací (LDM/STM) by mělo být zarovnané. Jádro zpracovává většinu z nich transparentně, ale s významnou režií.
Všechny přístupy k paměti bez mezipaměti musí být zarovnané, a to i pro celočíselné přístupy. Nerovnané přístupy způsobují chybu zarovnání.
Sada instrukcí
Instrukce nastavená pro Windows v ARM je přísně omezená na Thumb-2. Očekává se, že se spustí veškerý kód spuštěný na této platformě a vždy zůstane v režimu Thumb. Pokus o přepnutí na starší sadu instrukcí ARM může proběhnout úspěšně. Pokud ano, jakékoli výjimky nebo přerušení, ke kterým dojde, můžou vést k chybě aplikace v uživatelském režimu nebo kontrole chyb v režimu jádra.
Vedlejším účinkem tohoto požadavku je, že všechny ukazatele kódu musí mít nastavenou nízkou bitovou sadu. Když se pak načtou a rozvětvení přes BLX nebo BX, procesor zůstane v režimu Thumb. Nepokouší se spustit cílový kód jako 32bitové instrukce ARM.
Pokyny pro SDIV/UDIV
Použití celočíselného dělení instrukcí SDIV a UDIV je plně podporováno, a to i na platformách bez nativního hardwaru pro jejich zpracování. Dodatečná režie na procesor Cortex-A9 se dělí na SDIV nebo UDIV přibližně 80 cyklů. To se přidá do celkového rozdělení času 20–250 cyklů v závislosti na vstupech.
Celočíselné registry
Procesor ARM podporuje 16 celých registrů:
Registrovat | Nestálý? | Role |
---|---|---|
r0 | Nestálý | Parametr, výsledek, scratch register 1 |
r1 | Nestálý | Parametr, výsledek, scratch register 2 |
r2 | Nestálý | Parametr, scratch register 3 |
r3 | Nestálý | Parametr, scratch register 4 |
R4 | Nestálé | |
r5 | Nestálé | |
r6 | Nestálé | |
r7 | Nestálé | |
R8 | Nestálé | |
r9 | Nestálé | |
r10 | Nestálé | |
r11 | Nestálé | Ukazatel rámce |
r12 | Nestálý | Registrace pomocného volání uvnitř procedury |
r13 (SP) | Nestálé | Ukazatel zásobníku |
r14 (LR) | Nestálé | Registrace odkazu |
r15 (PC) | Nestálé | Čítač programu |
Podrobnosti o použití parametru a návratových registrů hodnot najdete v části Předávání parametrů v tomto článku.
Systém Windows používá r11 k rychlému procházení rámu zásobníku. Další informace najdete v části Stack Walking. Kvůli tomuto požadavku musí r11 vždy odkazovat na nejvyšší propojení v řetězci. Nepoužívejte r11 pro obecné účely, protože váš kód během analýzy nevygeneruje správné procházky zásobníkem.
Registrace VFP
Windows podporuje pouze varianty ARM, které mají podporu koprocesoru VFPv3-D32. Znamená to, že registry s plovoucí desetinou čárkou jsou vždy přítomné a dají se spoléhat na předávání parametrů. A úplná sada 32 registrů je k dispozici pro použití. Registry VFP a jejich využití jsou shrnuté v této tabulce:
Dvouhra | Double – prvky | Čtyřkolky | Nestálý? | Role |
---|---|---|---|---|
s0-s3 | d0-d1 | q0 | Nestálý | Parametry, výsledek, scratch register |
s4-s7 | d2-d3 | Otázka 1 | Nestálý | Parametry, scratch register |
s8-s11 | d4-d5 | Otázka č. 2 | Nestálý | Parametry, scratch register |
s12-s15 | d6-d7 | q3 | Nestálý | Parametry, scratch register |
s16-s19 | d8-d9 | q4 | Nestálé | |
s20-s23 | d10-d11 | q5 | Nestálé | |
s24-s27 | d12-d13 | q6 | Nestálé | |
s28-s31 | d14-d15 | q7 | Nestálé | |
d16-d31 | q8-q15 | Nestálý |
Následující tabulka znázorňuje bitová pole stavu s plovoucí desetinou čárkou a registr ovládacích prvků (FPSCR):
Bity | Význam | Nestálý? | Role |
---|---|---|---|
31-28 | NZCV | Nestálý | Příznaky stavu |
27 | QC | Nestálý | Kumulativní sytost |
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 vyprázdnění na nulu |
23-22 | RMode | Nestálé | Ovládací prvek režimu zaokrouhlování |
21-20 | Krok | Nestálé | Vektorová stride, musí být vždy 0 |
18-16 | Len | Nestálé | Délka vektoru musí být vždy 0. |
15, 12-8 | IDE, IXE atd. | Nestálé | Soutisk výjimek musí být vždy 0. |
7, 4-0 | IDC, IXC atd. | Nestálý | Příznaky kumulativní výjimky |
Výjimky s plovoucí desetinou čárkou
Většina hardwaru ARM nepodporuje výjimky IEEE s plovoucí desetinou čárkou. 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 FPSCR. Tato akce zajišťuje normalizované chování napříč variantami procesoru. V opačném případě by kód vyvinutý na platformě, která nemá podporu výjimek, mohl při spuštění na platformě s podporou výjimek obdržet neočekávané výjimky.
Předávání parametrů
Windows v ARM ABI se řídí pravidly ARM pro předávání parametrů pro jiné než variadické funkce. Pravidla ABI zahrnují rozšíření VFP a Advanced SIMD. Tato pravidla se řídí standardem volání procedur pro architekturu ARM v kombinaci s rozšířeními VFP. Ve výchozím nastavení se první čtyři celočíselné argumenty a až osm argumentů s plovoucí desetinou čárkou nebo vektorem předávají v registrech. Všechny další argumenty se předávají v zásobníku. Argumenty se přiřazují k registrům nebo zásobníku pomocí tohoto postupu:
Fáze A: Inicializace
Inicializace se provádí přesně jednou, před zahájením zpracování argumentu:
Číslo dalšího základního registru (NCRN) je nastavené na r0.
Registry VFP jsou označené jako nepřidělené.
Další skládaná argumentová adresa (NSAA) je nastavena na aktuální sp.
Pokud je volána funkce, která vrací výsledek v paměti, pak je adresa výsledku umístěna v r0 a NCRN je nastavena na r1.
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 je argument složený typ, jehož velikost nelze staticky určit volajícím i volaným, argument se zkopíruje do paměti a nahradí ukazatelem na kopii.
Pokud je argument bajt nebo 16bitové napůl slovo, je 32bitové celé slovo rozšířeno o 32bitové nebo znaménko rozšířené na 32bitové celé slovo a považuje se za 4 bajtový argument.
Pokud je argument složený typ, jeho velikost se zaokrouhlí nahoru na nejbližší násobek 4.
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 typem VFP a existuje dostatek po sobě jdoucích nepřidělených registrů VFP příslušného typu, pak je argument přidělen nejnižší sekvenci takových registrů.
Pokud je argument typem VFP, označí se všechny zbývající nepřidělené registry jako nedostupné. NSAA se upraví směrem nahoru, dokud není správně zarovnaná pro typ argumentu a argument se zkopíruje do zásobníku v upravené NSAA. NSAA se pak zvýší o velikost argumentu.
Pokud argument vyžaduje zarovnání 8 bajtů, číslo NCRN se zaokrouhlí nahoru na další sudé číslo registru.
Pokud velikost argumentu ve 32bitových slovech není větší než r4 minus NCRN, argument se zkopíruje do registrů jader počínaje ncRN s nejméně významnými bity, které zabírají registry s nižším číslem. NCRN se zvýší o počet použitých registrů.
Pokud je NCRN menší než r4 a NSAA je rovna SP, argument je rozdělen mezi základní registry a zásobník. První část argumentu se zkopíruje do základních registrů, počínaje ncRN, až do r3 a včetně r3. Zbytek argumentu se zkopíruje do zásobníku počínaje NSAA. NCRN je nastaven na r4 a NSAA se zvýší o velikost argumentu minus množství předané v registrech.
Pokud argument vyžaduje zarovnání 8 bajtů, nsaa se zaokrouhlí nahoru na další 8bajtů zarovnanou adresu.
Argument se zkopíruje do paměti v NSAA. NSAA se zvýší o velikost argumentu.
Registry VFP se nepoužívají pro variadické funkce a pravidla fáze C 1 a 2 se ignorují. To znamená, že variadická funkce může začínat volitelným zápisem {r0-r3}, aby se přednesly argumenty registru na všechny další argumenty předané volajícím, a pak přistupovat k celému seznamu argumentů přímo ze zásobníku.
Celočíselné hodnoty jsou vráceny v r0, volitelně rozšířené na r1 pro 64bitové vrácené hodnoty. Hodnoty typu VFP/NEON s plovoucí desetinou čárkou nebo SIMD se vrátí v s0, d0 nebo q0 podle potřeby.
Zásobník
Zásobník musí vždy zůstat zarovnaný se 4 bajty a musí být zarovnaný 8 bajtů na libovolné hranici funkce. Je nutné podporovat časté použití vzájemně uzamčených operací u 64bitových proměnných zásobníku. ARM EABI uvádí, že zásobník je 8 bajtů zarovnaný v libovolném veřejném rozhraní. Kvůli konzistenci systém Windows v ARM ABI považuje všechny hranice funkce za veřejné rozhraní.
Funkce, které musí používat ukazatel rámce , například funkce, které volají alloca
nebo mění ukazatel zásobníku dynamicky, musí nastavit ukazatel rámce v r11 v prologu funkce a nechat ho beze změny, dokud epilog. Funkce, které nevyžadují ukazatel rámce, musí provádět všechny aktualizace zásobníku v prologue a ponechat ukazatel zásobníku beze změny, dokud epilogue.
Funkce, které přidělují v zásobníku 4 kB nebo více, musí zajistit, aby se každá stránka před poslední stránkou dotkla v pořadí. Toto pořadí zajišťuje, že žádný kód nemůže "přeskočit" strážní stránky, které Systém Windows používá k rozšíření zásobníku. Rozšíření obvykle provádí __chkstk
pomocná rutina, která předává celkové přidělení zásobníku v bajtech vydělené 4 v r4 a která vrátí konečnou částku přidělení zásobníku v bajtech zpět v r4.
Červená zóna
Oblast 8 bajtů bezprostředně pod aktuálním ukazatelem zásobníku je vyhrazená pro analýzu a dynamické opravy. Umožňuje pečlivě vygenerovaný kód vložit, který ukládá 2 registry [sp, #-8]
a dočasně je používá pro libovolné účely. Jádro Windows zaručuje, že tyto 8 bajtů nebudou přepsány, pokud dojde k výjimce nebo přerušení v uživatelském režimu i v režimu jádra.
Zásobník jádra
Výchozí zásobník režimu jádra ve Windows je tři stránky (12 kB). Dávejte pozor, abyste nevytvořovali funkce, které mají velké vyrovnávací paměti zásobníku v režimu jádra. Přerušení může přijít s velmi malou místností zásobníku a způsobit kontrolu zásobníku panice.
Specifika jazyka C/C++
Výčty jsou 32bitové celočíselné typy, pokud alespoň jedna hodnota v výčtu nevyžaduje 64bitové dvouslovné úložiště. V takovém případě se výčet upřednostní na 64bitový celočíselnou hodnotu.
wchar_t
je definován jako ekvivalent unsigned short
, aby se zachovala kompatibilita s jinými platformami.
Stack walking
Kód Systému Windows je zkompilován s povolenými ukazateli snímků (/Oy (vynechání rámeček-ukazatel)), aby bylo možné rychle procházet zásobníky. Obecně platí, že registr r11 odkazuje na další propojení v řetězci, což je dvojice {r11, lr}, která určuje ukazatel na předchozí rámec zásobníku a zpáteční adresu. Pro lepší profilaci a trasování doporučujeme, aby váš kód povoloval také ukazatele snímků.
Odvíjení výjimek
Uvolnění zásobníku během zpracování výjimek je povoleno pomocí odvinutí kódů. Kódy odvíjení jsou posloupnost bajtů uložených v sekci .xdata spustitelného obrázku. Popisují operaci prologue funkce a epilogue kódu abstraktně, takže účinky prologue funkce lze vrátit zpět při přípravě na odvíjení na rámec zásobníku volajícího.
ARM EABI určuje model odvíjení výjimek, který používá kódy odvíjení. Tato specifikace však nestačí pro odvíjení ve Windows, což musí zpracovávat případy, kdy je procesor uprostřed prologue nebo epilogue funkce. Další informace o Systému Windows v datech výjimek ARM a odvíjení najdete v tématu Zpracování výjimek ARM.
Doporučujeme, aby se dynamicky generovaný kód popisoval pomocí tabulek dynamických funkcí zadaných ve voláních RtlAddFunctionTable
a přidružených funkcích, aby se vygenerovaný kód mohl účastnit zpracování výjimek.
Čítač cyklu
Procesory ARM se systémem Windows se vyžadují k podpoře čítače cyklu, ale použití čítače může způsobit problémy přímo. Aby se zabránilo těmto problémům, windows v ARM k vyžádání normalizované hodnoty 64bitového čítače cyklu používá nedefinovaný opcode. Z jazyka C nebo C++ použijte vnitřní objekt k vygenerování příslušného __rdpmccntr64
opcode. Ze sestavení použijte __rdpmccntr64
instrukci. Čtení čítače cyklu trvá přibližně 60 cyklů na Cortex-A9.
Čítač je skutečný čítač cyklu, nikoli hodiny; proto se počítání frekvence liší podle frekvence procesoru. Chcete-li měřit uplynulý čas hodin, použijte QueryPerformanceCounter
.
Viz také
Běžné problémy s migrací ARM v prostředí Visual C++
Zpracování výjimek ARM