ARM64EC ABI-konvenciók áttekintése

ARM64EC egy alkalmazás bináris felülete (ABI), amely lehetővé teszi, hogy az ARM64 bináris fájljai natívan és együttműködve fussanak x64-kóddal. Az ARM64EC ABI az x64-szoftverkonvenciákat követi, beleértve a hívási konvenciók, a veremhasználat és az adatok igazítását, így a ARM64EC és az x64-kód interoperábilissá válik. Az operációs rendszer emulálja a bináris x64 részét. (Az EC az ARM64EC-ben az emuláció kompatibilis.)

Az x64- és ARM64 ABI-konvenciókról további információt az x64 ABI-konvenciók áttekintése és az ARM64 ABI-konvenciók áttekintése című témakörben talál.

ARM64EC nem oldja meg az x64 és ARM-alapú architektúrák közötti memóriamodell-különbségeket. További információ: Gyakori Microsoft C++ ARM-migrálási problémák.

Meghatározások

  • ARM64 – A hagyományos ARM64-kódot tartalmazó ARM64-folyamatok kódstreamje.
  • ARM64EC – Az ARM64-regiszterkészlet egy részhalmazát használó kódstream, amely az x64-kód együttműködési képességét biztosítja.

Regiszter leképezése

Az x64-folyamatok ARM64EC kódot futtató szálakat tartalmazhatnak. Így mindig lekérhető egy x64-regiszterkörnyezet, ARM64EC az ARM64 magregisztrálók egy részhalmazát használja, amely az 1:1-et megfelelteti az x64-regiszterek emulálásához. Fontos, hogy ARM64EC soha nem használja az alhalmazon kívüli regisztereket, kivéve, amikor beolvassa a Szálkörnyezeti blokk (TEB) címét x18-ból.

A natív ARM64-folyamatok teljesítményének nem szabad romlania, amikor néhány vagy sok függvényt újrafordítanak ARM64EC-ként. A teljesítmény fenntartása érdekében az ABI az alábbi alapelveket követi:

  • A ARM64EC regisztrációs részhalmaz tartalmazza az ARM64 függvényhívási konvenció részét képező összes regisztert.

  • A ARM64EC hívási konvenció közvetlenül az ARM64 hívási konvencióhoz igazodik.

Speciális segítő rutinok, mint például a __chkstk_arm64ec, egyéni hívási konvenciókat és regisztereket használnak. Ezek a regiszterek a nyilvántartások ARM64EC részhalmazában is szerepelnek.

Egész számregiszterek leképezése

