Pull to refresh

LFS: Темная сторона Силы. Часть 1

Reading time 21 min
Views 70K

Предисловие


Для того чтобы установить на свой компьютер Linux и начать его использовать для конкретных задач существует масса способов. Выбор дистрибутивов чрезвычайно широк, на любой вкус и цвет — и «для домохозяек» и для продвинутых пользвателей, допускающие любой уровень кастомизации, в том числе и сборку из исходников под конкретное железо. Установка системы в принципе доступна любому, мало-мальски грамотному пользователю ПК. И, если не погружаться в популярные по сей дей холивары на тему «Linux vs Другая ОС», то и спользование данной системы не требует знаний, которые были обязательными для новоиспеченного линуксоида скажем десять лет назад. С моей, глубоко субъективной точки зрения, за более чем десять лет, которые я наблюдаю за развитием этой системы, линукс стал более дружественен к новичкам и избавился от многих проблем, присущих ему в прошлом. И это хорошо.

Пингвины ручной сборки...


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

Не буду витеивато излагать истории о том «как космические корабли бороздят… и когда Земля была огненным шаром...». Я отвечу на поставленный вопрос исходя из своей позиции — я собираю LFS потому, что мне просто интересно это сделать. Я вижу в этом процессе хорошую возможность заглянуть «под капот» системы. Допускаю, что этот путь сложно назвать оптимальным. Тем не менее, данная статья, и последующие за ней будут посвящены процессу ручной сборки Linux-системы. Данные статьи не будут переводом документации по сборке — в этом нет особой нужды. Акценты будут расставлены на специфике и ньюансах процесса, с которыми пришлось столкнуться автору лично. Будем считать этот цикл чем-то вроде дневника неофита.


1. Системные требования


Для сборки LFS необходим компьютер с установленной на нем ОС GNU/Linux. В принципе, подойдет любой дистрибутив. Допустим я, на своих машинах, использую Arch Linux. Что касается дистрибутивов основанных на Debian (Linux Mint, Ubuntu и т.п.), они разумеется тоже подходят, но спешу обратить внимание на следующее — на одном из начальных этапов работы, при инициализации переменных окружения, возникает предупреждение командной оболочки

$ source ~/.bash_profile
  dircolors: no SHELL environment variable, and no shell type option given

Это предупреждение я получал в Linux Mint 17 связано оно с косвенным вызовом dircolors (команда, раскрашивающая терминал) из /etc/profile при повторном входе пользователя и обновлении переменных окружения командой source. Оно не является критичным и его можно игнорировать.

В хостовой Linux-системе должны быть установленны пакеты, перечисленные в официальных системных требованиях. Большинство этих пакетов входят в систему «из коробки» или являются устанавливаемыми дополнительно средствами разработчика. В любом случает они легко устанавливаются из официального репозитория конкретного дистрибутива. Хорошим вариантом является использование LiveCD типа Knoppix, тогда установить LFS можно на машину, не содержащую никакой ОС. В этом смысле выбор остается за вами.

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

2. Документация по сборке минимальной системы


Вся необходимая документация сосредоточена в книге Linux Form Scratch (ссылка ведет на онлайн-версию крайнего стабильного релиза). Первый совет который хотелось бы дать новичку — брать последнюю стабильную версию на английском языке. Существуют русскоязычные переводы, однако они всегда отстают от актуальной версии. Кроме того сслыка на самую свежую русскую версию LFS 6.0 ведет в пустоту, а единственный доступный с оффициального сайта перевод касается LFS 5.0 но он, мягко говоря, устарел. Ядро системы и ПО проекта GNU претерпевают регулярные изменения, к тому же те версии пакетов, которые описаны в старых версиях LFS не всегда доступны для скачивания, а если и доступны, то их поиск придется производить вручную. Так что наш выбор — актуальная англоязычная версия LFS.

Примечание: пока писалась данная статья, в вики-статье от LFS была найдена ссылка на перевод LFS 7.3, что уже ближе к истине. Однако перевод этот еще не закончен — большая часть книги на английском.

Так же хочу предупредить читателя, что использование Current Development вресии книги так же может обернуться непредвиденными трудностями. Эту версию можно получить из ежедневно обновляемого репозитория SVN. Книга скачивается командой

$ mkdir ~/LFS && cd ~/LFS
$ svn co svn://svn.linuxfromscratch.org/LFS/trunk/BOOK/

И легко собирается, например так (для сборки html-версии необходим пакет tidyhtml)
$ cd BOOK
$ mkdir ../html
$ make BASEDIR=../html


Данная версия является самой свежей, ориентированной на использование недавно вышедшей «skynet-версии» ядра Linux 4.0.

Причем тут SkyNet?
А вот причем:



Первая проблема возникает уже при проверках контрольных сумм скачанных пакетов

