Aus Linux-Magazin 08/2021

Eine eigene Linux-Distribution zusammenbauen

© Kittipong Jirasukhanont / 123RF.com

Der erste Teil unserer kleinen Serie hat die Begriffe und Konzepte von LFS vorgestellt. In der zweiten Folge geht es nun darum, wie Sie in der Praxis ein Linux from Scratch aufsetzen.

Ohne einen perfekt funktionierenden Build-Host geht es nicht. Diese Anleitung setzt daher eine unbenutzte, virtuelle Maschine auf Basis eines voll gepatchten, aktuellen CentOS 8.3 Minimal Install mit BIOS-Boot voraus. Der C-Compiler GCC profitiert von einem schnellen Core. Die Dokumentation diskutiert zwar einen auf mehrere Cores verteilten Multi-Thread-Build, der sich aber wegen gelegentlich auftretender Race Conditions in der Praxis nicht empfiehlt.

Für den Build-Server genügen daher zwei Cores, 2 GByte RAM, ein SSD-basierter Hypervisor und eine Intel-e1000-NIC. Als Massenspeicher benötigen Sie eine Disk mit 10 GByte (VirtIO, »/dev/vda/«) für das System und eine mit 20 GByte (SATA oder SCSI, »/dev/sda/«) für LFS.

Vermeiden Sie VirtIO für die LFS-Disk und die Netzwerkkarte, da die Anleitung keinen VirtIO-Support in den Kernel einkompiliert. LFS freut sich über eine virtuelle SATA- oder SCSI-Disk, die es als »/dev/sda/« erkennt, sowie eine klassische Intel-e1000-Netzwerkkarte mit 1 Gbps. Die Disk-Bezeichnung wird wichtig, wenn es darum geht, LFS bootfähig zu machen. Der Name der Netzwerkkarte (etwa »enp1s0«) spielt eine Rolle, falls Sie Predictable Network Interface Names verwenden wollen.

Nach der CentOS-Installation müssen Sie die für die ersten Build-Prozesse notwendigen Pakete installieren. Das benötigte »makeinfo« aus dem Paket texinfo erhalten Sie ab CentOS 8.3 aus dem PowerTools-Repository. Die für den Build benötigten Pakete sind schnell installiert (Listing 1).

Listing 1

Benötigte Pakete installieren

$ dnf -y install yum-utils
$ yum config-manager --set-enabled powertools
§ dnf -y install bison byacc bzip2 gcc-c++ patch perl python3 tar texinfo wget

Die von CentOS installierten Versionen sollten Sie anschließend mithilfe eines kleinen Skripts prüfen: LFS v10.0 verlangt einen GCC ab Version 6.2 sowie GNU Make ab Version 4.0, die beispielsweise ein CentOS 7 nicht bereitstellt.

Passt hier alles, teilen Sie die zweite Disk des Build-Hosts in die Partitionen »/«, »swap« und »/home« auf, erstellen das Dateisystem mit Ext4 und definieren die für alle nachfolgenden Shell-Aufrufe essenzielle Umgebungsvariable »$LFS«, die den Root-Mountpoint auflöst. Die Variable muss daher unter allen Umständen funktionieren. Weiter sollten Sie die Locale-Einstellungen in CentOS 8 noch erledigen und die zweite Disk einhängen.

Die Platte »/dev/sda/« machen Sie per BIOS/MBR bootfähig und statten sie mit diversen Partitionen aus. Um dem Grub-eigenen »core.img« genügend Platz einzuräumen, startet die erste Partition für »/« mit 10 GByte Größe erst ab 1 MiB. Danach legen Sie »/swap« mit 2 GByte Größe an, der Rest der Disk bleibt für »/home« reserviert.

In diesem Setup benötigt »/boot« keine eigene Partition, sondern enthält als gewöhnliches Verzeichnis den Kernel sowie die Bootloader-Konfiguration. Eine eigene Partition brauchen Sie erst dann, wenn Sie ein Multiboot mit anderen Distributionen unterstützen wollen oder der Bootloader nicht mehr auf das Root-Dateisystem zugreifen kann. Letzteres kann an fehlenden Treibern, an Disk-Encryption, an einem Software-RAID oder LVM liegen. Alle nötigen Kommandos für die komplette Installation enthält das Open-Source-Admin-Handbuch der Linuxfabrik [1].

