Posts mit dem Label LFS werden angezeigt. Alle Posts anzeigen
Posts mit dem Label LFS werden angezeigt. Alle Posts anzeigen

10.02.2022

Linux from Scratch in einer Linux-VM installieren

Ich bin seit langem ein großer Fan von "Linux from Scratch", bei dem man ein komplettes Linux-System aus den Quelltexten kompiliert und installiert. Ich schrieb vor längerem schon mal darüber, wie man dieses Kompilieren automatisieren kann.

Das hat bislang auf "Blech" gut funktioniert. In letzter Zeit habe ich sehr viel mit virtuellen Maschinen gearbeitet und den Ehrgeiz entwickelt, auch ein LFS in einer VM zum Laufen zu bekommen.

Ich habe meine bisherigen LFS-Versionen direkt nach dem ersten erfolgreichen Booten als tar-Archiv gesichert, und ich dachte mir, es wäre doch schön, diese ganzen historischen Schätzchen auch ohne zugehörige Hardware weiter zu benutzen.

Es hat ein bißchen Schweiß gekostet, aber schlussendlich ist es gelungen. Ich habe eine VM mit einer alten LFS-Version bootfähig machen können. Zwischendurch gab es ein paar Stolpersteine, deshalb schreibe ich das alles mal auf ;)

Das Gastsystem ist ein Fedora mit dem installierten @Virtualization-Paket. Darin enthalten ist eine nette GUI namens virt-manager, mit der man bequem neue VMs erstellen oder eine VM aus einem bestehender Imagedatei importieren kann.

Genau wie "Linux from scratch" wollte ich aber auch eine "VM from scratch" bauen. Dafür brauche ich zuerst eine "leere" Datei im Format qcow2. Das ist gut für kleine Festplatten, weil es ein "sparse" Dateiformat ist - die Datei ist nicht so groß wie definiert, sondern wächst an ihren Aufgaben bzw. der Füllung, "leere" Lücken belegen keinen Speicherplatz. Diese leere Datei habe ich mit dem folgenden Befehl erzeugt:

# qemu-img create -f qcow2 LFS-11.0-32.qcow2 2048M

Wie üblich bei LFS braucht man für den allerersten Schritt ein Gastsystem, auf dem man die LFS-Quellen kompilieren kann. Wenn man schon ein fertiges LFS als Archiv hat, entfällt dieser Schritt, aber es ist sehr praktisch, auf dem Gastsystem eine ISO-Datei mit einem "Live Linux" für Reparaturzwecke greifbar zu haben. Diese ISO-Datei bietet man der VM als virtuelles CD- bzw. DVD-Laufwerk an und kann ggfs. sogar davon booten, um sich die VM "von innen" anzuschauen. Das funktioniert selbst dann, wenn das LFS noch nicht bootfähig ist.

qcow2 kann man als Dateisystem auf dem Gastsystem mounten, wenn die VM nicht gestartet ist. Auf diese Weise kann man die virtuelle Festplatte (in Linuxjargon aus Sicht der VM /dev/vda) partitionieren und formatieren.

Das Stichwort "virtuelle Festplatte" deutet schon den ersten Stolperstein an: als Sparbrötchen habe ich beim Kompilieren des Linuxkernels für LFS auf echtem Blech nur die allernötigsten Optionen eingeschaltet, und die reichen nicht aus für den Betrieb in einer VM. Man muss die Optionen für "virtio" (das bedeutet "ich bin eine virtuelle Maschine") aktivieren, und ohne Basteleien an der "initial ramdisk" initrd ist es besser, wenn man alle virtio-Funktionen fest in den Kernel aufnimmt und gerade nicht als Module. Wenn man das Modul virtio_blk beim Booten nicht hat, findet der Kernel seine virtuelle Festplatte nicht und stoppt mit einem "kernel panic".

Während man also die virtuelle Festplatte vorbereitet, kann in einem anderen Fenster nebenbei ein Kernel kompiliert werden ;). Mindestens die Optionen CONFIG_VIRTIO und CONFIG_VIRTIO_BLK müssen in der .config-Datei auf "y" gesetzt werden (nicht "m"). Alle einzuschalten (console, net, iommu etc.) ist natürlich nicht verkehrt, soviel größer wird der kompilierte Kernel dadurch nicht. Da ich nur eine Sorte Kernel kompilieren will, der auf Blech und virtuell funktionieren soll, brauche ich auch das Modul für SATA-Festplatten fest eingebaut, d.h.  das SCSI-Disk-Modul "sd" ist nötig, also ist auch CONFIG_BLK_DEV_SD auf "y" zu setzen. Wer sich mit initialen Ramdisks auskennt, kann natürlich sportlich alle Optionen auf "m" für "module compile" setzen und eine initrd erzeugen. Das wird aber in LFS nicht gut unterstützt und ist deshalb "left as an exercise to the reader" ;)