$ md5sum -c md5sums
.
.
lfs-bootscripts-20150222.tar.bz2: ПОВРЕЖДЁН
.
.
md5sum: ПРЕДУПРЕЖДЕНИЕ: НЕ совпала 1 вычисленная контрольная сумма


Указанный архив распаковывается, размеры файлов соотвествуют истине. Возможно в файл md5sums, содержащем список CRS просто закралась ошибка. Однако, дабы не рисковать, лучше взять стабильную версию книги.

3. Подготовка почвы


Прежде всего, для развертывания LFS, нам нужно дисковое пространство. Это может быть один раздел (или несколько разделов) на HDD, а может быть и каталог в хостовой файловой системе. Я остановился на варианте с разделом на HDD виртуальной машины, на которую предварительно водрузил 64-битный Arch Linux.

Минимальный размер потребного дискового пространства — 4 Гб. Я создал логический раздел размером в 40 Гб, отформатировав его в EXT4. Данный раздел необходимо смонтировать в хост-системе. Точку монтирования удобно сохранить в специальной переменной среды

# export LFS=/mnt/lfs

ибо по этому пути нам придется обращаться очень часто. Монтируем раздел командами (в моем случае это раздел sda6, в вашем, наверняка, будет иначе)

# mkdir -v $LFS
# mount /dev/sda6 $LFS


Теперь нам потребуется создать два каталога: $LFS/sources — для хранения тарболов с исходниками; $LFS/tools — для размещения временных истументальных средств сборки системы

# mkdir -v $LFS/sources
# mkdir -v $LFS/tools

Устанавливаем права на директорию $LFS/sources: разрешена запись (+w) всем пользователям (a) с возможностью удаления исключительно владельцем каталога (t)

# chmod -v a+wt $LFS/sources

в каталог $LFS/sources загружаем два файла — список скачиваемых пакетов wget-list и список контрольных сумм пакетов md5sums

# wget http://www.linuxfromscratch.org/lfs/view/stable/wget-list --directory-prefix=$LFS/sources
# wget http://www.linuxfromscratch.org/lfs/view/stable/md5sums --directory-prefix=$LFS/sources

ключ --directory-prefix указывает путь, по которому следует поместить скачанный файл. После этого приступаем к закачке исходников всего и вся, что нам предстоит установить

# wget --input-file=$LFS/sources/wget-list --continue --directory-prefix=$LFS/sources

ключ --input-file указывает на файл, содержащий список URL загружаемых пакетов; --continue — включает докачку частично скачанных файлов, в случае если соединение с сетью было прервано. Будет загружено около 325 Мб архивов с исходниками требуемого ПО а так же патчи, необходимые для сборки. После загрузки необходимо проверить целостность скачанных пакетов

# pushd $LFS/source
# md5sum -c md5sums
# popd

Сама проверка — вызов утилиты md5sum, выполняющей подсчет контрольных сумм всех файлов, содержащихся в передаваемом в качестве параметра списке md5sums и сверяющей результат со значением, указанном в том же списке. Результат, в случае успеха будет выглядеть так

