Skip to content
On this page

(c) Matthias Meier, Manuel Di Cerbo

3. Buildroot Builden

Zwecks Booten eines Linux-Systems benötigt man einen Bootloader, welcher wiederum einen Linux-Kernel lädt und startet und dieser wiederum mountet und startet aus dem so genanntes Rootfilesystem (RFS) das Programm init, welches die weitere Systeminitalisierung vornimmt. (Details zu diesem Prozess folgen im nächsten Versuch.) Das Rootfilesystem entspricht dabei einem Windows Laufwerk C: .

Bei einer universellen Linux-Distribution wie z.B. Ubuntu für den PC oder dem Raspberry Pi OS (alias Raspian) für's Raspberry Pi, füllen tausende Programme und Hilfsprogramme schnell einige GB im Rootfilesystem. Und zehntausende Programme mehr noch über den Package Manager.

Eine derartige Distribution veraltet natürlich relativ schnell und Sicherheitsupdates werden auch maximal wenige Jahre nachgeliefert, denn neue Programmversionen werden über neue Releases der betreffenden Linux-Distribution vertrieben und nicht bloss über den "apt upgrade" Prozess.

Bei einem Embedded System erwartet man hingegen einen über viele Jahre sicheren Betrieb auch ohne Nachinstallation von Sicherheitsupdates. Unter anderem auch aus diesem Grund will man bei Embedded Systemen nur das minimal Notwendige installiert haben, wodurch nebst höherer Sicherheit auch die Gesamtzuverlässigkeit des Systems steigt und der CPU-, Energie- und Speicher-Ressourcenbedarf sinkt.

Varianten für ein Embedded Linux Root File System

Busybox als RFS

Das schlankeste RFS würde man mit nur Busybox erhalten:

Busybox ist eine kompakte POSIX-Tool-Sammlung, welche in ein einziges Programm namens busybox gepackt ist und die Grundfunktionalität der wichtigsten Linux/UNIX/POSIX-Commands enthält, also alle Commands zum Starten und Administrieren des Systems. Busybox wird nicht nur bei schlanken Embedded Systemen gerne verwendet sondern auch in der Boot-Phase "normaler" Linux-Distributionen, um beim Booten aus der so genannte Initial Ramdisk die Disk- und Filesystem-Treibers zu laden, welche der Linux-Kernel zum Mounten des Rootfilesystems benötigt.

Ein Emedded Rootfilesystem eigenhändig auf Busybox aufzubauen ist jedich recht umständlich, denn oft brauch man noch zusätzliche Programme oder Funktionalitäten zwecks Aktivieren des WLANs und/oder ein SSH-Server, Grafik-Libraries etc, etc, welche dann alle eigenhändig crosscompiliert und dem Rootfilesystem manuell zugefügt werden müssten https://busybox.net/.

Buildroot

Einen wesentlichen Schritt weiter geht das Build-System Buildroot. Dabei handelt es sich um eine modulare Script- und Patchsammlung, welche selbständig auf Wunsch resp. wählbar die Cross-Toolchain, den Bootloader, den Linux-Kernel, Busybox (!) sowie viele weitere Programme und Libraries ab deren Original-Projektseiten im Quellcode herunterlädt, falls nötig zwecks Cross- Compilation patched und dann crosscompiliert.

Am Schluss erstellt Buildroot ein TAR-Archiv (entsprechend einer Zip-Datei) und/oder auf Wunsch ein flashbares Rootfilesystem Image. Die Auswahl der Programmpakete und Installationsoptionen lässt sich bei Buildroot relativ einfach über eine „menuconfig“ auswählen, sodass mit Buildroot ebenfalls eine maximal schlanke und sichere Installation möglich ist http://buildroot.uclibc.org.

Yocto und Android

Ähnlich, aber nochmals deutlich aufwändiger verfahren die Build-Systeme Android und Yocto, wobei letzteres auf das ältere OpenEmbedded Projekt basiert. Typischerweise hosten diese den Sourcecode vieler anderer Opensource Projekte selbst. Mittels Yocto lassen sich sogar „eigene“ schlanke Distributionen generieren, was v.a. für Hersteller interessant ist, welche eine grosse Produktpalette von Embedded Systemen mit ev. vielen unterschiedlichen Installationsoptionen anbieten, denn Yocto unterstützt im Gegensatz zu Buildroot auch einen kompakten Paketmanager.

Poky und Angstrom sind übrigens zwei schlanke universelle Distributionen, welche mit Yokto generiert werden. http://www.yoctoproject.org/, http://developer.android.com.

General Purpose Linux Distro