Für das Mounten der VM-Festplatte auf dem VM-Host ist ein Linuxmodul "nbd" nötig, das die Verbindung zwischen der qcow2-Datei und einem Device in /dev herstellt. Ob man nun eine Partition für alles baut oder sich am moderneren EFI-Layout mit separaten Partitionen für /boot etc. orientiert, ist bei LFS eigentlich ziemlich egal.

Nach dem Erzeugen der qcow2-Datei muss das Device mit qemu-nbd gemountet ("connected") werden. Danach kann man mit fdisk oder parted die gewünschten Partitionen anlegen und formatieren. Die Größe der Partitionen ist im Skript unten hartkodiert und muss ggfs. angepasst werden. Die Größe der 3. Partition (root) erfährt man mit dem Befehl "parted devicename print". Dies ist der Wert beim letzten mkpart-Befehl (hier fett hervorgehoben).

# parted /dev/nbd0 print
Model: Unknown (unknown)
Disk /dev/nbd0: 2147MB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:

Number  Start   End     Size    Type     File system  Flags
 1      1049kB  257MB   256MB   primary
 2      258MB   514MB   256MB   primary
 3      515MB   2147MB  1633MB  primary

Dieses Skript erledigt nun die ganze Arbeit, eine qcow2-Datei als virtuelle Festplatte für die VM vorzubereiten und mit Filesystemen zu versehen.

TO=/dev/nbd0
BOOT="${TO}p1"

SWAP="${TO}p2"

ROOT="${TO}p3"

img=~ths/kvm/LFS-10.1-32.qcow2
size=2048M
MNTROOT=/mnt/lfs
MNTBOOT="${MNTROOT}/boot"

modprobe nbd max_part=8
mkdir -p "${MNTROOT}"
qemu-img create -f qcow2 "${img}"
"${size}"

qemu-nbd --connect="${TO}" "${img}"

P="parted --script --fix ${TO}"

${P} mklabel msdos
${P} mkpart primary ext4   1  257
${P} mkpart primary ext4 258  514
${P} mkpart primary ext4 515 2147
${P} set 1 boot on
${P} print 

mkfs -t ext4 "${BOOT}"
mkfs -t ext4
"${ROOT}"
mkswap /dev/nbd0p2

mount "${ROOT}" "${MNTROOT}"
mkdir -p "${MNTBOOT}"
mount "${BOOT}" "${MNTBOOT}"

Als nächstes wird das LFS-Archiv ausgepackt. Mein Beispiel zeigt ein historisches LFS 8.0 mit einem damaligen Kernel 4.10 ;), man könnte aber auch ein aktuelles LFS verwenden.

cd /mnt/lfs
tar xf LFS-8.0-32.tar

Danach müssen die grub.cfg und die fstab angepasst werden, damit die Namen der Partitionen mit denen übereinstimmen, die die VM zur Verfügung stellt.

/dev/vda3 /      ext4  defaults  1 1
/dev/vda2 swap   swap  pri=1     0 0
/dev/vda1 /boot  ext4  defaults  1 1

Es ist auch eine gute Idee zu kontrollieren, ob die Verzeichnisse, die so ein Linuxsystem zum Betrieb benötigt, alle da sind, z.B. /tmp mit den richtigen Berechtigungen (wer weiß, was die "1" in "1777" bedeutet?), /var/log, /run, /sys usw. Danach folgt der (für mich) größte Stolperstein. Es ist nun nötig, dem Bootloader grub mitzuteilen, wie Linux von der Festplatte gestartet wird.

Der dazu nötige "Trick" ist sogar in der LFS-Anleitung beschrieben. Man muss nur auf die Idee kommen, dass diese Schritte auch für das Erzeugen einer VM nötig sind!

Die virtuellen Devices, die ein Linuxkernel zum Funktionieren benötigt, muss man in die künftige VM "duplizieren". Diesen Vorgang nennt man "bind mount", das ist eine Kopie eines Mounts an einer zweiten Stelle. Mit Hilfe dieser Devices ist grub erst in der Lage, die virtuelle Festplatte bootfähig zu machen.

