Übersicht über ARM64EC-ABI-Konventionen
ARM64EC ist eine Binärschnittstelle (Application Binary Interface, ABI), mit der ARM64-Binärdateien nativ und interoperabel mit x64-Code ausgeführt werden können. Insbesondere folgt die ARM64EC ABI x64-Softwarekonventionen, einschließlich Aufrufkonventionen, Stapelnutzung und Datenausrichtung, wodurch ARM64EC- und x64-Code interoperabel werden. Das Betriebssystem emuliert den x64-Teil der Binärdatei. (Die EC in ARM64EC steht für emulationskompatibel.)
Weitere Informationen zu den X64- und ARM64-ABIs finden Sie unter Übersicht über x64 ABI-Konventionen und Übersicht über ARM64 ABI-Konventionen.
ARM64EC löst keine Unterschiede zwischen x64- und ARM-basierten Architekturen im Speichermodell. Weitere Informationen finden Sie unter Allgemeine Probleme bei der Migration von Visual C++ ARM.
Definitionen
- ARM64 – Der Codestream für ARM64-Prozesse, die herkömmlichen ARM64-Code enthalten.
- ARM64EC – Der Codedatenstrom, der eine Teilmenge des ARM64-Registers verwendet, um die Interoperabilität mit x64-Code bereitzustellen.
Registrieren der Zuordnung
x64-Prozesse enthalten möglicherweise Threads, die ARM64EC Code ausführen. Daher ist es immer möglich, einen x64-Registerkontext abzurufen, ARM64EC eine Teilmenge der ARM64-Kernregister verwendet, die 1:1 zu emulierten x64-Registern zuordnen. Wichtig ist, dass ARM64EC niemals Register außerhalb dieser Teilmenge verwendet, außer zum Lesen der TEB-Adresse (Thread Environment Block) aus x18
.
Systemeigene ARM64-Prozesse sollten nicht in der Leistung zurücktreten, wenn einige oder viele Funktionen wie ARM64EC neu kompiliert werden. Um die Leistung aufrechtzuerhalten, folgt die ABI den folgenden Prinzipien:
Die ARM64EC Registeruntermenge enthält alle Register, die Teil der ARM64-Funktionsaufrufkonvention sind.
Die ARM64EC Anrufkonvention wird direkt der ARM64-Anrufkonvention zugeordnet.
Spezielle Hilfsroutinen wie __chkstk_arm64ec
verwenden benutzerdefinierte Anrufkonventionen und -register. Diese Register sind auch in der ARM64EC Teilmenge der Register enthalten.
Registerzuordnung für ganzzahlige Register
ARM64EC Registrier | x64-Register | ARM64EC Anrufkonvention | Aufrufkonvention bei ARM64-Systemen | Aufrufkonvention bei x64-Systemen |
---|---|---|---|---|
x0 |
rcx |
volatile | volatile | volatile |
x1 |
rdx |
volatile | volatile | volatile |
x2 |
r8 |
volatile | volatile | volatile |
x3 |
r9 |
volatile | volatile | volatile |
x4 |
r10 |
volatile | volatile | volatile |
x5 |
r11 |
volatile | volatile | volatile |
x6 |
mm1 (niedrige 64 Bit x87-Register R1 ) |
volatile | volatile | volatile |
x7 |
mm2 (niedrige 64 Bit x87-Register R2 ) |
volatile | volatile | volatile |
x8 |
rax |
volatile | volatile | volatile |
x9 |
mm3 (niedrige 64 Bit x87-Register R3 ) |
volatile | volatile | volatile |
x10 |
mm4 (niedrige 64 Bit x87-Register R4 ) |
volatile | volatile | volatile |
x11 |
mm5 (niedrige 64 Bit x87-Register R5 ) |
volatile | volatile | volatile |
x12 |
mm6 (niedrige 64 Bit x87-Register R6 ) |
volatile | volatile | volatile |
x13 |
N/V | Nicht zulässig | volatile | – |
x14 |
– | Nicht zulässig | volatile | N/V |
x15 |
mm7 (niedrige 64 Bit x87-Register R7 ) |
volatile | volatile | volatile |
x16 |
Hohe 16 Bits jeder der x87 R0 -R3 -Register |
volatil (xip0 ) |
volatil (xip0 ) |
volatile |
x17 |
Hohe 16 Bits jeder der x87 R4 -R7 -Register |
volatil (xip1 ) |
volatil (xip1 ) |
volatile |
x18 |
GS.base | fixed(TEB) | fixed(TEB) | fixed(TEB) |
x19 |
r12 |
Nicht flüchtig | Nicht flüchtig | Nicht flüchtig |
x20 |
r13 |
Nicht flüchtig | Nicht flüchtig | Nicht flüchtig |
x21 |
r14 |
Nicht flüchtig | Nicht flüchtig | Nicht flüchtig |
x22 |
r15 |
Nicht flüchtig | Nicht flüchtig | Nicht flüchtig |
x23 |
N/V | Nicht zulässig | Nicht flüchtig | – |
x24 |
– | Nicht zulässig | Nicht flüchtig | N/V |
x25 |
rsi |
Nicht flüchtig | Nicht flüchtig | Nicht flüchtig |
x26 |
rdi |
Nicht flüchtig | Nicht flüchtig | Nicht flüchtig |
x27 |
rbx |
Nicht flüchtig | Nicht flüchtig | Nicht flüchtig |
x28 |
N/V | Nicht zulässig | Nicht zulässig | N/V |
fp |
rbp |
Nicht flüchtig | Nicht flüchtig | Nicht flüchtig |
lr |
mm0 (niedrige 64 Bit x87-Register R0 ) |
beide | beide | beide |
sp |
rsp |
Nicht flüchtig | Nicht flüchtig | Nicht flüchtig |
pc |
rip |
Anweisungszeiger | Anweisungszeiger | Anweisungszeiger |
PSTATE Teilmengenplattform: N /Z /C /V /SS 1, 2 |
RFLAGS Teilmengenplattform: SF /ZF /CF /OF /TF |
volatile | volatile | volatile |
N/V | RFLAGS Teilmengenplattform: PF /AF |
– | – | volatile |
N/V | RFLAGS Teilmengenplattform: DF |
– | – | Nicht flüchtig |
1 Vermeiden Sie direktes Lesen, Schreiben oder Berechnen von Zuordnungen zwischen PSTATE
und RFLAGS
. Diese Bits können in Zukunft verwendet werden und können sich ändern.
2 Das ARM64EC-Carry-Flag C
ist das Gegenteil des x64-Carry-Flags CF
für Subtraktionsoperationen. Es gibt keine spezielle Behandlung, da das Flag volatil ist und daher beim Übergang zwischen Funktionen (ARM64EC und x64) gelöscht wird.
Registrieren der Zuordnung für Vektorregister
ARM64EC Register | x64-Register | ARM64EC Anrufkonvention | Aufrufkonvention bei ARM64-Systemen | Aufrufkonvention bei x64-Systemen |
---|---|---|---|---|
v0 -v5 |
xmm0 -xmm5 |
volatile | volatile | volatile |
v6 -v7 |
xmm6 -xmm7 |
volatile | volatile | Nicht flüchtig |
v8 -v15 |
xmm8 -xmm15 |
volatil 1 | volatil 1 | Nicht flüchtig |
v16 -v31 |
xmm16 -xmm31 |
Nicht zulässig | volatile | nicht zulässig (der x64-Emulator unterstützt AVX-512 nicht) |
FPCR 2 |
MXCSR[15:6] |
Nicht flüchtig | Nicht flüchtig | Nicht flüchtig |
FPSR 2 |
MXCSR[5:0] |
volatile | volatile | volatile |
1 Diese ARM64-Register sind speziell dafür, dass die unteren 64 Bits nicht veränderlich sind, aber die oberen 64 Bits veränderlich sind. Aus der Sicht eines x64-Anrufers sind sie praktisch volatil, da der Angerufene Daten zerstören würde.
2 Vermeiden Sie direktes Lesen, Schreiben oder Berechnen von Zuordnungen von FPCR
und FPSR
. Diese Bits können in Zukunft verwendet werden und können sich ändern.
Strukturverpackung
ARM64EC folgt den gleichen Strukturverpackungsregeln, die für x64 verwendet werden, um die Interoperabilität zwischen ARM64EC Code und x64-Code sicherzustellen. Weitere Informationen und Beispiele für die x64-Strukturverpackung finden Sie unter Übersicht über x64 ABI-Konventionen.
Emulationshilfs-ABI-Routinen
ARM64EC Code und Thunks verwenden Emulationshilfsroutinen für den Übergang zwischen x64 und ARM64EC Funktionen.
In der folgenden Tabelle werden die einzelnen speziellen ABI-Routinen und die ABI-Registrierungen beschrieben. Die Routinen ändern die aufgelisteten beibehaltenen Register nicht unter der Spalte ABI. Es sollten keine Annahmen über nicht aufgelistete Register getroffen werden. Auf dem Datenträger sind die ABI-Routinezeiger null. Zur Ladezeit aktualisiert das Ladeprogramm die Zeiger so, dass er auf die x64-Emulatorroutinen verweist.
Name | Beschreibung | ABI |
---|---|---|
__os_arm64x_dispatch_call_no_redirect |
Wird von einem Exit-Thunk aufgerufen, um ein x64-Ziel aufzurufen (entweder eine x64-Funktion oder eine x64-Schnellweiterleitungssequenz). Die Routine verschiebt die ARM64EC Absenderadresse (im LR Register) gefolgt von der Adresse der Anweisung, die eine blr x16 Anweisung erfolgreich ist, die den x64-Emulator aufruft. Anschließend wird die blr x16 Anweisung ausgeführt. |
Rückgabewert in x8 (rax ) |
__os_arm64x_dispatch_ret |
Aufgerufen von einem Eintrag Thunk, um zu seinem x64-Anrufer zurückzukehren. Sie füllt die x64-Absenderadresse aus dem Stapel und ruft den x64-Emulator auf, um dorthin zu springen | N/V |
__os_arm64x_check_call |
Wird von ARM64EC Code mit einem Zeiger auf ein Exit-Thunk und die indirekte ARM64EC auszuführende Zieladresse aufgerufen. Das ARM64EC Ziel gilt als patchbar, und die Ausführung wird immer mit denselben Daten, mit der sie aufgerufen wurde, oder mit geänderten Daten an den Aufrufer zurückgegeben | Argumente:x9 : Die Zieladressex10 : Die Exit-Thunk-Adressex11 : Die Adresse der schnellen WeiterleitungssequenzOut: x9 : Wenn die Zielfunktion umgeleitet wurde, enthält sie die Adresse der schnellen Weiterleitungssequenzx10 : Die Exit-Thunk-Adressex11 : Wenn die Funktion umgeleitet wurde, enthält sie die Ausgangs-Thunk-Adresse. Andernfalls springt die Zieladresse zuBeibehaltenen Register: x0 -x8 , x15 (chkstk ). und q0 -q7 geändert. |
__os_arm64x_check_icall |
Wird von ARM64EC Code mit einem Zeiger auf ein Exit-Thunk aufgerufen, um einen Sprung zu einer Zieladresse zu behandeln, die entweder x64 oder ARM64EC ist. Wenn das Ziel x64 ist und der x64-Code nicht gepatcht wurde, legt die Routine das Zieladressregister fest. Er verweist auf die ARM64EC Version der Funktion, sofern vorhanden. Andernfalls wird das Register so festgelegt, dass es auf den Ausgangs-Thunk zeigt, der zum x64-Ziel wechselt. Anschließend kehrt er zum aufrufenden ARM64EC Code zurück, der dann zur Adresse im Register springt. Diese Routine ist eine nicht optimierte Version von __os_arm64x_check_call , bei der die Zieladresse zur Kompilierungszeit nicht bekannt istWird an einem Anrufstandort eines indirekten Anrufs verwendet |
Argumente:x9 : Die Zieladressex10 : Die Exit-Thunk-Adressex11 : Die Adresse der schnellen WeiterleitungssequenzOut: x9 : Wenn die Zielfunktion umgeleitet wurde, enthält sie die Adresse der schnellen Weiterleitungssequenzx10 : Die Exit-Thunk-Adressex11 : Wenn die Funktion umgeleitet wurde, enthält sie die Ausgangs-Thunk-Adresse. Andernfalls springt die Zieladresse zuBeibehaltene Register: x0 -x8 , x15 (chkstk ) und q0 -q7 |
__os_arm64x_check_icall_cfg |
Identisch mit __os_arm64x_check_icall , aber überprüft auch, dass die angegebene Adresse ein gültiges Steuerelementflussdiagramm-indirektes Anrufziel ist |
Argumente:x10 : Die Adresse des Exit-Thunkx11 : Die Adresse der ZielfunktionOut: x9 : Wenn das Ziel x64 ist, wird die Adresse an die Funktion adressiert. Andernfalls wird der Wert nicht definiertx10 : Die Adresse des Exit-Thunkx11 : Wenn das Ziel x64 ist, enthält es die Adresse des Exit-Thunk. Andernfalls die Adresse der FunktionBeibehaltene Register: x0 -x8 , x15 (chkstk ) und q0 -q7 |
__os_arm64x_get_x64_information |
Ruft den angeforderten Teil des Live x64-Registrierungskontexts ab | _Function_class_(ARM64X_GET_X64_INFORMATION) NTSTATUS LdrpGetX64Information(_In_ ULONG Type, _Out_ PVOID Output, _In_ PVOID ExtraInfo) |
__os_arm64x_set_x64_information |
Legt den angeforderten Teil des Live x64-Registrierungskontexts fest | _Function_class_(ARM64X_SET_X64_INFORMATION) NTSTATUS LdrpSetX64Information(_In_ ULONG Type,_In_ PVOID Input, _In_ PVOID ExtraInfo) |
__os_arm64x_x64_jump |
Wird in signaturlosen Adjustern und anderen Thunks verwendet, die einen Aufruf direkt an eine andere Funktion weiterleiten (jmp ), die eine beliebige Signatur haben kann, und so die mögliche Anwendung des richtigen Thunks auf das eigentliche Ziel verschieben |
Argumente:x9 : Ziel für den Sprung zuAlle Parameterregister bleiben erhalten (weitergeleitet) |
Thunks
Thunks sind die Low-Level-Mechanismen zur Unterstützung von ARM64EC- und x64-Funktionen, die einander aufrufen. Es gibt zwei Arten: Eingabe-Thunks für die Eingabe ARM64EC Funktionen und Ausgabe-Thunks zum Aufrufen von x64-Funktionen.
Eingabe-Thunk und systeminterne Eingabe-Thunks: x64 zum ARM64EC Funktionsaufruf
Um x64-Aufrufer zu unterstützen, wenn eine C/C++-Funktion als ARM64EC kompiliert wird, generiert die Toolkette einen einzelnen Eingabe-Thunk, der aus ARM64EC Computercode besteht. Systeminterne haben einen eigenen Eingabe-Thunk. Alle anderen Funktionen teilen einen Eingabe-Thunk mit allen Funktionen, die über eine übereinstimmende Aufrufkonvention, Parameter und Rückgabetyp verfügen. Der Inhalt des Thunk hängt von der Aufrufkonvention der C/C++-Funktion ab.
Neben der Behandlung von Parametern und der Absenderadresse überbrückt der Thunk die Unterschiede bei der Volatilität zwischen ARM64EC und x64-Vektorregistern, die durch ARM64EC Vektorregisterzuordnung verursacht werden:
ARM64EC Register | x64-Register | ARM64EC Anrufkonvention | Aufrufkonvention bei ARM64-Systemen | Aufrufkonvention bei x64-Systemen |
---|---|---|---|---|
v6 -v15 |
xmm6 -xmm15 |
veränderlich, aber im Eingabe-Thunk gespeichert/wiederhergestellt (x64 bis ARM64EC) | veränderliche oder teilweise veränderliche obere 64 Bits | Nicht flüchtig |
Der Eingabe-Thunk führt die folgenden Aktionen aus:
Parameteranzahl | Stapelverwendung |
---|---|
0–4 | Speichert ARM64EC v6 und v7 im vom Anrufer zugewiesenen HeimbereichDa der Angerufene ARM64EC ist, der nicht über den Begriff eines Heimbereichs verfügt, werden die gespeicherten Werte nicht überschrieben. Weist dem Stapel zusätzliche 128 Bytes zu und speichert ARM64EC v8 bis v15 . |
5-8 | x4 = 5. Parameter aus dem Stapelx5 = 6. Parameter aus dem Stapelx6 = 7. Parameter aus dem Stapelx7 = 8. Parameter aus dem StapelWenn der Parameter SIMD ist, werden stattdessen die v4 -v7 Register verwendet |
+9 | Ordnet AlignUp(NumParams - 8 , 2) * 8 Bytes im Stapel zu. *Kopiert die 9. und verbleibenden Parameter in diesen Bereich |
* Das Ausrichten des Werts an einer geraden Zahl garantiert, dass der Stapel auf 16 Byte ausgerichtet bleibt
Wenn die Funktion einen ganzzahligen 32-Bit-Parameter akzeptiert, darf der Thunk nur 32 Bit anstelle der vollständigen 64 Bits des übergeordneten Registers pushen.
Als Nächstes verwendet der Thunk eine ARM64-Anweisung bl
, um die ARM64EC-Funktion aufzurufen. Nachdem die Funktion zurückgegeben wurde, gibt der Thunk folgendes zurück:
- Rückgängigmachen aller Stapelzuweisungen
- Ruft die
__os_arm64x_dispatch_ret
Emulatorhilfsprogramm auf, um die x64-Absenderadresse einzugeben und die x64-Emulation fortzusetzen.
Ausgabe-Thunk: ARM64EC zu x64-Funktionsaufruf
Für jeden Aufruf, den eine ARM64EC C/C++-Funktion an potenziellen x64-Code macht, generiert die MSVC-Toolkette einen usgabe-Thunk. Der Inhalt des Thunk hängt von den Parametern des x64 Angerufenen ab und ob der Angerufene die Standardanrufkonvention oder __vectorcall
verwendet. Der Compiler ruft diese Informationen aus einer Funktionsdeklaration für den Angerufenen ab.
Zuerst verschiebt der Thunk die Absenderadresse, die sich im ARM64EC lr
Register befindet, und einen Dummy 8-Byte-Wert, um sicherzustellen, dass der Stapel auf 16 Byte ausgerichtet ist. Zweitens behandelt der Thunk die Parameter:
Parameteranzahl | Stapelverwendung |
---|---|
0–4 | Ordnet 32 Byte Home Space auf dem Stapel zu |
5-8 | Weist AlignUp(NumParams - 4, 2) * 8 mehr Bytes höher auf dem Stapel zu. * Kopiert den 5. und alle nachfolgenden Parameter aus ARM64EC x4 -x7 in diesen zusätzlichen Bereich |
+9 | Kopiert die 9. und verbleibenden Parameter in den zusätzlichen Leerraum |
* Das Ausrichten des Werts an einer geraden Zahl garantiert, dass der Stapel auf 16 Bytes ausgerichtet bleibt.
Drittens ruft der Thunk die __os_arm64x_dispatch_call_no_redirect
Emulatorhilfsfunktion auf, um den x64-Emulator aufzurufen, um die x64-Funktion auszuführen. Der Anruf muss eine blr x16
Anweisung sein (glücklicherweise ist x16
ein veränderliches Register). Eine blr x16
Anweisung ist erforderlich, da der x64-Emulator diese Anweisung als Hinweis analysiert.
Die x64-Funktion versucht in der Regel, mithilfe einer x64-ret
-Anweisung zum Emulatorhilfsprogramm zurückzukehren. An diesem Punkt erkennt der x64-Emulator, dass er sich in ARM64EC Code befindet. Anschließend wird der vorherige 4-Byte-Hinweis gelesen, der als ARM64-blr x16
-Anweisung auftritt. Da dieser Hinweis darauf hinweist, dass sich die Absenderadresse in diesem Hilfsprogramm befindet, springt der Emulator direkt zu dieser Adresse.
Die x64-Funktion kann mithilfe einer verzweigten Anweisung, einschließlich x64 jmp
und call
. Der Emulator behandelt diese Szenarien ebenfalls.
Wenn der Helfer dann wieder zum Thunk zurückkehrt, geht der Thunk wie folgt vor:
- Rückgängigmachen aller Stapelzuweisungen
- Öffnen des ARM64EC
lr
Registers - Ausführung einer ARM64-Anweisung
ret lr
.
ARM64EC Funktionsname Dekoration
Ein ARM64EC Funktionsname weist nach jeder sprachspezifischen Dekoration eine sekundäre Dekoration auf. Für Funktionen mit C-Verknüpfung (ob als C oder mithilfe von extern "C"
kompiliert) wird dem Namen eine #
vorangestellt. Für C++-dekorierte Funktionen wird ein $$h
Tag in den Namen eingefügt.
foo => #foo
?foo@@YAHXZ => ?foo@@$$hYAHXZ
__vectorcall
Die ARM64EC Toolkette unterstützt __vectorcall
derzeit nicht. Der Compiler gibt einen Fehler aus, wenn er die __vectorcall
Verwendung mit ARM64EC erkennt.
Weitere Informationen
Grundlegendes zu ARM64EC ABI- und Assemblycode
Häufig auftretende ARM-Migrationsprobleme bei Visual C++
Dekorierte Namen