Перейти к содержанию

Архитектура процессора, введение в X86-64 и ARM assembler. Прерывания. Системные вызовы

Исходники по этой теме

  • API = Application Programming Interface - Интерфейс прикладного программирования. Набор функций, классов, методов и прочего, которые библиотека (или сервис) предоставляет разработчику.
  • ABI = Applications Binary Interface - Двоичный (бинарный) интерфейс приложений. Низкоуровневое соглашение о том, как данные передаются между частями программы уже после компиляции. Определяет как передаются аргументы в функции (через регистры или стек), как называются функции в файле, расположение структур и данных.

Кольца защиты и уровни привилегий

Наиболее часто используемые:

  • Ring 0 – привилегированный код (гипервизоры, ядро ОС)
  • Ring 3 - непривилегированный код (пользовательские приложения)
  • Ring 1 и 2 – обычно не используются (все еще иногда используются антивирусным программным обеспечением или ядрами гостевых ОС)

Выполнение привилегированных инструкций возможно только в кольце 0 (CPL = 0). Попытка их выполнения за пределами нулевого кольца, приведет к исключению #GP (general-protection exception). Подробнее про команды X86

https://gcc.gnu.org/onlinedocs/gcc/extensions-to-the-c-language-family/how-to-use-inline-assembly-language-in-c-code.html

Используется GAS. Сборка и выполнение примера:

make all
./build/asm_example

Пример из пренезнтации не работает для c99, c11 и c17. Инструкция asm - для gcc (работает, например, для gnu99). Для иных подходит __asm__ (указать voliative по необходимости).

Strace

Инструмент отслеживания вызовов ядра linux. В рамках примера интересен вызов с флагами -e trace=execve,write (остальные вызовы – системные).

strace -e trace=execve,write ./build/asm_example
execve("./build/asm_example", ["./build/asm_example"], 0x7ffc3dea7a60 /* 37 vars */) = 0
write(1, "2\n", 22
)                      = 2
+++ exited with 0 +++

В трассировке видно что запущено (execve) приложение вызовом ./build/asm_example" и запрошен вывод "2\n" в стандартный вывод (1).

В бинарнике приложения:

objdump -d build/asm_example

...
0000000000001060 <main>:
..
    106f:       ba 01 00 00 00          mov    $0x1,%edx
    1074:       48 8d 35 89 0f 00 00    lea    0xf89(%rip),%rsi        # 2004 <_IO_stdin_used+0x4>
    107b:       89 d2                   mov    %edx,%edx
    107d:       83 c2 01                add    $0x1,%edx
    1080:       e8 cb ff ff ff          call   1050 <__printf_chk@plt>
...
  • 106f - запись в edx 1;
  • 107d - добавление 1 к edx;
  • 1080 - вызов функции печати из библиотеки.

Выполнение задания

make all
strace ./build/variant1
strace ./build/variant2
strace ./build/variant3

hlt

Команда останавливает работу процессора, не имеет операндов и не изменяет никакие флаги. Из остановки процессор может вывести только аппаратное прерывание или перезагрузка. Если причиной было прерывание, то адрес возврата, помещаемый в стек для обработчика прерывания, указывает на следующую после HLT команду. Выполняется ТОЛЬКО в реальном режиме или с CPL = 0. Поскольку CPL = 0 - это уровень привилегий ядра операционной системы, то пользовательские программы в защищённом режиме не могут воспользоваться этой командой.

HLT - это сокращение от слова HaLT, что можно перевести как “остановить”, “прекратить”, “приостановить”.

Запуск:

Получаем (ожидаемо) ошибку:

write(1, "Try to: hlt\n", 12Try to: hlt
)           = 12
--- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=NULL} ---
+++ killed by SIGSEGV (core dumped) +++
[1]    929708 segmentation fault (core dumped)  strace ./build/variant1

cli

Выполнение этой команды отключает аппаратные прерывания (Сброс флага прерывания).

Может применяться в микроконтроллерах для настройки регистров и прерываний в момнет инициализации. После завершения настройки - вызвать sei. В Linux - STI.

Современные операционные системы устанавливают IOPL = 0, чтобы только ядро могло исполнять эту инструкцию.

CLI - это CLear IF (очистить IF). Полагаю, SEI - SEt Interrupt

Также получаем ошибку:

write(1, "Try to: cli\n", 12Try to: cli
)           = 12
--- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=NULL} ---
+++ killed by SIGSEGV (core dumped) +++
[1]    930126 segmentation fault (core dumped)  strace ./build/variant2

sti

STI (Set Interrupt)

Установка флага прерывания.

STI - значит SeT Interrupt

Ожидаемо не выолнится:

write(1, "Try to: sti\n", 12Try to: sti
)           = 12
--- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=NULL} ---
+++ killed by SIGSEGV (core dumped) +++
[1]    931823 segmentation fault (core dumped)  strace ./build/variant3