ARM64EC regisztráció x64-regisztráció ARM64EC hívási konvenció ARM64-hívási konvenció x64-hívási konvenció
x0 rcx illékony illékony illékony
x1 rdx illékony illékony illékony
x2 r8 illékony illékony illékony
x3 r9 illékony illékony illékony
x4 r10 illékony illékony illékony
x5 r11 illékony illékony illékony
x6 mm1 (az x87 R1 regiszter alacsony 64 bitje) illékony illékony illékony
x7 mm2 (az x87 R2 regiszter alacsony 64 bitje) illékony illékony illékony
x8 rax illékony illékony illékony
x9 mm3 (az x87 R3 regiszter alacsony 64 bitje) illékony illékony illékony
x10 mm4 (az x87 R4 regiszter alacsony 64 bitje) illékony illékony illékony
x11 mm5 (az x87 R5 regiszter alacsony 64 bitje) illékony illékony illékony
x12 mm6 (az x87 R6 regiszter alacsony 64 bitje) illékony illékony illékony
x13 Nincs adat. nem engedélyezett illékony Nincs adat.
x14 Nincs adat. nem engedélyezett illékony Nincs adat.
x15 mm7 (az x87 R7 regiszter alacsony 64 bitje) illékony illékony illékony
x16 Az egyes x87 R0-R3 regiszterek felső 16 bitje illékony(xip0) illékony(xip0) illékony
x17 Az egyes x87 R4-R7 regiszterek felső 16 bitje illékony(xip1) illékony(xip1) illékony
x18 GS.base rögzített (TEB) rögzített (TEB) rögzített (TEB)
x19 r12 nem változékony nem változékony nem változékony
x20 r13 nem változékony nem változékony nem változékony
x21 r14 nem változékony nem változékony nem változékony
x22 r15 nem változékony nem változékony nem változékony
x23 Nincs adat. nem engedélyezett nem változékony Nincs adat.
x24 Nincs adat. nem engedélyezett nem változékony Nincs adat.
x25 rsi nem változékony nem változékony nem változékony
x26 rdi nem változékony nem változékony nem változékony
x27 rbx nem változékony nem változékony nem változékony
x28 Nincs adat. nem engedélyezett nem engedélyezett Nincs adat.
fp rbp nem változékony nem változékony nem változékony
lr mm0 (az x87 R0 regiszter alacsony 64 bitje) mindkettő mindkettő mindkettő
sp rsp nem változékony nem változékony nem változékony
pc rip utasításmutató utasításmutató utasításmutató
PSTATErészhalmaz: N/Z/C/V/SS1, 2 RFLAGS részhalmaz: SF/ZF/CF/OF/TF illékony illékony illékony
Nincs adat. RFLAGS részhalmaz: PF/AF Nincs adat. Nincs adat. illékony
Nincs adat. RFLAGS részhalmaz: DF Nincs adat. Nincs adat. nem változékony

1 Kerülje a közvetlen olvasást, írást vagy számítást az PSTATE és RFLAGS közötti leképezésekre. Ezek a bitek a jövőben használhatók, és változhatnak.

2 Az ARM64EC jelzőbit C az x64 jelzőbit inverze a kivonási műveletekhez. Nincs speciális kezelés, mert a jelző nem stabil, ezért a (ARM64EC és x64) függvények közötti váltáskor elveszik.

Vektorregiszterek leképezése

ARM64EC regisztráció x64-regisztráció ARM64EC hívási konvenció ARM64-hívási konvenció x64-hívási konvenció
v0-v5 xmm0-xmm5 illékony illékony illékony
v6-v7 xmm6-xmm7 illékony illékony nem változékony
v8-v15 xmm8-xmm15 illékony 1 illékony 1 nem változékony
v16-v31 xmm16-xmm31 nem engedélyezett illékony letiltva (az x64 emulátor nem támogatja az AVX-512-t)
FPCR 2 MXCSR[15:6] nem változékony nem változékony nem változékony
FPSR 2 MXCSR[5:0] illékony illékony illékony

1 Ezek az ARM64-regiszterek abban különlegesek, hogy az alsó 64 bit nem változékony, a felső 64 bit azonban változékony. Az x64-hívók szempontjából gyakorlatilag változékonyak, mert a hívott felülírná az adatokat.

2 Kerülje a közvetlen olvasását, írását vagy a leképezések számítását FPCR és FPSR esetén. Ezek a bitek a jövőben használhatók, és változhatnak.

Strukturált csomagolás

ARM64EC ugyanazokat a szerkezetcsomagolási szabályokat követi, amelyeket az x64-hez használ a ARM64EC kód és az x64 kód közötti együttműködés biztosítása érdekében. További információ és példák az x64-alapú szerkezetek csomagolására: X64 ABI-konvenciók áttekintése.

Lebegőpontos kivételek

Annak megállapításához, hogy egy ARM CPU támogatja-e a kivételeket, írjon egy értéket, amely engedélyezi az FPCR-regisztráció kivételét, majd olvassa vissza. Ha a PROCESSZOR támogatja a lebegőpontos kivételeket, a támogatott kivételeknek megfelelő bitek továbbra is be lesznek állítva, míg a processzor alaphelyzetbe állítja a biteket a nem támogatott kivételek esetében.