export LFS=/mnt/lfs
mkdir -pv $LFS/{dev,proc,sys,run}
mknod -m 600 $LFS/dev/console c 5 1
mknod -m 666 $LFS/dev/null c 1 3
mount -v --bind /dev $LFS/dev
mount -v --bind /dev/pts $LFS/dev/pts
mount -vt proc proc $LFS/proc
mount -vt sysfs sysfs $LFS/sys
mount -vt tmpfs tmpfs $LFS/run
if [ -h $LFS/dev/shm ]; then
  mkdir -pv $LFS/$(readlink $LFS/dev/shm)
fi

Beim Auspacken eines "fertigen" LFS-Archivs sind die Device-Nodes für console und null natürlich schon da; die Fehlermeldungen kann man getrost ignorieren. Der Vollständigkeit halber habe ich die Schritte aus der LFS-Anleitung komplett zitiert.

Der nächste Schritt ist genauso wichtig: grub muss die "echten" Pfad- und Dateinamen sehen können. Dazu muss man mit chroot in das neue LFS-System wechseln.

export LFS=/mnt/lfs
chroot "${LFS}" /usr/bin/env -i \
    HOME=/root                  \
    TERM="${TERM}"              \
    PS1='(lfs chroot) \u:\w\$ ' \
    PATH=/usr/bin:/usr/sbin     \
    /bin/bash --login +h

In dieser schon "halb" simulierten Umgebung führt man grub-install auf die virtuelle Festplatte aus. Falls Host und Gast unterschiedlich "große" CPUs verwenden, muss grub Bescheid wissen, ob das Gast-LFS mit 32 Bit läuft. Ansonsten reicht es, das Device anzugeben.

grub-install /dev/nbd0

bzw.

grub-install --target=i386-pc /dev/nbd0

Danach kann man mit exit die chroot-Umgebung verlassen und die "bind mounts" wieder rückgängig machen.

export LFS=/mnt/lfs
umount $LFS/dev/pts
umount $LFS/dev
umount $LFS/proc
umount $LFS/sys
umount $LFS/run
 

Weiter oben hatte ich vorgeschlagen, in einem anderen Fenster einen Linuxkernel vorzubereiten, in dem die VIRTIO-Optionen alle fest in den Kernel eingebaut sind. Diesen Kernel sollte man nun nach /boot in die virtuelle Festplatte kopieren und den exakten Dateinamen in die grub.cfg eintragen. Die Anweisung "set root" in grub.cfg weist den Bootloader auf die Partition hin, in der der Kernel zu finden ist. In der Kernelkommandozeile hingegen ist "root=..." die Angabe, auf welcher Partition das Root-Filesystem dieser Linuxinstallation zu finden ist. Meine sehr schlichte /boot/grub/grub.cfg sieht so aus:

# Begin /boot/grub/grub.cfg
set default=0
set timeout=2

insmod ext2
insmod gzio
insmod part_msdos
set menu_color_normal=white/black
set menu_color_highlight=black/light-gray
font="/usr/share/grub/unicode.pf2"

menuentry "Linux from Scratch 11.0 5.15.21-64 vda3" {
    set root=(hd0,1)
    linux  /vmlinuz-5.15.21-64 root=/dev/vda3 ro
    initrd /initrd-5.15.21-64.img
}

Als vorletzten Schritt muss man nun die virtuellen Festplattenpartitionen unmounten und nbd anweisen, die Verbindung zwischen der qcow2-Datei und dem Device aufzuheben.

umount "${MNTBOOT}"
umount "${MNTROOT}"
qemu-nbd --disconnect "${TO}"
rmmod nbd

Nachdem die Datei der virtuellen Festplatte freigegeben ist, kann entweder mit dem GUI virt-manager oder auf der Kommandozeile mit virsh die eigentliche virtuelle Maschine aus der qcow2-Datei erzeugt werden. Im GUI klickt man sich durch das "create"-Menü und wählt "import existing disk image" und den Dateinamen aus. In der virsh verwendet man am besten eine bestehende VM als Muster, um die Definition als XML-Datei zu exportieren, ein paar Zeilen zu ändern (falls später die beiden VMs parallel laufen sollen), insbesondere natürlich den Dateinamen der neuen qcow2-Datei.

# virsh dumpxml vm-lfs1-11.0-64 > vm-lfs2-11.0-64.xml

Ich schlage vor, die folgenden Einträge in der XML-Datei zu verändern. Die man-Page von virsh empfiehlt, auch die UUID zu verändern oder die Zeile komplett zu löschen - dann vergibt virsh eine neue UUID.

