ii. Технические примечания по сборочным инструментам

В этом разделе объясняются технические детали, лежащие в основе сборки пакетов. Не обязательно сразу понимать все, что содержится в этом разделе. Большая часть этой информации станет более понятной после выполнения фактической сборки. К этому разделу можно обратиться в любое время по ходу процесса.

Основная задача Глава 5 и Глава 6 состоит в том, чтобы создать временную область, содержащую заведомо исправный набор инструментов, которые можно изолировать от хост-системы. Использовании команды chroot в последующих главах, обеспечит чистую и безотказную сборку целевой системы LFS. Процесс сборки разработан таким образом, чтобы свести к минимуму риски для новых читателей и в то же время обеспечить наибольшую образовательную ценность.

Сборка инструментария основана на процессе кросс-компиляции. Кросс-компиляция обычно используется для сборки компилятора и его инструментов для машины, отличной от той, которая используется для сборки. Строго говоря, это не требуется для LFS, так как машина, на которой будет работать новая система, та же, что и используемая для сборки. Но у кросс-компиляции есть большое преимущество, заключающееся в том, что все, что подвергается кросс-компиляции, не будет зависеть от окружения хоста.

О кросс-компиляции

[Примечание]

Примечание

Книга LFS не является руководством и не содержит общего руководства по созданию кросс (или собственного) тулчейна. Не используйте команды из книги для кросс-тулчейна, который планруете использовать для каких-либо других целей, кроме создания LFS, если у вас нет полного понимания, что вы делаете.

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

Давайте определим некоторые термины, используемые в этом контексте:

сборщик

это машина, на которой мы создаем программы. Обратите внимание, что этот компьютер упоминается как «хост» в других разделах.

хост

это машина/система, на которой будут выполняться встроенные программы. Обратите внимание, что используемое здесь значение слова «хост» отличается от того, которое прменяется в других разделах.

цель

используется только для компиляторов. Это машина, для которой компилятор создает код. Он может отличаться как от «сборщика», так и от «хоста».

В качестве примера представим следующий сценарий (иногда называемый «канадским крестом»): у нас есть компилятор на медленной машине, назовем ее машиной A, а компилятор ccA. У нас также есть быстрая машина (B), но без компилятора, и мы хотим создать код для другой медленной машины (C). Чтобы собрать компилятор для машины C, у нас будет три этапа:

Этап Сборщик Хост Цель Действие
1 A A B сборка кросс-компилятора cc1 с использованием ccA на машине A
2 A B C сборка кросс-компилятора cc2 с использованием cc1 на машине A
3 B C C сборка компилятора ccC с использованием cc2 на машине B

Затем все другие программы, необходимые для машины C, могут быть скомпилированы с помощью cc2 на быстрой машине B. Обратите внимание, что если B не может запускать программы, созданные для C, нет способа протестировать собранные программы, пока не будет запущена сама машина C. Например, для тестирования ccC мы можем добавить четвертый этап:

Этап Сборщик Хост Цель Действие
4 C C C пересобрать и протестировать ccC, используя себя на машине C

В приведенном выше примере только cc1 и cc2 являются кросс-компиляторами, то есть они создают код для машины, отличной от той, на которой они выполняются. Компиляторы ccA и ccC создают код для машины, на которой они выполняются. Такие компиляторы называются нативными компиляторами.

Реализация кросс-компиляции для LFS

[Примечание]

Примечание

Почти все системы сборки используют имена вида cpu-vendor-kernel-os, называемые машинным триплетом. Проницательный читатель может задаться вопросом, почему «триплет» относится к имени из четырех компонентов. Так сложилось исторически: изначально для однозначного обозначения машины было достаточно трех компонентов, но с появлением новых машин и систем этого оказалось недостаточно. Слово «триплет» осталось. Простой способ определить триплет вашей машины — запустить скрипт config.guess, который входит в исходный код многих пакетов. Распакуйте исходники binutils и запустите скрипт: ./config.guess, обратите внимание на выходные данные. Например, для 32-разрядного процессора Intel вывод будет i686-pc-linux-gnu. В 64-битной системе это будет x86_64-pc-linux-gnu.