Результат проверки целостности пакетов
acl-2.2.52.src.tar.gz: ЦЕЛ
attr-2.4.47.src.tar.gz: ЦЕЛ
autoconf-2.69.tar.xz: ЦЕЛ
automake-1.15.tar.xz: ЦЕЛ
bash-4.3.30.tar.gz: ЦЕЛ
bc-1.06.95.tar.bz2: ЦЕЛ
binutils-2.25.tar.bz2: ЦЕЛ
bison-3.0.4.tar.xz: ЦЕЛ
bzip2-1.0.6.tar.gz: ЦЕЛ
check-0.9.14.tar.gz: ЦЕЛ
coreutils-8.23.tar.xz: ЦЕЛ
dejagnu-1.5.2.tar.gz: ЦЕЛ
diffutils-3.3.tar.xz: ЦЕЛ
eudev-2.1.1.tar.gz: ЦЕЛ
eudev-2.1.1-manpages.tar.bz2: ЦЕЛ
e2fsprogs-1.42.12.tar.gz: ЦЕЛ
expat-2.1.0.tar.gz: ЦЕЛ
expect5.45.tar.gz: ЦЕЛ
file-5.22.tar.gz: ЦЕЛ
findutils-4.4.2.tar.gz: ЦЕЛ
flex-2.5.39.tar.bz2: ЦЕЛ
gawk-4.1.1.tar.xz: ЦЕЛ
gcc-4.9.2.tar.bz2: ЦЕЛ
gdbm-1.11.tar.gz: ЦЕЛ
gettext-0.19.4.tar.xz: ЦЕЛ
glibc-2.21.tar.xz: ЦЕЛ
gmp-6.0.0a.tar.xz: ЦЕЛ
gperf-3.0.4.tar.gz: ЦЕЛ
grep-2.21.tar.xz: ЦЕЛ
groff-1.22.3.tar.gz: ЦЕЛ
grub-2.02~beta2.tar.xz: ЦЕЛ
gzip-1.6.tar.xz: ЦЕЛ
iana-etc-2.30.tar.bz2: ЦЕЛ
inetutils-1.9.2.tar.gz: ЦЕЛ
intltool-0.50.2.tar.gz: ЦЕЛ
iproute2-3.19.0.tar.xz: ЦЕЛ
kbd-2.0.2.tar.gz: ЦЕЛ
kmod-19.tar.xz: ЦЕЛ
less-458.tar.gz: ЦЕЛ
lfs-bootscripts-20150222.tar.bz2: ЦЕЛ
libcap-2.24.tar.xz: ЦЕЛ
libpipeline-1.4.0.tar.gz: ЦЕЛ
libtool-2.4.6.tar.xz: ЦЕЛ
linux-3.19.tar.xz: ЦЕЛ
m4-1.4.17.tar.xz: ЦЕЛ
make-4.1.tar.bz2: ЦЕЛ
man-db-2.7.1.tar.xz: ЦЕЛ
man-pages-3.79.tar.xz: ЦЕЛ
mpc-1.0.2.tar.gz: ЦЕЛ
mpfr-3.1.2.tar.xz: ЦЕЛ
ncurses-5.9.tar.gz: ЦЕЛ
patch-2.7.4.tar.xz: ЦЕЛ
perl-5.20.2.tar.bz2: ЦЕЛ
pkg-config-0.28.tar.gz: ЦЕЛ
procps-ng-3.3.10.tar.xz: ЦЕЛ
psmisc-22.21.tar.gz: ЦЕЛ
readline-6.3.tar.gz: ЦЕЛ
sed-4.2.2.tar.bz2: ЦЕЛ
shadow-4.2.1.tar.xz: ЦЕЛ
sysklogd-1.5.1.tar.gz: ЦЕЛ
sysvinit-2.88dsf.tar.bz2: ЦЕЛ
tar-1.28.tar.xz: ЦЕЛ
tcl8.6.3-src.tar.gz: ЦЕЛ
texinfo-5.2.tar.xz: ЦЕЛ
tzdata2015a.tar.gz: ЦЕЛ
udev-lfs-20140408.tar.bz2: ЦЕЛ
util-linux-2.26.tar.xz: ЦЕЛ
vim-7.4.tar.bz2: ЦЕЛ
XML-Parser-2.44.tar.gz: ЦЕЛ
xz-5.2.0.tar.xz: ЦЕЛ
zlib-1.2.8.tar.xz: ЦЕЛ
bash-4.3.30-upstream_fixes-1.patch: ЦЕЛ
bc-1.06.95-memory_leak-1.patch: ЦЕЛ
bzip2-1.0.6-install_docs-1.patch: ЦЕЛ
coreutils-8.23-i18n-1.patch: ЦЕЛ
glibc-2.21-fhs-1.patch: ЦЕЛ
kbd-2.0.2-backspace-1.patch: ЦЕЛ
mpfr-3.1.2-upstream_fixes-3.patch: ЦЕЛ
readline-6.3-upstream_fixes-3.patch: ЦЕЛ
sysvinit-2.88dsf-consolidated-1.patch: ЦЕЛ

Отдельное внимание я обратил на пару команд pushd/popd. Хотя я и убежденный линуксоид, давно использующий данную систему, эти внутренние команды bash оказались для меня в новинку. Тем не менее, они очень удобны, когда приходится часто скакать по различным точкам файловой системы с последующим возвратом назад. Команда pushd временно переводит вас в указанный в качетве параметра каталог. Возврат назад происходит командой popd. При этом маршрут, по которому вы прыгаете организуется в виде стека и вам не надо заново вводить путь, по которому следует вернуться назад.

Теперь у нас имеется всё необходимое, чтобы начать сборку, но осталось выполнить последние подготовительные операции

4. Настройка рабочего окружения


Прежде всего создадим символическую ссылку на каталог $LFS/tools в корне хост-системы

# ln -sv $LFS/tools /

Для чего это требуется? Вообще, зачем нужны символические ссылки? Они создаются для обеспечения единообразия доступа к определенным точкам файловой системы. В данный момент, в хост системы путь к этому каталогу выглядит как /mnt/lfs/tools. Когда мы перейдем во временную систему, корень которой будет располагаться по пути /mnt/lfs, в этой временной системе содержимое каталога будет доступно по пути /tools. Те первичные инструменты, которые мы соберем будут обращаться к данному каталогу по пути /tools в не зависимости от того где они будут запускаться — этот путь будет жестко «вшит» в бинарники после компиляции. Поэтому создавая указанную ссылку, мы обеспечиваем возможность обращения к данному каталогу по пути /tools вне зависимости от текущего расположения корня системы. В противном случае сразу после перехода во временную систему нас ждет сбой при первой же компиляции.