Pakete und Patches laden

Das Kapitel 3 der LFS-Dokumentation definiert LFS in seiner späteren Ausstattung: Neben den Build-Tools laden Sie die gewünschten Quellen ausgewählter Softwarepakete herunter, die das System charakterisieren. Die Macher von LFS haben sich die Mühe gemacht, eine Vorauswahl zu treffen, deren Versionsabhängigkeiten aufzulösen und als Liste in einer Textdatei zusammenzufassen, die Sie herunterladen und ganz bequem Wget übergeben können (Listing 2).

Obendrein liefern die LFS-Entwickler eine Reihe von Patches, die Fehler beseitigen oder die Pakete in ihrer Ausprägung an das zukünftige Zielsystem anpassen. Darüber hinaus erklären sie alle Optionen der jeweiligen »configure«-Aufrufe. Eine ebenfalls bereitgestellte Md5sum-Prüfsummen-Liste [2] hilft dabei, die Checksummen der am Ende knapp 90 heruntergeladenen Pakete zu prüfen.

Listing 2

Softwareauswahl herunterladen

$ wget http://lfs.linux-sysadmin.com/lfs/downloads/stable-systemd/wget-list
$ wget --input-file=wget-list --continue --directory-prefix=$LFS/sources

Abschlussarbeiten

Im LFS-Kapitel 4 erstellt der Benutzer root die für den Build notwendigen Verzeichnisstrukturen »$LFS/{bin,etc,lib,lib64,sbin,tools,usr,var}« sowie den Benutzer lfs als Eigentümer der Build-Verzeichnisse. Anschließend gilt es, zum Benutzer lfs zu wechseln und dessen Shell-Environment zu leeren und zu optimieren.

Da es sich beim neuen Environment um eine Non-Login-Shell handelt, braucht es eine ».bashrc«-Datei, um die Shell-Umgebung zu erweitern und anzupassen, zum Beispiel mit Umgebungsvariablen wie »$LFS«, »LC_ALL« oder »PATH«. Das Verzeichnis »$LFS/tools/« enthält später den Cross-Compiler.

Haben Sie hier alles erledigt, steht ein Neustart des Build-Hosts an, um sicherzugehen, dass er ohne Altlasten daherkommt und funktioniert. Spätestens jetzt empfiehlt es sich, einen VM-Snapshot anzulegen [3].

Cross-Toolchain kompilieren

In Kapitel 5 des LFS-Handbuchs bleibt man als Benutzer lfs angemeldet, für den die eigentliche Arbeit beginnt: das Bauen des Cross-Compilers und seiner Tools (Listing 3). Der Cross-Compiler samt Werkzeugen landet temporär in »$LFS/tools/«, die Bibliotheken installieren Sie bereits an ihren zukünftigen Orten.

Listing 3

Test des cross-kompilierten GCC

$ echo 'int main(){}' > dummy.c
$ $LFS_TGT-gcc dummy.c
### Zeigt die Information aus den Segment-Headern
### der Datei, falls vorhanden
$ readelf -l a.out | grep '/ld-linux'
### Sollte etwas zurückgeben wie:
### [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]

Der Ablauf beim Bau der Softwarepakete bleibt bis zum Schluss fast immer gleich: Sie wechseln ins Verzeichnis »sources/«, wo Sie die Quelldateien mithilfe von »tar xf« auspacken. Dann wechseln Sie ins dabei erstellte neue Verzeichnis, wo Sie bei Bedarf ein »build/«-Verzeichnis erstellen.

Nun lässt sich das Paket kompilieren und installieren; anschließend löschen Sie das Quellcodeverzeichnis wieder. Das exerzieren Sie in einem ersten Durchlauf für Linker und Assembler, Cross-Compiler, die Linux-API-Headers, die Glibc und die Libstdc++ durch [4].

Temporär benötigte Tools bauen

