10.25
Afin de compiler du C pour le faire tourner sur une Fonera+, il est nécessaire d’avoir tous les outils adéquats. Parmi ces outils, le compilateur C est le plus évident, mais il ne servira à rien sans un assembleur et un linker (qui rassemble plusieurs fichiers assemblés en un binaire final). Un debugger est aussi un luxe dont on aura du mal à se passer. Heureusement pour nous, le projet GNU a créé des logiciels libres qui font tout ça : les GNU binutils, GCC et GDB. Il se trouve que le support de la cross-compilation avec ces outils est très bon, ce qui va bien nous arranger.
Tout d’abord, il a fallu décider de l’architecture précise pour laquelle compiler. En effet, le MIPS 4KEc de la Fonera+ peut fonctionner en mode little endian ou en mode big endian, au choix, et il n’est pas pratique de mélanger les deux. J’ai décidé d’utiliser pour DMMS le mode big endian, mais j’essaierai de temps en temps de compiler en mode little endian et de tester ça sur ma Fonera+.
Ensuite, on peut commencer à build les outils dont on aura besoin. J’ai trouvé pas mal de documentation sur le wiki de Linux MIPS ainsi que dans les PKGBUILD des paquets Archlinux sur l’Archlinux User Repository. Je vais tout de même tout résumer ici.
Comme vous le savez surement, la compilation d’un projet GNU se fait en général en deux étapes : la configuration (via le script ./configure) et la compilation (make). Lors de la configuration, on peut spécifier de compiler pour une certaine architecture cible, via l’option --target. L’architecture est désignée par ce que l’on appelle le target triplet qui est souvent sous la forme architecture-os-format. Dans notre cas, l’architecture sera mips, l’OS unknown (car on ne compile pas pour un OS connu) et le format elf, ce qui donne un triplet mips-unknown-elf. Si on voulait compiler pour du MIPS little endian, ça serait mipsel-unknown-elf (le « el » signifiant « little endian » mais inversé, notez l’humour très fin).
Maintenant que tout est expliqué, commençons. On va exporter quelques variables d’environnement dont on va se servir par la suite, notamment le chemin où installer les binaires compilés et le type de système pour lequel on compile :
$ export TARGET=mips-unknown-elf
$ export PREFIX=/usr/cross/fonera
Ensuite, on se place dans un dossier où il y a de l’espace disque (il faut environ 1.5G), puis on télécharge et on désarchive la dernière version de tous les outils qu’ils nous faut :
$ wget http://ftp.gnu.org/gnu/binutils/binutils-2.20.1.tar.bz2
$ wget http://ftp.gnu.org/gnu/gcc/gcc-4.5.1/gcc-core-4.5.1.tar.bz2
$ wget http://ftp.gnu.org/gnu/gdb/gdb-7.2.tar.bz2
$ for f in *.tar.bz2; do tar xjvf $f; rm $f; done;
C’est parti pour la compilation des différents logiciels. Tout d’abord, pour les binutils, rien de spécial :
$ ./configure --target=$TARGET --prefix=$PREFIX
$ make -j3 # environ 2 minutes sur mon i7
$ sudo make install
Vu que je suis curieux et impatient, testons l’assemblage d’une toute petite fonction en assembleur MIPS, qui fait simplement une boucle infinie (on écrit une instruction qui saute vers elle-même) :
$ cat > test.s <<EOF
loop_forever:
j loop_forever
EOF
On assemble le code en utilisant les binutils que l’on vient de compiler :
$ /usr/cross/fonera/bin/mips-unknown-elf-as test.s -o test.o
Puis pour tester et être sûr que tout est bon, on va désassembler avec objdump, qui fait lui aussi partie des binutils :
$ /usr/cross/fonera/bin/mips-unknown-elf-objdump -d test.o
test.o: file format elf32-bigmips
Disassembly of section .text:
00000000 <loop_forever>:
0: 08000000 j 0 <loop_forever>
4: 00000000 nop
L’output d’objdump nous dit que notre fichier compilé est au format elf32-bigmips (donc du MIPS big endian, impeccable) et on retrouve le code que l’on a mis dans notre test.s. Tout semble bon, on peut continuer la compilation des outils.
Maintenant, la bête : GCC. Comme il n’aime pas trop qu’on le compile directement dans le dossier qui contient ses sources, on va simplement créer un dossier gcc-build et se mettre dedans pour compiler. Au niveau des options du configure, on désactive une tonne de choses qui ne nous serviront pas : le support des threads (on ne compile pas pour linux ou pour windows, il n’aurait de toute façon pas su faire), la libgcj utilisée pour le Java (je doute vraiment en avoir besoin pour le moment), les bibliothèques dynamiques (on ne supportera pas ça tout de suite), le support de la compilation pour du little endian et des variantes de MIPS qui ne nous intéressent pas (--disable-multilib), la bibliothèque de GCC qui s’occupe de détecter les stack corruption, et enfin l’utilisation des headers de la libc de l’architecture cible, vu qu’on n’a pas de libc pour le moment. On définit également le CPU pour lequel on compile par défaut (un MIPS 4Kec). Quel bordel.
$ mkdir gcc-build && cd gcc-build
$ ../gcc-4.5.1/configure --target=$TARGET --prefix=$PREFIX --disable-threads --disable-libgcj --disable-shared --disable-multilib --disable-libssp --without-headers --with-arch=4kec --with-tune=4kec
$ make -j 3 # environ 5 minutes
$ sudo make install
Testons ça de la même manière que les binutils : compilons un test.c basique qui fait une boucle infinie, et désassemblons le :
$ cat > test.c <<EOF
void infinite_loop(void)
{
for (;;)
;
}
EOF
$ /usr/cross/fonera/bin/mips-unknown-elf-gcc -c test.c
$ /usr/cross/fonera/bin/mips-unknown-elf-objdump -d test.o
test.o: file format elf32-bigmips
Disassembly of section .text:
00000000 <infinite_loop>:
0: 27bdfff8 addiu sp,sp,-8
4: afbe0004 sw s8,4(sp)
8: 03a0f021 move s8,sp
c: 08000003 j c <infinite_loop+0xc>
10: 00000000 nop
Ça semble bien fonctionner ! Reste donc à compiler GDB, le debugger GNU. C’est en gros la même chose que pour les binutils :
$ ./configure --target=$TARGET --prefix=$PREFIX
$ make -j3 # environ 2 minutes
$ sudo make install
Voilà, nous avons maintenant toute une flopée d’outils pour travailler avec une architecture MIPS : un assembleur, un désassembleur, un linker, un compilateur, un debugger, un stripper, etc. Et c’est donc fini pour cette étape 0.5, dont j’avais prévu de mettre le contenu avec d’autres choses mais ça s’est avéré un peu trop long. Peut-être qu’on va enfin pouvoir exécuter un truc sur ce routeur la prochaine fois ?