OOStuBS - Technische Informatik II (TI-II)  2.4
Ausnahmebehandlungen der x86-Architektur

Inhaltsverzeichnis

Die hier vorgestellten Fehler beziehen sich auf die x86-Architektur von Intel. Andere Plattformen bieten meist ähnliche Ausnahmen an. Basierend auf der Mächtigkeit der Architektur könnte es sich allerdings nur um Auswahl oder gar um mehr Fehler handeln.

Grundlagen

Während der Prozessor die einzelnen Opcode-Befehle abarbeitet können Fehler auftreten. Ein gutes Beispiel ist die Division durch null bei ganzen Zahlen. Tritt diese auf, so ist das Ergebnis nicht definiert und der Prozessor alleine kann nicht entscheiden, wie damit umgegangen werden muss. Aus diesem Grund gibt es für verschiedene Fehler Interrupts, die für die Behandlung gelten. Da es sich dabei um für die Ausführung essentiell wichtige Anweisungen handelt, können diese Interrupts nicht deaktiviert werden. Auch das globale deaktivieren von Interrupts hat auf die Ausnahmebehandlungen keinen Einfluss. Folglich muss jedes Betriebssystem Routinen für diese bereitstellen.

Die Ausnahmen lassen sich auf zwei Arten einteilen.
Da wäre zuerst die Klassifikation nach:

Eine andere Möglichkeit ist die Aufteilung danach, ob zusätzlich ein 32-Bit-Fehlercodewort übergeben wird oder ist.

Stackaufbau

Tritt ein Fehler auf und geht die CPU über die entsprechende Behandlungsroutine aufzurufen, so werden einige Informationen auf den Stack gepackt, um die behandelnde Funktion zu unterstützen.
Zu diesen Informationen zählen der EIP der auslösenden Anweisung, das CS-Register und die EFLAGS. Je nach Umstand und Fehler erscheinen weitere Informationen auf den Stack.

Befindet sich der ausgeführte Befehl im Kernelmodus, also Sicherheitsring 0, so muss kein Kontextwechsel durchgeführt werden und es müssen weniger Informationen gespeichert werden.

Fehler in Ring 0, kein Fehlercode

niedrige Adresse
   
EIP ← esp nach dem Wechsel zur Behandlung
CS
EFLAGS
  ← esp vor dem Wechsel zur Behandlung
   
hohe Adresse

Fehler in Ring 0, mit Fehlercode

niedrige Adresse
   
Fehlercode ← esp nach dem Wechsel zur Behandlung
EIP
CS
EFLAGS
  ← esp vor dem Wechsel zur Behandlung
   
hohe Adresse

Fehler in Ring >0, mit Fehlercode

OOStuBS befindet sich durchgehend in Ring 0. Somit sind die folgenden Informationen für keine Aufgabe relevant.
Die folgende Darstellung bezieht sich auf den Kernelspeicher-Stack, der für die Behandlung vorgesehen ist. Die Informationen langen nicht auf dem Stack des gerade ausgeführten Programms, bei dem der Fehler auftrat.

niedrige Adresse
   
Fehlercode ← esp nach dem Wechsel zur Behandlung
EIP
CS
EFLAGS
ESP
SS
  ← esp vor dem Wechsel zur Behandlung
   
hohe Adresse

Fehlercode

Es gibt kein einheitliches Aussehen des Fehlercode. Stattdessen hängt es vom jeweiligen Fehler ab, wie er aufgebaut ist. Oft hat er allerdings den folgenden oder einen ähnlichen Aufbau.

Bit Bedeutung
0 Falls gesetzt gesetzt, so trat der Fehler nicht im gerade ausgeführten Programm statt - sondern z.B. in einem Interrupt.
1 Falls Bit 0 nicht gesetzt, so: falls gesetzt, so ist der Index von der IDT
Falls Bit 0 gesetzt, so: Bit 1 vom Segment Selector Index
2 Falls Bit 1 nicht gesetzt, so: falls gesetzt, so ist der Index von der LDT, sonst GDT
Falls Bit 1 gesetzt, so: Bit 2 vom Segment Selector Index
3-15 Bits 3-15 vom Segment Selector Index; der Index besagt, welcher Eintrag in der entsprechenden Tabelle ausgewählt war.
16-31 ungenutzt/reserviert

Liste der Ausnahmebehandlungen

Die folgende Liste wurde dem Intel-Architektur-Handbuch entnommen. Es gibt mehrere dieser Listen im Handbuch, doch leider sind sie nicht einheitlich.

Interrupt Bezeichnung Typ Fehlercode
0 Divide Error Fault ×
1 Debug/Reserved for Intel Fault/Trap ×
2 Nonmaskable Interrupt Interrupt ×
3 Breakpoint Trap ×
4 Overflow Trap ×
5 Bound Range Exceeded Fault ×
6 Invalid Opcode Fault ×
7 Device Not Available Fault ×
8 Double Fault Abort
9 Reserved (ehemals Coprocessor Segment Overrun) Fault ×
10 Invalid TSS Fault
11 Segment Not Present Fault
12 Stack-Segment Fault Fault
13 General Protection Fault
14 Page Fault Fault
15 Reserved for Intel ¿ ×
16 x87 FPU Floating-Point Error Fault
17 Alignment Check Fault ×
18 Machine Check Abort ×
19 SIMD Floating-Point Exception Fault ×
20 Virtualization Exception Fault ×
21-31 Reserviert

