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
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()
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