Alternativ kann man auf leistungsfähigeren Embedded-Boards (wie dem Raspberry Pi oder anderen Cortex-A basierten Boards) natürlich auch herkömmliche Linux-Distributionen wie Debian, Ubuntu, Arch Linux oder für das Raspberry Pi (bevorzugt) das Raspberry Pi OS (alias Raspian) installieren... … sofern die betreffende Distributionen für die gewünschte CPU-Architektur (ARM Cortex-A ?) übersetzt wird. Einmal installiert, kann man dann genauso bequem wie auf dem Hostsystem vorkompilierte Programme über den Packagemanager nachinstallieren oder sogar auf dem Embedded System selbst entwickeln, d.h. native compilieren und „builden“ - natürlich nur mit entsprechend langen Kompilationszeiten. Während der Entwicklungsphase eines Embedded Linux Systems ist dies sicher eine bequeme und produktive Umgebung, für ein Serienprodukt mit wunschgemäss langer und stabiler Lebensdauer bei möglichst wenigen (Security-)Updates ist diese Vorgehensweise aber eher ungeeignet. Ein weiteres Problem ist, dass das Rootfilesystem (resp. die SD-Karte) bei solche Systemen bei tromausfällen ev. zerstört werden kann.

Fazit

Die Wahl der Basis für das Rootfilesystem ist also ein Tradeoff zwischen Komfort, Funktionalität, Entwicklungsaufwand, (langfristiger) Wartbarkeit und Betriebssicherheit.

Hostsystem / Development Environment

Für die Entwicklung auf einen Linux Target eignet sich auf dem Hostsystem auch ein Linux Hossystem. Gerade der Umgang mit Linux auch auf dem Host unterstützt auch das Arbeiten auf dem Target, da viele der Tools auf beiden Systemen vorhanden sind.

Oft bietet auch der Entwicklungsboard-Hersteller selbst eine fertig zusammengestellte Entwicklungsplattform, z.B. in Form eines unter Virtualbox oder VMware lauffähigen Linux-Gastsystem-Images an, beinhaltend Cross-Toolchain, Linux-Kernel, Rootfilesystem etc, womit gerade unter unter Windows- und OS-X ein einfacher Einstieg möglich ist.

Buildroot als Root Filesystem generieren

Um möglichst alle Zusammenhänge eines Linux Systems zu verstehen, wählen wir für unser Versuchssystem die Installationsvariante über das Build System Buildroot.

Werfen Sie zuerst einen Blick auf die Buildroot Homepage auf: https://buildroot.org Auf https://buildroot.org/docs.html finden Sie bei Bedarf eine Dokumentation von Buildroot.

Erstellen Sie im Linux-Gastsystem unter Ihrem Homedirectory ein Ordner embedded-linux, wechseln Sie in diesen und clonen Sie das Git Repository von Buildroot gemäss Beschreibung auf https://buildroot.org/download.html. Stellen Sie sicher, dass git installiert ist (ggf. mit apt install git nachinstallieren).

Wechseln Sie danach per 'cd buildroot' in das "geclonte" Buildroot Git Reppository und verschaffen Sie sich einen Überblick:

Git Repo im überblick

Ein ls -a zeigt u.A. den bei Git verwalteten Projekten immer vorhandene versteckte Ordner .git. Unter diesem wird das geclonte Git Repo inklusive der ganzen Projekt-History gespeichert.

Weiter wird in der versteckten Datei .gitignore festgelegt, welche Dateien und Ordner im Git-Tree nicht erfasst werden - werfen Sie einen Blick in diese Datei (z. B. per cat .gitignore). Insbesondere der Ordner dl in welchem Buildroot die gewünschten Quellpakete herunterlädt sowie der Ordner output, unter welchem Zielsystem-Image erstellt wird.\

Weiter auch die Datei .config in welcher die ausgewählte anwendungsspezifische Buildroot-Konfiguration gespeichert wird.

git branches

Mit git branch erkenne Sie weiter, dass lokal derzeit nur der Branch master vorhanden ist und am Stern resp. mit git status, dass dieser offensichtlich auch ausgecheckt ist sowie identisch mit dem gleichnamigen Remote-Branch ist.

master ist nicht gleich stable

Achtung: Auf dem master Branch sollten Sie nie direkt arbeiten sondern nur auf stable Releases!