Также обратите внимание на название динамического компоновщика платформы, часто называемого динамическим загрузчиком (не путать со стандартным компоновщиком ld, который является частью binutils). Динамический компоновщик, предоставляемый Glibc, находит и загружает общие библиотеки, необходимые программе, подготавливает программу к запуску, а затем запускает ее. Имя динамического компоновщика для 32-разрядной машины Intel — ld-linux.so.2, а для 64-разрядных систем — ld-linux-x86-64.so.2. Надежный способ определить имя динамического компоновщика — проверить случайный двоичный файл из хост-системы, выполнив следующую команду: readelf -l <name of binary> | grep interpreter и зафиксировать результат. Официальный источник, охватывающий все платформы, находится в файле shlib-versions в корне дерева исходного кода Glibc.

Чтобы сымитировать кросс-компиляцию в LFS, имя триплета хоста немного подкорректировали, изменив поле "vendor" в переменной LFS_TGT. Мы также используем параметр --with-sysroot при сборке кросс-компоновщика и кросс-компилятора, чтобы сообщить им, где найти необходимые файлы хоста. Это гарантирует, что ни одна из программ, созданных в Глава 6, не сможет ссылаться на библиотеки на машине сборки. Для корректной работы, обязательны всего два этапа, еще один рекомендуется для тестирования:

Этап Сборщик Хост Цель Действие
1 ПК ПК LFS сборка кросс-компилятора cc1 с использованием cc-pc на ПК
2 ПК LFS LFS сборка компилятора cc-lfs с использованием cc1 на ПК
3 LFS LFS LFS пересборка и тестирование cc-lfs, используя себя в lfs

В приведенной выше таблице «на ПК» означает, что команды выполняются на компьютере с использованием уже установленного дистрибутива. «В lfs» означает, что команды выполняются в chroot-окружении.

Теперь подробнее о кросс-компиляции: язык C - это не просто компилятор, также он определяет стандартную библиотеку. В этой книге используется библиотека GNU C под названием glibc. Эта библиотека должна быть скомпилирована для машины lfs, то есть с использованием кросс-компилятора cc1. Но сам компилятор использует внутреннюю библиотеку, реализующую сложные инструкции, недоступные в наборе инструкций ассемблера. Эта внутренняя библиотека называется libgcc, и для полноценной работы ее необходимо связать с библиотекой glibc! Кроме того, стандартная библиотека для C++ (libstdc++) также должна быть связана с glibc. Решение этой проблемы курицы и яйца состоит в том, чтобы сначала собрать деградированный libgcc на основе cc1, в котором отсутствуют некоторые функции, такие как потоки и обработка исключений, затем собрать glibc с использованием этого деградированного компилятора (сам glibc не деградирован), а затем собрать libstdc++. Но в этой библиотеке не будет хватать тех же функций, что и в libgcc.

Это не конец истории: вывод из предыдущего абзаца заключается в том, что cc1 не может собрать полнофункциональную libstdc++, но это единственный компилятор, доступный для сборки библиотек C/C++ на этапе 2! Конечно, компилятор, созданный на этапе 2, cc-lfs, сможет собрать эти библиотеки, но (1) система сборки GCC не знает, что ее можно использовать на ПК, и (2) ее использование на ПК сопряжено с риском привязки к библиотекам ПК, поскольку cc-lfs является родным компилятором. Поэтому мы должны собрать libstdc++ позже, в chroot.

Другие детали процесса

Кросс-компилятор будет установлен в отдельный каталог $LFS/tools, так как он не будет частью конечной системы.

Сначала устанавливается Binutils, потому что во время выполнения команды configure GCC и Glibc выполняются различные тесты функций на ассемблере и компоновщике, чтобы определить, какие программные функции следует включить или отключить. Это важнее, чем может показаться на первый взгляд. Неправильно настроенный GCC или Glibc может привести к незначительной поломке сборочных инструментов, где последствия такой поломки могут проявиться ближе к концу сборки всего дистрибутива. Сбой тестов обычно выявляет эту ошибку до того, как будет выполнено много дополнительной работы.

