5.3.
5.3.
Этот раздел предназначен для объяснения некоторых логических и технических моментов рационального метода сборки. Ничего страшного, если вы не поймете все, что написано здесь. Большая часть из них имеет значение при выполнении сборки конкретных пакетов. Вы можете вернуться сюда в любое время.
Основной целью Главы 5 является подготовка окружения для входа через chroot для создания полноценной системы в Главе 6. По ходу дела мы соберем, используя основную систему, самодостаточные средства. Это будет сделано для обеспечения минимального риска и максимальной независимости одновременно. Другими словами, мы соберем инструменты для сборки системы.
Важно
Перед дальнейшей работой вы должны знать название вашей платформы, которое также называется target triplet. В некоторых случаях target triplet может быть, к примеру: i686-pc-linux-gnu. Простейшим способом определения вашего target triplet является запуск скрипта config.guess который содержится во многих пакетах. Распакуйте тарбол с исходниками Binutils, запустите скрипт: ./config.guess и запомните вывод.
Вам также необходимо знать имя динамичаского компоновщика для вашей платформы, его также называют динимическим загрузчиком (не спутайте его со стандартным компоновщиком ld который является частью Binutils). Динамический компоновщик является частью Glibc и служит для поиска и загрузки библиотек, в которых нуждается программа, подготовки программы к запуску и ее запуска. Как правило, динамический компоновщик называется ld-linux.so.2. На некоторых не очень распространенных платформах он называется ld.so.1, а на некоторых 64-битных платформах - по другому. Вы можете определить имя динамического компоновщика для вашей платформы, заглянув в директорию /lib вашей основной системы. Безошибочным способом проверки случайной библиотеки на вашей основной системе является запуск: readelf -l <name of binary> | grep interpreter и просмотр вывода. Ссылка, описывающая все платвормы, находится в файле shlib-versions в корне дерева исходников Glibc.
Некоторые технические моменты, которые позволяют методам сборки из Главы 5 работать:
-
Принципиальное сходство перекрестной компиляции, посредством чего установленные средства работают с одним префиксом и, таким образом, используют маленькое “волшебство” GNU.
-
Осторожная манипуляция путями поиска библиотек стандартным компоновщиком для того, чтобы убедиться, что программы были скомпонованы с использованием выбранных нами библиотек.
-
Осторожная манипуляция specs-файлом gcc для передачи компилятору информации об используемом динамическом компоновщике.
Binutils устанавливаются первыми потому, что GCC и Glibc выполняют тестирование ассемблера и компоновщика во время запуска ./configure для определения того, какие настройки программного обеспечения выбраны или отключены. Это очень важно при первой реализации. Неверно сконфигурированые GCC и Glibc могут испортить всю сборку средств и дистрибутив будет с ошибками. Благодаря тестированию мы можем опрделить это своевременно.
Binutils устанавливают ассемблер и компоновщик в два места: /tools/bin и /tools/$TARGET_TRIPLET/bin. Точнее, средства в одной из директорий являются жесткими ссылками на другие. Очень важным для компоновщика является порядок поиска библиотек. Точную информацию о нем можно получить от ld указав параметр --verbose. Например: ld --verbose | grep SEARCH покажет текущие пути поиска и их порядок. Вы можете увидеть, какие файлы скомпонованы с помощью ld при компиляции программы-пустышки и используя переключатель --verbose. Например: gcc dummy.c -Wl,--verbose 2>&1 | grep succeeded покажет все файлы, удачно открытые в процессе компоновки.
Следующим пакетом мы установим GCC и в процессе работы ./configure вы увидите, например:
checking what assembler to use... /tools/i686-pc-linux-gnu/bin/as checking what linker to use... /tools/i686-pc-linux-gnu/bin/ld
Это важно по причинам, описаным выше. И это также показывает, что скрипт конфигурации GCC не ищет в директориях $PATH какие средства использовать. Тем не менее, текущий процесс gcc не использует эти пути поиска. Вы можете определить, какой стандартный комполновщик gcc используется, запустив: gcc -print-prog-name=ld.
Детальная информация получается от gcc добавлением параметра -v при компиляции. Например: gcc -v dummy.c покажет полную информацию о препроцессоре, компиляторе и ассемблере, включая пути поиска gcc и другую информацию.
Следующим устанавливаемым пакетом будет Glibc. Наиболее важными зависимостями сборки Glibc являются компилятор, средства для бинарных файлов и заголовки ядра. Компилятор не представляет проблемы, Glibc всегда использует gcc из директории $PATH. Бинарные средства и заголовки ядра могут привести к некоторым проблемам. Поэтому мы не рискуем и используем переключатели конфигурации для правильного выбора. После запуска ./configure вы можете проверить содержимое файла config.make в директории glibc-build для получения информации. Вы будете использовать некотоые интересные параметры, такие как CC="gcc -B/tools/bin/", которые определяют используемые средства, а также флаги -nostdinc и -isystem для контроля за путем поиска для компилятора. Эти параметры показывают важный аспект пакета Glibc: он не обязательно полагается на средства по умолчанию.
После установки Glibc мы создадим некоторые установки для того, чтобы убедиться, что пути для поиска содержат только в директории /tools. Мы установим откорректированный ld, в котором будет жестко указан путь поиска в /tools/lib. Задем мы исправим specs-файл gcc для указания на наш новый динамический компоновщик в /tools/lib. Этот последний шаг жизненно важен для нашего процесса. Как указано выше, путь к динамическому компоновщику будет жестко вшит в каждый исполняемый файл ELF. Вы можете убедиться в этом, запустив 'readelf -l <name of binary> | grep interpreter' . Благодаря исправлению specs-файла gcc , мы убедимся, что все программы из оставшейся части Главы 5 будут использовать новый динамический комполовщик из /tools/lib.
Необходимость использования нового динамического компоновщика также является причиной, по которой мы применяем Specs патч на втором этапе сборки GCC. Опускание этого патча приведет к тому, что программы GCC будут использовать системный динамический компоновщик из директории /lib основной системы, а это будет означать зависимость от основной системы.
В ходе второго шага сборки Binutils мы будем использовать переключатель --with-lib-path для контроля пути поиска библиотек для ld. С этого момента все наши средства будут самодостаточными. Остальные пакеты из Главы 5 будут собраны с новым Glibc из /tools.
После входа в окружение chroot в Главе 6, мы первым делом установим Glibc по вышеописаным причинам. Теперь Glibc будет установлен в /usr и мы будем использовать средства по умолчанию для сборки остальных пакетов для LFS системы.
5.3.1. О статической компоновке
Многие программы выполняют помимо своей основной задачи и многие другие операции. Это включает распределение памяти, поиск директорий, четение и запись файлов, манипуляции со строками, использование шаблонов, арифметические операции и многое другое. Вместо того, чтобы включать все это в программу, GNU система поддерживает простые функции в библиотеках. Самой главной из них для любой Linux системы является Glibc.
Есть два варианта использования библиотечных функций: статически и динамически. Когда программа скомпонована статически, код используемых функций включается в программу и она становится более громоздкой. Когда же программа скомпонована динамически, в программу включаются только ссылка на динамический компоновщик, имя библиотеки и имя функции, в результате мы получаем более маленький исполняемый файл. (Есть еще третий путь - использование программного интерфейса для динамического компоновщика. Смотрите мануал по dlopen для более полной информации.)
Динамическая компоновка является компоновкой по умолчанию в Linux и имеет три главных преимущества перед статической компоновкой. Первое: вам достаточно иметь только одну копию исполняемого кода библиотеки на жестком диске в отличие от нескольких копий в каждом исполняемом файле в противном случае - вы экономите место на диске. Второе: когда несколько программ используют одну и ту же библиотечную фунцию одновременно, только одна копия этой функции находится в памяти - вы экономите оперативную память. Третье: если вы обнаружили ошибку в функции, достаточно перекомпилировать одну библиотеку в отличие от необходимости перекомпилировать все программы, использующие эту функцию, в противном случае.
Если динамическая компоновка настолько лучше, то почему мы ее не используем, а статически компонуем первые два пакета в этой главе? Есть тройственная причина для этого: историческая, образовательная и техническая. Историческая: потому что простейшая версия LFS статически компонует все программы из этой главы. Образовательная: потому что надо знать отличие между статической и динамической компоновкой. Техническая: потому что мы получаем элемент независимости от основной системы - наши программы не должны от нее зависеть, они должны иметь возможность работать самостоятельно. Таким образом, если мы хотим построить LFS систему, нам необходимо отказаться от динамической компоновки первых двух пакетов.