Informationen zu einigen Exceptions

Im Folgenden werden für einige Fehler etwas genauere Informationen geliefert.

Divide Error

Tritt bei einer Ganzzahl-Divisionsrechnung, wie z.B. das Modulo, ein Fehler auf, so wird dieser Interrupt aufgerufen. In der Regel handelt es sich dabei um eine Division durch null.

Breakpoint

Die Assembleranweisung int 3 ist genau ein Byte (0xCC) groß. Dadurch kann der Befehl an jeder Stelle stehen. Dieser Interrupt wird oft von Debuggern verwendet, beispielsweise um einen Breakpoint zu setzen. Dabei wird an der entsprechenden Stelle im Code das 0xCC geschrieben und die eigentliche Anweisung ausgelagert.
Einige Compiler bauen bei einer Debug-Version auch größzügig Blöcke von 0xCC vor und nach lokalen Variablen ein, um z.B. einige Buffer-Overrun-Fehler zu erkennen.

Invalid Opcode

Nicht jedes Byte bzw. Bytereihenfolge im Speicher kann die CPU als Befehl interpretieren. Trifft sie auf ein ungültiges Wort, so wird dieser Fehler ausgelöst. Oft ist es ein Indikator dafür, dass irgendwann ein Sprung an eine ungültige Stelle stattfand und die CPU nun irgendwelchen Datenmüll im Speicher ausführt.
Eine andere Option ist es, dass noch eine ältere CPU verwendet wird, die noch nicht über einen erweiterten Befehlssatz verfügt und damit den eigentlich gültigen Befehl nicht kennt. SIMD-Anweisungen wie von SSE4.2 sind dafür prädestiniert.

Double Fault

Tritt während einer Fehlerbehandlung erneut ein Fehler auf, so wird von der CPU diese Fehlerbehandlung aufgerufen. Ein Fehler in dieser Routine sollte vermieden werden, da ansonsten ein Tripple Fault auftritt und die CPU neu startet.

Am Besten sollte eine möglichst einfache Funktion hinterlegt werden, die einige Hilfsinformationen ausgibt und danach die CPU dauerhaft anhält. Es ist auch möglich, wichtige Daten vorher zu sichern. Dabei sollte aber ebenfalls darauf geachtet werden, dass einfache und damit möglichst fehlerfreie Funktionen verwendet werden.

General Protection

Viele Zugriffsfehler, die keine andere Exception auslösen, landen hier. Folglich gibt es keine eindeutige Ursache für den Fehler. Einige Assemblerbefehle führen Sicherheitskontrollen durch, schlagen diese Fehl, kann z.B. diese Ausnahme auftreten. Oft gibt es in einem Befehl sogar mehrere Kontrollen, es muss nur eine fehlschlagen. Somit ist sogar bei nur einem Befehl die Ursache nicht eindeutig. In dem Fall kann der Fehlercode einen ersten Ansatz liefern.

Page Fault

Greift ein Programm auf Speicher zu, dessen Speicherseite nicht vorhanden, nur lesbar markiert, reserviert markiert oder nicht für den Sicherheitsring des Programm freigegeben ist, so wird dieser Fehler ausgelöst. Für eine genauere Eingrenzung kann der Fehlercode verwendet werden. Der Fehlercode hat einen komplett anderen Aufbau, als der oben beschriebene.

Viele Betriebssysteme ermöglichen das Auslagern von Speicherseiten. Auch kann es für Programme erscheinen, dass sie den kompletten Speicher zur Verfügung haben, ohne, dass sie ihn wirklich besitzen. Dies wird umgesetzt, indem die einzelnen Speicherseiten als "nicht vorhanden" markiert werden und erst, wenn das Programm darauf zugreift, werden sie wirklich in den Speicher geladen.
Dieser Fehler ist also ein Mechanismus zur Speicherverwaltung, der auch von vielen Betriebssystemen eingesetzt und bewusst hervorgerufen wird.

Alignment Check

Einige, meist auf Geschwindigkeit optimierte Befehle erwarten eine bestimmte Ausrichtung von Daten, mit denen sie arbeiten. Ist dies nicht der Fall, wird dieser Fehler ausgelöst.

Für vielen Befehlen können die Tests darauf deaktiviert werden.

Triple Fault

Eine Art weiterer Fehler ist der sogenannte "Triple Fault". Anders als bei den anderen Exception wird hier kein Interrupt ausgelöst, sondern der Computer startet neu.

Tritt bei einer Fehlerbehandlung erneut ein Fehler auf, so wird eine Interruptbehandlung eines Double Faults ausgelöst.

Der Tripple Fault ist meist ein Indikator dafür, dass etwas grundlegendes mit dem im Betriebssystem implementierten Fehlerbehandlungsmechanismus nicht stimmt oder mindestens ein Segmentregister einen falschen Wert hat.

Literatur