Toolchain¶
Vorkompilierte Cross-Compiler¶
Viele Distributionen bieten in ihren Paketverwaltungssystem bereits fertige Binärpakte mit allen möglichen Cross-Compilern an. Wir werden an dieser Stelle die Installation auf Ubuntu, Arch-Linux und Gentoo vorstellen:
Installation auf Ubuntu:
apt-get install gcc-arm-linux-gnueabi make ncurses-dev
Installation auf ArchLinux:
# arm-linux-gnueabi-gcc in AUR Repository
# use --force to avoid the already exists error of glibc files
# tested on 11.06.2015 - Ferdinand Saufler
yaourt -S --force arm-linux-gnueabi-gcc
Installation auf Gentoo:
crossdev -S -v -t arm-unknown-linux-gnueabi
Cross-Toolchain mit crosstool-ng¶
Eine weitere gute Möglichkeit um an eine Cross-Toolchain zu gelangen ist die Verwendung von crosstool-ng.
Am besten ist es eine aktuelle Version von crosstool-ng direkt aus den Github Quellen zu bauen:
git clone https://github.com/crosstool-ng/crosstool-ng.git
cd crosstool-ng
./bootstrap
./configure
make
sudo make install
ARM-Toolchain mit crosstool-ng¶
Bevor wir beginnen, können wir uns alle verfügbaren Konfigurationen anzeigen lassen:
ct-ng list-samples
Wir wählen nun arm-unknown-linux-gnueabi aus und bauen die Toolchain:
ct-ng arm-unknown-linux-gnueabi
ct-ng build
Nach Abschluss findet man die Toolchain unter ~/x-tools/arm-unknown-linux-gnueabi
.
Cross-Toolchain selbst bauen¶
Die Erstellung einer eigenen ARM-Cross-Toolchain erfordert ein hohes Wissen die über die Konfiguration und das Zusammenspiel verschiedenster Softwarepakete.
Zuerst sollte man sich mit der Build-Host-Target Thematik auseinandersetzen:
- Build Machine
Auf diesem Rechner wird die Toolchain „gebaut“
- Host Machine
Auf diesem Rechner wird die Toolchain ausgeführt
- Target Machine
Für diesen Typ von Rechner erzeugt die Toolchain ausführbaren Code
An dieser Stelle unterscheiden wir noch 4 verschiedene Toolchain typen:
- Native Toolchain
Die Art von Toolchain läuft nativ auf einem System was bedeutet: Sie wurde für eine Architektur kompiliert, läuft auf dieser Architektur und erzeugt Code für diesen Architekturtyp.
- Cross Compilation Toolchain
Dieser Typ ist am interessantesten für Embedded Systems. Eine solche Toolchain wird beispielsweise für x86 kompiliert, läuft auf einem x86 System und erzeugt Code die Target Architektur (ARM, MIPS, PowerPC).
- Cross Native Toolchain
Dieser Typ könnte auf einem x86 System erzeugt worden sein, läuft aber auf der Target-Architektur und erzeugt Code für diese.
- Canadian Build / Canadian Cross Toolchain
Hierbei handelt es sich um den ausgefallensten Typ, eine Canadian Cross Toolchain könnte auf einem x86 System kompiliert worden sein, auf einer ARM-Architektur laufen und dort Code für MIPS erzeugen.
Grundlegende Komponenten einer Cross-Toolchain¶
Allgemein besteht eine Toolchain immer aus vier grundlegenden Komponenten, so wird es auch bei unserer selbsterstellten Toolchain sein:
- Binutils
Die GNU-Binutils. sind immer das erste Glied in der Kette. Sie beinhalten zwei wesentliche Komponenten:
as: den Assembler, der Assembler-Code in Maschinen-Code übersetzt
ld: den Linker, er linkt mehrere Objekte zu Bibliotheken oder ausführbaren Dateien zusammen.
- C, C++, Java, Ada, Fortran, Objective-C compiler
Die zweite Hauptkomponente einer Toolchain ist der Compiler. Ein möglicher Kandidat ist die GNU Compiler Collection, GCC. Sie unterstützt als Input Programmierspachen wie C, C++, Java Fortran oder Objective-C und als Ausgabe eine Vielzahl von Architekturen.
- C-Bibliothek
Eine C-Bibliothek implementiert die POSIX-API die für Programme der Userspace-Ebene benötigt wird. Sie interagiert mit dem Kernel über System-Calls und stellt darüber hinaus weitere High-Level Funktionen bereit. Wichtige Vertreter bekannter C-Bibliotheken sind unter anderem:
GLibc: Die am häufigigsten eingesetzte C-Bibliothek, die praktisch in jedem Desktop- und Server-System steckt. Sie bietet eine große Anzahl an Features ist dadurch aber auch etwas überladen.
uclibc: eine glibc Alternative die den Fokus auf wenig Platz- und Speicherverbrauch legt.
newlib: Die newlib ist eine speziell für Embedded Systms geschaffende Bibliothek, Sie wird von Redhat entwickelt und gewartet
musl: Eine relativ junge c-Bibliothek. Motto: lightweight, fast, simple, free.
- Debugger
Der Debugger ist nicht zwingend zum Erstellen von Applikationen notwendig ist aber dennoch meist Teil der Toolchain. In der Embedded Linux Welt ist der gebräuchlichste Vertreter der GNU Debugger GDB.
Allgemeiner Ablauf¶
Im allgemeinen ist die Erstellung einer Toolchain ein wiederkehrender Ablauf, der die folgenden Punkte beinhaltet:
Verzeichnisstruktur anlegen
Quellen laden und entpacken
Spezifische Patches hinzufügen
Werkzeuge auf dem Hostrechner überprüfen
Für jedes ausgewählte Paket: Für den Host-/Targetrechner konfigurieren, kompilieren und installieren
Reihenfolge der Paketkonfiguration¶
Beim Bau einer Toolchain muss in der Regel eine bestimmte Reihenfolge eingehalten werden, da die resultierenden Komponenten auf einander aufbauen:
Binutils
GCC Stage 1
Linux API Headers
GCC Stage 2
C-Bibliothek (glibc, newlib, etc)
GCC Final
Quellverzeichnis¶
Bevor wir uns den Paketen widmen legen wir uns ein Quellverzeichnis an das wir tarballs nennen. In dieses Verzeichnis werden alle *.tar.gz Quelldateien der einzelnen Projekte geladen.
mkdir ~/tarballs
export _tarballdir="${HOME}/tarballs"
Binutils 2.25¶
Beginnen wir mit binutils, zuerst werden die Quellen heruntergeladen und entpackt:
cd _tarballdir
wget http://ftp.gnu.org/gnu/binutils/binutils-2.25.tar.gz
tar -xvf binutils-2.25.tar
Nun fahren wir mit der Konfiguration und Installation fort:
export _target=arm-linux-gnueabi
export _host=$(uname -m)
export _basedir=$(pwd)
export _pkgdir="${_basedir}/pkg"
mkdir binutils-build
cd binutils-build
../binutils-2.25/configure \
--prefix=/usr \
--with-lib-path=/usr/lib/binutils/${_target} \
--program-prefix=${_target}- \
--enable-shared \
--disable-multilib \
--with-local-prefix=/usr/lib/${_target} \
--with-sysroot=/usr/${_target} \
--disable-nls \
--target=${_target} \
--host=$_host \
--build=$_host \
--disable-werror
make configure-host
make tooldir=/usr
rm -rf "$pkgdir"
make prefix="$pkgdir/usr" tooldir="$pkgdir/usr" install
rm -f "$pkgdir"/usr/bin/{ar,as,ld,nm,objdump,ranlib,strip,objcopy}
rm -f "$pkgdir"/usr/lib/libiberty.a
rm -rf "$pkgdir"/usr/{share,man}
GCC 5.1.0 Stage 1¶
Fahren wir fort mit der Gnu Compiler Collection - Phase 1 fort.
cd _tarballdir
wget https://ftp.gnu.org/gnu/gcc/gcc-5.1.0/gcc-5.1.0.tar.gz
tar -xvf gcc-5.1.0.tar.gz
Konfiguration und Installation:
export _target=arm-linux-gnueabi
export _host=$(uname -m)
export CFLAGS="-O2 -pipe"
export CXXFLAGS="-O2 -pipe"
export _basedir=$(pwd)
export _pkgdir="${_basedir}/pkg"
mkdir gcc-stage1-build
cd gcc-stage1-build
../gcc-5.1.0/configure \
--prefix=/usr \
--program-prefix=${_target}- \
--target=${_target} \
--host=$_host \
--build=$_host \
--disable-shared \
--disable-nls \
--disable-threads \
--enable-languages=c \
--enable-multilib \
--with-local-prefix=/usr/${_target} \
--with-sysroot=/usr/${_target} \
--with-as=/usr/bin/${_target}-as \
--with-ld=/usr/bin/${_target}-ld \
--enable-softfloat \
--with-float=soft \
--with-newlib \
--disable-libgomp \
--enable-interwork \
--enable-addons
make all-gcc all-target-libgcc
make DESTDIR="$pkgdir" install-gcc install-target-libgcc
rm -rf "$pkgdir"/usr/share/man/man7/
rm -rf "$pkgdir/usr/share/info"
cp -r "$pkgdir"/usr/libexec/* "$pkgdir/usr/lib/"
rm -rf "$pkgdir/usr/libexec"
# strip it manually
strip "$pkgdir"/usr/bin/* 2>/dev/null || true
find "$pkgdir"/usr/lib -type f -exec /usr/bin/${_target}-strip \
--strip-unneeded {} \; 2>/dev/null || true
Linux 4.1.2 API Headers¶
fahren wir mit den Linux API Headern fort:
cd _tarballdir
wget https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.1.2.tar.xz
tar -xvf linux-4.1.2.tar.xz
Konfiguration und Installation:
export _basedir=$(pwd)
export _pkgdir="${_basedir}/pkg"
export _target_arch="arm"
cd linux-4.1.2
make ARCH=${_target_arch} mrproper
make ARCH=${_target_arch} headers_check
make INSTALL_HDR_PATH="${_pkgdir}/usr/${_target}/" ARCH=${_target_arch} V=0 headers_install
GCC 5.1.0 Stage 2¶
Fahren wir fort mit der Gnu Compiler Collection - Phase 2 fort.
cd _tarballdir
rm -rf gcc-5.1.0
tar -xvf gcc-5.1.0.tar.gz
Konfiguration und Installation:
export _target=arm-linux-gnueabi
export _host=$(uname -m)
export CFLAGS="-O2 -pipe"
export CXXFLAGS="-O2 -pipe"
export _basedir=$(pwd)
export _pkgdir="${_basedir}/pkg"
mkdir gcc-stage2-build
cd gcc-stage2-build
../gcc-5.1.0/configure \
--prefix=/usr \
--program-prefix=${_target}- \
--target=${_target} \
--host=$_host \
--build=$_host \
--disable-nls \
--enable-languages=c \
--enable-multilib \
--with-local-prefix=/usr/${_target} \
--with-sysroot=/usr/${_target} \
--with-as=/usr/bin/${_target}-as \
--with-ld=/usr/bin/${_target}-ld \
--enable-softfloat \
--with-float=soft \
--disable-libgomp \
--enable-interwork \
--enable-addons
make all-gcc all-target-libgcc
make DESTDIR="$pkgdir" install-gcc install-target-libgcc
rm -rf "$pkgdir"/usr/share/{man/man7,info}/
cp -r "$pkgdir"/usr/libexec/* "$pkgdir"/usr/lib/
rm -rf "$pkgdir/usr/libexec"
# strip it manually
strip "$pkgdir"/usr/bin/* 2>/dev/null || true
find "$pkgdir"/usr/lib -type f -exec /usr/bin/${_target}-strip \
--strip-unneeded {} \; 2>/dev/null || true
Glibc 2.21¶
Fahren wir fort mit der Glibc fort.
cd _tarballdir
wget http://ftp.gnu.org/gnu/glibc/glibc-2.21.tar.gz
tar -xvf glibc-2.21.tar.gz
Konfiguration und Installation:
export _target=arm-linux-gnueabi
export _host=$(uname -m)
export CFLAGS="-U_FORTIFY_SOURCE -mlittle-endian -msoft-float -O2"
export CPPFLAGS="-U_FORTIFY_SOURCE -O2"
unset LD_LIBRARY_PATH
export BUILD_CC=gcc
export CC=${_target}-gcc
export CXX=${_target}-g++
export AR=${_target}-ar
export RANLIB=${_target}-ranlib
mkdir glibc-build
cd glibc-build
../glibc-2.21/configure \
--prefix=/ \
--target=${_target} \
--host=${_target} \
--build=$_host \
--with-headers=/usr/${_target}/include \
--enable-add-ons \
--enable-obsolete-rpc \
--enable-kernel=2.6.32 \
--enable-bind-now \
--disable-profile \
--enable-stackguard-randomization \
--enable-lock-elision \
--enable-shared \
--with-tls \
--with-__thread \
--without-cvs \
--without-gd \
--disable-werror
make
make "install_root=${pkgdir}/usr/${_target}" install
GCC 5.1.0 Final¶
Fahren wir fort mit der Gnu Compiler Collection - Final fort.
cd _tarballdir
rm -rf gcc-5.1.0
tar -xvf gcc-5.1.0.tar.gz
Konfiguration und Installation:
export _target=arm-linux-gnueabi
export _host=$(uname -m)
export CFLAGS="-O2 -pipe"
export CXXFLAGS="-O2 -pipe"
export _basedir=$(pwd)
export _pkgdir="${_basedir}/pkg"
mkdir gcc-final-build
cd gcc-final-build
../gcc-5.1.0/configure \
--prefix=/usr \
--program-prefix=${_target}- \
--target=${_target} \
--host=$_host \
--build=$_host \
--enable-shared \
--disable-nls \
--enable-threads=posix \
--enable-languages=c,c++ \
--enable-multilib \
--with-sysroot=/usr/${_target} \
--with-build-sysroot=/usr/${_target} \
--with-as=/usr/bin/${_target}-as \
--with-ld=/usr/bin/${_target}-ld \
--enable-softfloat \
--with-float=soft \
--enable-interwork \
--disable-libgomp \
--enable-__cxa_atexit \
--enable-addons
make all-gcc all-target-libgcc all-target-libstdc++-v3
make "DESTDIR=$pkgdir" install-gcc install-target-libgcc \
install-target-libstdc++-v3
rm -rf "$pkgdir"/usr/share/{man/man7,info}/
cp -r "$pkgdir"/usr/libexec/* "$pkgdir/usr/lib/"
rm -rf "$pkgdir/usr/libexec"
rm -rf "$pkgdir/usr/share/gcc-${pkgver}/python"
# strip it manually
strip "$pkgdir"/usr/bin/* 2>/dev/null || true
find "$pkgdir"/usr/lib -type f -exec /usr/bin/${_target}-strip \
--strip-unneeded {} \; 2>/dev/null || true
Vorgefertigte Toolchains¶
Neben Toolchain-Baukästen und der Möglichkeit sich seine eigene Toolchain zusammenzustellen gibt es auch eine Handvoll vorgefertigter Toolchains, die man nur Installieren muss.
- DENX ELDK (Embedded Linux Development Kit)
Toolkit Sammlung der Firma Denx Software Engeneering
- Scratchbox
Scratchbox stellt Toolchains für ARM und x86 Target-Architekturen zu Verfügung. Es werden uClibc und glibc unterstützt.
- CodeSourcery
CodeSourcery stellt eine auf Eclipse basierende IDE zu Verfügung die mit GNU Toolchain ausgeliefert wird.
- Linaro (ARM)
Linaro Produkte sind optimierte Toolchains für diverse ARM CPUs
Toolchain Baukästen¶
Mittlerweile gibt es eine beträchtliche Anzahl an Toolchain Baukästen, hier die wichtigsten Vertreter:
- Buildroot
Buildroot generiert Rootfile-System-Images fertig fürs Schreiben auf SD-Karte. Unter anderem stellt es auch eine Toolchain bereit um diese Pakete aus den Quellen zu kompilieren.
- OpenADK
OpenADK ist ein komplette Build System basierend auf dem Linux Kernel Konfigurations-System. Es unterstützt eine große Menge von Architekturen.
- Crossdev (Gentoo)
Crossdev ist ein Script, welches eine Toolchain generiert. Nur für Development-PCs auf denen Gentoo läuft.
- Crosstool-NG
Crosstool-ng ist ein gut geplegter Fork von crosstool. Leichte Konfiguration, unterstützt uClibc und glibc.
- OSELAS.Toolchain()_
Die OSELAS.Toolchain benutzt PTXdist, ein Userland Build-System basierend auf Kconfig. Beides Produkte von Pengutronix.