Теперь создадим новую группу и нового пользователя, от имени которого будем работать на первом этапе сборки временной системы. Работать от root точно не следует — вы, например при конфигурировании устанавливаемого пакета, можете перепутать или забыть задать префикс для каталога инсталляции и установкой собранного пакета в хост-систему поломать её (или осложнить работу пакетного менеджера в будущем). Работа от непривилегированного пользователя, у которого заданы права на запись только в определенные каталоги, позволит вам избежать неприятностей.

# groupadd lfs
# useradd -s /bin/bash -g lfs -m -k /dev/null lfs

Первая команда создает группу lfs. Вторая — создает пользователя lfs. Смысл параметров таков:

  • -s /bin/bash — используемая в сеансе пользователя командная оболочка
  • -g lfs — группа, в которую входит пользователь
  • -m — указывает, что необходимо создать домашний каталог пользователя /home/lfs
  • -k /dev/null — пустой каталог шаблонов. По-умолчанию при создании домашнего каталога в него помещаются файлы и папки, размещенные в /etc/skel. При желании этот шаблон можно сменить, но мы хотим создать пустую директорию /home/lfs, о чем указываем, передавая в качестве шаблона нулевой файл


Устанавливаем пароль пользователю lfs
# passwd lfs

и сделаем его владельцем каталогов $LFS/sources и $LFS/tools

# chown -v lfs $LFS/sources
# chown -v lfs $LFS/tools


Логинимся от имени lfs

# su - lfs

Черта в параметрах команды (сокращение от ключа -l или --login) указывает, что для данного пользователя надо создать «чистую» сессию, не наследующую значения переменных окружения из предыдущей сессии. Для данной сессии мы создадим собственный набор переменных окружения

Создаем в домашней директории пользователя файлы

~/.bash_profile
[lfs@arch-guest ~]/$ cat > ~/.bash_profile << "EOF"
> exec env -i HOME=$HOME TERM=$TERM PS1='\u:w\$ ' /bin/bash
> EOF

~/.bashrc
[lfs@arch-guest ~]/$ cat > ~/.bashrc << "EOF"
> set +h
> umask 022
> LFS=/mnt/lfs
> LC_ALL=POSIX
> LFS_TGT=$(uname -m)-lfs-linux-gnu
> PATH=/tools/bin:/bin:/usr/bin
> export LFS LC_ALL LFS_TGT PATH
> EOF


Файл ~/.bash_profile определяет индивидуальную пользовательску настройку переменных среды. Команда

exec env -i HOME=$HOME TERM=$TERM PS1='\u:W\$ ' /bin/bash 

создает «чистое» пользовательское окружение (ключ -i указывает игнорировать предыдущие настройки), присваивая значения переменным среды

  • HOME — домашний каталог, присваиваем текущее значение переменной, равное /home/lfs
  • TERM — импользуемый тип терминала, так же оставляем текущий
  • PS1 — формат приглашения командной строки. В данном случает задается формат вида имя пользователя: текущий рабочий каталог$, где спецсимвол \u — имя пользователя, \W — текущий рабочий каталог.
  • /bin/bash — команда, запускающая новый экземпляр командной оболочки


Файл ~/.bashrc определяет какие настройки должны быть выполнены при логине в сессию данного пользователя

  • set +h — отключает хеширование в bash. Командная оболочка запоминает пути к исполняемым файлам, запущенным ранее, что безусловно ускоряет работу системы. Однако мы будем вызывать разные версии одних и тех программ расположенных по разным путям, поэтому хеширование будет только вредить нам.
  • umask 022 — устанавливат права доступа к новым файлам и каталогам, задаваемые по умолчанию. Значение 022 означает, что файлы будут создаваться с правами 644 (rw- r-- r--) а каталоги — с правами 755 (rwx r-x r-x). Каждый взведенный бит umask означает сброс соответсвующего бита в атрибутах прав
  • LFS=/mnt/lfs — корень собираемой системы
  • LC_ALL=POSIX — определяет локализацию запускаемых программ. POSIX задает стандарт отображения независимый от способа кодировки символов.
  • LFS_TGT — задает описание целевой машины, используемое при построении временных иструментов сборки системы
  • PATH — пути поиска исполняемых файлов
  • export — присваивает указанным переменным заданные выше значения


Отдельно следует обратить внимание на способ создания файлов. Почему мы используем команду cat, почему нельзя воспользоваться любимым консольным редактором? Ну, хорошо, пока мы находимся в хост-системе, текстовый редактор нам доступен. А вот когда мы перейдем во временную систему, так у нас не будет никакого редактора. Поэтому приходится использовать команду cat, перенаправляющую поток вывода из одного файла в другой. Входной файл у нас не указан — значит будет использован стандартный поток ввода. В качестве выходного файла указан создаваемый нами файл, а в качестве условия завершения — появление в потоке ввода символьной последовательности конца файла — EOF. После ввода команды мы сможем набирать текст создаваемого файла прямо в терминале. Для прекращения процесса вводим строку EOF. Таким способом мы превращаем командную оболочку в примитивный текстовый редактор.

