mirror of
https://github.com/Poltern/lfs-ru.git
synced 2024-10-19 12:10:32 +03:00
386 lines
32 KiB
XML
386 lines
32 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!DOCTYPE sect1 PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
|
||
<!ENTITY % general-entities SYSTEM "../general.ent">
|
||
%general-entities;
|
||
]>
|
||
|
||
<sect1 id="ch-tools-toolchaintechnotes" xreflabel="Технические примечания по сборочным инструментам">
|
||
<?dbhtml filename="toolchaintechnotes.html"?>
|
||
|
||
<title>Технические примечания по сборочным инструментам</title>
|
||
|
||
<para>В этом разделе объясняются причины и некоторые технические детали, лежащие в
|
||
основе сборки пакетов. Не обязательно сразу понимать все, что содержится в этом
|
||
разделе. Большая часть этой информации станет более понятной после выполнения фактической
|
||
сборки. Возвращайтесь и перечитывайте этот раздел в любое время по ходу сборки.</para>
|
||
|
||
<para>Основная задача <xref linkend="chapter-cross-tools"/> и <xref
|
||
linkend="chapter-temporary-tools"/> состоит в том, чтобы создать временную
|
||
область, содержащую заведомо исправный набор инструментов, которые можно
|
||
изолировать от хост-системы. Использовании команды <command>chroot</command>
|
||
в последующих главах, обеспечит чистую и безотказную сборку целевой системы LFS.
|
||
Процесс сборки разработан таким образом, чтобы свести к минимуму риски для
|
||
новых читателей и в то же время обеспечить наибольшую образовательную ценность.</para>
|
||
|
||
<para>Сборка инструментария основана на процессе <emphasis>кросс-компиляции</emphasis>.
|
||
Кросс-компиляция обычно используется для сборки компилятора и его инструментов для
|
||
машины, отличной от той, которая используется для сборки. Строго говоря, это не требуется
|
||
для LFS, так как машина, на которой будет работать новая система, та же, что и
|
||
используемая для сборки. Но у кросс-компиляции есть большое преимущество, заключающееся
|
||
в том, что все, что подвергается кросс-компиляции, не будет зависеть от окружения хоста.</para>
|
||
|
||
<sect2 id="cross-compile" xreflabel="About Cross-Compilation">
|
||
|
||
<title>О кросс-компиляции</title>
|
||
|
||
<note>
|
||
<para>
|
||
Книга LFS не является руководством и не содержит общего руководства по
|
||
созданию кросс (или собственного) тулчейна. Не используйте команды из книги
|
||
для кросс-тулчейна, который планируете использовать для каких-либо других целей,
|
||
кроме создания LFS, если у вас нет полного понимания, что вы делаете.
|
||
</para>
|
||
</note>
|
||
|
||
<para>Кросс-компиляция включает в себя некоторые концепции, которые сами по себе
|
||
заслуживают отдельного раздела. Хотя этот раздел можно пропустить при первом
|
||
чтении, возвращение к нему позже будет полезно для полного понимания процесса.</para>
|
||
|
||
<para>Давайте определим некоторые термины, используемые в этом контексте.</para>
|
||
|
||
<variablelist>
|
||
<varlistentry><term>сборщик</term><listitem>
|
||
<para>это машина, на которой мы собираем программы. Обратите внимание, что
|
||
этот компьютер упоминается как <quote>хост</quote> в других разделах.</para></listitem>
|
||
</varlistentry>
|
||
|
||
<varlistentry><term>хост</term><listitem>
|
||
<para>это машина/система, на которой будут выполняться встроенные программы.
|
||
Обратите внимание, что используемое здесь значение слова <quote>хост</quote>
|
||
отличается от того, которое применяется в других разделах.</para></listitem>
|
||
</varlistentry>
|
||
|
||
<varlistentry><term>цель</term><listitem>
|
||
<para>используется только для компиляторов. Это машина, для которой компилятор
|
||
создает код. Он может отличаться как от <quote>сборщика</quote>, так и
|
||
от <quote>хоста</quote>.</para></listitem>
|
||
</varlistentry>
|
||
|
||
</variablelist>
|
||
|
||
<para>В качестве примера представим следующий сценарий (иногда называемый
|
||
<quote>канадским крестом</quote>): у нас есть компилятор на медленной
|
||
машине, назовем ее машиной A и компилятор ccA. У нас также есть быстрая
|
||
машина (B), но без компилятора, и мы хотим создать код для другой медленной
|
||
машины (C). Чтобы собрать компилятор для машины C, у нас будет три этапа:</para>
|
||
|
||
<informaltable align="center">
|
||
<tgroup cols="5">
|
||
<colspec colnum="1" align="center" colwidth="50pt"/>
|
||
<colspec colnum="2" align="center" colwidth="50pt"/>
|
||
<colspec colnum="3" align="center" colwidth="50pt"/>
|
||
<colspec colnum="4" align="center" colwidth="50pt"/>
|
||
<colspec colnum="5" align="left" colwidth="300pt"/>
|
||
<thead>
|
||
<row><entry>Этап</entry><entry>Сборщик</entry><entry>Хост</entry>
|
||
<entry>Цель</entry><entry>Действие</entry></row>
|
||
</thead>
|
||
<tbody>
|
||
<row>
|
||
<entry>1</entry><entry>A</entry><entry>A</entry><entry>B</entry>
|
||
<entry>Сборка кросс-компилятора cc1 с использованием ccA на машине A</entry>
|
||
</row>
|
||
<row>
|
||
<entry>2</entry><entry>A</entry><entry>B</entry><entry>C</entry>
|
||
<entry>Сборка кросс-компилятора cc2 с использованием cc1 на машине A</entry>
|
||
</row>
|
||
<row>
|
||
<entry>3</entry><entry>B</entry><entry>C</entry><entry>C</entry>
|
||
<entry>Сборка компилятора ccC с использованием cc2 на машине B</entry>
|
||
</row>
|
||
</tbody>
|
||
</tgroup>
|
||
</informaltable>
|
||
|
||
<para>Затем все другие программы, необходимые для машины C, могут быть
|
||
скомпилированы с помощью cc2 на быстрой машине B. Обратите внимание, что
|
||
до тех пор, пока B не может запускать программы, собранные для C, нет
|
||
способа протестировать программы, пока не будет запущена сама машина C.
|
||
Например, чтобы запустить набор тестов на ccC мы можем добавить четвертый этап:</para>
|
||
|
||
<informaltable align="center">
|
||
<tgroup cols="5">
|
||
<colspec colnum="1" align="center" colwidth="50pt"/>
|
||
<colspec colnum="2" align="center" colwidth="50pt"/>
|
||
<colspec colnum="3" align="center" colwidth="50pt"/>
|
||
<colspec colnum="4" align="center" colwidth="50pt"/>
|
||
<colspec colnum="5" align="left" colwidth="300pt"/>
|
||
<thead>
|
||
<row><entry>Этап</entry><entry>Сборщик</entry><entry>Хост</entry>
|
||
<entry>Цель</entry><entry>Действие</entry></row>
|
||
</thead>
|
||
<tbody>
|
||
<row>
|
||
<entry>4</entry><entry>C</entry><entry>C</entry><entry>C</entry>
|
||
<entry>Пересобрать и протестировать ccC, используя ccC на машине C</entry>
|
||
</row>
|
||
</tbody>
|
||
</tgroup>
|
||
</informaltable>
|
||
|
||
<para>В приведенном выше примере только cc1 и cc2 являются кросс-компиляторами,
|
||
то есть они создают код для машины, отличной от той, на которой они выполняются.
|
||
Компиляторы ccA и ccC создают код для машины, на которой они выполняются. Такие
|
||
компиляторы называются <emphasis>нативными</emphasis> компиляторами.</para>
|
||
|
||
</sect2>
|
||
|
||
<sect2 id="lfs-cross">
|
||
<title>Реализация кросс-компиляции для LFS</title>
|
||
|
||
<note>
|
||
<para>Все кросс-компилируемые пакеты в этой книге используют систему сборки на основе
|
||
autoconf. Система сборки на основе autoconf принимает типы систем вида cpu-vendor-kernel-os,
|
||
называемые системным триплетом. Поскольку поле vendor часто не содержит значения,
|
||
autoconf позволяет вам опустить его.</para>
|
||
|
||
<para>Проницательный читатель может задаться вопросом, почему название <quote>триплет</quote>
|
||
применяется к имени из четырех компонентов. Поле kernel и поле os ранее применялись как единый
|
||
элемент: <quote>system</quote>. Такая форма с тремя полями все еще актуальна для некоторых
|
||
систем, например, <literal>x86_64-unknown-freebsd</literal>. Но две системы могут использовать
|
||
одно и то же ядро и все же быть слишком разными, чтобы использовать одинаковый триплет для их
|
||
описания. Например, Android, работающий на мобильном телефоне полностью отличается от Ubuntu,
|
||
работающей на ARM64 сервере, хотя они оба работают на одном и том же типе процессора (ARM64) и
|
||
с одним ядром (Linux).</para>
|
||
|
||
<para>Без слоя эмуляции вы не сможете запустить исполняемый файл c сервера на мобильном телефоне
|
||
и наоборот. Итак, поле <quote>system</quote> было разделено на поля kernel и os, чтобы однозначно
|
||
их интерпретировать. В нашем примере Android обозначается как
|
||
<literal>aarch64-unknown-linux-android</literal>, а Ubuntu
|
||
<literal>aarch64-unknown-linux-gnu</literal>.</para>
|
||
|
||
<para>Слово <quote>триплет</quote> сохранилось в лексиконе. Простой способ определить
|
||
триплет вашей машины — запустить скрипт <command>config.guess</command>, который
|
||
входит в исходный код многих пакетов. Распакуйте исходники binutils и запустите
|
||
скрипт: <userinput>./config.guess</userinput>, обратите внимание на вывод.
|
||
Например, для 32-разрядного процессора Intel вывод будет
|
||
<emphasis>i686-pc-linux-gnu</emphasis>. В 64-битной системе это будет
|
||
<emphasis>x86_64-pc-linux-gnu</emphasis>. В большинстве систем Linux используют еще более
|
||
простую команду <command>gcc -dumpmachine</command>, которая предоставит вам аналогичную
|
||
информацию.</para>
|
||
|
||
<para>Вы также должны знать имя динамического компоновщика платформы,
|
||
часто называемого динамическим загрузчиком (не путать со стандартным компоновщиком
|
||
<command>ld</command>, который является частью binutils). Динамический компоновщик,
|
||
предоставляемый glibc, находит и загружает общие библиотеки, необходимые программе,
|
||
подготавливает программу к запуску, а затем запускает ее. Имя динамического
|
||
компоновщика для 32-разрядной машины Intel — <filename
|
||
class="libraryfile">ld-linux.so.2</filename>, а для 64-разрядных систем — <filename
|
||
class="libraryfile">ld-linux-x86-64.so.2</filename>. Надежный способ определить
|
||
имя динамического компоновщика — проверить случайный двоичный файл из хост-системы,
|
||
выполнив следующую команду: <userinput>readelf -l
|
||
<имя исполняемого файла> | grep interpreter</userinput> и зафиксировать результат.
|
||
Официальный источник, охватывающий все платформы, находится в файле
|
||
<filename>shlib-versions</filename> в корне дерева исходного кода glibc.</para>
|
||
</note>
|
||
|
||
<para>Чтобы сымитировать кросс-компиляцию в LFS, имя триплета хоста немного
|
||
подкорректировали, изменив поле "vendor" в переменной <envar>LFS_TGT</envar>
|
||
таким образом, чтобы оно указывало "lfs".
|
||
Мы также используем параметр <parameter>--with-sysroot</parameter> при сборке
|
||
кросс-компоновщика и кросс-компилятора, чтобы сообщить им, где найти необходимые
|
||
файлы хоста. Это гарантирует, что ни одна из программ, входящих в <xref
|
||
linkend="chapter-temporary-tools"/>, не сможет ссылаться на библиотеки на машине
|
||
сборки. Для корректной работы, обязательны всего два этапа, еще один
|
||
рекомендуется для тестирования:</para>
|
||
|
||
<informaltable align="center">
|
||
<tgroup cols="5">
|
||
<colspec colnum="1" align="center" colwidth="50pt"/>
|
||
<colspec colnum="2" align="center" colwidth="50pt"/>
|
||
<colspec colnum="3" align="center" colwidth="50pt"/>
|
||
<colspec colnum="4" align="center" colwidth="50pt"/>
|
||
<colspec colnum="5" align="left" colwidth="300pt"/>
|
||
<thead>
|
||
<row><entry>Этап</entry><entry>Сборщик</entry><entry>Хост</entry>
|
||
<entry>Цель</entry><entry>Действие</entry></row>
|
||
</thead>
|
||
<tbody>
|
||
<row>
|
||
<entry>1</entry><entry>ПК</entry><entry>ПК</entry><entry>LFS</entry>
|
||
<entry>Сборка кросс-компилятора cc1 с использованием cc-pc на ПК</entry>
|
||
</row>
|
||
<row>
|
||
<entry>2</entry><entry>ПК</entry><entry>LFS</entry><entry>LFS</entry>
|
||
<entry>Сборка компилятора cc-lfs с использованием cc1 на ПК</entry>
|
||
</row>
|
||
<row>
|
||
<entry>3</entry><entry>LFS</entry><entry>LFS</entry><entry>LFS</entry>
|
||
<entry>Пересборка и тестирование cc-lfs, используя cc-lfs в lfs</entry>
|
||
</row>
|
||
</tbody>
|
||
</tgroup>
|
||
</informaltable>
|
||
|
||
<para>В приведенной выше таблице <quote>ПК</quote> означает, что команды
|
||
выполняются на компьютере с использованием уже установленного дистрибутива.
|
||
<quote>В lfs</quote> означает, что команды выполняются в chroot-окружении.</para>
|
||
|
||
<para>Это еще не конец истории. Язык С - это не просто компилятор;
|
||
также он определяет стандартную библиотеку. В этой книге используется библиотека
|
||
GNU C под названием glibc (есть альтернативный вариант - "musl").
|
||
Эта библиотека должна быть скомпилирована для машины
|
||
lfs, то есть с использованием кросс-компилятора cc1. Но сам компилятор использует
|
||
внутреннюю библиотеку, реализующую сложные инструкции, недоступные в наборе
|
||
инструкций ассемблера. Эта внутренняя библиотека называется libgcc, и для
|
||
полноценной работы ее необходимо связать с библиотекой glibc! Кроме того,
|
||
стандартная библиотека для C++ (libstdc++) также должна быть связана с glibc.
|
||
Решение этой проблемы курицы и яйца состоит в том, чтобы сначала собрать
|
||
деградированную libgcc на основе cc1, в которой отсутствуют некоторые функциональные
|
||
возможности, такие как потоки и обработка исключений, затем собрать glibc с использованием
|
||
этого деградированного компилятора (сама glibc не деградирована), а затем собрать
|
||
libstdc++. В этой последней библиотеке будет не хватать некоторых функциональных
|
||
возможностей libgcc.</para>
|
||
|
||
<para>Выводом из предыдущего абзаца является то, что cc1
|
||
не может собрать полнофункциональную libstdc++ с деградированной libgcc, но это
|
||
единственный компилятор, доступный для сборки библиотек C/C++ на этапе 2. Есть
|
||
две причины, по которым мы не используем сразу компилятор cc-lfs,
|
||
собранный на этапе 2, для сборки этих библиотек.</para>
|
||
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>
|
||
Вообще говоря, cc-lfs не может работать на ПК (хост-системе). Хотя триплеты для ПК и
|
||
LFS совместимы друг с другом, исполняемый файл для lfs должен зависеть от
|
||
glibc-&glibc-version;; хост-дистрибутив может использовать либо другую реализацию
|
||
libc (например, musl), либо предыдущий выпуск glibc (например, glibc-2.13).
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
Даже если cc-lfs может работать на ПК, его использование на ПК сопряжено с риском
|
||
привязки к библиотекам ПК, так как cc-lfs является родным компилятором.
|
||
</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
|
||
<para>Поэтому, когда мы собираем gcc этап 2, мы даем указание системе сборки пересобрать
|
||
libgcc и libstdc++ с помощью cc1, но мы связываем libstdc++ с новой пересобранной libgcc
|
||
вместо старой, деградированной. Это делает пересобранную библиотеку libstdc++
|
||
полностью функциональной.</para>
|
||
|
||
<para>В &ch-final; (или <quote>этап 3</quote>) собраны все пакеты, необходимые для системы
|
||
LFS. Даже если пакет уже был установлен в системе LFS в предыдущей главе, мы все равно
|
||
пересобираем пакет. Основная причина пересборки этих пакетов состоит в том, чтобы сделать
|
||
их стабильными: если мы переустанавливаем пакет LFS в готовой системе LFS, содержимое пакета
|
||
должно совпадать с содержимым того же пакета при первой установке в &ch-final;. Временные
|
||
пакеты, установленные в &ch-tmp-cross; или &ch-tmp-chroot; не могут удовлетворять
|
||
этому требованию, потому что некоторые из них собраны без необязательных зависимостей и autoconf
|
||
не может выполнить некоторые проверки функций в &ch-tmp-cross; из-за кросс-компиляции, в результате
|
||
чего во временных пакетах отсутствуют дополнительные функции или используются не оптимальные
|
||
процедуры кода. Кроме того, второстепенной причиной для пересборки пакетов является выполнение
|
||
тестов.</para>
|
||
|
||
</sect2>
|
||
|
||
<sect2 id="other-details">
|
||
|
||
<title>Другие детали процесса</title>
|
||
|
||
<para>Кросс-компилятор будет установлен в отдельный каталог <filename
|
||
class="directory">$LFS/tools</filename>, так как он не будет частью конечной системы.</para>
|
||
|
||
<para>Сначала устанавливается Binutils, потому что во время выполнения команды
|
||
<command>configure</command> gcc и glibc выполняются различные тесты функций
|
||
на ассемблере и компоновщике, чтобы определить, какие программные функции
|
||
следует включить или отключить. Это важнее, чем может показаться на первый взгляд.
|
||
Неправильно настроенный gcc или glibc может привести к незначительной поломке
|
||
сборочных инструментов, где последствия такой поломки могут проявиться ближе
|
||
к концу сборки всего дистрибутива. Сбой тестов обычно выявляет эту ошибку до того,
|
||
как будет выполнено много дополнительной работы.
|
||
</para>
|
||
|
||
<para>Binutils устанавливает свой ассемблер и компоновщик в двух местах:
|
||
<filename class="directory">$LFS/tools/bin</filename> и <filename
|
||
class="directory">$LFS/tools/$LFS_TGT/bin</filename>. Инструменты в одном
|
||
месте жестко связаны с другими. Важным аспектом компоновщика является порядок
|
||
поиска в библиотеке. Подробную информацию можно получить от <command>ld</command>,
|
||
передав ей флаг <parameter>--verbose</parameter>. Например,
|
||
<command>$LFS_TGT-ld --verbose | grep SEARCH</command> покажет текущие пути
|
||
поиска и их порядок. Он показывает, какие файлы связаны с помощью
|
||
<command>ld</command>, путем компиляции фиктивной программы и передачи
|
||
параметра <parameter>--verbose</parameter> компоновщику. Например,
|
||
<command>$LFS_TGT-gcc dummy.c -Wl,--verbose 2>&1 | grep succeeded</command>
|
||
покажет все файлы, успешно открытые во время компоновки.</para>
|
||
|
||
<para>Следующий устанавливаемый пакет — gcc. Пример того, что можно увидеть
|
||
во время запуска <command>configure</command>:</para>
|
||
|
||
<screen><computeroutput>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</computeroutput></screen>
|
||
|
||
<para>Это важно по причинам, упомянутым выше. Также здесь демонстрируется, что
|
||
сценарий настройки gcc не просматривает значения переменной PATH, чтобы найти,
|
||
какие инструменты использовать. Однако во время фактической работы самого
|
||
<command>gcc</command> не обязательно используются одни и те же пути поиска.
|
||
Чтобы узнать, какой стандартный компоновщик будет использовать <command>gcc</command>,
|
||
запустите: <command>$LFS_TGT-gcc -print-prog-name=ld</command>.</para>
|
||
|
||
<para>Подробную информацию можно получить из <command>gcc</command>, передав
|
||
ему параметр <parameter>-v</parameter> при компиляции фиктивной программы.
|
||
Например, <command>gcc -v dummy.c</command> покажет подробную информацию об
|
||
этапах препроцессора, компиляции и сборки, включая указанные в <command>gcc</command>
|
||
пути поиска и их порядок.</para>
|
||
|
||
<para>Далее устанавливаются очищенные заголовочные файлы Linux API. Они позволяют
|
||
стандартной библиотеке C (Glibc) взаимодействовать с функциями, предоставляемыми
|
||
ядром Linux.</para>
|
||
|
||
<para>Следующий устанавливаемый пакет — glibc. Наиболее важными при сборке glibc
|
||
являются компилятор, бинарные инструменты и заголовочные файлы ядра. С компилятором,
|
||
как правило, не бывает проблем, поскольку glibc всегда будет использовать компилятор,
|
||
указанный в параметре <parameter>--host</parameter>, переданный скрипту configure;
|
||
например, в нашем случае компилятором будет <command>$LFS_TGT-gcc</command>. С бинарными
|
||
инструментами и заголовки ядра может быть немного сложнее. Поэтому мы не рискуем и
|
||
используем доступные параметры конфигурации, чтобы обеспечить правильный выбор.
|
||
После запуска <command>configure</command> проверьте содержимое файла
|
||
<filename>config.make</filename> в каталоге <filename
|
||
class="directory">сборки</filename> на наличие всех важных деталей. Обратите внимание
|
||
на использование опции <parameter>CC="$LFS_TGT-gcc"</parameter>
|
||
(с переменной <envar>$LFS_TGT</envar>) для управления используемыми бинарными
|
||
инструментами и использование флагов <parameter>-nostdinc</parameter> и
|
||
<parameter>-isystem</parameter> для управления включаемым путем поиска компилятора.
|
||
Эти пункты подчеркивают важный аспект пакета glibc — он очень самодостаточен
|
||
с точки зрения своего механизма сборки и, как правило, не полагается на значения по
|
||
умолчанию.</para>
|
||
|
||
<para>Как было сказано выше, затем компилируется стандартная библиотека C++, а
|
||
затем в <xref linkend="chapter-temporary-tools"/> все остальные программы, которым
|
||
необходимо разрешить проблему циклических зависимостей во время сборки. На этапе
|
||
установки всех этих пакетов используется переменная DESTDIR, для принудительной
|
||
установки в файловую систему LFS.</para>
|
||
|
||
<para>В конце <xref linkend="chapter-temporary-tools"/> устанавливается
|
||
собственный компилятор lfs. Сначала собирается binutils с той же
|
||
переменной <envar>DESTDIR</envar>, что и другие программы, затем повторно собирается
|
||
gcc, без сборки некоторых некритических библиотек. Из-за
|
||
какой-то странной логики в сценарии настройки GCC <envar>CC_FOR_TARGET</envar>
|
||
заканчивается как <command>cc</command>, когда хост совпадает с целью, но
|
||
отличается от системы сборки. Поэтому значение
|
||
<parameter>CC_FOR_TARGET=$LFS_TGT-gcc</parameter> явно указывается в
|
||
параметрах конфигурации.</para>
|
||
|
||
<para>После входа в среду chroot в <xref
|
||
linkend="chapter-chroot-temporary-tools"/> первой задачей является установка
|
||
libstdc++. Затем выполняется установка временных программ, необходимых для
|
||
правильной работы тулчейна. С этого момента основной набор инструментов является
|
||
самодостаточным и автономным. В <xref linkend="chapter-building-system"/>
|
||
собираются, тестируются и устанавливаются окончательные версии всех пакетов,
|
||
необходимых для полнофункциональной системы.</para>
|
||
|
||
</sect2>
|
||
|
||
</sect1>
|