Buildroot vs Crosstool-NG – Compilons notre cross-compiler

Publié par cpb
Mai 27 2011

Lorsqu’on entame un projet de développement sur Linux embarqué, il est nécessaire de disposer d’un cross-compiler (souvent traduit un peu maladroitement par le terme « compilateur croisé » ) c’est-à-dire un compilateur fonctionnant sur la machine de développement (appelé poste hôte) capable de produire des fichiers exécutables qui pourront s’exécuter sur le processeur du système embarqué (la cible). Ce compilateur est généralement mis à disposition (gratuitement ou non) par le fournisseur de la carte embarquée. Toutefois, il y a de nombreux cas où l’on aimerait pouvoir choisir sa propre chaîne de compilation, pour des raisons de compatibilité entre outils ou entre bibliothèques par exemple.

La génération du cross-compiler a longtemps été une opération compliquée, longue et fastidieuse, mais de nos jours plusieurs projets encadrent cette étape préliminaire et simplifient grandement la vie des développeurs pour l’embarqué.

J’ai choisi de comparer deux approches différentes (même si au final les étapes de compilation sont assez proches) : Crosstool-NG et Buildroot. Sachez cependant qu’il existe encore bien d’autres projets du même genre, comme Scratchbox, Open embedded, etc.

  • Crosstool-NG est un outil qui nous permet de générer notre chaîne de compilation croisée. Après une phase de configuration relativement simple, il télécharge les packages nécessaires, puis les compile et construit une toolchain complète.
  • Buildroot est un projet plus vaste, qui génère le compilateur croisé, mais peut ensuite enchaîner sur la compilation du noyau, des utilitaires de base, des bibliothèques supplémentaires, du système de fichiers, etc. Nous allons nous limiter ici (c’est la configuration que j’ai préparée) à la production du cross-compiler.

 

Toute application qui fonctionne sur Linux a besoin, soit au moment de la compilation, soit au moment de l’exécution, d’une bibliothèque C. Cette dernière ne se cantonne nullement aux fonctions utilitaires du langage C, mais contient aussi tous les points d’accès à l’API du noyau (les appels-système). Même une application écrite en Ada ou en Fortran aura besoin d’accéder à cette partie de la bibliothèque C. Il s’agit donc d’une pièce cruciale du système Linux, aussi importante que le kernel lui-même.

Il existe plusieurs versions de bibliothèque C. Les plus courantes sont :

  • la GlibC (Gnu libC), très complète, très riche, très grosse. Il en existe une variante un peu allégée : la EglibC
  • la uClibC beaucoup plus petite, mais n’implémentant pas toujours les extensions trop récentes de la Glibc, ou les fonctionnalités non-standard.

 

Le choix d’une bibliothèque doit être établi dès la conception initiale du projet, puisqu’il influe sur la génération de la toolchain. Buildroot ne sait compiler que la uClibC, en revanche Crosstool-NG peut compiler une GlibC, une EglibC  ou une uClibC. Nous allons préparer deux chaînes de compilation différentes, toutes les deux pour processeurs Arm, en employant ces deux projets successivement.

Crosstool-NG

Téléchargeons et décompressons les sources de crosstool-ng

[~]$ mkdir tmp
[~]$ cd tmp
[tmp]$ wget http://crosstool-ng.org/download/crosstool-ng/crosstool-ng-1.11.1.tar.bz2
--2011-05-07 09:38:28--  http://crosstool-ng.org/download/crosstool-ng/crosstool-ng-1.11.1.tar.bz2
[...]
2011-05-07 09:38:31 (564 KB/s) - «crosstool-ng-1.11.1.tar.bz2» sauvegardé [1838987/1838987]
[tmp]$ tar xjf crosstool-ng-1.11.1.tar.bz2
[tmp]$ cd crosstool-ng-1.11.1

Configurons Crosstool-NG pour préparer la compilation. Nous allons indiquer un chemin (absolu, pas de chemin relatif ou de ~/) pour l’installation des scripts de compilation. Ce répertoire pourra être supprimé par la suite.

