Sysroot linux ��� ���

Как выйти на путь разработки ОС

Чтобы понять, в чем заключается роль разработчика ОС, представим, что происходит после нажатия на кнопку включения ПК.

Сначала запускается BIOS и подготавливает жизненно важное оборудование, после чего загружает в память MBR загрузочного диска, содержащую код первой части загрузчика. Под непосредственно исполняемую часть отведено всего 446 байт, чего крайне недостаточно, поэтому мало загрузчиков действительно укладываются в эти границы. В связи с этим загрузчик обычно разделяется на две части, и единственное, что делает первая часть загрузчика — читает с диска и запускает вторую часть. Вторая часть уже может занимать хоть весь диск, и обычно переводит процессор в защищенный режим, загружает в память ядро и модули, после чего передает управление ядру.

Ядро полностью подготавливает оборудование и запускает первые пользовательские процессы, обеспечивая им и их потомкам runtime-поддержку.

Таким образом, минимальное ядро должно уметь читать программы с диска, запускать их и в дальнейшем исполнять системные вызовы. Также крайне желательны вывод на монитор и механизмы защиты.

Инструментарий

Теоретически, разработку можно вести на любой ОС, но большинство инструментов рассчитаны на UNIX-подобные системы, и хотя бы собрать их на Windows уже будет страданием. Более того, поскольку WSL не поддерживает модули ядра, смонтировать образ диска не получится, и придется настраивать коммуникацию между WSL и Windows. На этом этапе уже становится проще поставить виртуальную машину с Linux. В статье будут предоставлены инструкции для Linux и macOS.

Для полного цикла разработки понадобятся редактор кода, сборочная система, отладчик с поддержкой удаленной отладки, загрузчик, виртуальная машина и, желательно, отдельная реальная машина для тестирования.

На место виртуальной машины лучше всего подходят Bochs и QEMU, поскольку они быстро запускаются и предоставляют возможность отладки запущенного ядра.

Загрузчик некоторые мазох особенно идейные разработчики пишут сами, но, если речь идет о разработке собственно операционной системы, писать загрузчик будет очень скучно, а также ненужно, поскольку есть готовые решения. Благодаря спецификации Multiboot можно написать ядро, которое будет почти из коробки загружаться с помощью, например, GRUB или LILO.

Со сборкой же всё не так просто: понадобится кросс-компилятор под x86. Зачем кросс-компилятор, если собирать под ту же архитектуру? Дело в том, что стандартный компилятор генерирует код, опирающийся на ту же ОС, на которой он запущен, или т.н. hosted-код. Hosted-код использует системные вызовы, взаимодействует с другими процессами, но привязан к операционной системе. Freestanding-код существует сам по себе и для запуска требует только само оборудование. Ядро ОС относится к freestanding, а программы, им запускаемые — к hosted. Кросс-компилятору достаточно соответствующего флага, и будет сгенерирован freestanding-код.

Подготовка

Сборка инструментов

Эту часть легче всего произвести из командной строки. Для удобства можно создать для сборки отдельный каталог, который будет легко удалить после сборки, а также задать несколько переменных окружения:

$TARGET — система, под которую будет собирать полученный компилятор. Обычно она называется наподобие i686-linux-gnu , но здесь результат запускается без ОС, поэтому указывается просто формат исполняемого файла. Почему i686, а не i386? Просто архитектуре 80386 уже, кхм, много лет, и с тех пор многое изменилось; в частности, появились кэши, многоядерные и многопроцессорные системы, встроенные FPU, “большие” атомарные инструкции вроде CMPXCHG , так что, собирая под i386, можно сильно потерять в быстродействии и немного приобрести в поддержке старых компьютеров.

Читайте также:  Генератор hwid windows 10

$PREFIX — то, куда будут установлены инструменты. Обычно используются пути вроде /usr/i686-elf , /usr/local/i686-elf и подобные, но можно установить и в произвольную папку. Этот каталог также называется sysroot, поскольку он будет представлять собой корневой каталог для кросс-компилятора и утилит. Говоря точнее, это не полноправный путь, а именно префикс к пути; таким образом, для установки в корень $PREFIX будет представлять из себя пустую строку, а не / . На время сборки GCC потребуется добавить в PATH путь $PREFIX/bin .

Если потом ОС понадобится собирать под другую архитектуру, достаточно будет установить другие переменные окружения, а команды скопировать.

Binutils

Загружаем и распаковываем последнюю версию с официального FTP. Осторожно: minor-версии давно перешагнули за 10, вследствие чего сортировка по алфавиту сломалась, для поиска актуальной версии можно использовать сортировку по дате последнего изменения. На момент написания статьи актуальной версией Binutils является 2.29.

Binutils не поддерживает сборку в каталоге с исходным кодом, поэтому создаем каталог рядом с распакованным кодом и заходим в него. Далее обычная сборка из исходников:

Подробнее о параметрах:

—with-sysroot — использовать sysroot;
—disable-nls — выключить поддержку родного языка. OSDev-сообщество не так велико, чтобы на какую-нибудь непонятную ошибку сборки обязательно нашёлся человек, говорящий на языке того, у кого она возникла;
—disable-werror — компилятор при сборке Binutils выдает предупреждения, а с -Werror это приводит к остановке сборки.