Az ARM64EC alatt a Windows elkapja a processzor lebegőpontos kivételeit, és letiltja őket az FPCR-regiszterben. Ez biztosítja a különböző processzorvariánsok konzisztens viselkedését.

Emulációs segéd ABI-rutinok

ARM64EC kód és csonkok emulációs segédprogramokat használnak az x64 és ARM64EC függvények közötti váltáshoz.

Az alábbi táblázat az egyes speciális ABI-rutinokat és az ABI által használt nyilvántartásokat ismerteti. A rutinok nem módosítják az ABI oszlopban lévő felsorolt tartósított regisztereket. Nem szabad feltételezni a nem regisztrált nyilvántartásokat. A lemezen az ABI rutinmutatói null értékűek. Betöltési időben a betöltő frissíti a mutatókat, hogy az x64 emulátor rutinjaira mutasson.

Név Leírás ABI
__os_arm64x_dispatch_call_no_redirect Az exit thunk hív egy x64 célt (akár egy x64 függvényt, akár egy x64 gyorselőre sorozatot). A rutin leküldi az ARM64EC visszatérési címet (a LR regiszterben), majd az x64 emulátort meghívó blr x16 utasítást követő utasítás címét. Ezután futtatja az utasítást blr x16 visszatérési érték (x8rax)
__os_arm64x_dispatch_ret Egy belépési mechanizmus hívást eszközöl, hogy visszatérjen az x64-es hívójához. Ekkor megjelenik az x64 visszatérési cím a veremből, és meghívja az x64 emulátort, hogy ugorjon rá Nincs adat.
__os_arm64x_check_call ARM64EC kód hívja meg, amely egy kilépési csonkra mutató mutatóval és a végrehajtáshoz szükséges közvetett ARM64EC célcímmel rendelkezik. A ARM64EC cél javíthatónak minősül, és a végrehajtás mindig ugyanazokkal az adatokkal tér vissza a hívóhoz, mint amellyel meghívták, vagy módosított adatokkal. Érvek:
x9: A célcím
x10: A kilépési thunk cím
x11: A gyors továbbítási szekvencia címe

Kint van:
x9: Ha a célfüggvényt eltérítették, az tartalmazza a gyors előrelépési sorozat címét.
x10: A kilépési thunk cím
x11: Ha a függvényt eltérítették, az tartalmazza a kilépési "thunk" címet. Ellenkező esetben a célcím a következőre ugrott:

Megőrzött regiszterek: x0-x8, x15 ().chkstk és q0-q7
__os_arm64x_check_icall A ARM64EC kód hívható meg egy kilépési csonkra mutató mutatóval egy x64 vagy ARM64EC nevű célcímre való ugrás kezeléséhez. Ha a cél x64, és az x64-kód nem lett javítva, a rutin beállítja a célcím-nyilvántartást. Ha létezik ilyen, a függvény ARM64EC verziójára mutat. Ellenkező esetben a regisztert úgy állítja be, hogy az x64-es célhoz való átmenetet kezelő kilépő pontra mutasson. Ezután visszatér a hívási ARM64EC kódhoz, amely ezután a regisztrációs címre ugrik. Ez a rutin a __os_arm64x_check_call nevű rutin nem optimalizált verziója, ahol a célcím a fordítás idején nem ismert.

Közvetett hívás helyén használatos
Érvek:
x9: A célcím
x10: A kilépési thunk cím
x11: A gyors továbbítási szekvencia címe

Kint van:
x9: Ha a célfüggvényt eltérítették, az tartalmazza a gyors előrelépési sorozat címét.
x10: A kilépési thunk cím
x11: Ha a függvényt eltérítették, az tartalmazza a kilépési "thunk" címet. Ellenkező esetben a célcím a következőre ugrott:

Tartósított nyilvántartások: x0-x8, x15 (chkstk) és q0-q7
__os_arm64x_check_icall_cfg Ugyanaz, mint __os_arm64x_check_icall, de azt is ellenőrzi, hogy a megadott cím egy érvényes Control Flow Graph közvetett hívási cél-e. Érvek:
x10: A kilépési thunk címe
x11: A célzott függvény címe