[crosstool-ng-1.11.1]$ ./configure --prefix=/home/votre-login/tmp/ctng
Checking for 'grep'... /bin/grep
Checking whether '/bin/grep' supports -E... yes
[...]
[crosstool-ng-1.11.1]$ make
  SED    'ct-ng'
  SED    'scripts/crosstool-NG.sh'
[...]
  SED    'docs/ct-ng.1'
  GZIP   'docs/ct-ng.1.gz'
[crosstool-ng-1.11.1]$ make install
  MKDIR   '/home/cpb/tmp/ctng/bin/'
  INST    'ct-ng'
[...]
For auto-completion, do not forget to install 'ct-ng.comp' into
your bash completion directory (usually /etc/bash_completion.d)
[crosstool-ng-1.11.1]$

Si la commande configure se plaint de l’absence de makeinfo, sachez qu’il s’agit du package texinfo qu’il faut installer.

Nous pouvons à présent supprimer ce répertoire de sources, et créer un répertoire de travail dans lequel seront stockés les fichiers temporaires pendant la génération du cross-compiler.

[crosstool-ng-1.11.1]$ cd ..
[tmp]$ rm -rf crosstool-ng-1.11.1
[tmp]$ mkdir ctng-build
[tmp]$ cd ctng-build/
[ctng-build]$

Il est important d’ajouter dans notre variable d’environnement PATH le chemin d’accès au répertoire bin/ dans le répertoire précédemment fourni :

[ctng-build]$ PATH=$PATH:~/tmp/ctng/bin/
[ctng-build]$

Nous allons maintenant configurer la compilation. Pour cela une interface en mode texte semi-graphique semblable à celle du noyau Linux est disponible. La configuration est enregistrée dans un fichier .config que l’on peut sauvegarder et restaurer manuellement. Vous en trouverez une version prête à télécharger.

[ctng-build]$ cp ~/config-ctng-1.11.1-arm-linux-generic ./.config
[ctng-build]$ ct-ng menuconfig
  MKDIR kconfig
  DEP   kconfig/lxdialog/menubox.dep
  DEP   kconfig/lxdialog/textbox.dep
[...]

Après examen des options disponibles, quittons en sauvegardant la configuration, puis lançons la compilation :

[ctng-build]$ ct-ng build
[INFO ]  Performing some trivial sanity checks
[INFO ]  Build started 20110507.100411
[INFO ]  Building environment variables
[WARN ]  Directory '/home/cpb/tmp/download' does not exist.
[WARN ]  Will not save downloaded tarballs to local storage.
[INFO ]  =================================================================
[INFO ]  Retrieving needed toolchain components' tarballs
[INFO ]  Retrieving needed toolchain components' tarballs: done in 727.77s (at 12:09)
[INFO ]  =================================================================
[INFO ]  Extracting and patching toolchain components
[INFO ]  Extracting and patching toolchain components: done in 73.42s (at 13:22)
[INFO ]  =================================================================
[INFO ]  Installing binutils
[INFO ]  Installing binutils: done in 33.22s (at 13:55)
[INFO ]  =================================================================
[INFO ]  Installing static core C compiler
[INFO ]  Installing static core C compiler: done in 75.13s (at 15:11)
[INFO ]  =================================================================
[INFO ]  Installing kernel headers
[INFO ]  Installing kernel headers: done in 5.10s (at 15:16)
[INFO ]  =================================================================
[INFO ]  Installing C library headers & start files
[INFO ]  Installing C library headers & start files: done in 79.14s (at 16:35)
[INFO ]  =================================================================
[INFO ]  Installing shared core C compiler
[INFO ]  Installing shared core C compiler: done in 112.33s (at 18:27)
[INFO ]  =================================================================
[INFO ]  Installing C library
[INFO ]  Installing C library: done in 463.23s (at 26:10)
[INFO ]  =================================================================
[INFO ]  Installing final compiler
[INFO ]  Installing final compiler: done in 85.82s (at 27:36)
[INFO ]  =================================================================
[INFO ]  Installing cross-gdb
[INFO ]  Installing cross-gdb: done in 83.66s (at 29:00)
[INFO ]  =================================================================
[INFO ]  Installing gdbserver
[INFO ]  Installing gdbserver: done in 7.75s (at 29:08)
[INFO ]  =================================================================
[INFO ]  Cleaning-up the toolchain's directory
[INFO ]    Stripping all toolchain executables
[INFO ]  Cleaning-up the toolchain's directory: done in 1.26s (at 29:09)
[INFO ]  Build completed at 20110507.103320
[INFO ]  (elapsed: 29:08.89)
[INFO ]  Finishing installation (may take a few seconds)...
[ctng-build]$ cd ..
[tmp]$ rm -rf ctng*
[tmp]$