>   <name>vm-lfs2-11.0-64</name>
>   <title>vm-lfs2-11.0-64</title>
>   <description>vm-lfs2-11.0-64</description>
<       <source file='/home/ths/kvm/vm-lfs1-11.0-64.qcow2'/>
>       <mac address='52:54:00:01:d1:22'/>

Dann kann man mit "define" die neue "Domain" bekannt machen. "Domain" ist der Begriff, mit dem KVM die virtuellen Maschinen bezeichnet.

# virsh define vm-lfs2-11.0-64.xml

Die VM kann jetzt gestartet werden, entweder im GUI des virt-manager oder mit virsh auf der Kommandozeile. Falls der Bootvorgang mit einem "kernel panic" und dem Hinweis "unknown block device (0,0)" endet, kann der Linuxkernel die virtuelle Festplatte nicht erkennen. Dann hat etwas beim Kompilieren des Kernels nicht geklappt mit der Aktivierung der VIRTIO-Optionen.

# virsh start vm-lfs2-11.0-64

Eine alternative Möglichkeit zur Reparatur ist das Starten der VM mit einer ISO-Datei eines Live-Linux-Systems. Dieses System sollte die virtuelle Festplatte ebenfalls erkennen, und man kann erneut versuchen, mit dem chroot-Schritt von oben die VM bootfähig zu machen.

15.03.2018

Ich bau mir ein Linux wie es mir gefällt - 1

Seit langen Jahren bin ich begeisterter Nutzer einer ganz speziellen Linux-Variante namens "Linux from scratch".

Wie der Name sagt, wird dabei ein Linux-System von Grund auf ("from scratch") komplett neu gebaut. Dazu werden die Quelltexte verwendet, um selbst alle Pakete zu übersetzen und einen PC (oder ein anderes Rechnersystem) mit einem von Festplatte startfähigen Linux auszustatten.

Dabei hat man natürlich ein Henne-Ei-Problem: man braucht ein Linux, um das neue Linux zusammen zu bauen. Für dieses Problem gibt es mehrere Lösungsmöglichkeiten: zum Einen kann man das LFS in einer virtuellen Maschine zusammenbauen. Alternativ kann man auf dem Zielrechner zunächst eine kleine Partition mit einem anderen Linux einrichten, von dort starten und dann das LFS auf den freien Platz der Festplatte werfen. Das Linux zum Bauen kann man dann hinterher als Notfallsystem behalten, oder aus der Partition dann die Swappartition machen oder sie anderweitig verwenden. Für das allererste ("bootstrap") Linux reicht eine Minipartition von 8-16 GB, und dort kann man dann z.B. ein Linux Mint oder Fedora installieren.

"Linux from Scratch" ist nicht nur eine Anleitung für ein Linux zum Selbstbasteln, sondern ist hauptsächlich dazu gedacht, Erfahrung beim Bauen von Linuxprogrammen aus den Quelltexten zu sammeln.

Man lernt dabei eine ganze Menge, nämlich vor allem Zusammenhänge zwischen den verschiedenen Programmen, Konfigurationsdateien, aber auch viel über Netzwerke, und vor allem natürlich über die technischen Voraussetzungen, darunter die Verwendung von Makefiles, und man lernt, Logfiles zu lesen, um die Probleme zu beseitigen.

Bei "LFS" gibt es verschiedene Steigerungsmöglichkeiten, wie man sich selbst das Leben schwer und spannend machen kann. Man kann das Basissystem bauen, dann hat man "nur" eine Linux-Kommandozeile.

Darauf aufbauend ist es mit dem Fortsetzungsbuch "Beyond Linux from scratch" (BLFS) möglich, einen Server zu bauen, mit dem man z.B. eine Firewall, ein NAS, einen Webserver, oder noch viel mehr zusammen bauen kann. Oder man baut sich ein Desktopsystem mit einer grafischen Oberfläche, und am Ende steht dann der selbst kompilierte Firefox. Hört sich das nicht cool an?

Eine Schwierigkeitsstufe darüber steht die Automation des Bau-Vorgangs - und hauptsächlich darüber will ich hier schreiben. Die Anleitung von "LFS" für ein Basissystem und für das "BLFS" sind ziemlich gut. Wenn man sich daran hält, hat man gute Chancen, manuell Schritt für Schritt ein lauffähiges System zu erstellen. Man geht am Browser durch alle Kapitel der Reihe nach durch und führt gemäß Anleitung die einzelnen Schritte durch. Das ist beim ersten Mal ehrlich gesagt mühsam - ich spreche aus Erfahrung ;-). Andererseits ist es spannend zu erleben, wie das Linuxsystem mit jedem Schritt wächst und tatsächlich immer mehr dem ähnelt, was man als Linuxbenutzer kennt.

