Générer sa crosstoolchain sur mesure grâce à crosstool-ng

1 – define()

Dans le monde de l’embarqué, nombreuses sont les architectures sur lesquelles il est possible de travailler. Cependant, il est relativement compliqué de trouver les outils nécessaires pour le développement. Avec un peu de chance, ils sont fourni par le fabricant/distributeur, mais dans la plupart des cas il y a souvent un tas de versions de retard, ou alors ils prennent la forme d’un IDE plus ou moins bien réussi (quand ce n’est pas qu’un fork bancal de Eclipse…) ou bien l’environnement n’est utilisable que sous Windows et ça, ça pose un réel problème de maitrise pour les personnes qui comme moi, aiment savoir ce qu’il se passe derrière. (Même s’il faut l’avouer, c’est toujours le même principe…).

Buildroot est une solution séduisante et très efficace, cependant parfois il n’est pas nécessaire d’avoir toute cette usine (pas forcément à gaz) pour n’en utiliser qu’une petite partie. Notamment si le système visé est ultra simple et sans OS.

Une petite solution est offerte par l’outil crosstool-ng, moyennant un peu de maitrise et de customisation de son environnement (oui, les doigts doivent être sur le clavier et non ailleurs!) et je vais tenter de vous expliquer de quoi il s’agit.

2 – init()

Crosstool-ng est un outil Linux capable de générer une crosstoolchain spécifique pour un matériel donné. C’est un outil assez puissant, mais qui demande une certaine maitrise et connaissance des systèmes sous-jacents et surtout, bien connaitre la cible sur laquelle on va travailler. Par contre, avant de pouvoir se servir de la bête, il faut le compiler et l’installer (mais c’est un détail pour toi, professionnel de l’informatique!)

La démonstration suivante est faite sur un système Debian 8.1 (Jessie), 32bits. Comme ça, même dans 10 ans quand cet article resurgira des méandres de l’internet, il sera possible de reproduire toutes ces étapes finger in ze nose.
Je passe sur l’installation d’un système Linux, si vous ne savez pas faire, je suis désolé mais la suite risque de ne guère vous intéresser… Je vous propose d’aller sur 9gag en attendant la fin de ce tuto 🙂

Pour ce qui est des dépendances, bien entendu c’est une machine de développement, il faut alors installer tout le touti pour faire des builds…

apt-get install build-essential

Et n’oubliez pas vos outils d’édition préférés. Pour info, je fais tout en console, ce tuto ne comporte aucune manipulation graphique…

Crosstool-ng n’étant pas disponible sur les dépôts Debian, il faut le récupérer sur le site officiel crosstool-ng.org:

wget http://crosstool-ng.org/download/crosstool-ng/crosstool-ng-1.22.0.tar.bz2

On crée un dossier dans lequel on fera le build:

mkdir crosscompile
cd crosscompile

On extrait:

tar xvf ../crosstool-ng-1.22.0.tar.bz2
cd crosstool-ng

On choisit la destination pour l’installation de crosstool-ng (pas du crosscompilo résultant).
Mon imagination débordante me pousse à créer un dossier ct-ng. Impressionnant de créativité. Wow.

mkdir ../ct-ng

On configure le build pour prendre en compte ce chemin

./configure --prefix=/home/toto/crosscompile/ct-ng

Note: C’est le moment rigolo où on voit bien qu’il nous manque des dépendances 😛 hop on les résout à grand coup de apt-get install machinkimank (aller je suis sympa je les donne: gawk bison flex help2man gperf libtool-bin automake cvs libncurses5-dev)

Puis on lance le build et l’installation:

make
make install

Sans oublier d’exporter le chemin pour avoir accès facilement à l’outil:

export PATH="${PATH}:/home/toto/crosscompile/ct-ng/bin"

Astuce de pro:
Mettre cette dernière ligne de commande dans son .bashrc (ou .autreRC) permet d’avoir cette configuration même après un redémarrage! Amazing!

Nous voilà prêt à nous servir de cet outil, ce que nous allons faire de ce pas car le gentil client ne peut plus attendre et veut absolument sa démo pour son objet connecté kifépouet pour 18h ce soir…

3 – main()

Maintenant que crosstool-ng est correctement installé, il faut l’utiliser. Eh oui.

