Zum Inhalt springen

Software-Interrupt

aus Wikipedia, der freien Enzyklopädie
Dies ist die aktuelle Version dieser Seite, zuletzt bearbeitet am 24. September 2025 um 13:40 Uhr durch imported>Skranon.
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)

Vorlage:Hinweisbaustein

Ein Software-Interrupt ist ein expliziter Aufruf einer Unterfunktion (meist einer Betriebssystem-Funktion).<ref>Jörg Wiegelmann: Softwareentwicklung in C für Mikroprozessoren und Mikrocontroller: C-Programmierung für Embedded-Systeme. 7. Auflage. VDE Verlag GmbH, Berlin/Offenbach 2017, ISBN 978-3-8007-4328-5, S. 82–83.</ref> Er hat nichts mit einem Interrupt (asynchrone Unterbrechung) zu tun, obwohl häufig der gleiche Sprungverteiler (Interrupt-Tabelle) benutzt wird. Solche Funktionsaufrufe werden von Programmen mit Hilfe von speziellen architekturabhängigen Befehlen aufgerufen. Dazu muss die Nummer für die benötigte Unterfunktion bekannt sein. Diese Nummer wird als Index in einer Sprungtabelle (meist Interrupt-Vektor-Tabelle) verwendet, die die Startadresse des Unterprogrammes enthält.

Mnemonics

Übliche Mnemonics sind:

Hintergründe

Ursprünglich nur als bequemer und portabler Sprungverteiler (MS-DOS) verwendet – man vermied damit versionsabhängige Einsprünge direkt in den Betriebssystem-Code (z. B. JSR $EDB9) – haben diese Funktionsaufrufe bei modernen Betriebssystemen weitere Funktionen bekommen. Mit diesen Befehlen sind Context- und Task-Wechsel möglich, die sich mit klassischen Befehlen (absichtlich) nicht realisieren lassen. So lässt sich der INT 21h unter MS-DOS auch durch klassische Befehle (ca. 20 Stück) nachbilden, allerdings nicht mehr der INT 80h, der zum Aufruf von Betriebssystemfunktionen in Unix-Binaries verwendet wird.

Diese Technik ermöglicht erst geschützte Betriebssysteme, da der Wechsel in den Kontext des Betriebssystems nur an genau definierten Stellen erfolgen kann.

Beispiel für Aufruf (Unix, Intel i386)

Es soll die POSIX-Funktion read (Lesen von Daten von einem Filehandle in den Speicher) implementiert werden: <syntaxhighlight lang="cpp">

   read ( int FileHandle, void* Buffer, unsigned int BufferLength ) ;

</syntaxhighlight>

Die (Minimal-)Implementierung (in der libc) sieht dann so aus: <syntaxhighlight lang="asm" style="border: dashed 1px #2F6FAB; padding: 1em">

Intel-Syntax
Instruktion Ziel Quelle
   read  proc
         push ebx              ; Sichern des Inhalts des Registers ebx auf den Stack
         push ecx              ; Sichern des Inhalts des Registers ecx auf den Stack
         push edx              ; Sichern des Inhalts des Registers edx auf den Stack
         mov  ebx, [esp+16]    ; FileHandle
         mov  ecx, [esp+20]    ; Buffer
         mov  edx, [esp+24]    ; BufferLength
         mov  eax, 0003h       ; Funktionnummer für read
         int  80h              ; Interruptaufruf
         pop  edx              ; Zuvor gesicherte Inhalte werden in umgekehrter Reihenfolge vom Stack
                               ; zurück in die entsprechenden Register kopiert
         pop  ecx
         pop  ebx
         cmp  eax, -124        ; Werte von 0...0FFFFFF84h sind Rückgabewerte, -123...-1 sind (negierte) Fehlernummern
         jbe  .noError
         neg  eax              ; Aus Rückgabewerten -1...-123 werden die Fehlernummern 1...123
         mov  __errno, eax     ; Fehler in errno abspeichern
         mov  eax, -1
   .noError:
         ret
         end  proc

</syntaxhighlight>

Behandlung vom Prozessor und Betriebssystem

Der Befehl INT 80h bewirkt folgendes:

  • Da der Sprung über ein Call Gate geht, findet ein Wechsel des Prioritätslevel von 3 (User) auf 0 (Kernel) statt,
  • dann wird durch den Prozessor vom Userspace-Stack auf den Kernelspace-Stack umgeschaltet,
  • dann das Flagregister gerettet und
  • schließlich erfolgt ein Sprung zu einer vom Betriebssystem hinterlegten Adresse.

Die erste Aktion im Kernel ist das Abspeichern und Testen der Argumente:

  • Darf der Prozess den Speicherbereich zwischen Buffer und Buffer+BufferLength-1 beschreiben?
    • wenn nein ⇒ EFAULT
  • Ist der FileHandle für diesen Prozess gültig?
    • wenn nein ⇒ EBADF
  • Ist der Speicherbereich zwischen Buffer und Buffer+BufferLength-1 im Hauptspeicher?

Ein ungenügender Test der Argumente war Anfang bis Mitte der 1990er Jahre noch üblich. Das Aufrufen von Systemfunktionen mit rein zufälligen Werten reichte aus, um Betriebssysteme zu „crashen“, das Testprogramm crashme konnte das eindrucksvoll zeigen. Heutzutage ist jedes Betriebssystem um Größenordnungen resistenter gegen solche Angriffe. Mit zufälligen Werten sind keine Angriffe mehr möglich, dafür sind ausgefeilte Szenarien notwendig.

Am Ende hat der Kernel den Funktionsaufruf abgearbeitet und gibt mittels IRET die Kontrolle wieder an das Programm zurück. Der Rücksprung erfolgt wieder über ein Call Gate.

  • Rücksprungadresse vom Stack holen
  • Flagregister rekonstruieren
  • Auf Userspace-Stack zurückschalten
  • Prioritätslevel wieder auf 3 setzen

Siehe auch

Einzelbelege

<references />