lfs-ru/part3intro/toolchaintechnotes.xml

331 lines
26 KiB
XML
Raw Normal View History

<?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="Toolchain Technical Notes">
<?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"/>
<colspec colnum="2" align="center"/>
<colspec colnum="3" align="center"/>
<colspec colnum="4" align="center"/>
<colspec colnum="5" align="left"/>
<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"/>
<colspec colnum="2" align="center"/>
<colspec colnum="3" align="center"/>
<colspec colnum="4" align="center"/>
<colspec colnum="5" align="left"/>
<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, используя себя на машине C</entry>
</row>
</tbody>
</tgroup>
</informaltable>
<para>В приведенном выше примере только cc1 и cc2 являются кросс-компиляторами,
то есть они создают код для машины, отличной от той, на которой они выполняются.
Компиляторы ccA и ccC создают код для машины, на которой они выполняются. Такие
компиляторы называются <emphasis>нативными</emphasis> компиляторами.</para>
</sect2>
<sect2 id="lfs-cross">
<title>Реализация кросс-компиляции для LFS</title>
<note>
<para>Почти все системы сборки используют имена вида cpu-vendor-kernel-os,
называемые машинным триплетом. Проницательный читатель может задаться
вопросом, почему <quote>триплет</quote> относится к имени из четырех компонентов.
Так сложилось исторически: изначально для однозначного обозначения машины было
достаточно трех компонентов, но с появлением новых машин и систем этого оказалось
недостаточно. Слово <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>.</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
&lt;name of binary&gt; | grep interpreter</userinput> и зафиксировать результат.
Официальный источник, охватывающий все платформы, находится в файле
<filename>shlib-versions</filename> в корне дерева исходного кода Glibc.</para>
</note>
<para>Чтобы сымитировать кросс-компиляцию в LFS, имя триплета хоста немного
подкорректировали, изменив поле &quot;vendor&quot; в переменной <envar>LFS_TGT</envar>.
Мы также используем параметр <parameter>--with-sysroot</parameter> при сборке
кросс-компоновщика и кросс-компилятора, чтобы сообщить им, где найти необходимые
файлы хоста. Это гарантирует, что ни одна из программ, созданных в <xref
linkend="chapter-temporary-tools"/>, не сможет ссылаться на библиотеки на машине
сборки. Для корректной работы, обязательны всего два этапа, еще один
рекомендуется для тестирования:</para>
<informaltable align="center">
<tgroup cols="5">
<colspec colnum="1" align="center"/>
<colspec colnum="2" align="center"/>
<colspec colnum="3" align="center"/>
<colspec colnum="4" align="center"/>
<colspec colnum="5" align="left"/>
<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, используя себя в lfs</entry>
</row>
</tbody>
</tgroup>
</informaltable>
<para>В приведенной выше таблице <quote>на ПК</quote> означает, что команды
выполняются на компьютере с использованием уже установленного дистрибутива.
<quote>В lfs</quote> означает, что команды выполняются в chroot-окружении.</para>
<para>Теперь подробнее о кросс-компиляции: язык C - это не просто компилятор,
также он определяет стандартную библиотеку. В этой книге используется библиотека
GNU C под названием glibc. Эта библиотека должна быть скомпилирована для машины
lfs, то есть с использованием кросс-компилятора cc1. Но сам компилятор использует
внутреннюю библиотеку, реализующую сложные инструкции, недоступные в наборе
инструкций ассемблера. Эта внутренняя библиотека называется libgcc, и для
полноценной работы ее необходимо связать с библиотекой glibc! Кроме того,
стандартная библиотека для C++ (libstdc++) также должна быть связана с glibc.
Решение этой проблемы курицы и яйца состоит в том, чтобы сначала собрать
деградированный libgcc на основе cc1, в котором отсутствуют некоторые функции,
такие как потоки и обработка исключений, затем собрать glibc с использованием
этого деградированного компилятора (сам glibc не деградирован), а затем собрать
libstdc++. Но в этой библиотеке не будет хватать тех же функций, что и в libgcc.</para>
<para>Это не конец истории: вывод из предыдущего абзаца заключается в том, что cc1
не может собрать полнофункциональную libstdc++, но это единственный компилятор,
доступный для сборки библиотек C/C++ на этапе 2! Конечно, компилятор, созданный на
этапе 2, cc-lfs, сможет собрать эти библиотеки, но (1) система сборки GCC не
знает, что ее можно использовать на ПК, и (2) ее использование на ПК сопряжено
с риском привязки к библиотекам ПК, поскольку cc-lfs является родным компилятором.
Поэтому мы должны собрать libstdc++ позже, в chroot.</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&gt;&amp;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 &mdash; он очень самодостаточен
с точки зрения своего механизма сборки и, как правило, не полагается на значения по
умолчанию.</para>
<para>Как было сказано выше, затем компилируется стандартная библиотека C++, а
затем в <xref linkend="chapter-temporary-tools"/> все программы, которые необходимо
собрать. На этапе установки всех этих пакетов используется переменная DESTDIR,
чтобы программы помещались в файловую систему LFS.</para>
<para>В конце <xref linkend="chapter-temporary-tools"/> устанавливается
собственный компилятор lfs. Сначала собирается binutils-pass2 с той же
переменной <envar>DESTDIR</envar>, что и другие программы, затем собирается
второй проход GCC, опуская libstdc++ и другие не важные библиотеки. Из-за
какой-то странной логики в сценарии настройки 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>