Revenons à notre bon vieux jeune projet. Si vous avez bien fait les choses, vous devez avoir un répertoire de travail bien distinct, avec un nom réfléchi tel que « workspace » ou « superProjet ». Bien, créons un sous-répertoire dans celui-ci, que nous appellerons "crosstoolchain". (notez une fois encore la justesse et la pertinence du nom de dossier choisi):

cd workspace/
mkdir crosstoolchain
cd crosstoolchain

Alors, pourquoi je fais cela. La raison est simple: Vous voulez un cross-compilo pour un projet donné. il est plutôt bienvenu de le garder pas trop loin dudit projet, d’où ce sous-répertoire. En réalité, tout ce qui sera généré dans celui-ci ne sera pas utile, mais la configuration, elle, est importante à garder. Ainsi, il sera possible de le régénérer (en théorie) même si l’environnement de développement évolue. Mais j’y reviendrais.

Nous allons commencer avec un exemple en utilisant une architecture cible déjà prédéfinie par crosstool-ng.
Normalement, si vous avez bien fait l’export ci-dessus, vous avec accès à la commande ct-ng partout dans le système. On peut alors se lancer joyeusement dans la grande aventure de la génération d’un cross compilateur!

Tout d’abord, regardons quelles sont les architectures existantes :

14:14:16 tfo@debianblog:~/workspace/crosstoolchain %%EDITORCONTENT%%gt; ct-ng list-samples
Status  Sample name
  LN    config
  MKDIR config.gen
  IN    config.gen/arch.in
  IN    config.gen/kernel.in
  IN    config.gen/cc.in
  IN    config.gen/binutils.in
  IN    config.gen/libc.in
  IN    config.gen/debug.in
[G..]   alphaev56-unknown-linux-gnu
[G..]   alphaev67-unknown-linux-gnu
[G..]   arm-bare_newlib_cortex_m3_nommu-eabi
[G..]   arm-cortex_a15-linux-gnueabi
[G.X]   arm-cortexa5-linux-uclibcgnueabihf
[G..]   arm-cortex_a8-linux-gnueabi
[G.X]   arm-cortexa9_neon-linux-gnueabihf
[G..]   armeb-unknown-eabi
[G..]   armeb-unknown-linux-gnueabi
[G..]   armeb-unknown-linux-uclibcgnueabi
[G..]   arm-unknown-eabi
[G..]   arm-unknown-linux-gnueabi
[G.X]   arm-unknown-linux-musleabi
[G..]   arm-unknown-linux-uclibcgnueabi
[G.X]   arm-unknown-linux-uclibcgnueabihf
[G..]   armv6-rpi-linux-gnueabi
[G..]   armv7-rpi2-linux-gnueabihf
[G..]   avr
[G..]   i586-geode-linux-uclibc
[G..]   i586-mingw32msvc,i686-none-linux-gnu
[G..]   i686-nptl-linux-gnu
[G.X]   i686-w64-mingw32
[G..]   m68k-unknown-elf
[G..]   m68k-unknown-uclinux-uclibc
[G..]   mips64el-n32-linux-uclibc
[G..]   mips64el-n64-linux-uclibc
[G..]   mips-ar2315-linux-gnu
[G..]   mipsel-sde-elf
[G..]   mipsel-unknown-linux-gnu
[G..]   mips-malta-linux-gnu
[G..]   mips-unknown-elf
[G..]   mips-unknown-linux-uclibc
[G.X]   i686-w64-mingw32,nios2-spico-elf
[G..]   powerpc-405-linux-gnu
[G..]   powerpc64-unknown-linux-gnu
[G..]   powerpc-860-linux-gnu
[G..]   powerpc-e300c3-linux-gnu
[G..]   powerpc-e500v2-linux-gnuspe
[G..]   powerpc-unknown-linux-gnu
[G..]   powerpc-unknown-linux-uclibc
[G..]   powerpc-unknown_nofpu-linux-gnu
[G.X]   s390-ibm-linux-gnu
[G..]   s390x-ibm-linux-gnu
[G..]   sh4-unknown-linux-gnu
[G..]   sparc-unknown-linux-gnu
[G.X]   x86_64-w64-mingw32,x86_64-pc-linux-gnu
[G..]   x86_64-unknown-linux-gnu
[G..]   x86_64-unknown-linux-uclibc
[G.X]   x86_64-w64-mingw32
[G..]   xtensa-unknown-linux-uclibc
 L (Local)       : sample was found in current directory
 G (Global)      : sample was installed with crosstool-NG
 X (EXPERIMENTAL): sample may use EXPERIMENTAL features
 B (BROKEN)      : sample is currently broken