Созданные скрипты будут исполнятся при каждом логине от имени lfs. Обновим пользовательское окружение командой

[lfs@arch-guest ~]/$ source ~/.bash_profile


Убеждаемся, что необходимые переменные окружения установлены
[lfs@arch-guest~]/$ set


5. Сборка временного набора инструментов (Temporary System)


Начиная собирать систему, мы сталкиваемся с классической проблемой «курицы и яйца». Для сборки системы нам необходимы компилятор, линковщик и библиотеки. Но у нас нет ничего из перечисленного, только их исходники. У нас есть компилятор в хост-системе, и собирать необходимое ПО мы начнем именно им. Однако если просто собрать тот же gcc с настройками по-умолчанию он не будет работать в LFS-системе на начальном этапе её построения — он использует динамическую компоновку необходимых для работы библиотек. Весь необходимый для работы код надо будет скомпоновать статически, то есть явным образом включить в исполняемый файл.

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

Вся работа по сборке временной системы выполняется в каталоге $LFS/sources, поэтому переходим туда

[lfs@arch-guest ~]/$ cd $LFS/sources

Для всех собираемых пакетов порядок сборки содержит следующую последовательность действий (если не оговорено иного!):

  • Распаковываем архив с пакетом
    $ tar -pxf <package-archive-file-name>
    

  • Переходим в распакованный каталог
    $ cd <package-dir-name>
    

  • Выполняем инструкции по сборке (у каждого пакета имеются ньюансы, подробно описанные в документации)
  • Переходим на каталог выше, назад в $LFS/source, и удаляем все созданные при распаковке и сборке директории


Время сборки пакетов различно — от практически мгновенной, до двух с половиной часов (сборка gcc после «чрута» со всеми тестами длится примерно столько). Так как быстродействие компьютеров различается, в качестве единицы измерения времени сборки выбрана относительная единица, именуемая Standard Build Unit (SBU). Один SBU равен времени сборки пакета binutils, который компилируется самым первым. Продолжительность 1.0 SBU по времени можно ориентировочно оценить для вашей системы, но об этом чуть ниже, а пока

Рекомендация: не собирайте систему в несколько потоков. Если у вас многоядерный процессор, то ключик -j ускорит работу, но позже это может обернуться непрохождением некоторых важных тестов и нестабильной работой собранного ПО. Сборка в один поток не так уж и продолжительна — гораздо большее время будет затрачено на разгребание нажитых многопоточной сборкой проблем.

Мы не будем рассматривать сборку каждого пакета — на это есть сама книга LFS. Однако мы остановимся на некоторых ньюансах сборки, рассмотрев компиляцию наиболее важных пакетов

6. Сборка Binutils — первый проход. Измерение SBU


Binutils — пакет, содержащий вспомогательные утилиты для сборки ПО. В их число входят ассемблер (as) и компоновщик (ld). Они необходимы glibc и gcc для прохождения некоторых тестов, поэтому данный пакет собирается в первую очередь.

Распаковываем архив и переходим в каталог с его содержимым

$ tar -pxf binutils-2.25.tar.bz2
$ cd binutils-2.25


Для сборки этого пакета рекомендуется создать отдельный каталог, создаем его и переходим туда

$ mkdir -v ../binutils-build
$ cd ../binutils-build


Теперь надо выполнить конфигурирование пакета, чтобы сгенерировать правильный Makefile.

$ ../binutils-2.25/configure  \ 
> --prefix=/tools \
> --with-sysroot=$LFS \ 
> --with-lib-path=/tools/lib  \
> --target=$LFS_TGT  \ 
> --disable-nls  \ 
> --disable-werror  


Отдельное внимание ключам:

  • --prefix=/tools — указывает на то, что собранные пакеты будут устанавливаться в директорию /tools, где будет размещаться временная система
  • --with-sysroot=$LFS — корневой каталог системы, относительно которого будет производится поиск библиотек и прочих ресурсов, необходимых для работы программ во временной системе
  • --with-lib-path=/tools/lib — путь поиска библиотек, указываемый для компоновщика ld
  • --target=$LFS_TGT — задаем описание целевой машины
  • --disable-nls — отключает интернационализацию собираемых пакетов. В данный момент она без надобности, а её наличие может вызвать ряд сбоев
  • --disable-werror — отключает остановку компиляции при получении предупреждений от компилятора хост-системы


После того как отработает конфигуратор, на выходе мы получим Makefile, предназначенный для сборки и установки пакета. Собираем пакет и измеряем SBU

$ time { make; }

Время измеряем утилитой time {...}, которая возвращает время выполнения команд, заключенных в фигурные скобки. Это даст возможность ориентировочно оценить SBU, чтобы иметь представление о времени сборки остальных пакетов.

