lfs-ru/part3intro/toolchaintechnotes.xml

333 lines
27 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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" 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, используя себя на машине 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" 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, используя себя в 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>