Czy ktoś może wyjaśnić, co robi następujący kod asemblera?
int 0x80
Czy ktoś może wyjaśnić, co robi następujący kod asemblera?
int 0x80
Odpowiedzi:
Przekazuje sterowanie, aby przerwać wektor 0x80
Zobacz http://en.wikipedia.org/wiki/Interrupt_vector
W systemie Linux spójrz na to : był używany do obsługi system_call
. Oczywiście w innym systemie operacyjnym może to oznaczać coś zupełnie innego.
int 0x80
jako o specjalnym rodzaju call
funkcji w jądrze (wybieranej przez eax
).
int
oznacza przerwanie, a liczba 0x80
jest numerem przerwania. Przerwanie przenosi przepływ programu do tego, kto obsługuje to przerwanie, którym 0x80
w tym przypadku jest przerwanie . W Linuksie obsługa 0x80
przerwań jest jądrem i jest używana do wykonywania wywołań systemowych jądra przez inne programy.
Jądro jest powiadamiane o tym, które wywołanie systemowe chce wykonać program, sprawdzając wartość w rejestrze %eax
(składnia AT&T i EAX w składni Intela). Każde wywołanie systemowe ma inne wymagania dotyczące wykorzystania innych rejestrów. Na przykład wartość 1
in %eax
oznacza wywołanie systemowe exit()
, a wartość in %ebx
zawiera wartość kodu statusu dla exit()
.
Pamiętaj, że 0x80
= 80h
=128
Widzisz tutaj, że INT
jest to tylko jedna z wielu instrukcji (właściwie reprezentacja języka asemblera (lub powinienem powiedzieć „mnemonik”) tego), które istnieją w zestawie instrukcji x86. Więcej informacji na temat tej instrukcji można również znaleźć w podręczniku firmy Intel, który można znaleźć tutaj .
Podsumowując z pliku PDF:
INT n / INTO / INT 3 - Procedura wywołania przerwania
Instrukcja INT n generuje wywołanie procedury obsługi przerwania lub wyjątku określonej w operandzie przeznaczenia. Operand docelowy określa wektor od 0 do 255, zakodowany jako 8-bitowa wartość pośrednia bez znaku. Instrukcja INT n jest ogólnym mnemonikiem do wykonywania wywołania generowanego programowo do obsługi przerwań.
Jak widać, argumentem docelowym w pytaniu jest 0x80 . W tym momencie procesor wie, że powinien wykonać kod znajdujący się w jądrze, ale jaki kod? Jest to określane przez wektor przerwań w systemie Linux.
Jednym z najbardziej przydatnych przerwań programowych DOS było przerwanie 0x21. Wywołując go z różnymi parametrami w rejestrach (głównie ah i al), można uzyskać dostęp do różnych operacji IO, wyjścia łańcuchowego i innych.
Większość systemów uniksowych i pochodnych nie używa przerwań programowych, z wyjątkiem przerwania 0x80, używanego do wykonywania wywołań systemowych. Odbywa się to poprzez wprowadzenie 32-bitowej wartości odpowiadającej funkcji jądra do rejestru EAX procesora, a następnie wykonanie INT 0x80.
Spójrz na to, gdzie pokazane są inne dostępne wartości w tabelach obsługi przerwań:
Jak widać, tabela wskazuje procesorowi wykonanie wywołania systemowego. Tabelę wywołań systemowych Linuksa znajdziesz tutaj .
Więc przenosząc wartość 0x1 do rejestru EAX i wywołując INT 0x80 w swoim programie, możesz sprawić, że proces zacznie wykonywać kod w jądrze, który zatrzyma (zakończy) aktualnie działający proces (w systemie Linux, procesor Intel x86).
Przerwania sprzętowego nie należy mylić z przerwaniem programowym. Oto bardzo dobra odpowiedź na ten temat.
To też jest dobre źródło.
int 0x80
I386 Linux wywołanie systemowe ABI jest bardzo podobna do DOS int 0x21
ABI. Umieść numer wywoławczy w rejestrze (AH dla DOS, EAX dla Linuksa) i inne argumenty w innych rejestrach, a następnie uruchom instrukcję przerwania oprogramowania. Główna różnica polega na tym, na co pozwalają wywołania systemowe (dostęp do sprzętu bezpośrednio w DOS, ale nie w Linuksie), a nie w sposobie ich wywoływania.
/usr/include/x86_64-linux-gnu/asm/unistd_64.h
Przykład minimalnego uruchomienia systemu Linux
Linux konfiguruje obsługę przerwań w 0x80
taki sposób, że implementuje wywołania systemowe, sposób komunikowania się programów środowiska użytkownika z jądrem.
.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
Skompiluj i uruchom z:
as -o main.o main.S
ld -o main.out main.o
./main.out
Wynik: program drukuje na stdout:
hello world
i wychodzi czysto.
Nie możesz ustawić własnych programów obsługi przerwań bezpośrednio z przestrzeni użytkownika, ponieważ masz tylko pierścień 3, a Linux ci to uniemożliwia .
GitHub upstream . Testowane na Ubuntu 16.04.
Lepsze alternatywy
int 0x80
zostało zastąpione lepszymi alternatywami wykonywania wywołań systemowych: najpierw sysenter
, a następnie VDSO.
x86_64 ma nową syscall
instrukcję .
Zobacz też: Co jest lepsze "int 0x80" lub "syscall"?
Minimalny przykład 16-bitowy
Najpierw dowiedz się, jak stworzyć minimalny system operacyjny bootloadera i uruchomić go na QEMU i prawdziwym sprzęcie, jak wyjaśniłem tutaj: https://stackoverflow.com/a/32483545/895245
Teraz możesz działać w 16-bitowym trybie rzeczywistym:
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
Zrobiłoby to w kolejności:
Do 0.
Do 1.
hlt
: zatrzymaj wykonywanieZwróć uwagę, jak procesor szuka pierwszego programu obsługi pod adresem 0
, a drugiego pod adresem 4
: to jest tablica programów obsługi nazywana IVT , a każdy wpis ma 4 bajty.
Minimalny przykład, który wykonuje niektóre operacje we / wy, aby wyświetlić moduły obsługi.
Przykład minimalnego trybu chronionego
Nowoczesne systemy operacyjne działają w tzw. Trybie chronionym.
Obsługa ma więcej opcji w tym trybie, więc jest bardziej złożona, ale duch jest ten sam.
Kluczowym krokiem jest użycie instrukcji LGDT i LIDT, które wskazują adres struktury danych w pamięci (tablica deskryptorów przerwań), która opisuje procedury obsługi.
int 0x80 to instrukcja języka asemblera, która jest używana do wywoływania wywołań systemowych w Linuksie na procesorach x86 (tj. zgodnych z Intelem).
Instrukcja „int” powoduje przerwanie.
Prosta odpowiedź: przerwanie, mówiąc najprościej, jest zdarzeniem, które przerywa procesor i nakazuje mu wykonanie określonego zadania.
Szczegółowa odpowiedź :
Procesor posiada tablicę procedur obsługi przerwań (lub ISR) przechowywanych w pamięci. W Real (16-bit) Tryb ten jest przechowywany jako IVT , albo ja nterrupt V ector T stanie. IVT zwykle znajduje się pod adresem 0x0000:0x0000
(adres fizyczny 0x00000
) i jest to seria adresów przesuniętych w segmentach, które wskazują na ISR. System operacyjny może zastąpić istniejące wcześniej wpisy IVT swoimi własnymi ISR.
(Uwaga: rozmiar IVT jest ustalony na 1024 (0x400) bajtów.)
W trybie chronionym (32-bitowym) procesor używa IDT. IDT jest strukturą o zmiennej długości, która składa się z deskryptorów (zwanych inaczej bramkami), które informują procesor o obsłudze przerwań. Struktura tych deskryptorów jest znacznie bardziej złożona niż proste wpisy przesunięcia segmentów w IVT; tutaj jest:
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
* IDT może mieć zmienną wielkość, ale musi być sekwencyjny, tj. Jeśli deklarujesz IDT od 0x00 do 0x50, musisz mieć każde przerwanie od 0x00 do 0x50. System operacyjny niekoniecznie używa ich wszystkich, więc obecny bit pozwala procesorowi poprawnie obsłużyć przerwania, których system operacyjny nie zamierza obsłużyć.
Kiedy pojawia się przerwanie (albo przez zewnętrzny wyzwalacz (np. Urządzenie sprzętowe) w IRQ, albo przez int
instrukcję z programu), CPU wypycha EFLAGS, następnie CS, a następnie EIP. (Są one automatycznie przywracane przeziret
instrukcję powrotu przerwania). System operacyjny zwykle przechowuje więcej informacji o stanie maszyny, obsługuje przerwanie, przywraca stan maszyny i kontynuuje.
W wielu systemach operacyjnych * NIX (w tym Linux), wywołania systemowe są oparte na przerwaniach. Program umieszcza argumenty wywołania systemowego w rejestrach (EAX, EBX, ECX, EDX, itd.) I wywołuje przerwanie 0x80. Jądro ustawiło już IDT tak, aby zawierał obsługę przerwań na 0x80, która jest wywoływana, gdy otrzyma przerwanie 0x80. Następnie jądro odczytuje argumenty i odpowiednio wywołuje funkcję jądra. Może przechowywać zwrot w EAX / EBX. Wywołania systemowe zostały w dużej mierze zastąpione przez instrukcje sysenter
i sysexit
(lub syscall
i sysret
na AMD), które pozwalają na szybsze wejście do pierścienia 0.
To przerwanie może mieć inne znaczenie w innym systemie operacyjnym. Koniecznie sprawdź jego dokumentację.
eax
służy do numer funkcji. asm.sourceforge.net/intro/hello.html
Jak wspomniano, powoduje przeskok sterowania do wektora przerwania 0x80. W praktyce oznacza to (przynajmniej pod Linuksem), że wywoływane jest wywołanie systemowe; dokładne wywołanie systemowe i argumenty są zdefiniowane przez zawartość rejestrów. Na przykład exit () można wywołać, ustawiając% eax na 1, a następnie „int 0x80”.
Mówi procesorowi, aby aktywował wektor przerwań 0x80, który w systemach operacyjnych Linux jest przerwaniem wywołania systemowego, używanym do wywoływania funkcji systemowych, takich jak open()
dla plików, i tak dalej.
int to nic innego jak przerwanie, tj. procesor wstrzyma bieżące wykonanie.
0x80 to nic innego jak wywołanie systemowe lub wywołanie jądra. tzn. funkcja systemowa zostanie wykonana.
Mówiąc konkretnie, 0x80 reprezentuje rt_sigtimedwait / init_module / restart_sys, różni się w zależności od architektury.
Więcej informacji można znaleźć pod adresem https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md