Die Automation "Automated Linux from scratch" (ALFS) hingegen ist in einer README-Datei nur spärlich beschrieben. Dafür hat man, wenn man das System einmal verinnerlicht hat, eine wunderbare und sehr esoterische Möglichkeit, Updates zu installieren oder das gesamte System auf Knopfdruck noch einmal komplett neu zu erzeugen.

Auf diese Weise habe ich über Nacht ein Linux gebaut - besser gesagt: bauen lassen, das mit dem neuesten Kernel 4.15 und C-Compiler gcc 7.3 gegen Spectre und Meltdown gefeit wäre. - Wenn, ja wenn mein Bastelsystem ein 64-bit-System gewesen wäre (x86_64) und nicht 32 Bit (i686). Und wenn ich das Gefühl hätte, dass ich wirklich von diesen Sicherheitslücken bedroht bin. Aber zum Thema Risikoanalyse muss ich noch mal einen eigenen Artikel schreiben, denke ich.

Und noch eine Stufe darüber verwendet man nicht die "stable" Variante der Bücher, sondern die LFS- und BLFS-Bücher, die gerade in der Entwicklung sind. Die Autoren der Kapitel sind permanent damit beschäftigt, neue Versionen der Pakete zu integrieren und die Artikel anzupassen. Wenn sich z.B. bei einem Paket das Verfahren ändert, wie man das Paket lauffähig kompiliert, wird das enorm schnell in die "development"-Variante von LFS bzw. BLFS übernommen. Genau das ist mir während des Schreibens dieses Artikels passiert: ich habe das Makefile angestoßen, und mittendrin wurde im Buch auf ncurses 6.1 umgestellt. Kaum war ich fertig, veröffentlichen die Entwickler die neue glibc-Version 2.27. So kann's gehen ...

Um noch mal auf "Meltdown" und "Spectre" zurück zu kommen: das LFS-Buch enthält seit kurzem brandneue Kernel- und gcc-Versionen, die diese Sicherheitslücken beheben sollen. Das ist natürlich immer "work in progress". Der aktuelle Stand ist, dass Spectre V2 und Meltdown repariert sind, und für Spectre V1 ist noch einiges an Entwicklerarbeit zu leisten. Die alten 32-bit-Prozessoren hinken hier leider hinterher, für 64 bit (also alles, was grob jünger als 10 Jahre ist) sollten alle Fixes auf jeden Fall in Arbeit sein.

So, jetzt aber wie versprochen eine genauere Beschreibung der Automation für LFS (ALFS). Dazu lädt man sich zunächst das aktuelle Paket herunter. Der Paketname lautet aus historischen Gründen "jhalfs" und ehrt damit den ersten Entwickler des Verfahrens mit seinen Initialen "jh".

Damit jhalfs funktioniert, benötigt es ein paar zusätzliche Linux-Pakete auf dem Gastsystem, die man nachinstallieren muss, weil sie normalerweise nicht für Endbenutzer erforderlich sind. Dazu gehören natürlich der C-Compiler gcc und einige Hilfsprogramme und Bibliotheken wie bison, awk, ncurses etc. Das Schöne ist: wenn man jhalfs startet, kontrolliert es, was noch fehlt und beschwert sich. Für die Verwendung der "development"-Fassung muss man außerdem noch Subversion installieren, um die tagesaktuellen Dateien aus dem Versionskontrollsystem herunter zu laden.

Die Automation setzt tatsächlich schon bei den allerersten Schritten ein, die man gemäß LFS-Buch durchführen würde: dem Erzeugen eines neuen Unixbenutzers "lfs" für das Kompilieren der Pakete, dem Anlegen von Verzeichnissen usw. Der einzige Schritt, den man selbst durchführen muss, ist das Erzeugen einer Festplattenpartition und sie dann so zu mounten, dass die Skripte sie finden können. Traditionell ist das /mnt/lfs, man kann es aber nach Belieben ändern, wenn man weiß, was man tut.