Так же загружаем, распаковываем и создаем каталог для сборки. Процесс сборки немного отличается. Понадобятся библиотеки GMP, MPFR и MPC. Их можно установить из стандартных репозиториев многих пакетных менеджеров, а можно запустить из каталога с исходным кодом скрипт contrib/download_prerequisites , который их скачает и использует при сборке. Конфигурацию выполняем так:

—disable-nls — то же самое, что и для Binutils;
—without-headers — не предполагать, что на целевой системе будет стандартная библиотека (этим, собственно, и отличается необходимый нам компилятор от стандартного);
—enable-languages=c,c++ — собрать компиляторы только для выбранных языков. Опционально, но существенно ускоряет сборку.

В условиях отсутствия целевой ОС обычный make && make install не подойдет, поскольку некоторые компоненты GCC ориентируются на готовую операционную систему, поэтому собираем и устанавливаем только необходимое:

libgcc — библиотека, в которой содержатся внутренние функции компилятора. Компилятор вправе вызывать их для некоторых вычислений, например, для 64-битного деления на 32-битной платформе.

На большинстве дистрибутивов Linux эту секцию можно пропустить, поскольку на них уже установлены подходящие утилиты для работы с GRUB. Для других же ОС его потребуется загрузить и собрать. Также понадобится маленькая утилита objconv:

На время сборки GRUB потребуется добавить в PATH только что собранный objconv
и кросс-инструменты (i686-elf-*).

GDB (для macOS)

Стандартная версия GDB не знает об ELF-файлах, поэтому при использовании GDB его потребуется пересобрать с их поддержкой. Загрузка, распаковка, сборка:

Образ диска

Процесс создания такового в разных ОС происходит по-своему, поэтому здесь я приведу отдельные инструкции.

Создаем пустой файл:

Создаем таблицу разделов:

Создаём файловую систему:

В дальнейшем можно будет монтировать посредством

Устанавливаем загрузчик (здесь GRUB):

Создаем пустой файл:

Разделяем таблицу разделов и единственный раздел:

Подключаем раздел как диск:

Создаем ФС, здесь FAT32:

“Склеиваем” MBR и ФС обратно:

Подключаем и запоминаем точку монтирования (обычно “/Volumes/NO NAME”):

Образ диска после этого спокойно подключается встроенными средствами системы. Можно на собственное усмотрение создать иерархию директорий и настроить загрузчик. Например, для GRUB можно создать такой grub.cfg в /boot/grub:

Настройка сборочной системы

Популярных сборочных систем в мире великое множество, поэтому инструкций к каждой здесь не будет, но общие моменты всё же опишу.

Ассемблерные файлы собираем в объектные формата ELF (32 бита):

C-файлы собираем при помощи кросс-компилятора с флагом -ffreestanding:

Для компоновки используем всё тот же кросс-компилятор, но указываем чуть больше информации:

Читайте также:  Драйвер для принтера samsung scx 4321 для windows 10

-ffreestanding — генерировать freestanding-код;
-nostdlib — не включать стандартную библиотеку, поскольку ее реализация является hosted-кодом и будет совершенно бесполезна;
-lgcc — подключаем описанную выше libgcc. Ее подключение всегда идет после остальных объектных файлов, иначе компоновщик будет жаловаться на неразрешенные ссылки;
-T — поскольку нужно где-то разместить заголовок Multiboot, обычная раскладка ELF-файла не подойдёт. Ее можно изменить при помощи скрипта компоновщика, который и задает этот флаг. Вот готовый его вариант:

Минимальное ядро

Получаем управление

Получаем управление от загрузчика в небольшом ассемблерном файле:

Proof of Work

Чтобы хоть как-то увидеть, что код действительно выполняется, можно вывести что-то на экран. Полноценный драйвер терминала — тема большая, но, вкратце, по адресу 0xB8000 располагается буфер на 2000 записей, каждая из которых состоит из атрибутов и символа. Белому тексту на черном фоне соответствует байт атрибутов 0x0F. Попробуем что-либо вывести при помощи заранее подготовленной строки:

Запуск

Копируем ядро в образ диска по нужному пути, и после этого любая виртуальная машина должна его успешно загрузить.

Отладка

Для отладки в QEMU можно задать флаги -s -S . QEMU будет дожидаться отладчика и включит сетевую отладку. Также стоит заметить, что отладка не будет работать при использовании ускорителя, так что флаг —enable-kvm придется убрать, если он используется.

Bochs понадобится собрать с —enable-gdb-stub , а в конфиг включить строку наподобие gdbstub: enabled=1, port=1234, text_base=0, data_base=0, bss_base=0 .
В GDB можно подключиться и запустить машину таким образом (kernel.elf — файл ядра):

