Содержание статьи
Нередко бывает, что в теории язык или его конкретная реализация выглядит хорошо, но попытки использовать его на практике упираются в труднопреодолимые препятствия: сложность взаимодействия с другими языками или распространения исполняемых файлов.
Когда я впервые познакомился с адой, мне стало интересно попробовать ее в реальности, но мне была нужна подходящая задача — достаточно небольшая, чтобы цена провала оказалась невелика, но и достаточно сложная, чтобы выявить как можно больше потенциальных проблем.
Про сборку программ на аде было рассказано в моей предыдущей статье, но если пропустил — не страшно, ничего сложного в этом нет. Нужно поставить GNAT — он входит в состав GCC и всегда есть в репозиториях, — сохранить код в файл something.adb
и выполнить gnatmake something.adb
.
Желательно, чтобы на месте something
в имени файла было имя основной процедуры, иначе компилятор выдаст предупреждение. Исполняемый файл gnatmake
автоматически назовет по имени файла с кодом, а не a.out
.
INFO
Один из моих проектов — VyOS — дистрибутив GNU/Linux для маршрутизаторов. Над ним работала команда специалистов, в которой я выступал координатором.
По ряду причин я решил написать утилиты для определения гипервизора, в котором работает виртуальная машина. В VyOS мы включаем эту информацию в вывод команды show version
. Для получения самой информации исторически использовалась самописная утилита на довольно грязном C, которая не поддерживала некоторые менее популярные гипервизоры, и у меня давно было желание ее чем-нибудь заменить.
Существующие решения, такие как virt-what, вызывают у меня смешанные чувства. Смесь C и скриптовых языков, на мой взгляд, выглядит неэстетично. Эстетика — вещь субъективная, но есть и объективные проблемы, например поддержка только GNU/Linux и отказ работать без прав суперпользователя.
Мне хотелось, чтобы замена старому коду принесла пользу не только мне и пользователям VyOS, поэтому я поставил следующие требования:
- поддержка как минимум GNU/Linux и FreeBSD;
- возможность работы с правами обычного пользователя;
- по крайней мере техническая возможность работы на разных архитектурах;
- простая и доступная незнакомому с адой пользователю процедура сборки.
Задача мне показалась вполне подходящей для тестирования нового языка. В случае провала я всегда мог бы переписать код на Rust или взять одну из существующих утилит. Эксперимент завершился, на мой взгляд, успешно, результат был назван hvinfo и уже давно используется в VyOS. Исходный код можно найти по адресу github.com/dmbaturin.
В этой статье мы рассмотрим проект изнутри и познакомимся с возможностями языка ада и инструментами GNAT, которые потребовались для его разработки.
Способы определения гипервизора
Как, собственно, определить, работает ли система в виртуальной машине, и если да, то на каком гипервизоре? На платформе x86 все системы виртуализации с этой точки зрения можно поделить на две группы: одни поддерживают общий стандарт de facto — передачу информации через вызов инструкции cpuid
, другие не поддерживают.
Определение через cpuid
К первой группе относятся Xen в режиме аппаратной виртуализации, KVM, bhyve, VMware и Hyper-V. Я не уверен, кто из них ввел этот механизм первым, но работает он у всех одинаково.
Инструкция cpuid
была впервые реализована компанией Intel и с тех пор присутствует во всех процессорах x86. Стоит отметить, что для совместимости она использует 32-разрядные регистры даже в 64-разрядном режиме. Вид возвращаемой информации зависит от значения в регистре eax
.
Гипервизоры из первой группы перехватывают вызовы cpuid
и обладают дополнительными возможностями. Для передачи информации о самом факте работы ОС в виртуальной машине применяется разряд 31-го регистра ecx
. На физических машинах он всегда установлен на ноль согласно документации Intel, а гипервизоры устанавливают его на единицу.
Получить название гипервизора можно, вызвав cpuid
со значением 0x40000000
в регистре eax
. Название передается в виде строки длиной до двенадцати символов в регистрах ebx
, ecx
и edx
. К примеру, Xen использует строку XenVMMXenVMM
, а VMware — VMwareVMware
.
INFO
Название производителя процессора передается таким же способом. Именно поэтому используются строки вроде GenuineIntel
и AuthenticAMD
— не чтобы убедить пользователя, что процессор не поддельный, а чтобы строка укладывалась в три 32-разрядных регистра без дополнения нулями.
Использование инструкций процессора дает нам отличную возможность увидеть работу с ассемблерными вставками, двоичную арифметику и условную компиляцию.
Прочие способы
Некоторые гипервизоры не используют сложившийся интерфейс cpuid
, несмотря на полную виртуализацию, например VirtualBox в режиме двоичной трансляции. Паравиртуальный Xen просто не может его использовать.
В этих случаях приходится применять другие способы, такие как проверка названия производителя из SMBIOS или наличия специфичных устройств PCI, вроде видеокарты innotek Gmbh в VirtualBox.
Эти способы, в отличие от cpuid
, не так универсальны, и на разных ОС их придется реализовать по-разному. Чтобы это сделать, нам придется использовать интерфейс с libc и работу с файлами.
Работа с машинным кодом и данными
Ассемблерные вставки
Ада проектировалась как язык системного программирования, а какое системное программирование совсем без машинного кода? Все возможности для этого присутствуют.
Прежде всего нам потребуются беззнаковые целочисленные типы. Пакет Interfaces предоставляет все распространенные типы, включая нужный нам для работы с 32-разрядными регистрами Unsigned_32
.
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»