Immer noch als Benutzer lfs kompilieren Sie mit der vorher erzeugten Cross-Toolchain im LFS-Kapitel 6 unter anderem Bash, Grep und den nativen LFS-GCC. Die Applikationen installieren Sie im bereits rudimentär erstellten LFS-Dateisystem, sie lassen sich aber noch nicht einsetzen; für bestimmte Aufgaben benötigen Sie also immer noch das Host-System. Immerhin werden die in LFS installierten Bibliotheken bereits zum Linken verwendet. Wirklich zum Einsatz kommen die Applikationen aber erst im kommenden Abschnitt in der Chroot-Umgebung.

Temporär benötigen und kompilieren Sie im zweiten Durchlauf einen Makroprozessor, einen Linker und einen Assembler, den nativen GCC-Compiler, eine TUI-Bibliothek sowie die Bash. Daneben entstehen eine Reihe von Tools, darunter Awk, Chmod, Cp, Dd, Diff, File, Grep, Gzip, Make, Patch, Sed, Tar und Xz [5].

Zusätzliche temporäre Tools

Bis hierhin war der Benutzer lfs Eigentümer des LFS-Dateisystems; in Abschnitt 7 der LFS-Doku ändert sich das zu root. Anschließend erzeugen Sie auf der Platte die Devices »console« und »null«, die der Kernel beim Boot-Vorgang erwartet.

Durch die aufgelösten zirkulären Abhängigkeiten können Sie erstmals als Benutzer root in eine Chroot-Umgebung wechseln, die das Betriebssystem des Build-Hosts für weitere Aufgaben beinahe komplett aussperrt. Eine Ausnahme bildet der laufende Kernel: Damit die Chroot-Umgebung funktioniert, wird die Kommunikation mit ihm über das Virtual Kernel File System (VFS) konfiguriert.

Bei VFS handelt es sich um Dateisysteme wie etwa »tmpfs/«, die keinen Plattenplatz beanspruchen und komplett im Hauptspeicher liegen. Normalerweise mountet das System Geräte unterhalb von »/dev« als virtuelles Dateisystem und erstellt diese beim Erkennen oder beim ersten Zugriff während des Boot-Vorgangs via Udev. Im Augenblick fehlt Udev noch, also müssen Sie die benötigten Geräte manuell anlegen und per Bind-Mount einhängen. Stolperfalle: Falls Sie den LFS Build-Host später neu starten, müssen Sie auch die Mount-Befehle für die virtuellen Devices vor den weiteren Arbeiten erneut ausführen.

Beim ersten Wechsel in die Chroot meldet sich ein ungewöhnlicher Bash-Prompt – es fehlt noch eine »/etc/passwd«. Nun kommt die endgültige Verzeichnisstruktur inklusive korrekt gesetzter Berechtigungen plus einiger Log-Dateien an die Reihe. Die Verzeichnisstruktur folgt dem Filesystem Hierarchy Standard (FHS [6]) und legt unter anderem die Verzeichnisse »{boot,home,mnt,opt,srv}« an, lässt Unnötiges wie »/usr/local/games« aber weg.

Es fehlen noch Standardbenutzer und -gruppen. Da es hier keine einheitlichen Vorgaben gibt, orientiert sich LFS an Udev und anderen Distributionen. Sie kompilieren und installieren anschließend noch Software, um andere Programme zu testen (unter einem temporär angelegten Benutzer tester). Hinzu kommen fehlende Bausteine der Toolchain: Als Erstes erstellen Sie die Libstdc++ (zweiter Durchlauf), ein Paket für Internationalisierungs- und Lokalisierungssupport, einen Parser-Generator sowie die Programmiersprachen Perl und Python. Es folgen Tools für Info-Pages und kleinere Utilities wie Blkid, Dmesg und andere.

Die derart komplettierte Toolchain läuft jetzt unabhängig vom Build-Host. Am Ende des Abschnitts kümmert sich die LFS-Doku [7] noch um Stripping, Backups und Restore, worauf wir hier aber nicht genauer eingehen.

Basissoftware installieren

Der nächste Abschnitt der LFS-Dokumentation gibt zunächst einen Ausblick auf das Thema Paketmanagement. Dabei kommt allerdings heraus, dass das Projekt keinen Paketmanager benutzt; er würde schlicht den Rahmen sprengen und nicht zur Ausrichtung von LFS passen.

