web-dev-qa-db-de.com

Was bedeutet "int 0x80" im Assembler-Code?

Kann jemand erklären, was der folgende Assembly-Code tut?

 int 0x80  
73
Josh Curren

Es übergibt die Steuerung an den Unterbrechungsvektor 0x80

Siehe http://en.wikipedia.org/wiki/Interrupt_vector

Schauen Sie sich unter Linux this an: es wurde verwendet, um system_call. Natürlich könnte dies auf einem anderen Betriebssystem etwas völlig anderes bedeuten.

54
jldupont

int bedeutet Interrupt und die Nummer 0x80 ist die Interruptnummer. Ein Interrupt überträgt den Programmablauf an denjenigen, der diesen Interrupt bearbeitet, in diesem Fall Interrupt 0x80. Unter Linux ist der Interrupt-Handler 0x80 Der Kernel und wird verwendet, um Systemaufrufe durch andere Programme an den Kernel zu richten.

Der Kernel wird benachrichtigt, welchen Systemaufruf das Programm ausführen möchte, indem der Wert im Register %eax (Gassyntax und EAX in Intel-Syntax) überprüft wird. Jeder Systemaufruf hat unterschiedliche Anforderungen an die Verwendung der anderen Register. Ein Wert von 1 In %eax Bedeutet beispielsweise einen Systemaufruf von exit(), und der Wert in %ebx Enthält den Wert des Statuscodes für exit().

106
Polat Tuzla

Denken Sie daran, dass 0x80 = 80h = 128