Все остальное работает так же, как и всегда — точки останова, чтение памяти и пр. Также можно включить отладчик в само ядро, что позволит производить отладку на реальной машине. Можно написать его самостоятельно, но отладка ошибки в отладчике принесет много-много радости. GNU распространяет почти готовые отладчики, требующие от ядра только несколько функций. Например, для i386. Впрочем, пока это делать рано, поскольку еще нет необходимых функций, таких как установка обработчика прерывания или получения/отправки данных через последовательный порт.

Заключение

На текущий момент до минимальной рабочей операционной системы остается настроить следующее:

Источник

GDC/Cross Compiler/Existing Sysroot

This describes how to build or use a cross compiler if you have access to an existing system root (short sysroot). A sysroot is a folder which contains a minimal filesystem (especially libraries, the C library and header files). An example sysroot could look like this: ‘sysroot/usr’, ‘sysroot/usr/lib’, ‘sysroot/usr/include’.

Contents

The sysroot

We assume that a sysroot is available at $SYSROOT. If the sysroot directory is inside the prefix where we’ll install the compiler, the complete prefix directory can be moved to different locations. If it’s not in the prefix directory GCC always uses the absolute path for the sysroot and if you move the sysroot the compiler will not work anymore.

Sysroot over SSH

If your target machine offers a fast SSH access it’s possible to use the file system of the remote machine directly. This way the crosscompiler has full access to all libraries of the remote system. If a library is missing simply install it on the remote machine using the standard package manager.

You can mount a remote sysroot by using SSHFS:

user@targethost is an SSH user / hostname account. $SYSROOT is the location where the sysroot will be mounted.

The follow_symlinks option is important. The remote filesystem will have absolute symlinks (/usr/lib/libdl.so —> /usr/lib/libdl.so.2) .follow_symlinks makes sure we really get access to the remote /usr/lib/libdl.so.2 and not to the local one.

Using a compiler from gdcproject.org/downloads

You can use the compilers from http://gdcproject.org/downloads with a SSH sysroot, but the system accessed over SSH must be similar to the system the cross-compilers «emulate». For example ArchlinuxARM systems are known to work, debian systems will not work (debian uses additional patches for binutils which were not compiled into the compilers from gdcproject.org).

Читайте также:  Linux bash string concatenation

To use such an compiler with the SSH sysroot, use the ‘—sysroot’ switch:

Now you can link with any library installed on the remote machine:

Note: When using a Debian based sysroot system, the compiler binaries from gdcproject.org might fail to find installed libraries. Use the following options for Debian to explicitly specify the path to libraries:

You may also have to explicitly pass the ‘-ldl’ option for debian when using certain libraries.

You can also use dub to build any dub package with a cross compiler:

Building a new compiler

Specifying a target and installation folder

Replace with the absolute path to the folder the toolchain should be installed in.

GNU Binutils

Note: Debian targets

If you’re target sysroot is a recent debian distribution (>= wheezy) you’ll have to apply one of the debian patches to binutils. Download it from here: http://bazaar.launchpad.net/

You then have to set DEB_TARGET_MULTIARCH before building binutils:

The correct value can be obtained by executing this on the target:

Источник

set sysroot command

Specifies the local directory that contains copies of target libraries in the corresponding subdirectories. This option is useful when debugging with gdbserver.

Syntax

Parameters

Typical use

This command is useful when debugging remote programs via gdbserver and the libraries on the target machine (running gdbserver) do not match the libraries on the source machine (running gdb). In order to set breakpoints and find source lines that correspond to different code locations GDB needs to access the library files containing symbol information. Copying those libraries to locations under a local directory and specifying its path via set sysroot allows GDB find them.

Default value

The default value for the set sysroot variable depends on your toolchain. If your GDB binary was compiled with the —sysroot argument, you won’t need to run the set sysroot command — the sysroot will be automatically set to the location specified during compilation. Otherwise the default value will be «» and you might need to set it manually if you are debugging remote processes.

Examples

In this example we will debug a simple shared library with gdbserver:

We will use a simple program to test our library:

First, we build the application and the library and deploy it to another machine:

cd /home/testuser/libtest
g++ -ggdb -fPIC -shared lib.cpp -o libTest.so
g++ -ggdb main.cpp libTest.so -o testApp -Wl,—rpath=’$ORIGIN’
scp testApp libTest.so deploy_machine:/tmp

Second, we create a local sysroot directory that will contain copies of system libraries from the target machine:

mkdir /home/testuser/libtest/sysroot
cd /home/testuser/libtest/sysroot
scp -r deploy_machine:/lib lib

Third, we move the libTest.so library to the directory in sysroot that corresponds to /tmp on the target machine (where we have deployed our library):

mkdir tmp
mv ../libTest.so tmp

Then we run gdbserver on the deploy_machine machine:

Finally we run GDB:

Note how after we issue the set sysroot command, GDB will read all the symbols from the copied DLLs inside the /home/testuser/libtest/sysroot.

We could also have avoided copying libraries manually. If you specify remote:/ as the sysroot, GDB will download the libraries automatically:

Compatibility with VisualGDB

Cross-compilation toolchains provided with VisualGDB are configured to use correct sysroot. If you want to override this behavior you can specify the set sysroot command in the GDB Startup commands in VisualGDB Project Properties.

Источник

Оцените статью