Anschließend ist Fleißarbeit angesagt. So kümmern Sie sich in Kapitel 8 per »configure«, »make« und »make install« darum, dass neben wichtigen Bibliotheken eine ganze Reihe an Applikationen auf der Platte landen. Dazu gehören neben der Bash auch Textprozessoren wie Awk, Grep und Sed sowie Packer und Archivprogramme wie Bzip, Gzip, Tar und Xz. Hinzu kommen der GCC, der Bootloader Grub, Man und Mandb, OpenSSL, Perl und Python, Vim sowie Udev, das sich um Geräte kümmert.

Symptom

Ursache

Lösung

Build-Vorgang meldet libtool: warning: remember to run ‘libtool –finish /usr/lib’ (File-5.39)

Durch Prefix-Parameter verursacht.

Nichts ausführen und Meldung ignorieren.

The system has no more ptys. Ask your system administrator to create more.

Möglicherweise haben Sie den Build-Host nach Schritt 7.3 neu gestartet und vergessen, die Device-Nodes vor dem Chroot erneut zu mounten.

Führen Sie Code-Abschnitt 7.3 und 7.4 nochmals aus.

LFS bleibt beim Boot einfach mit der Meldung Grub stehen.

Die LFS-Disk wurde mit BIOS/GPT eingerichtet.

Lagern Sie »/boot« auf eine eigene Partition aus.

In Grub erscheint beim Booten in LFS die Meldung error: hd1 cannot get C/H/S values.

Tritt oft bei manuell erstellter »/boot/grub/grub.cfg« auf.

Spezifizieren Sie die LFS-Disk als zweite Disk im System als erstes Boot-Medium, muss die Datei den Eintrag »set root=(hd0,1)« aufweisen, als zweites Boot-Medium »set root=(hd1,1)«.

Kernel Panic – not syncing: VFS: Unable to mount root fs on unknown-block(0,0)

Es fehlen höchstwahrscheinlich Treiber für das verwendete Medium.

Falls Sie VirtIO-Disks verwenden, fügen Sie die Treiber hinzu, oder steigen Sie auf SATA-Disks um.

In diesem Abschnitt ist das Übersetzen des GCC der mit Abstand der zeitintensivste Task im gesamten Projekt. Dieser Schritt beansprucht durch das Ausführen der Test-Suites fast die Hälfte der gesamten Kompilierungszeit. Sie erhalten hier jede Menge FAILED-Meldungen, um die Sie sich aber nicht zu kümmern brauchen, solange ein »grep« auf die von der Testsuite erzeugten Log-Dateien die erwarteten Ergebnisse liefert. Auch während der Compile-Vorgänge auftretende Warnungen in Bezug auf C-Syntax können Sie getrost ignorieren.

Die Unit-Tests erledigen verschiedene Variationen des Befehls »make check« oder »make test«, teils unter dem Benutzer tester (per »su tester -c«). Überspringen Sie diese Unit-Tests, sparen Sie viel Zeit, riskieren aber auch, das ein oder andere Problem im Build-Prozess zu übersehen.

Nach der ganzen Arbeit geht es ans Aufräumen: Nicht mehr benötigte Bibliotheken und Tools entfernen Sie ebenso wie den temporären Benutzer tester. Soll das erzeugte LFS nicht zum Programmieren dienen und benötigen Sie keine Debugging-Funktionalität, können Sie durch Bereinigen der Debugging-Symbole 2 GByte Platz einsparen (was der Code-Abschnitt berücksichtigt).

Zu guter Letzt verlassen Sie die Chroot-Umgebung, um sie gleich danach mit eingeschaltetem Bash-Path-Hashing (und ab nun immer mit dieser Konfiguration) erneut zu betreten [8].

System konfigurieren

In Kapitel 9 konfigurieren Sie den Netzwerk-Stack: Hier lässt sich auf Wunsch die Verwendung der sogenannten Predictable Network Interface Device Names abschalten, was wir tun (Listing 4). Auf diese Weise erkennt LFS die erste Netzwerkkarte im System klassisch als »eth0«. Die IP-Adresse liefert DHCP, anschließend konfigurieren Sie die Namensauflösung und vergeben einen Host-Namen.

Listing 4

Klassische Interface-Namen nutzen