Vorab ein paar Worte zum Design von jhalfs: die Autoren erstellen LFS und die weiteren Bücher in einem speziellen XML-Format namens "DocBook"; daraus werden mit trickreichen XSLT-Transformationen dann lesbare Bücher in HTML, PDF oder sogar TeX. jhalfs verwendet ebenfalls XSLT-Transformationen, um aus dem Buch die Kommandozeilenanweisungen herauszufiltern, und erstellt daraus ein Makefile und Installationsskripte für jedes Kapitel, d.h. jedes zu installierende Paket. Die Automation mit ALFS ist also ein zweistufiger Prozess: zuerst werden aus dem Buch die Befehle zum Bauen herausgefiltert und in ein Makefile mit Hilfsskripten umgewandelt, und in einem zweiten Schritt wird diese Befehlsliste ausgeführt.

Die Autoren liefern außerdem eine komplette Liste der Softwarepakete mit Versionsnummern, die man entweder selbst herunterladen kann oder die automatisch nach Bedarf geholt werden, wenn man eine einigermaßen schnelle Internetverbindung hat. Grob geschätzt muss man für LFS ca. 400 MB an Paketen herunterladen und für BLFS, je nach Umfang, bis zu 2 GB.

Die Pakete sollte man in einem Verzeichnis /mnt/lfs/sources unterhalb der zukünftigen LFS-Partition ablegen, damit während des Ablaufs alle Pakete gefunden werden können. Hierzu wird eine Unix-Technik namens "chroot" (change root) verwendet, um einem laufenden Programm eine andere Festplattenstruktur vorzutäuschen - effektiv wird das verwendet, um so zu tun, als würde es schon in der "richtigen" LFS-Umgebung ausgeführt werden.

Das Makefile für LFS wird in einem neuen Unterverzeichnis erzeugt, in das man nach der Festlegung von ein paar Grundannahmen wechseln muss. Genau wie /sources sollte dieses Verzeichnis auf der zukünftigen LFS-Partition liegen, damit alle Skripte auch in der "chroot"-Umgebung erreichbar sind. Wenn man nun dort "make" startet, sollte nach einigen Stunden (je nach Geschwindigkeit des Rechners*) ein fast schon startfähiges Linux auf der neuen Partition vorliegen. Schritte, die man automatisieren kann, aber nicht muss, sind das Kompilieren eines Kernels und das Festlegen der Partitionen, die beim Starten gemountet werden sollen.

*) Als grobe Richtschnur zwei Vergleichszahlen, die ich mit der aktuellen LFS-Version gemessen habe. Das Paket, das mit großem Abstand am längsten dauert, ist der finale Schritt, den C-Compiler zu kompilieren. Auf einem alten Pentium 4 dauert das 540 Minuten, auf einem etwas aktuelleren i5-3350 immer noch knapp 230 Minuten. Eine SSD statt einer Festplatte ist hierbei eine große Hilfe, Zeit zu sparen.

Der erste Schritt nach dem Auspacken des jhalfs-Pakets ist, in das Verzeichnis zu wechseln und dort "make" einzugeben. Wer schon mal einen Kernel selbst kompiliert hat, wird gleich einen Aha-Effekt erleben: die Konfiguration verwendet dieselbe Textoberfläche für die Menüauswahl wie der Kernel beim "make menuconfig". Hier kann man LFS/BLFS wählen, "stable" oder "development", ob der Kernel auch automatisiert kompiliert werden soll und einiges mehr. Die Grundeinstellungen sind gar nicht so schlecht, und man sollte nur ändern, was man auch versteht ;-)

Nach dem "exit" aus diesem ALFS-Menü kommt die Frage, ob man zufrieden ist mit der Konfiguration. Bei "yes" werden die schon erwähnten Prüfungen durchgeführt, ob alle Developerpakete auf dem Gastlinux vorhanden sind und bestimmte Mindestvoraussetzungen erfüllen, wie z.B. gcc mindestens in Version 4.6 (weil frühere Versionen Fehler enthalten, so dass das LFS nicht funktionieren würde oder Pakete nicht kompiliert werden können usw.). Wenn das alles erfolgreich geprüft wurde, wechselt man (als Benutzer, nicht als root) in das neue Verzeichnis und startet dort erneut "make".

Coole Socken schauen sich im Unterverzeichnis lfs-commands die Dateien unterhalb von chapter040506 und 07 erst mit dem Editor an, bevor sie make starten. Man könnte insbesondere bei den Dateien für Kapitel 7 direkt auf die Idee kommen, dort die Stellen noch zu bearbeiten, die mit **EDITME** markiert sind ;-)