Mit git log sieht man sequenziell rückwärts alle Commits (Abbruch mittels q). Buildroot verwendet Tags um jene Commits zu bezeichnen, welche als Release freigegeben wurden. Ein git tag listet somit alle Releases (versuchen Sie's!) wobei folgende Namenskonvention eingehalten wird:

JJJJ.MMBezeichnet einen (ersten) stabilen Release (JJJJ=Jahr, MM=Monat)
JJJJ.MM.<n>Bugfix Releases, um Fehler in einem Release zu korrigieren (<n> ≥ 1).
JJJJ.MM_rc<n>rc steht für "Release Candiate", also ein unstabiler Entwicklerrelease.

Mittels git branch -r sehen Sie weiter die Buildroot "Remote Branches" in der Form:

origin/JJJJ.MM.x sowie zuunterst der vorher erwähnte automatisch ausgecheckte Branch master.

Sowohl Tags als auch Remote Branches als auch beliebige Commits könnten ausgechecked werden.

Der Vorteil beim Auschecken der *.x Branches ist aber, dass diese nach Erscheinen von Bugfixes immer auf den aktuellsten Bugfix des betreffenden Releases aktualisiert werden. Erscheint also ein neuer Bugfix Relase, kann der lokale Branch dann einfach per git pull aktualisiert werden oder falls Änderungen an den lokalen Sourcen vorgenommen wurden, mittels git rebase.

  • Checken Sie also den aktuellsten stable Release über den Remote Branch 2023.11.x aus und erstellen Sie gleichzeitig einen darauf basierenden lokalen Branch Namens 2023.11.x per git checkout -b 2023.11.x.

Ein erneutes git status zeigt nun, dass Sie auf dem lokalen Branch 2023.11.x arbeiten und bei Eingabe von git log zudem ist im obersten Commit, welchem Tag dies entspricht.

Mit einem Blick in den Unterordner configs (per ls configs) erkennen Sie, dass Buildroot recht viele Boards resp. SoC Platformen unterstützt! Beachten Sie die raspberrypi* Einträge: Für das Raspi 4 könnten wir offensichtlich entweder 64-bit Code (arm64) oder 32-bit Code (arm) generieren lassen.

Buildroot configurieren

Wir wählen als Basis die 32-Bit Variante per

bash
make raspberrypi4_defconfig

Die angegebene Default-Konfig-Datei wird damit auf die Datei .config kopiert sowie vervollständigt. Werfen Sie mit less .config einen Blick in diese Textdatei - sie enthält bloss viele Einträge der Art BR2_*=y und noch viel mehr auskommentierte Zeilen (Abbruch mittels q). Mittels nachfolgender menuconfig passen Sie diese Vorauswahl dann noch weiter an ...

make Tools

Oftmals werden für C und C++ Projekte immer noch die make-Tools verwendet. Hierzu installieren wir das Paket make mit sudo apt install make.

Listen Sie aus dem buildroot-Verzeichnis mittels 'make help' die möglichen Optionen resp. make Phony Targets auf. Für uns wichtig sind: menuconfig, linux-menuconfig und busybox-menuconfig.

Zuerst konfigurieren Sie Buildroot selbst:

bash
# bevor Sie diesen command ausführen, sollten Sie
# make raspberrypi4_defconfig ausgeführt haben
make menuconfig

ncurses fehlt, gcc fehlt

Im Fehlerfall fehlen vermutlich die Headerdateien der ncurses Library: installieren Sie hierzu libncurses-dev über den Packet Manager (in *-dev Packages sind die Headerdateien zu einer Library).

Installieren Sie auch das gcc Package, falls nicht bereits vorhanden.

Falls das Terminal zu klein ist, kann menuconfig nicht angezeigt werden. Fehlermeldung: Your display is too small to run Menuconfig!

In Menuconfig drin, helfen folgende Tipps:

  • Wenn eine Unterebene (--->) betreten wurde, können Sie diese mittels ESC wieder verlassen!
  • Eine Volltext-Suche ist nach Eingabe eines Slash / möglich (listet die Hierarchie der Funde auf).
  • Kurzhilfe zu einem Eintrag ist mit Eingabe eines Fragezeichens ? möglich.
  • Ein Eintrag kann mittels der Leerschlag-Taste selektiert / deselektiert werden.

Wir wählen folgende Optionen:

Option
Toolchain
Toolchain TypeExternal Toolchainaus Zeitgründen verwenden wir eine vorkompilierte Toolchain
ToolchainArm ARM xzyDie Linaro Toolchain ist nicht mehr auf dem aktuellsten Standard (Linaro selber verweist auf die akutellen Releases von ARM selber)
Toolchain Origin(Toolchain to be downloaded and installed)
Copy gdb server to the Targetyes
System Configuration
System-Bannerbuildroot-2023.11.xEntsprechend Bezeichnung des lokalen Branches
Filesystem images
ext2/3/4 root filesystem
exact size500Mgeben Sie 500M an, sodass für das ext4 RFS Image mehr Platz reserviert wird
Target packages
Debugging, profiling benchmark
ltracezwecks Debugging der Library Calls einer Anwendung
stracezwecks Debugging der System Calls einer Anwendung
Target packages > HW Handling
Firmwarebrcmfmac sdio für Wifi auswählen
Networking Applications
dropbearschlanker SSH Server
iperf3Messung von Throughput auf L3
iwWireless Tools
wireless-regdbWLAN Datenbank, in welchem Land welche Frequenzen zulässig sind
wpa_supplicantDienst-Programm für verschlüsselte WLANs
wpa_supplicant > Enable WPSWPS ermöglicht einfaches WLAN-Einrichten u.a. per „Push-Button“ Methode
wpa_supplicant > Install wpa_cli binaryhierzu nötiges Command Line Tool
wpa_supplicant > Install wpa_client shared librarysowie zugehöriger shared Library
Text Editors and Viewers > nanoEin Texteditor

Weitere Packages

An dieser Stelle können Sie auch weitere Packages selektieren, wie z.B. python oder rust. Die Buildzeit wird demetsprechend auch (zum Teil erheblich) verlängert.

Python

Falls Python dann vermutlich sowohl mit .py als auch .pyc Unterstützung. Wenn Sie dann auch noch die Python Library pip zufügen, lassen sich zur Laufzeit weitere Python Modules per pip zufügen. Nicht immer klappt dies aber so einfach, da für einige Python Modules C Build Tools und/oder SSL Libraries etc notwendig sind.

Kernel

Neben den Target Packages erstellt Buildroot auch den Linux Kernel. Unter Kernel > Linux Kernel > Kernel Version: Custom Tarball, ist bereits ein Kernel Image selektiert. Dieses wurde bereits in der defconfig angegeben.

Kernel Sourcen

Defaultmässig wird die Kernel Source ab https://github.com/raspberrypi/linux heruntergeladen und kompiliert. Statt dieser Vorgabe aus raspberrypi4_defconfig könnten Sie auch eine andere Quelle angeben.

Der Mainline Kernel ab kernel.org bootet jedoch auf dem Raspi 4 nicht! Für's Raspi gepatchte Kernel könnten Sie auswählen per:

  • Kernel Version: [x] Cusom Git repository
  • URL of cusom repository: https://github.com/raspberrypi/linux.git
  • Cusom repository version: beliebiger verfügbarer Branch, z.B. rpi-6.6.y . Beachten Sie aber, dass Buildroot keineGit History herunterlädt, also bloss mit Historytiefe 0 cloned.

Wir werden in einem späteren Versuch einen Realtime Kernel generieren und hierfür den Kernel nochmals separat herunterladen, denn der Linux Realtime-Patch gibts nicht für alle Kernel-Releases und Patchlevels: https://wiki.linuxfoundation.org/realtime/preempt_rt_versions

Bootloader

Beim Raspberry Pi ist absichtlich kein Bootloader ausgewählt. Der Raspberry Pi Bootloader ist nämlich eine Closed Source von Broadcom und kann deshalb nicht von Buildroot kompiliert werden. Dieser Bootloader wird aufgrund folgender vorausgewählter Auswahl binär heruntergeladen:

Target packages -> Hardware handling -> Firmware -> rpi-firmware -> [*] rpi 4

Um die Konfiguration zu speichern, gehen Sie mit ESC zurück, bis der "Save" Dialog erscheint! Bestätigen Sie das Speichern durch Betätigen der <enter> Taste, wodurch die .config Datei aktualisiert wird. Zwecks Backup der Konfig könnten Sie einfach die .config Datei auf den Shared Folder sichern!

Installieren Sie nun noch folgende Packages:

Buildroot kompilieren

Installieren Sie nun auch noch das Package build-essential, welches von Buildroot verwendet wird.

Um das Kompilieren von RFS und Kernel zu starten, können Sie nun folgenden Command verwenden:

bash
make

Falls Sie über ein Multicoresystem verfügen, so können Sie den Vorgant mit Multithreading etwas beschleunigen:

bash
make -j4 # 4 Threads

Es dauert auf virtualisierten Systemen mindestens eine Stunde bis alles erstellt ist! Wenn Sie vorher unterbrechen möchten, können Sie dies mittels Ctrl-C.

Prüfen Sie, sobald make beendet wird die letzten Zeilen auf Fehlermeldungen! Bei erfolgreichem Build, finden Sie im Unterordner output/images die Datei sdcard.img welche Sie im nächsten Versuch auf die SD-Karte flashen werden.

Fehler beim kompilieren

Vielleicht werden beim Ausführen von make einige Programme oder Libs fehlen, wie bison, flex oder libssl. Installieren Sie diese gegebenenfalls, wobei betreffend libssl die Headerdateien nötig sind (also libssl-dev). Um die Buildzeit zu messen, könnten Sie übrigens dem 'make' Command 'time' voranstellen, also 'time make' .