Et voilà ! notre nouvelle toolchain est prête, disponible dans ~/cross-arm-linux-ctng/bin, les utilitaires étant préfixés par arm-generic-linux-gnueabi-

Utilisation du cross-compiler

Nous allons employer toolchain obtenue avec Crosstool-NG pour compiler un noyau Linux et une application utilisateur (Busybox par exemple). J’utiliserai ici des fichiers de configuration fonctionnant pour la carte Pandaboard.

Compilation du kernel Linux

Téléchargement et préparation des sources :

[~]$ cd tmp
[tmp]$ wget http://www.kernel.org/pub/linux/kernel/v2.6/testing/linux-2.6.39-rc6.tar.bz2
[...]
2011-05-08 03:40:59 (1,60 MB/s) - «linux-2.6.39-rc6.tar.bz2» sauvegardé [76087903/76087903]
[tmp]$ tar -xjf linux-2.6.39-rc6.tar.bz2 
[tmp]$ cd linux-2.6.39-rc6

Configuration de la compilation ; le fichier proposé ci-dessous est une configuration pour la carte Pandaboard que j’ai utilisée dans les articles des semaines précédentes (1, 2, et 3)

[linux-2.6.39-rc6]$ cp ~/config-linux-2.6.39-rc6-pandaboard-cpb ./.config
[linux-2.6.39-rc6]$ make ARCH=arm menuconfig

Après avoir vérifié la configuration, nous quittons et lançons la compilation ainsi :

[linux-2.6.39-rc6]$ PATH=$PATH:~/Projets/Panda/u-boot/tools/
[linux-2.6.39-rc6]$ make ARCH=arm CROSS_COMPILE=~/cross-arm-linux-ctng/bin/arm-generic-linux-gnueabi- -j 16 uImage

Deux remarques :

  • La modification du PATH permet d’inclure le répertoire dans lequel nous avions compilé U-boot, afin d’avoir accès à l’utilitaire mkimage qui permet de produire des fichiers uImage. Pour en savoir plus, se reporter à cet article.
  • Il est possible d’enregistrer le préfixe du cross-compiler directement dans la configuration du kernel (dans le menu General Setup), mais j’ai préféré indiquer ce préfixe explicitement sur la ligne de commande, car nous ré-utiliserons le même fichier de configuration pour comparer la compilation avec Buildroot.

La compilation se termine ainsi :

Image Name:   Linux-2.6.39-rc6-cpb-ctng
Created:      Sun May  8 03:52:04 2011
Image Type:   ARM Linux Kernel Image (uncompressed)
Data Size:    3053156 Bytes = 2981.60 kB = 2.91 MB
Load Address: 80008000
Entry Point:  80008000
  Image arch/arm/boot/uImage is ready
[linux-2.6.39-rc6]$ ls -l arch/arm/boot/uImage 
-rw-rw-r-- 1 cpb cpb 3053220  8 mai   03:52 arch/arm/boot/uImage
[linux-2.6.39-rc6]$

Après avoir sauvegardé l’image obtenue, passons un petit coup de « M. Propre » sur les sources du noyau afin d’effacer tous les fichiers de compilation