Parallel dazu empfiehlt es sich, dieselbe Version des Buchs in einem Browser zu öffnen und die Kapitel zu lesen, die der Automatismus auch gerade zu bauen versucht. Bei Problemen kann man dann im jeweiligen Kapitel nachlesen, was gerade passiert.

Wahrscheinlich wird es beim ersten Mal nicht gleich komplett erfolgreich funktionieren; das ist aber nicht schlimm: wenn ein Fehler gemeldet wird, kann man sich die Logdatei dieses Schritts aus dem Unterverzeichnis logs anschauen, den Fehler beheben und dann einfach wieder "make" eingeben. Das make-Programm ist so schlau, dass es an derselben Stelle weitermacht. Manche Pakete werden mehrfach gebaut, z.B. der C-Compiler und einige Hilfsprogramme. Dies hat den Zweck, dass man nicht aus Versehen Abhängigkeiten zum Gastsystem einbaut. Wenn ein wichtiges Programm mit der C-Bibliothek des Gastsystems gelinkt wäre, würde das nach dem Booten ja nicht mehr funktionieren. Also wird ein "Zwischensystem" in einem anderen Pfad (/tools) gebaut, das dann das endgültige LFS kompilieren kann.

Ein paar generelle Tipps, wenn das Kompilieren eines Pakets fehlschlägt: gcc 7.3 ist "bleeding edge", also so brandneu, dass man sich bei Verwendung auch mal in die Finger schneiden kann ;-). Ich will damit sagen, dass hier soviele Änderungen drin sind, z.B. Anpassungen an die aktuellen C- und C++-Standards, die in den meisten Paketen und in den Gehirnen der Entwickler noch nicht angekommen sind. Aber: man will gcc 7.3 und aktuelle binutils einsetzen, weil hier die Reparaturarbeiten für Meltdown und Spectre stattfinden. Trotzdem nochmal die Empfehlung: erst mal ein "stable" LFS bauen und danach als Steigerung auf "development" umsteigen.

Typisches Problem beim Kompilieren:
  • ein C-Programm verwendet Datentypen, die jetzt in einer neu eingeführten include-Datei <stdint.h> deklariert werden und nicht mehr in <stdlib.h>. Vorläufig muss man also irgendwo den zusätzlichen include-Befehl unterbringen, bevor das Programm erfolgreich kompiliert werden kann. Freundlicherweise sagt make sehr genau, wo ein Problem aufgetreten ist.
  • beim Korrigieren eines Problems hat man Dateien oder Verzeichnisse als root editiert oder bearbeitet. Das führt zu einem Folgefehler, weil LFS alles mit dem Benutzer "lfs" durchführen will und dann ein Abbruch wegen Berechtigungsproblemen passieren kann (bringe ich laufend fertig).
    chown lfs:lfs is your friend.
Was mir noch untergekommen ist:
  • das gcc-interne Makro __sigemptyset gibt es nicht mehr. Man kann stattdessen sigemptyset (ohne die Unterstriche) verwenden.
  • die Makros major und minor für Devices sind jetzt in <sys/sysmacros.h> zu finden.
  • grub lässt sich mit binutils 2.30 auf 32-bit-Systemen nicht übersetzen, man muss zusätzlich zu den configure-Switches im LFS-Buch noch den Switch "--enable-64-bit-bfd" angeben. Alternativ binutils 2.29.1 statt 2.30 verwenden.
Zwei Schritte sind im LFS-Buch etwas vage beschrieben, weil sie sehr stark vom eigenen PC abhängen: die Konfiguration des Kernels und die bootfähige Festplatte. Wenn man ein Linux-Gastsystem mit "kernelconfig"-Unterstützung hat, kann man sich selbst einen maßgeschneiderten Kernel bauen mit "make oldconfig", das verwendet dann nämlich genau die Einstellungen, die im Moment aktuell sind.

Der einzige wichtige Punkt ist, dass das Filesystemformat des Bootlaufwerks in den Kernel fest kompiliert sein soll. Es nutzt nichts, wenn das Root-Filesystem mit ext4 formatiert ist und der Kernel die ext4-Fähigkeit erst mit einem Modul lernen soll, das auf der ext4-Festplatte liegt - hier beißt sich die Katze in den Schwanz. Ach ja: nach dem Kompilieren von Kernel und Modulen unbedingt in der chroot-Shell "make modules_install" durchführen und in /lib/modules kontrollieren!