arm, ppc, x86, mips… il y a déjà pas mal de choix. Pour l’application géniale du client (génial lui aussi), nous allons dire que nous avons un petit processeur ARM tout simple, mais nous n’allons pas utiliser d’OS. On va donc partir sur une architecture dite baremetal. Oh, comme c’est étrange, il existe déjà la configuration pour la cible arm-unknown-eabi! Quelle surprise!

Mais, qu’est-ce que signifie ce terme barbare, pour ne par dire viking? En fait, les cibles que traite crosstool-ng se décomposent ainsi : archi-cpu-system-libC.

Dans notre cas, on sait que l’on va générer du code pour architecture ARM, sur lequel on ne connaît pas la famille (ou ça nous importe peu), et on utilise une libC adaptée au baremetal (ici ce sera newlib, mais il y en a d’autres qu’il est possible d’utiliser) donc eabi. Si nous avions un Linux, la cible serait alors arm-unknown-linux-gnueabi.

Pour illustrer ces différences et avoir un résumé rapide de la cible, il suffit d’ajouter le préfixe show- devant le nom de cible. Par exemple pour nous:

ct-ng show-arm-unknown-eabi
14:14:20 tfo@debianblog:~/workspace/crosstoolchain %%EDITORCONTENT%%gt; ct-ng show-arm-unknown-eabi
IN config.gen/arch.in
IN config.gen/kernel.in
IN config.gen/cc.in
IN config.gen/binutils.in
IN config.gen/libc.in
[G..] arm-unknown-eabi
OS : bare-metal
Companion libs : gmp-6.0.0a mpfr-3.1.3 mpc-1.0.3
binutils : binutils-2.25.1
C compilers : gcc | 5.2.0
Languages : C,C++
C library : newlib-2.2.0 (threads: none)
Tools :

Ce qui est bien différent de:

ct-ng show-arm-unknown-linux-gnueabi
14:15:29 tfo@debianblog:~/workspace/crosstoolchain %%EDITORCONTENT%%gt; ct-ng show-arm-unknown-linux-gnueabi
[G..] arm-unknown-linux-gnueabi
OS : linux-4.3
Companion libs : gmp-6.0.0a mpfr-3.1.3 mpc-1.0.3 libelf-0.8.13 expat-2.1.0 ncurses-6.0
binutils : binutils-2.25.1
C compilers : gcc | 5.2.0
Languages : C,C++
C library : glibc-2.22 (threads: nptl)
Tools : dmalloc-5.5.2 duma-2_5_15 gdb-7.10 ltrace-0.7.3 strace-4.10

Notez la version du noyaux Linux ciblé, les langages de programmation supportés par le cross-compilateur et les outils fournis.

Mais continuons avec notre petit cpu ARM. Il est temps de passer à la création effective de ce satané compilateur tant désiré. Pour cela, il faut sélectionner la cible simplement avec la commande :

ct-ng arm-unknown-eabi
14:17:37 tfo@debianblog:~/workspace/crosstoolchain %%EDITORCONTENT%%gt; ct-ng arm-unknown-eabi
CONF config/config.in
#
# configuration written to .config
#

***********************************************************

Initially reported by: YEM
URL: http://ymorin.is-a-geek.org/

***********************************************************

Now configured for "arm-unknown-eabi"

C’est ce fichier .config qu’il faut conserver avec le projet. Avec une cible make par exemple, il sera facile de régénérer la crosstoolchain.

On peut lancer la génération tout de suite :

ct-ng build

Et hop, crosstool-ng se charge de récupérer les sources de l’ensemble des logiciels nécessaires, et à la bonne version! Il ne reste plus qu’à attendre sagement la fin, et prier pour n’avoir aucune rupture de dépendance (ça peut arriver). Pour info, la compilation s’est effectuée en 20 minutes pour moi.