Sie können sehen, dass hier [~ # ~] int [~ # ~] nur eine der vielen Anweisungen ist (tatsächlich) die Assemblersprachendarstellung (oder sollte ich 'mnemonisch' sagen), die im x86-Befehlssatz vorhanden ist. Weitere Informationen zu dieser Anleitung finden Sie auch in Intels eigenem Handbuch hier .

Zusammenfassend aus dem PDF:

INT n/INTO/INT 3 - Aufruf der Interrupt-Prozedur

Die Anweisung INT n generiert einen Aufruf an den mit dem Zieloperanden angegebenen Interrupt- oder Exception-Handler. Der Zieloperand gibt einen Vektor von 0 bis 255 an, der als vorzeichenloser 8-Bit-Zwischenwert codiert ist. Der Befehl INT n ist die allgemeine Kurzbezeichnung für die Ausführung eines durch Software erzeugten Aufrufs an einen Interrupt-Handler.

Wie Sie sehen können, ist 0x80 der Zieloperand in Ihrer Frage. Zu diesem Zeitpunkt weiß die CPU, dass sie Code ausführen sollte, der sich im Kernel befindet, aber welcher Code? Das wird durch den Interrupt-Vektor in Linux bestimmt.

Einer der nützlichsten DOS-Software-Interrupts war Interrupt 0x21. Durch Aufrufen mit verschiedenen Parametern in den Registern (meistens ah und al) können Sie auf verschiedene IO Operationen, String-Ausgaben und mehr zugreifen.

Die meisten Unix-Systeme und -Derivate verwenden keine Software-Interrupts, mit Ausnahme von Interrupt 0x80, die zum Ausführen von Systemaufrufen verwendet werden. Dies wird erreicht, indem ein 32-Bit-Wert, der einer Kernelfunktion entspricht, in das EAX-Register des Prozessors eingegeben wird und anschließend INT 0x80 ausgeführt wird.

Sehen Sie sich dies bitte an, wo andere verfügbare Werte in den Interrupt-Handler-Tabellen angezeigt werden:

enter image description here

Wie Sie in der Tabelle sehen können, weist die CPU darauf hin, einen Systemaufruf auszuführen. Sie finden die Linux-Systemaufruftabelle hier .

Wenn Sie also den Wert 0x1 in das EAX-Register verschieben und die INT 0x80 in Ihrem Programm aufrufen, können Sie den Prozess veranlassen, den Code im Kernel auszuführen, wodurch der aktuell ausgeführte Prozess gestoppt (beendet) wird (unter Linux, x86 Intel-CPU).

Ein Hardware-Interrupt darf nicht mit einem Software-Interrupt verwechselt werden. hier ist diesbezüglich eine sehr gute Antwort.

Dies ist auch eine gute Quelle.

Sie können int 80h in Aktion sehen hier .

35
Koray Tugay

int 0x80 ist die Assembler-Anweisung, die zum Aufrufen von Systemaufrufen unter Linux auf x86-Prozessoren (d. h. Intel-kompatiblen Prozessoren) verwendet wird.

http://www.linfo.org/int_0x80.html

9
Tom

Beispiel für einen minimalen lauffähigen Linux-Systemaufruf

Linux richtet den Interrupt-Handler für 0x80 So ein, dass er Systemaufrufe implementiert, eine Möglichkeit für Userland-Programme, mit dem Kernel zu kommunizieren.

.data
    s:
        .ascii "hello world\n"
        len = . - s
.text
    .global _start
    _start:

        movl $4, %eax   /* write system call number */
        movl $1, %ebx   /* stdout */
        movl $s, %ecx   /* the data to print */
        movl $len, %edx /* length of the buffer */
        int $0x80

        movl $1, %eax   /* exit system call number */
        movl $0, %ebx   /* exit status */
        int $0x80

Kompilieren und ausführen mit:

as -o main.o main.S
ld -o main.out main.o
./main.out

Ergebnis: Das Programm druckt auf stdout:

hello world

und verlässt sauber.

Sie können Ihre eigenen Interrupt-Handler nicht direkt vom Userland aus einstellen, da Sie nur Ring 3 haben und Linux dies verhindert .

GitHub upstream . Getestet unter Ubuntu 16.04.

Bessere Alternativen

int 0x80 Wurde durch bessere Alternativen für Systemaufrufe ersetzt: zuerst sysenter, dann VDSO.

x86_64 hat eine neue syscall Anweisung .

Siehe auch: Was ist besser "int 0x80" oder "syscall"?

Minimales 16-Bit-Beispiel

Erfahren Sie zunächst, wie Sie ein minimales Bootloader-Betriebssystem erstellen und auf QEMU und realer Hardware ausführen, wie hier erläutert: https://stackoverflow.com/a/32483545/895245

Jetzt können Sie im 16-Bit-Real-Modus ausgeführt werden:

    movw $handler0, 0x00
    mov %cs, 0x02
    movw $handler1, 0x04
    mov %cs, 0x06
    int $0
    int $1
    hlt
handler0:
    /* Do 0. */
    iret
handler1:
    /* Do 1. */
    iret

Dies würde in der Reihenfolge tun:

  • Do 0.
  • Do 1.
  • hlt: Ausführung beenden

Beachten Sie, wie der Prozessor den ersten Handler unter der Adresse 0 Und den zweiten unter 4 Sucht: Dies ist eine Tabelle von Handlern mit dem Namen [~ # ~]. ivt [~ # ~] und jeder Eintrag hat 4 Bytes.

Minimales Beispiel, das einige IO ausführt, um Handler sichtbar zu machen.

Beispiel für minimalen geschützten Modus

Moderne Betriebssysteme laufen im sogenannten geschützten Modus.

Das Handling hat in diesem Modus mehr Optionen, ist also komplexer, aber der Geist ist der gleiche.

Der Schlüsselschritt ist die Verwendung der LGDT- und LIDT-Anweisungen, die auf die Adresse einer speicherinternen Datenstruktur (der Interrupt-Deskriptor-Tabelle) verweisen, die die Handler beschreibt.

Minimales Beispiel

Der Befehl "int" verursacht einen Interrupt.

Was ist ein Interrupt?

Einfache Antwort: Einfach ausgedrückt ist ein Interrupt ein Ereignis, das die CPU unterbricht und anweist, eine bestimmte Task auszuführen.

Detaillierte Antwort :

In der CPU ist eine Tabelle mit Interrupt-Serviceroutinen (oder ISRs) gespeichert. Im Real (16-Bit) -Modus wird dies als [~ # ~] ivt [~ # ~] oder [~ # ~ gespeichert ] i [~ # ~] nterrupt [~ # ~] v [~ # ~] ector [~ # ~] t [~ # ~] fähig. Das IVT befindet sich normalerweise unter 0x0000:0x0000 (physikalische Adresse 0x00000), und es handelt sich um eine Reihe von Segment-Offset-Adressen, die auf die ISRs verweisen. Das Betriebssystem kann die vorhandenen IVT-Einträge durch seine eigenen ISRs ersetzen.

(Hinweis: Die Größe des IVT ist auf 1024 (0x400) Byte festgelegt.)

Im geschützten 32-Bit-Modus verwendet die CPU einen IDT. Die IDT ist eine Struktur variabler Länge, die aus Deskriptoren (auch als Gates bezeichnet) besteht, die der CPU die Interrupt-Handler mitteilen. Die Struktur dieser Deskriptoren ist viel komplexer als die einfachen Segment-Offset-Einträge des IVT. hier ist es:

bytes 0, 1: Lower 16 bits of the ISR's address.
bytes 2, 3: A code segment selector (in the GDT/LDT)
byte 4: Zero.
byte 5: A type field consisting of several bitfields.
    bit 0:  P (Present): 0 for unused interrupts, 1 for used interrupts.*
    bits 1, 2: DPL (Descriptor Privilege Level): The privilege level the descriptor (bytes 2, 3) must have.
    bit 3: S (Storage Segment): Is 0 for interrupt and trap gates. Otherwise, is one. 
    bits 4, 5, 6, 7: GateType:
        0101: 32 bit task gate
        0110: 16-bit interrupt gate
        0111: 16-bit trap gate
        1110: 32-bit interrupt gate
        1111: 32-bit trap gate

* Die IDT kann von variabler Größe sein, muss jedoch sequentiell sein, d. H., Wenn Sie für Ihre IDT einen Wert zwischen 0x00 und 0x50 festlegen, muss jeder Interrupt zwischen 0x00 und 0x50 vorhanden sein. Das Betriebssystem verwendet nicht unbedingt alle von ihnen, sodass die CPU mit dem Present-Bit Interrupts ordnungsgemäß verarbeiten kann, die das Betriebssystem nicht verarbeiten möchte.

Wenn ein Interrupt auftritt (entweder durch einen externen Trigger (z. B. ein Hardwaregerät) in einem IRQ oder durch den Befehl int aus einem Programm), drückt die CPU EFLAGS, dann CS und dann EIP. (Diese werden automatisch von iret, der Anweisung zur Unterbrechungsrückgabe, wiederhergestellt.) Das Betriebssystem speichert normalerweise mehr Informationen über den Zustand der Maschine, verarbeitet die Unterbrechung, stellt den Zustand der Maschine wieder her und fährt fort.

In vielen * NIX-Betriebssystemen (einschließlich Linux) basieren Systemaufrufe auf Interrupts. Das Programm legt die Argumente für den Systemaufruf in den Registern (EAX, EBX, ECX, EDX usw.) ab und ruft den Interrupt 0x80 auf. Der Kernel hat den IDT bereits so eingestellt, dass er einen Interrupt-Handler auf 0x80 enthält, der aufgerufen wird, wenn er den Interrupt 0x80 empfängt. Der Kernel liest dann die Argumente und ruft entsprechend eine Kernelfunktion auf. Es kann eine Rücksendung in EAX/EBX speichern. Systemaufrufe wurden weitgehend durch die Anweisungen sysenter und sysexit (oder syscall und sysret bei AMD) ersetzt, die einen schnelleren Einstieg in Ring 0 ermöglichen.

Dieser Interrupt kann in einem anderen Betriebssystem eine andere Bedeutung haben. Überprüfen Sie unbedingt die Dokumentation.

3
Adrian Zhang

Wie bereits erwähnt, springt die Steuerung zum Interrupt-Vektor 0x80. In der Praxis bedeutet dies (zumindest unter Linux), dass ein Systemaufruf aufgerufen wird. Der genaue Systemaufruf und die Argumente werden durch den Inhalt der Register definiert. Zum Beispiel kann exit () aufgerufen werden, indem% eax auf 1 gesetzt wird, gefolgt von 'int 0x80'.

2
Steve Smith

Sie weist die CPU an, den Interrupt-Vektor 0x80 zu aktivieren, der unter Linux-Betriebssystemen der Systemaufruf-Interrupt ist und zum Aufrufen von Systemfunktionen wie open() für Dateien usw. verwendet wird.

1
Amber