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

Antworten:

68

Es übergibt die Steuerung an den Interruptvektor 0x80

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

Unter Linux haben Sie einen Blick auf diese : es wurde verwendet , um handhaben system_call. Auf einem anderen Betriebssystem könnte dies natürlich etwas völlig anderes bedeuten.

jldupont
quelle
5
durch verkürzen der langen Geschichte, die Anweisungen bedeutet, bedeutet, dass DO IT für Anweisungen vorher war.
Yuda Prawira
2
@YudaPrawira: Sie sollten sich die früheren Anweisungen als das Einrichten von Argumenten in Registern und int 0x80als eine besondere Art calleiner Funktion im Kernel vorstellen (ausgewählt von eax).
Peter Cordes
Warum hast du gesagt "Wurde benutzt?" Wird es nicht mehr verwendet?
Liga
124

intbedeutet Interrupt, und die Nummer 0x80ist die Interrupt-Nummer. Ein Interrupt überträgt den Programmablauf an denjenigen, der diesen Interrupt behandelt, 0x80in diesem Fall Interrupt . Unter Linux ist der 0x80Interrupt-Handler der Kernel und wird verwendet, um Systemaufrufe des Kernels durch andere Programme durchzuführen.

Der Kernel wird benachrichtigt, welchen Systemaufruf das Programm ausführen möchte, indem der Wert im Register %eaxüberprüft wird (AT & T-Syntax und EAX in Intel-Syntax). Jeder Systemaufruf hat unterschiedliche Anforderungen an die Verwendung der anderen Register. Zum Beispiel ein Wert von 1in%eax einen Systemaufruf von exit(), und der Wert in %ebxenthält den Wert des Statuscodes für exit().

Polat Tuzla
quelle
44

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

Sie können hier sehen , dass dies INTnur eine der vielen Anweisungen (tatsächlich die Assembler-Darstellung (oder sollte ich 'mnemonisch' sagen)) ist, die im x86-Befehlssatz vorhanden sind. Weitere Informationen zu dieser Anleitung finden Sie auch in Intels eigenem Handbuch hier .

Um aus dem PDF zusammenzufassen:

INT n / INTO / INT 3 - Aufruf zum Unterbrechen der Prozedur

Der Befehl 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 Mnemonik zum Ausführen eines durch Software generierten 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? Dies wird durch den Interrupt-Vektor unter 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 E / A-Operationen, Zeichenfolgenausgabe und mehr zugreifen.

Die meisten Unix-Systeme und -Derivate verwenden keine Software-Interrupts, mit Ausnahme des Interrupts 0x80, der zum Tätigen von Systemaufrufen verwendet wird. Dies wird erreicht, indem ein 32-Bit-Wert, der einer Kernelfunktion entspricht, in das EAX-Register des Prozessors eingegeben und dann INT 0x80 ausgeführt wird.

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

Geben Sie hier die Bildbeschreibung ein

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

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

Ein Hardware-Interrupt darf nicht mit einem Software-Interrupt verwechselt werden. Hier ist eine sehr gute Antwort in dieser Hinsicht.

Dies ist auch eine gute Quelle.

Koray Tugay
quelle
2
Linux System Call Tabelle Link ist defekt = \
Miguel Angelo
1
Die meisten Unix-Systeme und -Derivate verwenden keine Software-Interrupts (außer int 0x80). Dies scheint eine seltsame Art zu sein. Der int 0x80i386 Linux-Systemaufruf ABI ist dem DOS int 0x21ABI sehr ähnlich . Fügen Sie eine Rufnummer in ein Register (AH für DOS, EAX für Linux) und andere Argumente in andere Register ein und führen Sie dann eine Software-Interrupt-Anweisung aus. Der Hauptunterschied besteht darin, was Sie mit den Systemaufrufen tun können (Zugriff auf Hardware direkt unter DOS, jedoch nicht unter Linux) und nicht darauf, wie Sie sie aufrufen.
Peter Cordes
Hier ist ein nicht unterbrochener Syscall-Tabellenlink. syscalls.kernelgrok.com Erweitern Sie es einfach, um alle Anrufe oben anzuzeigen .
ollien
11

Beispiel für einen minimal ausführbaren Linux-Systemaufruf

Linux richtet den Interrupt-Handler 0x80so ein, dass er Systemaufrufe implementiert, sodass Userland-Programme mit dem Kernel kommunizieren können.

.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 geht sauber aus.

Sie können Ihre eigenen Interrupt-Handler nicht direkt vom Benutzerland aus festlegen, da Sie nur Ring 3 haben und Linux Sie daran hindert .

