Системы компиляции
Sysroot
Любой компилятор должен "знать", где находятся стандартные заголовочные файлы, стандартные библиотеки и среда выполнения. Все они упакованы вместе для каждой платформы (например, arm64, x86) в каталоге с именем sysroot. Когда мы компилируем программу, нам нужно передать путь к sysroot, чтобы компилятор знал, где искать стандартные заголовочные файлы во время компиляции и где искать общие библиотеки (libc, libstdc++ и т. д.) во время компоновки.
Обычно, когда мы компилируем программу для той же машины, компилятор использует стандартные заголовочные файлы, доступные в /usr/include, и библиотеки из /usr/lib. Эти пути встроены в исходный код самого компилятора, так что нам никогда не придётся об этом думать. Однако при создании собственного компилятора или при кросс-компиляции программ мы сообщаем компилятору, где находится sysroot, передавая флаг (например, gcc --sysroot="/path/to/arm64/sysroot/usr" hello.cpp). Чаще всего предварительно упакованные кросс-компиляторы поставляются со сценарием/двоичным файлом, в который встроен путь sysroot (например, aarch64-linux-gnu-gcc) пакет g++-10-aarch64-linux-gnu (10.4.0-4ubuntu1~22.04cross1 и другие).
Система компиляции
Помимо sysroot, система компиляции содержит различные двоичные файлы, помогающие в процессе компиляции. В некоторых случаях сам компилятор входит в систему. Ниже приведен список элементов, упакованных с системой компиляции:
- binutils (ассемблер, компоновщик и т.д.);
- различные компиляторы (gcc, g++ и т.д.);
- C-библиотеки (glibc, uClibc и т.д.);
- библиотеки поддержки времени выполнения (crtbegin.o, crtend.o и т. д.);
- отладчик (gdb);
- стандартные заголовочные файлы C/C++ (iostream, stdio.h и т.д.);
- стандартные библиотеки (libstdc++, libm, libgcc, libunwind и т.д.);
- заголовочные файлы компилятора (stdint.h, stdc-predef.h);
- библиотеки поддержки времени выполнения для инструментов отладки (libasan, libubsan и т. д.).
Примечание: в предоставляемой системе компиляции могут присутствовать не все из них, в зависимости от поставщика. С более подробной информацией можно ознакомиться здесь:
Полезные ресурсы
- Константин Владимиров - Специальный выпуск: лекции по тулчейну, часть 1, фронтенд
- Константин Владимиров - Специальный выпуск: лекции по тулчейну, часть 2, миддленд и ассемблер
- Константин Владимиров - Специальный выпуск: лекции по тулчейну, часть 3, линкеры
Кросс-компиляция
Чтобы понять концепцию кросс-компиляции, давайте вернемся к определению компилятора. Компилятор - это программа, которая преобразует программу с одного языка на другой. Но слово "компилятор" часто используется для обозначения программы, которая переводит программу на машинный язык для создания исполняемого файла, работающего на вычислительном устройстве. Обычно компилятор используется для генерации машинного кода для той же машины, на которой работает сам компилятор. Под одной и той же машиной мы подразумеваем одну и ту же архитектуру. Например, компилятор, работающий на машине с linux-x64, компилирует программу на C++ и генерирует машинный код для той же машины с linux-x64. Эта программа может работать на всех машинах linux-x64, если предоставляется аналогичная среда.
Однако бывают ситуации, когда мы хотим сгенерировать двоичные файлы для машин другого типа, а не того, на котором запускается компилятор. Например, если целевая машина недостаточно мощная. Это часто имеет место при создании двоичных файлов для встраиваемых устройств, мобильных приложений и т. д. Кросс-компилятор создает двоичные файлы, которые будут работать на другом компьютере (целевом компьютере), а не на том, на котором работает сам компилятор (хост-компьютер). Это немного более сложный процесс, так как он требует присутствия всех зависимостей целевой машины на хост-машине.
Например, при компиляции простой программы hello-world для хост-компьютера используется заголовочный файл stdio.h на месте, подобном /usr/include/stdio.h. Для создания кросс-компилируемой программы hello-world файл stdio.h будет находиться в другом sysroot. Итак, вызов компилятора может выглядеть так:
gcc --sysroot=/path/to/aarch64/sysroot -march=armv8-a hello.c
Еще более запутанная система - канадская сборка2 Примечание переводчика: приведённые в курсе описание и иллюстрация канадской сборки могут лишь запутать читателя, рекомендуется ознакомиться с ней отдельно. В ней участвуют два кросс-компилятора. В этой настройке есть три машины A, B и C. Кросс-компилятор в A (CA) сгенерирует другой кросс-компилятор (CB), который будет работать на B. CB сгенерирует код для машины C.