Binutils устанавливает свой ассемблер и компоновщик в двух местах: $LFS/tools/bin и $LFS/tools/$LFS_TGT/bin. Инструменты в одном месте жестко связаны с другими. Важным аспектом компоновщика является порядок поиска в библиотеке. Подробную информацию можно получить от ld, передав ей флаг --verbose. Например, $LFS_TGT-ld --verbose | grep SEARCH покажет текущие пути поиска и их порядок. Он показывает, какие файлы связаны с помощью ld, путем компляции фиктивной программы и передачи параметра --verbose компоновщику. Например, $LFS_TGT-gcc dummy.c -Wl,--verbose 2>&1 | grep succeeded покажет все файлы, успешно открытые во время компоновки.

Следующий устанавливаемый пакет — GCC. Пример того, что можно увидеть во время запуска configure:

checking what assembler to use... /mnt/lfs/tools/i686-lfs-linux-gnu/bin/as
checking what linker to use... /mnt/lfs/tools/i686-lfs-linux-gnu/bin/ld

Это важно по причинам, упомянутым выше. Также здесь демонстрируется, что сценарий настройки GCC не просматривает значеня переменной PATH, чтобы найти, какие инструменты использовать. Однако во время фактической работы самого gcc не обязательно используются одни и те же пути поиска. Чтобы узнать, какой стандартный компоновщик будет использовать gcc, запустите: $LFS_TGT-gcc -print-prog-name=ld.

Подробную информацию можно получить из gcc, передав ему параметр -v при компиляции фиктивной программы. Например, gcc -v dummy.c покажет подробную информацию об этапах препроцессора, компиляции и сборки, включая указанные в gcc пути поиска и их порядок.

Далее устанавливаются очищенные заголовочные файлы Linux API. Они позволяют стандартной библиотеке C (Glibc) взаимодействовать с функциями, предоставляемыми ядром Linux.

Следующий устанавливаемый пакет — Glibc. Наиболее важными при сборке Glibc являются компилятор, бинарные инструменты и заголовочные файлы ядра. С компилятором, как правило, не бывает проблем, поскольку Glibc всегда будет использовать компилятор, указанный в параметре --host, переданный скрипту configure; например, в нашем случае компилятором будет $LFS_TGT-gcc. С бинарными инструментами и заголовки ядра может быть немного сложнее. Поэтому мы не рискуем и используем доступные параметры конфигурации, чтобы обеспечить правильный выбор. После запуска configure проверьте содержимое файла config.make в каталоге сборки на наличие всех важных деталей. Обратите внимание на использование опции CC="$LFS_TGT-gcc" (с переменной $LFS_TGT) для управления используемыми бинарными инструментами и использование флагов -nostdinc и -isystem для управления включаемым путем поиска компилятора. Эти пункты подчеркивают важный аспект пакета Glibc — он очень самодостаточен с точки зрения своего механизма сборки и, как правило, не полагается на значения по умолчанию.

Как было сказано выше, затем компилируется стандартная библиотека C++, а затем в Глава 6 все программы, которые необходимо собрать. На этапе установки всех этих пакетов используется переменная DESTDIR, чтобы программы помещались в файловую систему LFS.

В конце Глава 6 устанавливается собственный компилятор lfs. Сначала собирается binutils-pass2 с той же переменной DESTDIR, что и другие программы, затем собирается второй проход GCC, опуская libstdc++ и другие не важные библиотеки. Из-за какой-то странной логики в сценарии настройки GCC CC_FOR_TARGET заканчивается как cc, когда хост совпадает с целью, но отличается от системы сборки. Поэтому значение CC_FOR_TARGET=$LFS_TGT-gcc явно указывается в параметрах конфигурации.

После входа в среду chroot в Глава 7 первой задачей является установка libstdc++. Затем выполняется установка временных программ, необходимых для правильной работы тулчейна. С этого момента основная цепочка инструментов является самодостаточной и автономной. В Глава 8 собираются, тестируются и устанавливаются окончательные версии всех пакетов, необходимых для полнофункциональной системы.