Il faut également noter que la génération est native, c’est à dire qu’elle se fait pour la machine hôte (ici i686). Il est possible de cross compiler le cross-compilateur (crosscompiloception) mais l’utilité d’un tel process est toute relative…

La génération s’est bien finie ? Super, ça veut donc dire que le crosscompilo est prêt à être utilisé ! Il est disponible dans le répertoire $HOME/x-tools :

ls -l /home/toto/x-tools
14:38:09 tfo@debianblog:~/workspace/crosstoolchain %%EDITORCONTENT%%gt; ls -l /home/tfo/x-tools/
total 4
dr-xr-xr-x 8 tfo tfo 4096 août 24 14:38 arm-unknown-eabi

Et plus précisément:

ls -l /home/toto/x-tools/arm-unknown-eabi/bin
14:40:01 tfo@debianblog:~/workspace/crosstoolchain %%EDITORCONTENT%%gt; ls -l /home/tfo/x-tools/arm-unknown-eabi/bin/
total 17908
-r-xr-xr-x 1 tfo tfo  755272 août  24 14:38 arm-unknown-eabi-addr2line
-r-xr-xr-x 2 tfo tfo  784652 août  24 14:38 arm-unknown-eabi-ar
-r-xr-xr-x 2 tfo tfo 1314828 août  24 14:38 arm-unknown-eabi-as
-r-xr-xr-x 2 tfo tfo  813684 août  24 14:38 arm-unknown-eabi-c++
lrwxrwxrwx 1 tfo tfo      20 août  24 14:38 arm-unknown-eabi-cc -> arm-unknown-eabi-gcc
-r-xr-xr-x 1 tfo tfo  753096 août  24 14:38 arm-unknown-eabi-c++filt
-r-xr-xr-x 1 tfo tfo  813684 août  24 14:38 arm-unknown-eabi-cpp
-r-xr-xr-x 1 tfo tfo    2685 août  24 14:18 arm-unknown-eabi-ct-ng.config
-r-xr-xr-x 1 tfo tfo   27652 août  24 14:38 arm-unknown-eabi-elfedit
-r-xr-xr-x 2 tfo tfo  813684 août  24 14:38 arm-unknown-eabi-g++
-r-xr-xr-x 2 tfo tfo  809588 août  24 14:38 arm-unknown-eabi-gcc
-r-xr-xr-x 2 tfo tfo  809588 août  24 14:38 arm-unknown-eabi-gcc-5.2.0
-r-xr-xr-x 1 tfo tfo   23960 août  24 14:38 arm-unknown-eabi-gcc-ar
-r-xr-xr-x 1 tfo tfo   23896 août  24 14:38 arm-unknown-eabi-gcc-nm
-r-xr-xr-x 1 tfo tfo   23960 août  24 14:38 arm-unknown-eabi-gcc-ranlib
-r-xr-xr-x 1 tfo tfo  488636 août  24 14:38 arm-unknown-eabi-gcov
-r-xr-xr-x 1 tfo tfo  451764 août  24 14:38 arm-unknown-eabi-gcov-tool
-r-xr-xr-x 1 tfo tfo  829640 août  24 14:38 arm-unknown-eabi-gprof
-r-xr-xr-x 4 tfo tfo 1091468 août  24 14:38 arm-unknown-eabi-ld
-r-xr-xr-x 4 tfo tfo 1091468 août  24 14:38 arm-unknown-eabi-ld.bfd
-r-xr-xr-x 2 tfo tfo  767880 août  24 14:38 arm-unknown-eabi-nm
-r-xr-xr-x 2 tfo tfo  945672 août  24 14:38 arm-unknown-eabi-objcopy
-r-xr-xr-x 2 tfo tfo 1167816 août  24 14:38 arm-unknown-eabi-objdump
-r-xr-xr-x 2 tfo tfo  784716 août  24 14:38 arm-unknown-eabi-ranlib
-r-xr-xr-x 1 tfo tfo  445352 août  24 14:38 arm-unknown-eabi-readelf
-r-xr-xr-x 1 tfo tfo  756296 août  24 14:38 arm-unknown-eabi-size
-r-xr-xr-x 1 tfo tfo  755208 août  24 14:38 arm-unknown-eabi-strings
-r-xr-xr-x 2 tfo tfo  945672 août  24 14:38 arm-unknown-eabi-strip