GitHub stromaufwärts . Getestet unter Ubuntu 16.04.

Bessere Alternativen

int 0x80wurde durch bessere Alternativen für Systemaufrufe abgelöst: zuerst sysenterVDSO.

x86_64 hat eine neue syscallAnweisung .

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 arbeiten:

    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: Stoppen Sie die Ausführung

Beachten Sie, wie der Prozessor nach dem ersten Handler an der Adresse 0und dem zweiten unter der Adresse sucht 4: Dies ist eine Tabelle von Handlern, die als IVT bezeichnet wird , und jeder Eintrag hat 4 Bytes.

Minimales Beispiel, das einige E / A- Vorgänge ausführt , um Handler sichtbar zu machen.

Beispiel für einen 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 (Interrupt Descriptor Table) verweisen, die die Handler beschreibt.

Minimales Beispiel

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
quelle
4

Der Befehl "int" verursacht einen Interrupt.

Was ist eine Unterbrechung?

Einfache Antwort: Ein Interrupt ist einfach ausgedrückt ein Ereignis, das die CPU unterbricht und sie anweist, eine bestimmte Aufgabe auszuführen.

Detaillierte Antwort :

In der CPU ist eine Tabelle mit Interrupt Service Routines (oder ISRs) gespeichert. Im Real (16-Bit) -Modus wird dies als IVT gespeichert , oder ich nterrupt V ector T können. Das IVT befindet sich normalerweise unter 0x0000:0x0000(physikalische Adresse 0x00000) und besteht aus einer Reihe von Segmentversatzadressen, die auf die ISRs verweisen. Das Betriebssystem kann die bereits vorhandenen IVT-Einträge durch eigene ISRs ersetzen.

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

Im geschützten (32-Bit) Modus verwendet die CPU eine IDT. Das IDT ist eine Struktur variabler Länge, die aus Deskriptoren (auch als Gates bezeichnet) besteht, die die CPU über die Interrupt-Handler informieren. Die Struktur dieser Deskriptoren ist viel komplexer als die einfachen Segmentversatzeinträ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 eine variable Größe haben, muss jedoch sequentiell sein. Wenn Sie also angeben, dass Ihre IDT zwischen 0x00 und 0x50 liegt, muss jeder Interrupt zwischen 0x00 und 0x50 liegen. Das Betriebssystem verwendet nicht unbedingt alle, daher ermöglicht das Present-Bit der CPU, Interrupts, die das Betriebssystem nicht verarbeiten möchte, ordnungsgemäß zu behandeln.

Wenn ein Interrupt auftritt (entweder durch einen externen Trigger (z. B. ein Hardwaregerät) in einem IRQ oder durch die intAnweisung eines Programms), drückt die CPU EFLAGS, dann CS und dann EIP. (Diese werden automatisch von wiederhergestelltiret die Interrupt-Rückgabeanweisung .) Das Betriebssystem speichert normalerweise mehr Informationen über den Status der Maschine, behandelt den Interrupt, stellt den Maschinenzustand 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 das IDT bereits so eingestellt, dass es 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. Möglicherweise wird eine Rückgabe in EAX / EBX gespeichert. Systemaufrufe wurden weitgehend durch sysenterund ersetztsysexit (oder syscallund ersetztsysret Anweisungen auf AMD) ersetzt, die einen schnelleren Eintritt in Ring 0 ermöglichen.

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

Adrian
quelle
Unterhaltsame Tatsache: FreeBSDs i386-Systemaufruf ABI übergibt Argumente auf dem User-Space-Stack. Nur eaxwird für die Syscall-Nummer verwendet. asm.sourceforge.net/intro/hello.html
Peter Cordes
2

Wie bereits erwähnt, springt die Steuerung zum Interruptvektor 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 gefolgt von 'int 0x80' gesetzt wird.

Steve Smith
quelle
1

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

Bernstein
quelle
9
Genau genommen sagt es dem Kernel nichts ... Es sagt der CPU, die den Handler in der IDT nachschlägt, was letztendlich ein Zeiger auf einen Kernel-Code ist.
Asveikau
Wahr. Ich nehme an, die bessere Formulierung wäre, wenn die CPU angewiesen wird, den Vektor zu aktivieren, und der Vektor (als Teil des Kernels) die Funktion aufruft.
Amber
was am Ende das tut, was am Ende das tut, was dann das tut, was dann verwirrt dorthin geht . : / Amber hat eine Antwort, die verständlich ist .. das ist es ..
Afzaal Ahmad Zeeshan