Примечание : LFS содержит рекомендацию измерять время конфигурирования сборки и установки. Но эта рекомендация носит пространный характер. Основное время работы машины уходит на make и проведение тестов. Кроме того, между make и make install могут выполнятся и другие действия, в которые приходится вникать. Так что я принял решение выполнить оценку описанным выше способом, учитывая примерный характер измерения, которое все равно определяется ещё и общей загрузкой системы другой работой.

Для моей системы — виртуальная машина на 4-х ядрах Intel Core i7 c 4096 Мб ОЗУ, работающей поверх хоста на том же Intel Core i7-2600K (4 ядра с гипертрейдингом) c 16 Гб ОЗУ — SBU составил 2 минуты 32 секунды при сборке в один поток.

Время сборки остальных пакетов дается в книге в долях SBU, так что теперь можно оценивать примерное время сборки всего остального, планируя таким образом работу (например запустить финальную сборку сборку gcc вместе с тестами и уйти делать другие дела, ибо занимает это всё 63 SBU — нет смысла с надеждой пялится в экран)

После того как сборка окончена, создаем символические ссылки на каталоги библиотек. Данная операция необходима, если ваша система 64-разрядная, так что нижеприведенная команда сначала проверяет разрядность системы, а уж потом при необходимости создает симлинки.

$ case $(uname -m) in
> x86_64) mkdir -v /tools/lib && ln -sv lib /tools/lib64 ;;
> esac

После этого устанавливаем собранное

$ make install

Выходим наверх и удаляем ненужные более каталоги

$ cd ..
$ rm -r binutils-2.25
$ rm -r binutils-build


7. Сборка GCC — первый проход


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

Во-первых распакуем архив с исходниками и перейдём в созданный каталог

$ tar -pxf gcc-4.9.2.tar.bz2
$ cd ../gcc-4.9.2


Для успешной компиляции нам потребуются исходники ещё трех пакетов: GMP, MPFR и MPC. Распакуем их в каталог исходников компилятора и переименуем каталоги так, как на них ссылаются в исходниках gcc

$ tar -pxf ../gmp-6.0.0a.tar.xz
$ tar -pxf ../mpfr-3.1.2.tar.xz
$ tar -pxf ../mpc-1.0.2.tar.gz
$ mv -v gmp-6.0.0a gmp
$ mv -v mpfr-3.1.2 mpfr
$ mv -v mpc-1.0.2 mpc

Кроме того, придется отредактировать исходники gcc. Дело в том, что путь поиска компоновщика ld в них определен как для нормальной работающей системы. В нашем случае компоновщик располагается в каталоге /tools (он был собран в составе binutils). Некоторые из макроопределений так же должны ссылаться на /tools, поэтому трансформируем исходники, применяя монструозную команду

$ for file in \
> $(find gcc/config -name linux64.h -o -name linux.h -o -name sysv4.h)
> do
> cp -uv $file{,.orig}
> sed -e 's@/lib\(64\)\?\(32\)\?/ld@/tools&@g' \
> -e 's@/usr@/tools@g' $file.orig > $file
> echo '
> #undef STANDARD_STARTFILE_PREFIX_1
> #undef STANDARD_STARTFILE_PREFIX_2
> #define STANDARD_STARTFILE_PREFIX_1 "/tools/lib/"
> #define STANDARD_STARTFILE_PREFIX_2 ""' >> $file
> touch $file.orig
> done

М-да… первый взгляд на эту команду вселил в меня ужас. На самом же деле тут в цикле перебираются файлы inux64.h, linux.h, sysv4.h, при условии их наличия в каталоге gcc/config. Каждый из них копируется в новый файл с суффиксом *.orig (сохраняем оригиналы исходников — затем они будут понставлены в качестве потока ввода для sed), а затем в каждом из найденных файлов производится подстановка /tools к пути поиска линкера ld и замена /usr на /tools. Кроме того в конец каждого из файлов добавляется переопредение макросов STANDARD_STARTFILE_PREFIX_1 и STANDARD_STARTFILE_PREFIX_2, так, чтобы они указывали на верные пути к библиотекам. Далее командой touch модифицируется время последнего изменения всех обрабатываемых файлов на текущее, что в сочетании с ключом -u команды cp (копировать если файл-источник новее чем файл назначения) предотвращает нежелательные изменения при случайном повторном выполнении команды.

Несколько слов о ситаксисе sed в данном примере
sed — потоковый текстовый редактор, выполняющий операции над текстовыми файлу по шаблонам, определяемым регулярными выражениями.

Когда я первый раз открыл LFS, я имел представление о синтаксисе sed ровно такое же, как слушательница курсов исскусств имени Леонардо да Винчи о сельском хозяйстве. Однако упорное гугление привело меня к следующим мыслям

  • так как в обрабатываемых путях присутствует символ "/", в sed вместо разделителя операндов использован символ "@"
  • конструкции вида "\(64\)\?" и "\(32\)\?" сообщаяют sed-у, что искомая строка может как содержать, так и не содержать строки «64» и «32» — операция должна быть произведена в обоих случаях.
  • символ "&" указывает, что /tools будет поставлено перед искомыми строками (то есть на месте "&" будет находится найденное выражение)


