Может кто-нибудь объяснить, что делает следующий код сборки?
int 0x80
Может кто-нибудь объяснить, что делает следующий код сборки?
int 0x80
Он передает управление для прерывания вектора 0x80
См. http://en.wikipedia.org/wiki/Interrupt_vector
В Linux, посмотрите this: он использовался для обработки system_call
. Конечно, на другой ОС это может означать нечто совершенно другое.
int
означает прерывание, а номер 0x80
является номером прерывания.
Прерывание передает поток программы тому, кто обрабатывает это прерывание, в данном случае это прерывание 0x80
.
В Linux обработчик прерываний 0x80
является ядром и используется для выполнения системных вызовов ядра другими программами.
Ядро уведомляется о том, какой системный вызов хочет сделать программа, проверяя значение в регистре %eax
(синтаксис газа и EAX в синтаксисе Intel). Каждый системный вызов предъявляет разные требования к использованию других регистров. Например, значение 1
в %eax
означает системный вызов exit()
, а значение в %ebx
содержит значение кода состояния для exit()
.
Имейте в виду, что 0x80 = 80h = 128
Вы можете видеть здесь, что INT является лишь одной из многих инструкций (фактически это представление языка ассемблера (или я должен сказать "мнемоника")), которые существуют в наборе команд x86, Вы также можете найти более подробную информацию об этой инструкции в собственном руководстве Intel, которое можно найти здесь.
Подводя итог из PDF:
INT n/INTO/INT 3 - вызов процедуры прерывания
Инструкция INT n генерирует вызов прерывания или исключения обработчик, указанный с операндом назначения. Пункт назначения операнд задает вектор от 0 до 255, закодированный как 8-битный без знака промежуточное значение. Инструкция INT n является общей мнемоникой для выполнение программно-сгенерированного вызова обработчика прерываний.
Как вы можете видеть, 0x80 является операндом назначения в вашем вопросе. В этот момент процессор знает, что он должен выполнить некоторый код, который находится в ядре, но какой код? Это определяется вектором прерывания в Linux.
Одним из наиболее полезных программных прерываний DOS было прерывание 0x21. Вызывая его с различными параметрами в регистрах (в основном, ah и al), вы можете получить доступ к различным операциям ввода-вывода, выводу строки и многому другому.
Большинство систем и производных Unix не используют программные прерывания, за исключением прерывания 0x80, используемого для системных вызовов. Это достигается путем ввода 32-битного значения, соответствующего функции ядра, в регистр EAX процессора и последующего выполнения INT 0x80.
Посмотрите на это, где показаны другие доступные значения в таблицах обработчиков прерываний:
Как видно из таблицы, процессор указывает на выполнение системного вызова. Вы можете найти таблицу системных вызовов Linux здесь.
Таким образом, перемещая значение 0x1 в регистр EAX и вызывая INT 0x80 в вашей программе, вы можете заставить процесс выполнить код в Kernel, который остановит (выйдет) текущий запущенный процесс (в Linux, процессор Intel x86).
Аппаратное прерывание не следует путать с программным прерыванием. Здесь очень хороший ответ на этот счет.
Это также хороший источник.
int 0x80 - это язык ассемблера инструкция, которая используется для вызова системные вызовы в Linux на x86 (т.е. Intel-совместимые).
Пример минимального запуска системного вызова Linux
Linux устанавливает обработчик прерываний для 0x80
таким образом, чтобы он реализовывал системные вызовы, способ взаимодействия пользовательских программ с ядром.
.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
Скомпилируйте и запустите с помощью:
as -o main.o main.S
ld -o main.out main.o
./main.out
Результат: программа печатает на стандартный вывод:
hello world
и выходит чисто.
Вы не можете устанавливать свои собственные обработчики прерываний непосредственно из пользовательского пространства, потому что у вас есть только кольцо 3, а Linux не позволяет вам делать это.
GitHub upstream. Проверено на Ubuntu 16.04.
Лучшие альтернативы
int 0x80
был заменен лучшими альтернативами для системных вызовов: сначала sysenter
, затем VDSO.
В x86_64 есть новая инструкция syscall
.
Смотрите также: Что лучше " int 0x80" или "системный вызов"?
Минимальный 16-битный пример
Сначала узнайте, как создать минимальный загрузчик ОС и запустить его на QEMU и реальном оборудовании, как я объяснил здесь: fooobar.com/questions/30228/...
Теперь вы можете работать в 16-битном реальном режиме:
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
Это будет сделано по порядку:
Do 0.
Do 1.
hlt
: прекратить выполнениеОбратите внимание, как процессор ищет первый обработчик по адресу 0
, а второй - по 4
: это таблица обработчиков, называемая IVT, и каждая запись имеет 4 байта.
Минимальный пример, который делает IO, чтобы сделать обработчики видимыми.
Пример минимального защищенного режима
Современные операционные системы работают в так называемом защищенном режиме.
В этом режиме обработка имеет больше опций, поэтому она более сложная, но дух тот же.
Ключевым этапом является использование инструкций LGDT и LIDT, которые указывают адрес структуры данных в памяти (таблица дескрипторов прерываний), которая описывает обработчики.
Инструкция int вызывает прерывание.
Простой ответ: Прерывание, проще говоря, - это событие, которое прерывает процессор и указывает ему выполнить определенную задачу.
Подробный ответ:
ЦПУ имеет таблицу подпрограмм обработки прерываний (или ISR), хранящихся в памяти. В реальном (16-разрядном) режиме это сохраняется в состоянии IVT или I nterrupt V ector T. IVT обычно расположен в 0x0000:0x0000
(физический адрес 0x00000
), и это серия адресов со смещением сегмента, которые указывают на ISR. Операционная система может заменить существующие записи IVT собственными ISR.
(Примечание. Размер IVT фиксирован и составляет 1024 (0x400) байта.)
В защищенном (32-разрядном) режиме процессор использует IDT. IDT - это структура переменной длины, состоящая из дескрипторов (иначе называемых gates), которые сообщают ЦПУ об обработчиках прерываний. Структура этих дескрипторов намного сложнее, чем простые записи смещения сегментов IVT; вот оно:
bytes 0, 1: Lower 16 bits of the ISR 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 может иметь переменный размер, но он должен быть последовательным, т.е. если вы объявляете свою IDT в диапазоне от 0x00 до 0x50, вы должны иметь каждое прерывание от 0x00 до 0x50. ОС не обязательно использует их все, поэтому бит Present позволяет процессору правильно обрабатывать прерывания, которые ОС не собирается обрабатывать.
Когда происходит прерывание (либо с помощью внешнего триггера (например, аппаратного устройства) в IRQ, либо с помощью команды int
из программы), ЦП выдвигает EFLAGS, затем CS, а затем EIP. (Они автоматически восстанавливаются с помощью iret
, инструкции возврата прерывания.) ОС обычно хранит больше информации о состоянии машины, обрабатывает прерывание, восстанавливает состояние машины и продолжает работу.
Во многих ОС * NIX (включая Linux) системные вызовы основаны на прерываниях. Программа помещает аргументы системного вызова в регистры (EAX, EBX, ECX, EDX и т.д.) И вызывает прерывание 0x80. Ядро уже установило IDT для обработки обработчика прерываний по адресу 0x80, который вызывается при получении прерывания 0x80. Затем ядро считывает аргументы и соответственно вызывает функцию ядра. Может хранить возврат в EAX/EBX. Системные вызовы были в основном заменены инструкциями sysenter
и sysexit
(или syscall
и sysret
для AMD), которые позволяют быстрее входить в кольцо 0.
Это прерывание может иметь другое значение в другой ОС. Обязательно ознакомьтесь с его документацией.
Как уже упоминалось, это приводит к тому, что управление переходит на вектор прерывания 0x80. На практике это означает (по крайней мере, под Linux), что вызывается системный вызов; точный системный вызов и аргументы определяются содержимым регистров. Например, exit() можно вызвать, установив% eax в 1, а затем "int 0x80".
Он сообщает процессору активировать вектор прерывания 0x80, который на ОС Linux является прерыванием системного вызова, используемым для вызова системных функций, таких как open()
для файлов и т.д.