# ln -s /dev/null /etc/systemd/network/99-default.link

Kleinere Konfigurationsarbeiten an der Systemuhr, der Linux-Konsole und den Einstellungen der System Locales runden das System ab (das entsprechende Linuxfabrik-Listing [9] stellt ein schweizerdeutsches Tastaturlayout ein.) Bei Zweifeln hinsichtlich der passenden Locales suchen Sie sich in LFS aus »/usr/share/keymaps« und »/usr/share/consolefonts« das Passende heraus.

System bootfähig machen

Nun gilt es, LFS bootfähig einzurichten. Abschnitt 10 startet mit dem Anlegen einer »/etc/fstab«, die auf die LFS-Partitionen »sda1« (System) und »sda2« (Swap) verweist. Anschließend (und erst jetzt) bauen und installieren Sie den Linux-Kernel. Da spätere Erweiterungen wie in BLFS immer wieder Umkonfigurationen am Kernel erfordern, löschen Sie dessen Quellcode nach dem Build nicht. Mithilfe von Grub-install gelangt der Bootloader auf die Platte, den Sie anschließend noch per Grub-mkconfig konfigurieren.

LFS konfiguriert den Kernel im Code-Abschnitt per »make defconfig« ohne Interaktion mit sinnvollen Vorgabewerten. Bei Bedarf ändern Sie das und nutzen per »make menuconfig« die TUI-Variante, um die unzähligen Parameter des Kernels manuell zu setzen. Wichtig: Die Macher von Systemd empfehlen dringen die Verwendung von IPv6. Entsprechend sollten Sie die Kernel-Features aus Listing  4 ein- beziehungsweise abschalten. Auch zu Kapitel 10 finden sich passende Listings bei der Linuxfabrik [10].

Listing 5

Wichtige Kernel-Features

General setup -->
  [ ] Auditing Support [CONFIG_AUDIT]
  [*] Control Group support [CONFIG_CGROUPS]
  [ ] Enable deprecated sysfs features to support old userspace tools [CONFIG_SYSFS_DEPRECATED]
  [*] Configure standard kernel features (expert users) [CONFIG_EXPERT] --->
    [*] open by fhandle syscalls [CONFIG_FHANDLE]
  Processor type and features  --->
    [*] Enable seccomp to safely compute untrusted bytecode [CONFIG_SECCOMP]
  Firmware Drivers  --->
    [*] Export DMI identification via sysfs to userspace [CONFIG_DMIID]
  Networking support  --->
    Networking options  --->
      <*> The IPv6 protocol [CONFIG_IPV6]
  Device Drivers  --->
    Generic Driver Options  --->
      [ ] Support for uevent helper [CONFIG_UEVENT_HELPER]
      [*] Maintain a devtmpfs filesystem to mount at /dev [CONFIG_DEVTMPFS]
  Firmware Loader --->
    [ ] Enable the firmware sysfs fallback mechanism [CONFIG_FW_LOADER_USER_HELPER]
  File systems  --->
    [*] Inotify support for userspace [CONFIG_INOTIFY_USER]
    Pseudo filesystems  --->
      [*] Tmpfs POSIX Access Control Lists [CONFIG_TMPFS_POSIX_ACL]

Abschluss und Reboot

Sie haben LFS nun vollständig installiert und dürfen sich stolz in den beiden Dateien »/etc/lsb-release« und »/etc/os-release« verewigen. Ein Logout aus der Chroot, das Setzen eines Root-Passworts, ein Aushängen von »$LFS« – und einem Reboot steht nichts mehr im Weg. Haben Sie die LFS-Disk als primäres Boot-Medium in der Build-VM ausgewählt, meldet sich nach kurzer Zeit ihr LFS. Listings zu Kapitel 11 gibt es wieder bei der Linuxfabrik [11]. (jcb/jlu)

Der Autor

Markus Frei beschäftigt sich seit über 25 Jahren mit Server-Anwendungen sowie dem Zusammenspiel von Infrastruktur und Softwareentwicklung. Er ist Mitinhaber der Linuxfabrik in Zürich, die Service und Support für Linux und Open-Source-Applikationen sowie Managed Hosting in einem Datacenter in der Schweiz anbietet.

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 4 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Nach oben