Systemnähe beginnt beim Build
Im ersten Artikel dieser Serie habe ich beschrieben, warum ich wieder näher an die Systeme heran will. Weniger Plattform, mehr Betriebssystem. Weniger Konsum, mehr Konstruktion. Nicht aus Ablehnung gegenüber modernen Ansätzen, sondern aus dem Wunsch heraus, die Dinge wieder vollständig zu durchdringen.
Dieser Anspruch lässt sich nicht einlösen, wenn man bei vorgefertigten Artefakten stehen bleibt.
Ein System wirklich zu verstehen bedeutet, seinen Entstehungsprozess zu kennen. Zu sehen, welche Werkzeuge beteiligt sind, wo Abhängigkeiten entstehen und an welchen Stellen Entscheidungen getroffen werden. Erst wenn Kernel, Toolchain und Userspace nicht mehr als gegebene Blackbox erscheinen, sondern als Ergebnis konkreter Build-Schritte, wird ein System greifbar.
Ein selbstgebautes Betriebssystem verändert den Blick. Build-Abhängigkeiten werden sichtbar, Versionsstände bekommen Bedeutung, und „das System“ wird wieder zu etwas, das man erklären kann – nicht nur benutzen.
Genau deshalb beginne ich dieses Projekt nicht mit der Installation eines fertigen Images, sondern mit dem bewussten Aufbau von NetBSD aus dem offiziellen Source-Tree. Nicht als Selbstzweck und nicht als sportliche Übung, sondern als konsequente Fortsetzung der im ersten Artikel formulierten Haltung: Systeme bauen, nicht nur betreiben.
Reproduzierbarkeit als Ausgangspunkt
NetBSD bringt ein sehr leistungsfähiges Cross-Build-System mit. Es ist explizit dafür entworfen, auf fremden Hosts zu laufen und komplette Zielsysteme inklusive Toolchain zu erzeugen. In der Praxis heißt das: Man kann NetBSD auf Linux, macOS oder anderen BSDs bauen und dabei beliebige Zielarchitekturen ansprechen.
Theoretisch könnte ich den gesamten Build also direkt von meinem Linux-Notebook aus durchführen.
Das NetBSD-Buildsystem ist dafür ausgelegt, auf nicht-NetBSD-Systemen zu laufen.
Linux, macOS, andere BSDs – alles wird offiziell unterstützt.
Dokumentation:
https://www.netbsd.org/docs/guide/en/chap-build.html
Ich entscheide mich hier bewusst anders. Für dieses Projekt nutze ich ein altes ThinkPad X230 als dedizierte Build-Maschine – mein „Basecamp“. Nicht, weil es technisch notwendig wäre, sondern weil ich den gesamten Stack in einer Hand haben möchte: Firmware, Betriebssystem, Toolchain und Build-Umgebung.
Schritt 1: Offizielles NetBSD installieren
Bevor ich irgendetwas selbst baue, installiere ich zunächst ein offizielles, unverändertes NetBSD 10.1 auf dem X230. Diese Installation dient mir als saubere Referenzbasis, ohne eigene Anpassungen, ohne selbstgebaute Artefakte und ohne implizite Abhängigkeiten.
Ich orientiere mich dabei an der offiziellen Installationsanleitung von NetBSD, benutze allerdings EFI-Boot und entscheide mich für eine vollständige Installation statt einzelner Distributionskomponenten.
https://www.netbsd.org/docs/guide/en/chap-exinst.html
Erst offizielles Release, dann eigener Build.
Nach der Installation liegt ein vollständig unterstütztes, unverändertes NetBSD 10.1 auf dem System. Das ist der Ausgangspunkt für alle weiteren Schritte.
Einordnung: Releases, Stable und Branches
Bevor wir in den Build einsteigen, ist ein kurzer Blick auf die Release-Politik von NetBSD sinnvoll. Sie unterscheidet klar zwischen formalen Releases, Stable-Branches und den jeweils aktuellen Ständen innerhalb dieser Branches.
NetBSD arbeitet unter anderem mit:
- formalen Releases wie 10.1
- Stable-Branches wie netbsd-10
- aktuellen Ständen innerhalb des Branches, zum Beispiel 10.1_STABLE
Beispiel:
- NetBSD 10.1 → offizielles, veröffentlichtes Release
- netbsd-10 → Stable-Branch für die 10er-Serie
- NetBSD 10.1_STABLE → aktueller Stand des netbsd-10-Branches
Der Stable-Branch ist weiter als das letzte Release, garantiert ABI-stabil und wird kontinuierlich gepflegt.
Dokumentation:
https://www.netbsd.org/releases/release-map.html
Für dieses Projekt bedeutet das: Ich arbeite nicht auf dem Entwicklungs-Head und auch nicht auf einem eingefrorenen Release, sondern gezielt auf dem stabilen, gepflegten netbsd-10-Branch.
Schritt 2: Quellen beziehen
NetBSD wird primär in CVS entwickelt, stellt aber einen offiziellen Git-Mirror bereit. Für meinen Workflow ist Git deutlich praktischer, da es sich nahtlos in meine gewohnte Arbeitsweise einfügt.
Kernel & Base System:
https://github.com/NetBSD/src
X11 (xsrc):
https://github.com/NetBSD/xsrc
$ git clone https://github.com/NetBSD/src.git
$ git clone https://github.com/NetBSD/xsrc.git
Schritt 3: Auf den Stable-Branch wechseln
Nach dem Klonen wechsle ich in beiden Repositories gezielt auf den netbsd-10-Branch. Damit ist klar definiert, welcher Code-Stand gebaut wird und welche Stabilitätsannahmen gelten.
$ (cd src; git checkout netbsd-10)
$ (cd xsrc; git checkout netbsd-10)
Ein stabiler, nachvollziehbarer Stand.
Schritt 4: Build des gesamten Systems
Der eigentliche Build erfolgt über das zentrale build.sh-Skript. Dieses Skript ist der definierte Einstiegspunkt für alle Build-Schritte und kapselt den kompletten Prozess von der Toolchain bis zu den Installationsmedien.
Ich baue in dieser Reihenfolge:
- Toolchain
- Kernel, Userspace, X11
- Release-Sets
- Archive des verwendeten Quellcodes
- Installationsmedien
Der Build erfolgt in einem separaten Objektverzeichnis, um Quellen und Artefakte sauber zu trennen.
$ cd src
$ ./build.sh -m amd64 -x -X ../xsrc -O ../obj -j 2 -U tools
$ ./build.sh -m amd64 -x -X ../xsrc -O ../obj -j 2 -U release
$ ./build.sh -m amd64 -x -X ../xsrc -O ../obj -j 2 -U sourcesets
$ ./build.sh -m amd64 -x -X ../xsrc -O ../obj -j 2 -U iso-image
$ ./build.sh -m amd64 -x -X ../xsrc -O ../obj -j 2 -U install-image
Toolchain → World → Release → Images.
Reproducible Builds sind Build-Prozesse, die sicherstellen, dass aus dem gleichen Quellcode unter identischen Bedingungen bit-für-bit identische Binärartefakte entstehen. Dadurch lässt sich verifizieren, dass ein bestimmter Build wirklich aus dem angegebenen Quellcode stammt und weder in der Build-Umgebung noch im Build-Prozess verändert wurde.
NetBSD hat seit einigen Jahren an diesem Thema gearbeitet. Ab der amd64- und SPARC64-Architektur erreicht das Build-System heute reproduzierbare Builds, indem es deterministische Timestamps, geordnete Build-Schritte und stabile Build-Environment-Parameter verwendet. Ein spezielles Build-Flag (-P in build.sh) setzt intern Variablen wie MKREPRO und MKREPRO_TIMESTAMP, sodass die Artefakte auf Basis des neuesten Quellcode-Zeitstempels erzeugt werden – sehr ähnlich dem Standard SOURCE_DATE_EPOCH.
Technisch ist das im Wesentlichen eine Kombination aus:
- kontrollierten Timestamps (z. B. über
MKREPRO_TIMESTAMP/SOURCE_DATE_EPOCHstatt der aktuellen Uhrzeit) - deterministischem Dateisystem- und Build-Verhalten
- kein Einbetten von Build-Host-Informationen, die bei jedem Build variieren würden
- sortierten Build-Schritten und Artefakt-Listen
Das Ergebnis: Mehrere Builds aus dem gleichen Source-Tree ergeben identische Binaries.
Reproduzierbare Builds sind eine zusätzliche Qualitätssicherungsmaßnahme, die insbesondere bei sicherheitsrelevanten Systemen oder der Weitergabe von Artefakten an Dritte ihre Stärke ausspielen. Sie erhöhen die Transparenz des Build-Prozesses und ermöglichen es, Ergebnisse unabhängig nachzuvollziehen und zu verifizieren.
Dabei ist zu berücksichtigen, dass dieser Ansatz in der Praxis einen weiteren vollständigen Build-Durchlauf erfordert und sich die Gesamtbuildzeit entsprechend verlängert. Der zusätzliche Aufwand steht jedoch in einem klaren Verhältnis zum gewonnenen Vertrauen in die Artefakte und ist ein bewährtes Mittel, um Build-Prozesse noch robuster und überprüfbarer zu gestalten.
Ergebnis: Eigene Installationsmedien
Nach erfolgreichem Build liegen die erzeugten Artefakte im Verzeichnis obj/releasedir/images.
$ ls -la obj/releasedir/images
Beispiel:
-r--r--r-- 1 mpeterma wheel 445964077 NetBSD-10.1_STABLE-amd64-bios-install.img.gz
-r--r--r-- 1 mpeterma wheel 446075767 NetBSD-10.1_STABLE-amd64-install.img.gz
-r--r--r-- 1 mpeterma wheel 642115584 NetBSD-10.1_STABLE-amd64.iso
Das sind vollwertige Installationsimages, gebaut aus dem aktuellen Stable-Branch und mit meiner eigenen Toolchain.
Schritt 5: Boot-Datenträger erstellen
$ gunzip -k NetBSD-10.1_STABLE-amd64-install.img.gz
$ doas dd if=NetBSD-10.1_STABLE-amd64-install.img of=/dev/sd0 bs=4M
Ein Image, ein Blockdevice, ein Schreibvorgang.
Warum dieser Umweg?
Natürlich hätte ich auch einfach das offizielle Image verwenden können. Das wäre schneller und bequemer gewesen. Für dieses Projekt ist das aber nicht der Punkt.
Mir geht es darum, zu wissen, was in meinem System ist, woher es kommt und warum es so ist. Ein selbstgebautes System zwingt zu Klarheit und verhindert, dass sich implizite Annahmen einschleichen.
Es ist eine bewusste Arbeitsweise.
Fazit
Mit diesem Schritt ist die Basis gelegt. Ein offizielles NetBSD 10.1 dient als Referenz, der aktuelle Stable-Branch ist gebaut, und eigene Installationsmedien liegen vor. Ab hier bewege ich mich vollständig innerhalb meines eigenen Systemraums.
Im nächsten Artikel geht es um die Installation des selbstgebauten Systems, das Disk-Layout, die Basiskonfiguration und die ersten strukturellen Entscheidungen.
Bildnachweis: Das NetBSD-Logo ist eine eingetragene Marke der NetBSD Foundation, Inc. Verwendung im Rahmen redaktioneller Berichterstattung gemäß den Richtlinien zur Logonutzung.