Что означает "int 0x80" в коде сборки?

Может кто-нибудь объяснить, что делает следующий ассемблерный код?

 int 0x80  

8 ответов

Решение

Он передает управление на вектор прерывания 0x80

Смотрите http://en.wikipedia.org/wiki/Interrupt_vector

На Linux, посмотрите на это: он был использован для обработки 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 в вашей программе, вы можете заставить процесс выполнить код в ядре, который остановит (выйдет) текущий запущенный процесс (в Linux, процессор Intel x86).

Аппаратное прерывание не следует путать с программным прерыванием. Вот очень хороший ответ на этот счет.

Это также хороший источник.

Вы можете увидеть int 80h в действии здесь.

Пример минимального запуска системного вызова 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 вверх по течению. Проверено на Ubuntu 16.04.

Лучшие альтернативы

int 0x80 была заменена лучшими альтернативами для системных вызовов: сначала sysenter, то ВДСО.

x86_64 имеет новый syscall инструкция

Смотрите также: что лучше "int 0x80" или "syscall"?

Минимальный 16-битный пример

Сначала узнайте, как создать минимальный загрузчик ОС и запустить его на QEMU и реальном оборудовании, как я объяснил здесь: /questions/8306063/kak-zapustit-programmu-bez-operatsionnoj-sistemyi/8306066#8306066

Теперь вы можете работать в 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 0x80 - это инструкция на языке ассемблера, которая используется для вызова системных вызовов в Linux на процессорах x86 (т.е. Intel-совместимых).

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

Инструкция int вызывает прерывание.

Что за прерывание?

Простой ответ: Проще говоря, прерывание - это событие, которое прерывает процессор и говорит ему выполнить определенную задачу.

Подробный ответ:

ЦПУ имеет таблицу подпрограмм обработки прерываний (или ISR), хранящихся в памяти. В реальном (16-битном) режиме он сохраняется как IVT, или I nruptrupt V ector Table. IVT обычно находится в 0x0000:0x0000 (Физический адрес 0x00000), и это серия адресов со смещением сегмента, которые указывают на ISR. ОС может заменить ранее существующие записи IVT своими собственными ISR.

(Примечание. Размер IVT фиксирован и составляет 1024 (0x400) байта.)

В защищенном (32-разрядном) режиме процессор использует IDT. IDT - это структура переменной длины, состоящая из дескрипторов (иначе называемых gates), которые сообщают ЦПУ об обработчиках прерываний. Структура этих дескрипторов намного сложнее, чем простые записи IVT со смещением сегмента; вот:

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 может иметь переменный размер, но он должен быть последовательным, т. Е. Если вы объявляете свою 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'.

Int - это не что иное, как прерывание, т.е. процессор приостанавливает свое текущее выполнение.

0x80 - это не что иное, как системный вызов или вызов ядра. т.е. системная функция будет выполнена.

Чтобы быть конкретным, 0x80 представляет rt_sigtimedwait/init_module/restart_sys, он варьируется от архитектуры к архитектуре.

Для получения дополнительной информации см. https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md.

Он говорит процессору активировать вектор прерывания 0x80, который в ОС Linux является прерыванием системного вызова, используемым для вызова системных функций, таких как open() для файлов и так далее.

Другие вопросы по тегам