Таким образом выражение 's@/lib\(64\)\?\(32\)\?/ld@/tools&@g' разобрано мной так — найти все строки, содержащие "/lib", "/lib64" и "/lib32" и поставить перед ними строку "/tools", а выражение 's@/usr@/tools@g' — найти все строки, содержащие "/usr" и заменить их на "/tools"


В общем, сборка временной системы (да и основной тоже) буквальна пропитана применением подобных конструкций. Желательно постараться вникнуть в их смысл, а команды набивать руками. Тогда к концу процесса сборки вас ожидает просветеление по многим вопросам, касающихся приемов обработки текстов и работы в *nix-консоли.

Следующая команда:

$ sed -i '/k prot/agcc_cv_libc_provides_ssp=yes' gcc/configure

вносит исправление в скрипт конфигуратора, однако в моем случае она не внесла в него никаких изменений. По книге она направлена на изменения характера работы gcc с защитой стека.

Далее нам надо выполнить конфигурирование

$ ../gcc-4.9.2/configure                               \
>    --target=$LFS_TGT                                \
>    --prefix=/tools                                  \
>    --with-sysroot=$LFS                              \
>    --with-newlib                                    \
>    --without-headers                                \
>    --with-local-prefix=/tools                       \
>    --with-native-system-header-dir=/tools/include   \
>    --disable-nls                                    \
>    --disable-shared                                 \
>    --disable-multilib                               \
>    --disable-decimal-float                          \
>    --disable-threads                                \
>    --disable-libatomic                              \
>    --disable-libgomp                                \
>    --disable-libitm                                 \
>    --disable-libquadmath                            \
>    --disable-libsanitizer                           \
>    --disable-libssp                                 \
>    --disable-libvtv                                 \
>    --disable-libcilkrts                             \
>    --disable-libstdc++-v3                           \
>    --enable-languages=c,c++


Ключи несут следующий смысл:

  • --with-newlib — предотвращает компиляцию любого кода, тербующего поддержку libc — данных библиотек у нас пока нет, они будут собраны позднее
  • --without-headers — отключает использование стандартных заголовочных файлов, которые нам пока что недоступны
  • --with-local-prefix=/tools — локальный путь по которому GCC будет искать заголовочные файлы. По умолчанию его значение /usr/local
  • --with-native-system-header-dir=/tools/include — путь поиска системных заголовков
  • --disable-shared — указывает на то, что весь библиотечный код должен компоноваться статически
  • --disable-decimal-float, --disable-threads, --disable-libatomic, --disable-libgomp, --disable-libitm, --disable-libquadmath, --disable-libsanitizer, --disable-libssp, --disable-libvtv, --disable-libcilkrts, --disable-libstdc++-v3 — отключает все ненужные на данном этапе расширения компилятора — работу с плавающей точкой, многопоточность и параллелизм, а так же стандартную библиотеку C++ и многое другое.
  • --disable-multilib — отключает поддержку сборки 32-разрядного кода в 64-разрядной среде
  • --enable-languages=c,c++ — ограничиваемся поддержкой C/C++


Настроив сборку таким образом, выполняем её и устанавливаем пакет
$ make
$ make install


после чего прибираемся за собой
$ cd ..
$ rm -r gcc-build && rm -r gcc-4.9.2

8. Сборка остальных пакетов временной системы


Дальнейший ход сборки временной системы в пояснениях не нуждается и я отсылаю читателя к книге LFS. Единственное что стоит отметить — на протяжении всего процесса придется править исходники и устанавливать специальные опции конфигуратора, причем этот процесс должен быть вдумчивым и неторопливым. Ошибка на данном этапе может стоить довольно дорого, а найти её, неподготовленному ньюфагу, достаточно тяжело.

