Sdílet prostřednictvím


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:

  1. Číslo dalšího základního registru (NCRN) je nastavené na r0.

  2. Registry VFP jsou označené jako nepřidělené.

  3. Další skládaná argumentová adresa (NSAA) je nastavena na aktuální sp.

  4. 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:

  1. 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.

  2. 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.

  3. 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:

  1. 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ů.

  2. 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.

  3. Pokud argument vyžaduje zarovnání 8 bajtů, číslo NCRN se zaokrouhlí nahoru na další sudé číslo registru.

  4. 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ů.

  5. 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.

  6. Pokud argument vyžaduje zarovnání 8 bajtů, nsaa se zaokrouhlí nahoru na další 8bajtů zarovnanou adresu.

  7. 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