Création d'une toolchain pour la cross-compilation d'un système uClibc
La Soekris utilise un processeur compatible x86 (et même Pentium), or ma seule machine un peu puissante est mon iBook (PowerPC G4). Si je veux préparer un système personnalisé, je vais donc devoir le cross-compiler, d'où cette page. Il existe certes des outils capables de faciliter cette tâche, comme buildroot ou scratchbox, mais aucun ne fonctionne sur PPC. Et puis, tout faire soi-même, c'est plus instructif. La création d'une toolchain de cross-compilation est une tâche délicate et complexe, et ce document pourra faire gagner un temps non négligeable. La procédure en bref
- Décompresser et configurer un kernel Linux
- Configurer uClibc en fonction de ce kernel et en installer les headers
- Compiler les binutils
- Compiler GCC
- Compiler uClibc
Procédure détaillée
Pour généraliser un peu le code, je règle les variables suivantes. Il ne faut pas les mettre dans l'environnement car elles seraient passées implicitement aux makefiles, et dans un but didactique, je préfère le faire explicitement.
PREFIX=/home/julien/cross BUILDDIR=/home/julien/build ARCHIVEDIR=/home/julien/archives ARCH=i386 TARGET=i386-linux-uclibc
Ainsi, j'installerai le cross-compilateur dans $PREFIX, téléchargerai le code source de la toolchain dans $ARCHIVEDIR et le compilerai dans $BUILDDIR. Configuration du kernel
Télécharger le source d'un kernel sur http://kernel.org (j'ai pris le dernier en date: le 2.6.17.11) et le placer dans $ARCHIVEDIR. Ensuite, le décompresser et le configurer en spécifiant l'architecture cible lors de la configuration:
cd $BUILDDIR tar -xvjf $ARCHIVEDIR/linux-2.6.17.11.tar.bz2 cd linux-2.6.17.11 make ARCH=$ARCH menuconfig
Petit truc: à la place de menuconfig, on peut utiliser defconfig, qui va configurer le kernel avec les paramètres par défaut, ou copier le fichier .config d'une autre configuration et utiliser la cible oldconfig.
Installation des headers d'uClibc
Récupérer la dernière version d'uClibc depuis http://uclibc.org, l'extraire, la configurer et installer les headers:
cd $BUILDDIR tar -xvjf $ARCHIVEDIR/uClibc-0.9.28.tar.bz2 cd uClibc-0.9.28 make menuconfig && make headers && make PREFIX=$PREFIX/ install_dev
Quelques options sont importantes lors du make menuconfig:
- RUNTIME_PREFIX=/
- DEVEL_PREFIX=/
- KERNEL_SOURCE=$BUILDDIR/linux-2.6.17.11 (à taper en toutes lettres, sans passer par la variable $BUILDDIR)
- DOPIC=y et ENABLE_SHARED=y (ces options se trouvent dans "General Library Settings" et permettent de créer des exécutables liés dynamiquement
- CROSS_COMPILER_PREFIX=$PREFIX/bin/$TARGET-: le préfixe du cross-compilateur pour compiler uClibc (à taper en toutes lettres, et ne pas oublier le tiret à la fin.
Comme pour le kernel, on peut récupérer le fichier .config d'une précédente configuration et utiliser la cible oldconfig
Installation des binutils et de gcc
Aller sur http://ftp.gnu.org/gnu, télécharger les paquets gcc-core-4.1.0.tar.bz2 et binutils-2.16.1.tar.bz2, et les décompresser.
cd $BUILDDIR tar -xvzf ../archives/binutils-2.17.tar.gz tar -xvjf ../archives/gcc-core-4.1.1.tar.bz2
La procédure d'installation des binutils est documentée dans binutils/README une fois l'archive extraite. Une fois dans build-binutils, compiler et installer les binutils par:
cd $BUILDDIR mkdir build-binutils cd build-binutils ../binutils-2.17/configure --target=$TARGET --prefix=$PREFIX --disable-nls && make && make install
Ensuite, compiler GCC. L'option --enable-languages=c ne va compiler que le compilateur C et --with-headers va lui faire prendre en compte uClibc. Quant à libssp, il s'agit d'une fonctionnalité de protection contre certaines attaques, mais sa cross-compilation semble problématique. --enable-shared requiert un fichier crti.o qui sera créé par uClibc, et donc on utilise plutôt --disable-shared. Cela ne désactive pas la production de binaires liés dynamiquement, mais ne compile pas certaines bibliothèques de GCC en dynamique. Cette étape demande beaucoup de mémoire disponible. 512 ou 768 MB de RAM ne sont probablement pas de trop pour ne pas avoir de swap excessif. Ce qui est sûr, c'est que 256 MB sont vraiment trop étroits et provoquent énormément de swap, et que 768 MB sont suffisants et rendent compilation beaucoup plus courte.
cd $BUILDDIR mkdir build-gcc cd build-gcc ../gcc-4.1.1/configure --target=$TARGET --prefix=$PREFIX --disable-nls --enable-languages=c --with-headers=$PREFIX/include --disable-libssp --disable-shared make PATH=$PREFIX/bin:$PATH all-gcc && make install
Compilation d'uClibc
uClibc a déjà été configuré, et maintenant qu'on a un compilateur, on peut le compiler:
cd $BUILDDIR/uClibc-0.9.28 make && make PREFIX=$PREFIX/$TARGET install
Le PREFIX lors de l'installation va placer les fichiers dans le répertoire où le compilateur attend ses bibliothèques.
Ca y est, la toolchain est prête à être utilisée; il suffit d'ajouter $PREFIX/bin dans le $PATH et de préfixer et d'utiliser $TARGET-gcc au lieu de gcc, $TARGET-strip au lieu de strip ...
Il est possible que la recompilation de gcc avec l'option --enable-shared permette de gagner un peu de place dans le système final, mais la situation actuelle me semble déjà satisfaisante.
Cross-compilation d'un kernel
La cross-compilation d'un kernel est un peu particulière, mais pas compliquée; il a déjà été configuré, il ne reste plus qu'à le compiler:
cd $BUILDDIR/linux-2.6.17.11 make ARCH=$ARCH CROSS_COMPILE=$PREFIX/bin/$TARGET- make modules_install INSTALL_MOD_PATH=/chemin/vers/repertoire/vide
Etape suivante: compiler le reste du système ...