OOStuBS - Technische Informatik II (TI-II)
2.4
|
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.
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.
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.
niedrige Adresse | |
EIP | ← esp nach dem Wechsel zur Behandlung |
CS | |
EFLAGS | |
← esp vor dem Wechsel zur Behandlung | |
hohe Adresse |
niedrige Adresse | |
Fehlercode | ← esp nach dem Wechsel zur Behandlung |
EIP | |
CS | |
EFLAGS | |
← esp vor dem Wechsel zur Behandlung | |
hohe Adresse |
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 |
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 |
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 |
Im Folgenden werden für einige Fehler etwas genauere Informationen geliefert.
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.
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.
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.
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.
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.
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.
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.
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.