[linux-2.6.39-rc6]$ cp arch/arm/boot/uImage ~/tmp/uImage-ctng
[linux-2.6.39-rc6]$ make mrproper
  CLEAN   .
[...]
  CLEAN   .config .config.old .version include/linux/version.h Module.symvers
[linux-2.6.39-rc6]$

Compilation de busybox

Busybox est un package essentiel pour les systèmes embarqués, que l’on retrouve dans de très nombreux projets.

[~]$ cd tmp
[tmp]$ wget http://www.busybox.net/downloads/busybox-1.18.4.tar.bz2
[...]
2011-05-08 03:59:21 (523 KB/s) - «busybox-1.18.4.tar.bz2» sauvegardé [2130598/2130598]
[tmp]$ tar -xjf busybox-1.18.4.tar.bz2
[tmp]$ cd busybox-1.18.4
[busybox-1.18.4]$

Le fichier de configuration fourni ici est très complet, ceci nous donnera un fichier exécutable assez volumineux

[busybox-1.18.4]$ cp ~/config-busybox-1.18.4 ./.config
[busybox-1.18.4]$ make CROSS_COMPILE=~/cross-arm-linux-ctng/bin/arm-generic-linux-gnueabi-
[...]
  AR      shell/lib.a
  AR      libbb/lib.a
  LINK    busybox_unstripped
Trying libraries: crypt m
 Library crypt is not needed, excluding it
 Library m is needed, can't exclude it (yet)
Final link with: m
[busybox-1.18.4]$ ls -l busybox
-rwxrwxr-x 1 cpb cpb 896108  8 mai   04:07 busybox
[busybox-1.18.4]$ cp busybox ~/tmp/busybox-ctng[busybox-1.18.4]$

Pour que ce fichier exécutable puisse fonctionner, il faudra disposer sur la cible :

  • du chargeur de fichiers exécutables dynamiques ld-linux.so.3 (lien symbolique vers ld-2.12.2.so) ;
  • de la bibliothèque C : libc.so.6 (lien symbolique vers libc-2.12.2.so) ;
  • de la bibliothèque mathématique libm.so.6 (lien symbolique vers libm-2.12.2.so).

Tous ces fichiers ont été compilés avec la toolchain et se trouvent dans ~/cross-arm-linux-ctng/arm-generic-linux-gnueabi/sysroot/lib/. Voyons leurs tailles :

[lib]$ ls -l ld-2.12.2.so 
-r-xr-xr-x 1 cpb cpb 161023  7 mai   10:30 ld-2.12.2.so
[lib]$ ls -l libc-2.12.2.so 
-r-xr-xr-x 1 cpb cpb 1521599  7 mai   10:30 libc-2.12.2.so
[lib]$ ls -l libm-2.12.2.so 
-r-xr-xr-x 1 cpb cpb 729489  7 mai   10:29 libm-2.12.2.so
[lib]$

Au total, il nous faudra installer sur notre cible au minimum :

  • kernel Linux : 3053220 octets ≈ 3Mo
  • exécutable Busybox : 896108 octets ≈ 1 Mo
  • bibliothèques : 161023 + 1521599 + 729489 = 2412111 octets ≈ 2,3 Mo

 

Soit un total d’environ 6 Mo.

Buildroot

Obtention et préparation des sources de Buildroot :

[~]$ cd tmp
[tmp]$ wget http://buildroot.uclibc.org/downloads/buildroot-2011.02.tar.bz2
[...]
2011-05-08 04:28:58 (492 KB/s) - «buildroot-2011.02.tar.bz2» sauvegardé [1945895/1945895]
[tmp]$ tar xjf buildroot-2011.02.tar.bz2
[tmp]$ cd buildroot-2011.02
[buildroot-2011.02]$

Configuration de la compilation :

[buildroot-2011.02]$ cp ~/config-buildroot-arm-generic ./.config
[buildroot-2011.02]$ make menuconfig

Dans le menu de configuration, nous devons préciser l’emplacement final de la toolchain par un chemin absolu. Il faut donc modifier l’entrée « Host dir » du menu « Build Options » pour qu’elle contienne le répertoire d’installation du compilateur (remplacez mon nom de login « cpb » par le vôtre).