C’est magistralement beau hein? Je pense qu’il est temps de tester toute cette profusion de binaires fraîchement acquis.

Reprenons notre projet génial, kifaitpouet. Nous l’avons fort heureusement écrit en C en anticipant un peu le portage.

/home/toto/workspace/src/example.c

#if defined(__linux__)
  #include 
  #define do_print(x) printf(x)
#elif defined(__arm__)

  void do_print(const char* str)
  {
    // TODO quand on aura les specs de la board
  }

#else
  #error "no worky"
#endif

const unsigned int SLEEP_TIME = 1000000;

int main()
{
  while(1)
  {
    // reset sleep time
    unsigned int uiSleep = SLEEP_TIME;
    // do pouet
    do_print("pouet\n");
    // sleep a little
    for( ; uiSleep > 0; uiSleep--);
  }
}

Plus qu’à crosscompiler, en indiquant les bonnes bibliothèques à utiliser :

../../x-tools/arm-unknown-eabi/bin/arm-unknown-eabi-cc example.c -I../../x-tools/arm-unknown-eabi/sys-root/usr/include -L../../x-tools/arm-unknown-eabi/sys-root/lib --specs=nosys.specs -o example

L’option --specs=nosys.specs est necessaire lorsque l’on ne fait pas de semihosting (mecanisme qui permet de debugger du code ARM sur son hôte).

Et voilà!
Une petite vérification s’impose:

file example
14:44:47 tfo@debianblog:~/workspace/src %%EDITORCONTENT%%gt; file example
example: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, not stripped

Victoire!!

Pour aller plus loin, dans un prochain article j’expliquerai la mise en place de QEMU afin de simuler notre cible hardware et pouvoir executer ce petit bout de code.

4 – more()

Nous avons vu là une possibilité de base offerte par crosstool-ng. Mais il est possible de créer son système cible personnalisé.

Pour cela, il suffit, après avoir choisi une cible exemple qui se rapproche de la notre, de bidouiller un tas de choses à notre convenance:

ct-ng menuconfig

Les adeptes de compilation de noyaux compulsifs se retrouveront en terrain familier, les autres tâtonneront un petit peu plus mais ça reste assez clair.

Il est donc possible de modifier les options de compilation de crosstool-ng, mais aussi et surtout les paramètres de configuration de la cible:
mmu ou pas, endianness, bitness, jeux d’instructions, FPU ou pas,… Bref, tout ce qu’il faut pour pimper son crosscompilo!

Je n’irai pas plus loin sur ce point, peut être pour un prochain article mais je me vois mal vous prendre par la main et commenter chaque option… Un peu de curiosité ne fait pas de mal! 🙂

Autre chose: si vous versionnez votre projet (ce que tout développeur sensé et mentalement stable se doit de faire), n’oublier pas d’ajouter l’ensemble (ou pas) du sous repertoire workspace/crosstoolchain, la génération pourra alors simplement se refaire avec la commande ct-ng build. (rien ne vous empêche de garder votre crosscompilo aussi, ça va un peu plus vite si on déploie sur le même environnement de developpement)

4 – stop()

A travers ces quelques lignes, nous avons vu qu’il est relativement simple de nos jours de mettre en place un environnement de developpement embarqué. Un peu de rigeur et de bon sens facilite la chose, mais même en étant bordélique comme je le suis, on finit par y arriver. Surtout avec des articles de grandes qualités comme celui-ci!

À une prochaine pour des nouvelles aventures passionnantes!

Tomtom, out.

5 – Includes()

http://crosstool-ng.org/

Partager cet article

Partager sur facebook
Partager sur twitter
Partager sur linkedin
Partager sur pinterest
Partager sur print
Partager sur email

UN BLOG DE

CIO Systèmes Embarqués – 1 Rue de la Presse, 42 000 Saint-Étienne – contact@ciose.fr – 04 77 93 34 32 

CIO  Systèmes Embarqués est le nom commercial de la SAS CIO Informatique Industrielle 

CIO Informatique Industrielle © 1991-2020 v3.0

Mentions Légales