Ограничусь описанием минимального набора пакетов временной системы и некоторыми важными пояснениями. Следом за gcc надо установить

  • заголовки ядра Linux-3.19 — необходимы библиотекам GLibc
  • Glibc-2.21 — основная библиотека C, обеспечивающая выделение памяти, работу с файловой системой, базовую арифметику.

    После сборки Glibc необходимо провести первый тест собранного компилятора. Создаем примитивную программу и собираем её компилятором из временного набора

    $ echo 'main(){}' > dummy.c
    $ $LFS_TGT-gcc dummy.c
    


    Парсим получившийся бинарник на предмет наличия и содержания пути поиска загрузчика динамических библиотек

    $ readelf -f a.out | grep ': /tools'
    

    В моем случае получен следующий результат:

          [Requesting program interpreter: /tools/lib64/ld-linux-x86-64.so.2]
    

    что говорит о правильной ссылке на линкер. В книге приводится результат для 32-разрядной системы, я же собирал 64-разрядную, и приведенный здесь путь отличается от указанного вкниге.

    Убедившись, что всё в порядке, удаляем тестовую программу
    $ rm -v dummy.c a.out
    

  • Libstdc++-4.9.2 — стандратные библиотеки C++ необходимые для работы компилятора g++
  • Binutils-2.25 — второй проход сборки, выполняемый с учетом уже установленных пакетов. Сборка выполняется с использованием уже собранных на первом проходе компилятора, архиватора ar (входит в состав binutils), и библиотек, задаваемых через переменные окружения CC, AR и RANLIB

    $ CC=$LFS_TGT-gcc                \
    > AR=$LFS_TGT-ar                 \
    > RANLIB=$LFS_TGT-ranlib         \
    >../binutils-2.25/configure     \
    > --prefix=/tools            \
    > --disable-nls              \
    > --disable-werror           \
    > --with-lib-path=/tools/lib \
    > --with-sysroot
    

  • GCC-4.9.2 — второй проход, выполняемый средствами временной системы

    $ CC=$LFS_TGT-gcc                                      \
    > CXX=$LFS_TGT-g++                                     \
    > AR=$LFS_TGT-ar                                       \
    > RANLIB=$LFS_TGT-ranlib                               \
    > ../gcc-4.9.2/configure                               \
    > --prefix=/tools                                  \
    > --with-local-prefix=/tools                       \
    > --with-native-system-header-dir=/tools/include   \
    > --enable-languages=c,c++                         \
    > --disable-libstdcxx-pch                          \
    > --disable-multilib                               \
    > --disable-bootstrap                              \
    > --disable-libgomp
    


    По мере сборки пакетов, они постепенно вводятся в работу, делая нас всё более и более независимыми от хост-системы. На этом этапе необходим повторный тест компилятора

    $ echo 'main(){}' > dummy.c
    $ cc dummy.c
    $ readelf -l a.out | grep ': /tools'
    $       [Requesting program interpreter: /tools/lib64/ld-linux-x86-64.so.2]
    $ rm -r dummy.c a.out
    


  • Tcl-8.6.3 — пакет поддержки языка Tcl, необходимый компилятору для выполнения тестов.
  • Expect-5.45 — иструмент автоматизации тестирования, используемый как расширение Tcl
  • DejaGNU-1.5.2 — фреймворк для написания тестов программ
  • Check-0.9.14 — фреймворк для организации unit-тестов C-программ
  • Ncurses-5.9 — библиотека организации пстевдографического терминального UI, в частности используемая некоторыми конфигурационными утилитами (в том числе при конфигурировании сборки ядра Linux) и такой популярной программой как mc.
  • Bash-4.3.30 — командная оболочка. Без комментариев.
  • Bzip2-1.0.6 — поддержка архивов bzip2.
  • Coreutils-8.23 — один из основных пакетов GNU-окружения, содержащий внешние команды оболочки, такие как cat, ls, rm и так далее
  • Diffutils-3.3 — утилиты для сравнения файлов и каталогов.
  • File-5.22 — утилиты для определения типа файлов.
  • Findutils-4.4.2 — утилиты для поиска файлов и каталогов. Там содержится, например, широко используемый find
  • Gawk-4.1.1 — GNU-реализация утилиты awk — построчный разбор и обработка текстовых файлов и других входных потоков.
  • Gettext-0.19.4 — пакет утилит поддержки интернационализации программ.
  • Grep-2.21 — утилита текстового поиска в потоке ввода
  • Gzip-1.6 — ещё один архиватор
  • M4-1.4.17 — макропроцессор, используемый средствами конфигурирования и сборки ПО
  • Make-4.1 — утилита автоматической сборки ПО
  • Patch-2.7.4 — утилита для работы с патчами исходных кодов
  • Perl-5.20.2 — интерпертатор языка Perl
  • Sed-4.2.2 — потоковый текстовый редактор
  • Tar-1.28 — куда же без него
  • Texinfo-5.2 — утилиты для работы с info-страницами документации
  • Util-linux-2.26 — основной пакет утилит Linux-системы. Содержит, например, mount
  • Xz-5.2.0 — ещё один архиватор.


9. Обрезка отладочной иформации


После того как всё будет собрано, для экономии места рекомендуют обрезать отладочную информацию от собранных бинарников. Делаем это командами

$ strip --strip-debug /tools/lib/*
$ /usr/bin/strip --strip-unneeded /tools/{,s}bin/*


Кроме того, можно удалить установленную но не нужную пока документацию — всё равно она будет собрана и установлена заново и уже на чистовую

$ rm -rf /tools/{,share}/{info,man,doc}


Итог


В итоге мы имеем, размещенную в каталоге $LFS/tools временную систему, в которую перейдем на следующем этапе для финальной сборки GNU-окружения и настройки системы. Но об этом я расскажу позже.

Спасибо за внимание к моей писанине!

Продолжение следует...
Tags:
Hubs:
+18
Comments 21
Comments Comments 21

Articles