Puis on lance la compilation :

[buildroot-2011.02]$ make
[...]
if [ -x "/home/cpb/cross-arm-linux-buildroot/usr/bin/arm-unknown-linux-uclibcgnueabi-ldconfig" ]; 
then 
	/home/cpb/cross-arm-linux-buildroot/usr/bin/arm-unknown-linux-uclibcgnueabi-ldconfig -r /home/cpb/tmp/buildroot-2011.02/output/target; 
else 
	/sbin/ldconfig -r /home/cpb/tmp/buildroot-2011.02/output/target; 
fi
/home/cpb/cross-arm-linux-buildroot/usr/bin/arm-unknown-linux-uclibcgnueabi-ldconfig: can't resolve /usr/X11R6/lib in chroot .: No such file or directory
/home/cpb/cross-arm-linux-buildroot/usr/bin/arm-unknown-linux-uclibcgnueabi-ldconfig: skipping /usr/X11R6/lib: No such file or directory
echo 2011.02 > /home/cpb/tmp/buildroot-2011.02/output/target/etc/br-version
rm -f /home/cpb/tmp/buildroot-2011.02/output/build/.fakeroot*
[buildroot-2011.02]$ cd ..
[tmp]$ rm -rf buildroot-2011.02*
[tmp]$

Utilisation de Buildroot

Commençons par compiler un noyau Linux comme précédemment (nous avions nettoyé les sources de tous les fichiers de compilation précédents) :

[~]$ cd tmp/
[tmp]$ cd linux-2.6.39-rc6
[linux-2.6.39-rc6]$ cp ~/config-linux-2.6.39-rc6-pandaboard-cpb ./.config
[linux-2.6.39-rc6]$ make ARCH=arm menuconfig
  HOSTCC  scripts/basic/fixdep
  HOSTCC  scripts/basic/docproc
[...]

Vérifions la configuration et sauvegardons-la :

#
# configuration written to .config
[...]
[linux-2.6.39-rc6]$ PATH=$PATH:~/Projets/Panda/u-boot/tools/
[linux-2.6.39-rc6]$ make ARCH=arm CROSS_COMPILE=~/cross-arm-linux-buildroot/usr/bin/arm-linux- -j 16 uImage
[...]
Image Name:   Linux-2.6.39-rc6-cpb-buildroot
Created:      Sun May  8 09:31:34 2011
Image Type:   ARM Linux Kernel Image (uncompressed)
Data Size:    3050288 Bytes = 2978.80 kB = 2.91 MB
Load Address: 80008000
Entry Point:  80008000
  Image arch/arm/boot/uImage is ready
[linux-2.6.39-rc6]$ ls -l arch/arm/boot/uImage 
-rw-rw-r-- 1 cpb cpb 3050352  8 mai   09:31 arch/arm/boot/uImage
[linux-2.6.39-rc6]$ cp arch/arm/boot/uImage ../uImage-buildroot
[linux-2.6.39-rc6]$

La taille du noyau est approximativement égale à celle du noyau obtenu avec Crosstool-NG. Vérifions à présent la compilation d’une application utilisateur : Busybox.

[linux-2.6.39-rc6]$ cd ~/tmp/busybox-1.18.4
[busybox-1.18.4]$ make distclean
  CLEAN   applets
[...]
  CLEAN   .config include/autoconf.h include/bbconfigopts.h include/usage_compressed.h include/applet_tables.h
[busybox-1.18.4]$ cp ~/config-busybox-1.18.4 ./.config
[busybox-1.18.4]$ make menuconfig

Puis compilons Busybox :

[busybox-1.18.4]$ make CROSS_COMPILE=~/cross-arm-linux-buildroot/usr/bin/arm-linux- 
[...]
Trying libraries: crypt m
 Library crypt is not needed, excluding it
 Library m is needed, can't exclude it (yet)
