Архитектура процессора, введение в 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