Für spätere Bildungs- und Forschungszwecke kann man natürlich auch eine "initiale Ramdisk" (initrd) vorsehen und dort benötigte Module und sonstige Files ablegen, aber wenn man sich einen maßgeschneiderten Kernel für diesen speziellen PC erzeugt, ist das erst mal nicht nötig. Man kann es später machen, um Microcode-Updates per "early load" zu aktivieren, aber lebensnotwendig ist es erst mal nicht. Für intel-Prozessoren gibt es hier die Microcode-Dateien.

Vor dem Reboot sollte man dann im neuen /etc-Verzeichnis (das derzeit noch /mnt/lfs/etc heißt) nach Dateien schauen, die ein "**EDITME**" enthalten. Dort kann man dann Hostname, IP-Adresse, Nameserver etc. einbauen, die zur eigenen Umgebung passen (wenn man keinen eigenen DNS-Server betreibt, die IP-Adresse des DSL-Routers, der als DNS-Forwarder die Anfragen an den Provider weiterreicht). Die Dateien findet man mit einem beherzten "grep -l EDITME /mnt/lfs/etc/*", und wenn man gleich auf einen Schwung alle bearbeiten will, wirft man die Liste der Ergebnisse einfach einem Editor vor: "vi $(grep -l EDITME /mnt/lfs/etc/*)".

Die wichtigste Datei ist hier die /etc/fstab, die beschreibt, welche Partition der Festplatte welchen Zweck hat und wohin im Filesystem gehört. Eine Swappartition sollte man auf jeden Fall vorsehen, selbst wenn der PC gefühlt genug RAM eingebaut hat. Eine Begründung dafür findet sich hier (der Autor des Artikels arbeitet bei Facebook in einem Rechenzentrum). Früher gab es mal eine Regel "doppelt soviel Swap wie RAM", aber ich würde prinzipiell für normale Desktopsysteme zwischen 2 und 8 GB Swap vorsehen.

Was man auf keinen Fall vergessen darf: dem root-User des neuen LFS-Systems vor dem Reboot ein Passwort zu geben. Sonst kann man zwar wunderbar booten, aber sich nicht anmelden ;-)

Ganz ehrlich: das Gefühl ist unbeschreiblich, wenn das selbstkompilierte Linux dann tatsächlich zum ersten Mal bootet und man den Loginprompt sieht ;-)

Wenn das neue System nicht booten will, muss man Ursachenforschung betreiben. Ein "kernel panic" tritt auf, wenn das Root-Filesystem nicht gefunden wurde. Vermutlich fehlt dann das Kernelmodul für das betreffende Filesystem. Oder die Angabe in der /boot/grub/grub.cfg passt nicht, welche Partition das ist. Die Schreibweise von grub und Linux unterscheiden sich hier leider etwas. "sda" für Linux ist "hd0" in grub.

Ein Fehler, den ich mal gemacht habe: alle USB-Treiber als Module deklariert und dann nicht in /etc/modules definiert. In dieser Situation wird es für eine USB-Tastatur schwierig, ihre Eingaben abzuliefern :-).

In solchen Situationen bootet man dann erneut das Gastsystem, mountet die LFS-Partition wieder unter /mnt/lfs und fängt an zu reparieren.

Wenn es bootet und man sich anmelden kann, ist das Reparieren leichter: die Fehlermeldungen z.B. der init-Skripte stehen ja noch auf dem Bildschirm, und man kann Schritt für Schritt alles Nötige korrigieren und das neue, selbstgebaute Linuxsystem einrichten. Das LFS hat schon alles an Bord, um eigenständig Pakete zu übersetzen und zu installieren.

Nach dem erfolgreichen LFS geht es dann mit dem BLFS weiter.
Ein paar Vorschläge, wie man sich das LFS etwas bequemer bedienbar gestaltet:
  • gpm (copy+paste mit Maus in der Textoberfläche)
  • cpio (zum Bauen von initrd)
  • openssl (ssl-Bibliothek)
  • openssh (ssh server und client)
  • dhcpcd (IP-Adresse über DHCP setzen lassen)
  • nfs-utils (wenn man ein NAS hat, z.B. eine Fritzbox, oder selbst bauen will)
  • samba (wenn man selbst ein NAS aufbauen will oder einen Windows-Domaincontroller)
  • lm_sensors (Hardwareüberwachung von CPU und Lüfter)
  • lsusb (list USB devices)
  • lspci (list PCI devices)
  • ghostscript (für PDF)
  • cups (zum Drucken)
  • Firefox (für Katzenvideos im Internet)