Final link with: m
  DOC     busybox.pod
  DOC     BusyBox.txt
  DOC     busybox.1
  DOC     BusyBox.html
[busybox-1.18.4]$
[busybox-1.18.4]$ ls -l busybox
-rwxrwxr-x 1 cpb cpb 892444  8 mai   09:38 busybox

Les bibliothèques utiles se trouvent à présent dans ~/cross-arm-linux-buildroot/usr/arm-linux/sysroot/lib/

[cross-arm-linux-buildroot]$ ls -l /cross-arm-linux/usr/arm-linux/sysroot/lib/ld-uClibc-0.9.31.so 
-rwxr-xr-x 1 root root 21324  8 mai   04:55 /cross-arm-linux/usr/arm-linux/sysroot/lib/ld-uClibc-0.9.31.so
[cross-arm-linux-buildroot]$ ls -l /cross-arm-linux/usr/arm-linux/sysroot/lib/libuClibc-0.9.31.so
-rwxr-xr-x 1 root root 403651  8 mai   04:55 /cross-arm-linux/usr/arm-linux/sysroot/lib/libuClibc-0.9.31.so
[cross-arm-linux-buildroot]$ ls -l /cross-arm-linux/usr/arm-linux/sysroot/lib/libm-0.9.31.so
-rwxr-xr-x 1 root root 64479  8 mai   04:55 /cross-arm-linux/usr/arm-linux/sysroot/lib/libm-0.9.31.so

Nous pouvons récapituler les éléments nécessaires

  • kernel Linux : 3050288 octets ≈ 3Mo
  • exécutable Busybox : 892444 octets ≈ 1 Mo
  • bibliothèques : 21324 + 403651 + 64479 = 489454 octets ≈ 478 ko

 

Soit environ 4,4 Mo

Conclusion

Comme on pouvait s’y attendre la taille des bibliothèques nécessaires en utilisant la uClibC est sensiblement plus petite que celle de la EglibC. En revanche les tailles des exécutables (avec éditions des liens dynamique) et du noyau Linux sont équivalentes à celles obtenues avec la toolchain de Crosstool-NG.

Il y a des différences fonctionnelles entre les bibliothèques EglibC (ou GlibC) et uClibC. Cette dernière par exemple s’appuie encore pour offrir un support des threads Posix sur l’ancienne bibliothèque LinuxThreads, alors que la EglibC utilisent la bibliothèque NPTL plus riche et plus performante. C’est le cas pour d’autres fonctionnalités également (cela fera l’objet d’un prochain article).

Sur des systèmes fortement contraints, où l’économie de quelques méga-octets est appréciable, on pourra se tourner facilement vers la uClibC à l’aide de Buildroot. Sur des systèmes moins limités en mémoire, on pourra profiter de la richesse de la GlibC en utilisant le compilateur croisé fourni par Crosstool-NG.

 

4 Réponses

  1. DHAZE dit :

    Le fichier de configuration fourni ici est très complet, ceci nous donnera un fichier exécutable assez volumineux

    [busybox-1.18.4]$ cp ~/config-busybox-1.18.4 ./.config

    config-busybox-1.18.4 —— >> lien perdu

  2. Hatsuharu dit :

    Bonjour,

    je suis actuellement en train de compiler busybox (j’utilise la dernière version soit la 1.20.2) avec crosstool-ng pour ma pandaboard mais j’ai le message suivant:

    networking/inetd.c:176:22: fatal error: rpc/rpc.h: No such file or directory
    compilation terminated.
    make[1]: *** [networking/inetd.o] Erreur 1
    make: *** [networking] Erreur 2

    J’ai beau chercher sur internet, je ne trouve pas de solution :/

    Avez-vous une idée pour régler ce problème ?

    Merci d’avance,
    Nicolas

  3. Usermind42 dit :

    Merci pour cet article très intéressant tout comme vos livres!

    @Nicolas et pour d’autres peut être

    Le message d’erreur dit seulement qu’il te manque les headers RPC qui normalement ce trouve dans /usr/include/rpc

URL de trackback pour cette page