Kint van:
x9: Ha a cél x64, a függvény címe. Ellenkező esetben nem definiált
x10: A kilépési thunk címe
x11: Ha a cél x64, az tartalmazza a kilépési thunk címét. Ellenkező esetben a függvény címe

Tartósított nyilvántartások: x0-x8, x15 (chkstk) és q0-q7
__os_arm64x_get_x64_information Lekéri az élő x64-regisztrációs környezet kért részét _Function_class_(ARM64X_GET_X64_INFORMATION) NTSTATUS LdrpGetX64Information(_In_ ULONG Type, _Out_ PVOID Output, _In_ PVOID ExtraInfo)
__os_arm64x_set_x64_information Az élő x64-regisztrációs környezet kért részének beállítása _Function_class_(ARM64X_SET_X64_INFORMATION) NTSTATUS LdrpSetX64Information(_In_ ULONG Type,_In_ PVOID Input, _In_ PVOID ExtraInfo)
__os_arm64x_x64_jump Aláírás nélküli beállítóban és más olyan csonkokban használatos, amelyek közvetlenül továbbítják (jmp) a hívást egy másik függvénynek, amely bármilyen aláírással rendelkezhet, és halasztja a jobb oldali thunk lehetséges alkalmazását a valódi célhoz Érvek:
x9: cél, amelyhez ugrik

Minden paraméterregisztrátor megőrzve (továbbítva)

Csonkok

A thunkok olyan alacsony szintű mechanizmusok, amelyek támogatják az ARM64EC és x64 függvények egymás közti hívását. Két típus létezik: a ARM64EC függvények beírására szolgáló beviteli thunkok és az x64-függvények meghívására szolgáló kilépési thunkok .

Bejegyzés hívásának technikája és belső technikai bejegyzés: x64-ről ARM64EC-re történő függvényhívás

Az ARM64EC fordítású C/C++ függvények x64-hívók általi támogatása érdekében az eszközlánc egyetlen bejegyzésáthidalót hoz létre, amely ARM64EC gépi kódot tartalmaz. Az intrinsicsnek saját belépési útja van. Minden más függvény egy belépési hidat oszt meg az összes olyan függvénnyel, amely egyező hívási konvencióval, paraméterekkel és visszatérési típussal rendelkezik. A thunk tartalma a C/C++ függvény hívási konvenciójától függ.

A paraméterek és a visszatérési cím kezelése mellett a thunk áthidalja az ARM64EC és az x64 vektorregiszterek volatilitásbeli különbségeit, melyeket az ARM64EC vektorregiszter térképezés okoz.

ARM64EC regisztráció x64-regisztráció ARM64EC hívási konvenció ARM64-hívási konvenció x64-hívási konvenció
v6-v15 xmm6-xmm15 illékony, de mentett/visszaállított az entry thunkban (x64-ről ARM64EC-re) illékony vagy részben illékony felső 64 bit nem változékony

A bejegyzési thunk a következő műveleteket hajtja végre:

Paraméterszám Veremhasználat
0-4 Az ARM64EC-t v6 és v7 a hívó által lefoglalt memóriaterületre tárolja.

Mivel a hívott ARM64EC, amely nem rendelkezik a "home space" fogalmával, a tárolt értékek nem lesznek felülírva.

További 128 bájtot foglal le a veremben, és az ARM64EC v8-tól v15-ig tárolja.
5-8 x4 = 5. paraméter a veremből
x5 = a hatodik paraméter a veremből
x6 = a verem 7. paramétere
x7 = 8. paraméter a veremből

Ha a paraméter SIMD, a rendszer inkább a v4-v7 regisztereket használja
9+ Bájtokat AlignUp(NumParams - 8 , 2) * 8 foglal le a veremen. *

Másolja a 9. és a fennmaradó paramétereket erre a területre.

* Az érték páros számhoz igazítása garantálja, hogy a verem 16 bájthoz igazodik

