x86-Architektur
Der Intel x86-Prozessor verwendet komplexe Anweisungssatz-Computerarchitektur (CISC), was bedeutet, dass es eine geringe Anzahl von speziellen Registern anstelle großer Mengen von allgemeinen Registern gibt. Es bedeutet auch, dass komplexe Sonderanweisungen vorherrschen.
Der x86-Prozessor verfolgt sein Erbe mindestens so weit zurück wie der 8-Bit Intel 8080-Prozessor. Viele Besonderheiten im x86-Anweisungssatz sind auf die Abwärtskompatibilität mit diesem Prozessor (und mit seiner Zilog Z-80 Variante) zurückzuführen.
Microsoft Win32 verwendet den x86-Prozessor im 32-Bit-Flachmodus. Diese Dokumentation konzentriert sich nur auf den flachen Modus.
Register
Die x86-Architektur besteht aus den folgenden nicht privilegierten ganzzahligen Registern.
eax |
Akkumulator |
ebx |
Basisregister |
ecx |
Leistungsindikatorregister |
edx |
Datenregister – kann für E/A-Portzugriff und arithmetische Funktionen verwendet werden |
Esi |
Quellindexregister |
edi |
Zielindexregister |
ebp |
Basiszeigerregister |
ASW |
Stack-Pointer |
Alle ganzzahligen Register sind 32 Bit. Viele davon verfügen jedoch über 16-Bit- oder 8-Bit-Unterregister.
Axt |
Niedrige 16 Bit eax |
Bx |
Niedrige 16 Bits von ebx |
cx |
Niedrige 16 Bit ecx |
Dx |
Niedrige 16 Bits von Edx |
si |
Niedrige 16 Bits von esi |
di |
Niedrige 16 Bit edi |
Bp |
Niedrige 16 Bit ebp |
sp |
Niedrige 16 Bits von esp |
al |
Niedrige 8 Bit eax |
ah |
Hohe 8 Bit ax |
bl |
Niedrige 8 Bits von ebx |
bh |
Hohe 8 Bits von bx |
Cl |
Niedrige 8 Bit ecx |
Ch |
Hohe 8 Bits cx |
dl |
Niedrige 8 Bits von edx |
dh |
Hohe 8 Bits von dx |
Das Arbeiten auf einer Unterregisterregister betrifft nur das Unterregister und keines der Teile außerhalb der Unterregisterregister. Das Speichern im Ax-Register lässt beispielsweise die hohen 16 Bit des Eax-Registers unverändert.
Bei Verwendung der ? (Auswerten des Ausdrucks) Befehl, Register sollten einem "at"-Zeichen ( @ ) vorangestellt werden. Sie sollten z. B. ? @ax anstelle von ? ax verwenden. Dadurch wird sichergestellt, dass der Debugger ax nicht als Symbol, sondern als Register erkennt.
Das (@) ist jedoch im Befehl r (Registers) nicht erforderlich. Beispielsweise wird r ax=5 immer richtig interpretiert.
Zwei weitere Register sind für den aktuellen Zustand des Prozessors wichtig.
eip |
Anweisungszeiger |
flags |
flags |
Der Anweisungszeiger ist die Adresse der ausgeführten Anweisung.
Das Kennzeichenregister ist eine Sammlung von Single-Bit-Flags. Viele Anweisungen ändern die Flags, um das Ergebnis der Anweisung zu beschreiben. Diese Flags können dann durch bedingte Sprunganweisungen getestet werden. Details finden Sie unter "x86 Flags ".
Aufrufkonventionen
Die x86-Architektur verfügt über verschiedene Aufrufkonventionen. Glücklicherweise befolgen sie alle die gleichen Regeln für die Registerarchivierung und Funktionsrückgabe:
Funktionen müssen alle Register beibehalten, mit Ausnahme von eax, ecx und edx, die über einen Funktionsaufruf geändert werden können, und esp, die gemäß der Aufrufkonvention aktualisiert werden müssen.
Das Eax-Register empfängt Funktionsrücklaufwerte, wenn das Ergebnis 32 Bit oder kleiner ist. Wenn das Ergebnis 64 Bit ist, wird das Ergebnis im edx:eax-Paar gespeichert.
Es folgt eine Liste der Aufrufkonventionen, die für die x86-Architektur verwendet werden:
Win32 (__stdcall)
Funktionsparameter werden im Stapel übergeben, rechts nach links verschoben, und der Angerufene bereinigt den Stapel.
Systemeigener C++-Methodenaufruf (auch bekannt als thiscall)
Funktionsparameter werden an den Stapel übergeben, rechts nach links verschoben, der Zeiger "this" wird im ecx-Register übergeben, und der Angerufene bereinigt den Stapel.
COM (__stdcall für C++-Methodenaufrufe)
Funktionsparameter werden im Stapel übergeben, von rechts nach links verschoben, dann wird der Zeiger "this" auf den Stapel verschoben, und dann wird die Funktion aufgerufen. Der Angerufene bereinigt den Stapel.
__fastcall
Die ersten beiden DWORD-oder-kleineren Argumente werden in den ecx - und edx-Registern übergeben. Die verbleibenden Parameter werden an den Stapel übergeben, rechts nach links verschoben. Der Angerufene bereinigt den Stapel.
__cdecl
Funktionsparameter werden im Stapel übergeben, rechts nach links verschoben, und der Aufrufer bereinigt den Stapel. Die __cdecl Aufrufkonvention wird für alle Funktionen mit Parametern mit variabler Länge verwendet.
Debuggeranzeige von Registern und Flags
Hier ist eine Beispieldebuggerregisteranzeige:
eax=00000000 ebx=008b6f00 ecx=01010101 edx=ffffffff esi=00000000 edi=00465000
eip=77f9d022 esp=05cffc48 ebp=05cffc54 iopl=0 nv up ei ng nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000286
Im Benutzermodusdebugging können Sie die iopl und die gesamte letzte Zeile der Debuggeranzeige ignorieren.
x86-Flags
Im vorangehenden Beispiel sind die Zwei-Buchstaben-Codes am Ende der zweiten Zeile Kennzeichnungen. Dies sind Single-Bit-Register und haben eine Vielzahl von Verwendungsmöglichkeiten.
In der folgenden Tabelle sind die x86-Flags aufgeführt:
Kennzeichnungscode | Flagname | Wert | Kennzeichnungsstatus | Beschreibung |
---|---|---|---|---|
von | Überlaufkennzeichnung | 0 1 | nvov | Kein Überlauf - Überlauf |
df | Richtungsflag | 0 1 | updn | Richtung nach oben - Richtung nach unten |
if | Interrupt-Kennzeichnung | 0 1 | diei | Interrupts disabled - Interrupts enabled |
sf | Signaturkennzeichnung | 0 1 | Plng | Positiv (oder Null) - Negativ |
zf | Nullkennzeichnung | 0 1 | nzzr | Nonzero - Null |
af | Hilfstragsflagge | 0 1 | naac | Kein Hilfstrag - Hilfstrag |
pf | Paritätskennzeichen | 0 1 | Pepo | Parität ungerade - Parität sogar |
vgl | Kennzeichnung tragen | 0 1 | nccy | Kein Tragen - Tragen |
tf | Trap-Flag | Wenn tf gleich 1 ist, löst der Prozessor nach der Ausführung einer Anweisung eine STATUS_SINGLE_STEP Ausnahme aus. Dieses Kennzeichen wird von einem Debugger verwendet, um die Ablaufverfolgung in einem Schritt zu implementieren. Sie sollte nicht von anderen Anwendungen verwendet werden. | ||
iopl | E/A-Berechtigungsstufe | E/A-Berechtigungsstufe Dies ist eine zwei-Bit-Ganzzahl mit Werten zwischen Null und 3. Es wird vom Betriebssystem verwendet, um den Zugriff auf Hardware zu steuern. Sie sollte nicht von Anwendungen verwendet werden. |
Wenn Register als Ergebnis eines Befehls im Fenster "Debuggerbefehl" angezeigt werden, handelt es sich um den Flagstatus, der angezeigt wird. Wenn Sie jedoch ein Flag mit dem Befehl r (Registers) ändern möchten, sollten Sie über den Kennzeichnungscode darauf verweisen.
Im Fenster "Registers" von WinDbg wird der Kennzeichencode verwendet, um Flags anzuzeigen oder zu ändern. Der Kennzeichnungsstatus wird nicht unterstützt.
Beispiel: In der vorherigen Registeranzeige wird der Kennzeichnungsstatus ng angezeigt. Dies bedeutet, dass das Kennzeichen derzeit auf 1 festgelegt ist. Um dies zu ändern, verwenden Sie den folgenden Befehl:
r sf=0
Dadurch wird das Kennzeichen auf Null festgelegt. Wenn Sie eine andere Registeranzeige ausführen, wird der ng-Statuscode nicht angezeigt. Stattdessen wird der Pl-Statuscode angezeigt.
Die Kennzeichnung "Sign Flag", "Zero Flag" und "Carry Flag" sind die am häufigsten verwendeten Flags.
Bedingungen
Eine Bedingung beschreibt den Zustand einer oder mehrerer Flags. Alle bedingten Vorgänge auf dem x86 werden in den Bedingungen ausgedrückt.
Der Assembler verwendet eine oder zwei Buchstaben Abkürzung, um eine Bedingung darzustellen. Eine Bedingung kann durch mehrere Abkürzungen dargestellt werden. Beispielsweise ist AE ("über oder gleich") die gleiche Bedingung wie NB ("nicht darunter"). In der folgenden Tabelle sind einige allgemeine Bedingungen und deren Bedeutung aufgeführt.
Bedingungsname | Flags | Bedeutung |
---|---|---|
Z |
ZF=1 |
Das Ergebnis des letzten Vorgangs war Null. |
NZ |
ZF=0 |
Das Ergebnis des letzten Vorgangs war nicht null. |
K |
CF=1 |
Der letzte Vorgang erforderte ein Tragen oder Ausleihen. (Bei nicht signierten ganzzahligen Zahlen gibt dies einen Überlauf an.) |
NC |
CF=0 |
Der letzte Vorgang erforderte kein Tragen oder Ausleihen. (Bei nicht signierten ganzzahligen Zahlen gibt dies einen Überlauf an.) |
S |
SF=1 |
Das Ergebnis des letzten Vorgangs hat seinen hohen Bitsatz. |
NS |
SF=0 |
Das Ergebnis des letzten Vorgangs weist seine hohe Bitlöschung auf. |
O |
OF=1 |
Bei der Behandlung als signierter ganzzahliger Vorgang verursachte der letzte Vorgang einen Überlauf oder Unterlauf. |
NO |
OF=0 |
Bei der Behandlung als signierter ganzzahliger Vorgang hat der letzte Vorgang keinen Überlauf oder Unterlauf verursacht. |
Bedingungen können auch verwendet werden, um zwei Werte zu vergleichen. Die cmp-Anweisung vergleicht die beiden Operanden und legt dann Flags so fest, als würde ein Operand vom anderen subtrahiert werden. Die folgenden Bedingungen können verwendet werden, um das Ergebnis von cmp-Wert1, Wert2 zu überprüfen.
Bedingungsname | Flags | Bedeutung nach einem CMP-Vorgang. |
---|---|---|
E |
ZF=1 |
wert1 == Wert2. |
NE |
ZF=0 |
wert1 != Wert2. |
GE NL | SF=OF |
value1>= value2. Werte werden als signierte ganze Zahlen behandelt. |
LE NG | ZF=1 oder SF!=OF |
value1<= value2. Werte werden als signierte ganze Zahlen behandelt. |
G NLE | ZF=0 und SF=OF |
wert1>Wert2. Werte werden als signierte ganze Zahlen behandelt. |
L NGE | SF!=OF |
wert1<Wert2. Werte werden als signierte ganze Zahlen behandelt. |
AE NB | CF=0 |
value1>= value2. Werte werden als ganze Zahlen ohne Vorzeichen behandelt. |
BE NA | CF=1 oder ZF=1 |
value1<= value2. Werte werden als ganze Zahlen ohne Vorzeichen behandelt. |
A NBE | CF=0 und ZF=0 |
wert1>Wert2. Werte werden als ganze Zahlen ohne Vorzeichen behandelt. |
B NAE | CF=1 |
wert1<Wert2. Werte werden als ganze Zahlen ohne Vorzeichen behandelt. |
Bedingungen werden in der Regel verwendet, um auf das Ergebnis einer Cmp - oder Testanweisung zu reagieren. Beispiel:
cmp eax, 5
jz equal
Vergleicht das Eax-Register mit der Zahl 5, indem der Ausdruck (eax - 5) und die Einstellungskennzeichnungen entsprechend dem Ergebnis ermittelt werden. Wenn das Ergebnis der Subtraktion null ist, wird das Zr-Flag festgelegt, und die jz-Bedingung ist true, sodass der Sprung ausgeführt wird.
Datentypen
Byte: 8 Bit
Wort: 16 Bit
dword: 32 Bit
qword: 64 Bit (enthält Gleitkomma-Doubles)
zweird: 80 Bits (einschließlich erweiterter Gleitkomma-Doppelpunkte)
oword: 128 Bit
Notation
In der folgenden Tabelle ist die Schreibweise angegeben, die zum Beschreiben von Anweisungen zur Assemblysprache verwendet wird.
Notation | Bedeutung |
---|---|
r, r1, r2... |
Register |
m |
Speicheradresse (weitere Informationen finden Sie im Abschnitt "Adressierungsmodi erfolgreich".) |
#n |
Direktkonstante |
r/m |
Registrieren oder Arbeitsspeicher |
r/#n |
Register- oder direktkonstante |
r/m/#n |
Registrieren, Arbeitsspeicher oder sofortige Konstante |
cc |
Ein im vorherigen Abschnitt "Bedingungen" aufgeführter Bedingungscode. |
T |
"B", "W" oder "D" (Byte, Wort oder dword) |
accT |
Größe T Akkumulator: al if T = "B", ax if T = "W" oder eax if T = "D" |
Adressierungsmodi
Es gibt mehrere verschiedene Adressierungsmodi, aber sie alle nehmen die Form T ptr [ausdruck], wobei T ein Datentyp ist (siehe vorherigen Abschnitt Datentypen) und Ausdruck ist ein Ausdruck, der Konstanten und Register umfasst.
Die Notation für die meisten Modi kann ohne große Schwierigkeiten abgeleitet werden. Beispielsweise bedeutet BYTE PTR [esi+edx*8+3] folgendes: "Nehmen Sie den Wert des esi-Registers ein, addieren Sie ihn acht mal den Wert des edx-Registers , fügen Sie drei hinzu, und greifen Sie dann an der resultierenden Adresse auf das Byte zu."
Befehlsverknüpfung
Der Prozessor ist duales Problem, was bedeutet, dass es bis zu zwei Aktionen in einer Taktuhr ausführen kann. Allerdings sind die Regeln, wann sie in der Lage ist, zwei Aktionen gleichzeitig (als Kopplung bezeichnet) auszuführen, sehr kompliziert.
Da x86 ein CISC-Prozessor ist, müssen Sie sich keine Gedanken über Sprungverzögerungsmodule machen.
Synchronisierter Speicherzugriff
Lade-, Änderungs- und Speicheranweisungen können ein Sperrpräfix erhalten, das die Anweisung wie folgt ändert:
Vor dem Ausgeben der Anweisung löscht die CPU alle ausstehenden Speichervorgänge, um die Kohärenz sicherzustellen. Alle Datenvorschubs werden abgebrochen.
Beim Ausstellen der Anweisung hat die CPU exklusiven Zugriff auf den Bus. Dadurch wird die Atomität des Lade-/Änderungs-/Speichervorgangs sichergestellt.
Die xchg-Anweisung befolgt automatisch die vorherigen Regeln, wenn ein Wert mit Speicher ausgetauscht wird.
Alle anderen Anweisungen sind standardmäßig nicht sperrend.
Sprungvorhersage
Bedingungslose Sprünge werden vorhergesagt.
Bedingte Sprünge werden vorhergesagt oder nicht genommen, je nachdem, ob sie das letzte Mal ausgeführt wurden. Der Cache für den Aufzeichnungssprungverlauf ist in der Größe begrenzt.
Wenn die CPU nicht über einen Datensatz verfügt, ob der bedingte Sprung ausgeführt wurde oder nicht das letzte Mal ausgeführt wurde, prognostiziert sie rückwärtsbedingte Sprünge wie angenommen und vorwärts bedingte Sprünge, wie sie nicht genommen wurden.
Ausrichtung
Der x86-Prozessor korrigiert automatisch den nicht ausgerichteten Speicherzugriff bei Leistungseinbußen. Es wird keine Ausnahme ausgelöst.
Ein Speicherzugriff wird als ausgerichtet betrachtet, wenn die Adresse ein ganzzahliges Vielfaches der Objektgröße ist. Beispielsweise werden alle BYTE-Zugriffe ausgerichtet (alles ist eine ganze Zahl von 1), WORD-Zugriffe auf gerade Adressen werden ausgerichtet, und DWORD-Adressen müssen ein Vielfaches von 4 sein, um ausgerichtet zu werden.
Das Sperrpräfix sollte nicht für nicht ausgerichtete Speicherzugriffe verwendet werden.