Ha a függvény egy 32 bites egész szám paramétert fogad el, a thunk csak 32 bitet küldhet le a szülőregisztrálás teljes 64 bitje helyett.

Ezután a thunk egy ARM64 bl utasítással hívja meg a ARM64EC függvényt. A függvény visszatérése után a thunk következik:

  1. A veremfoglalások visszavonása
  2. Meghívja az __os_arm64x_dispatch_ret emulátor segédprogramot az x64 visszatérési címének kipattintására és az x64 emuláció folytatására.

Kilépési hiba: ARM64EC x64-es függvényhíváshoz

Minden olyan hívásnál, amelyet egy ARM64EC C/C++ függvény a lehetséges x64-kódhoz hoz létre, az MSVC eszközlánc létrehoz egy kilépési hibát. A thunk tartalma az x64 helyettesítési rutin paramétereitől függ, és hogy a helyettesítési rutin a szabványos hívási konvenciót vagy egy nem szabványos konvenciót, például a __vectorcall használ-e. A fordító ezt az információt a hívó függvénydeklarációjából szerzi be.

Először a thunk leküldi a ARM64EC lr regiszterben lévő visszatérési címet, és egy 8 bájtos hamis értéket, hogy a verem 16 bájthoz legyen igazítva. Másodszor, a thunk kezeli a paramétereket:

Paraméterszám Veremhasználat
0-4 32 bájtnyi helyet foglal le a stacken
5-8 A AlignUp(NumParams - 4, 2) * 8 több bájtot magasabban foglalja le a veremen. *

Másolja az 5. és az azt követő paramétereket a ARM64EC-ból x4-x7 ebbe a plusz helyre
9+ A 9. és az utána következő paraméterek másolása a szabad helyre

* Az érték páros számhoz igazítása garantálja, hogy a verem 16 bájthoz igazodik.

Harmadszor, a thunk meghívja az __os_arm64x_dispatch_call_no_redirect emulátor segédet, hogy meghívja az x64 emulátort az x64 függvény futtatásához. A hívásnak blr x16 utasításnak kell lennie (kényelmesen, x16 egy volatilis regiszter). Utasításra blr x16 van szükség, mert az x64 emulátor tippként értelmezi ezt az utasítást.

Az x64 függvény általában x64 ret utasítással próbál visszatérni az emulátor segédhöz. Ezen a ponton az x64 emulátor észleli, hogy ARM64EC kódban van. Ezután felolvassa az előző 4 bájtos tippet, amely történetesen az ARM64 blr x16 utasítás. Mivel ez a tipp azt jelzi, hogy a visszatérési cím ebben a segédben található, az emulátor közvetlenül erre a címre ugrik.

Az x64 függvény bármilyen águtasítással visszatérhet az emulátort segítő funkcióhoz, beleértve az x64 jmp és call utasításokat is. Az emulátor ezeket a forgatókönyveket is kezeli.

Amikor a segítő visszatér a csonkhoz, a thunk:

  1. Feloldja bármely veremfoglalást
  2. A ARM64EC lr regisztrációjának előugró ablaka
  3. ARM64 ret lr utasítást hajt végre.

ARM64EC függvénynév díszítése

ARM64EC függvénynév egy másodlagos jelölést kap, amelyet a nyelvspecifikus jelölés alkalmazása után adnak hozzá. C kapcsolattal rendelkező függvények esetében (akár C-ként fordítva, akár extern "C" használatával), a # kerül a név elé. A C++ által dekorált függvények esetében a rendszer egy címkét $$h szúr be a névbe.

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

__vectorcall

A ARM64EC eszközlánc jelenleg nem támogatott __vectorcall. A fordító hibát ad ki, amikor észleli a __vectorcall használatot ARM64EC-vel.

Lásd még

Az ARM64EC ABI és a szerelvénykód ismertetése
A Microsoft C++ ARM áttelepítésének gyakori problémái
Kitüntetett nevek