Construire son environnement de "cross-compiling" ------------------------------------------------- { Auteur: Guillaume Thouvenin, initialement publié dans le journal LJNB } Plan 0) Introduction 1) Préliminaires 2) Mise en place du cross-compiler 2.1) Compilation de binutils 2.2) Compilation de GCC 2.3) Compilation de la Glibc 2.4) Compilation supplémentaires 3) Test 4) Conclusion 5) Références --------------------------------------------------------------------------- 0 - Introduction ================ Un "cross-compiler" est un compilateur qui produit du code pour une machine possédant une architecture différente de celle sur laquelle il s'exécute. Dans cet article nous allons voir comment configurer un environnement permettant de compiler un programme pour une architecture powerpc (la cible) en utilsant une architecture i686 (l'hôte). Nous présenterons les différentes étapes de la compilation avant d'entrer dans le vif du sujet. 1 - Préliminaires ================= Nous pouvons distinguer quatre étapes dans la compilation: Étape 1 - pré-traitement "preprocessing" (cpp0) qui traite les directives de compilation, les macros et en règle générale tout ce qui commence par #. Étape 2 - la compilation (cc1) qui produit du code assembleur (et donc spécifique à une architecture donnée) Étape 3 - l'assemblage (as) qui produit un fichier objet à partir du code assembleur issu de la compilation. L'un des formats les plus répandus est le format ELF [1]. Étape 4 - l'édition de lien (collect2, nm, strip, ld, ...) dont le rôle est de faire le lien entre les différentes fonctions utilisées dans les fichiers objets. C'est à cette étape que le fichier exécutable est généré Voici une compilation faites étapes par étapes : [guill@leffe] $ gcc -E prog1.c > prog1.i [guill@leffe] $ /usr/lib/gcc-lib/i386-redhat-linux/2.96/cc1 prog1.i main Execution times (seconds) parser : 0.01 (100%) usr 0.00 ( 0%) sys 0.01 (100%) wall varconst : 0.00 ( 0%) usr 0.00 ( 0%) sys 0.00 ( 0%) wall jump : 0.00 ( 0%) usr 0.00 ( 0%) sys 0.00 ( 0%) wall flow analysis : 0.00 ( 0%) usr 0.00 ( 0%) sys 0.00 ( 0%) wall local alloc : 0.00 ( 0%) usr 0.00 ( 0%) sys 0.00 ( 0%) wall global alloc : 0.00 ( 0%) usr 0.00 ( 0%) sys 0.00 ( 0%) wall flow 2 : 0.00 ( 0%) usr 0.00 ( 0%) sys 0.00 ( 0%) wall shorten branches : 0.00 ( 0%) usr 0.00 ( 0%) sys 0.00 ( 0%) wall reg stack : 0.00 ( 0%) usr 0.00 ( 0%) sys 0.00 ( 0%) wall final : 0.00 ( 0%) usr 0.00 ( 0%) sys 0.00 ( 0%) wall symout : 0.00 ( 0%) usr 0.00 ( 0%) sys 0.00 ( 0%) wall rest of compilation : 0.00 ( 0%) usr 0.00 ( 0%) sys 0.00 ( 0%) wall TOTAL : 0.01 0.00 0.01 [guill@leffe] $ as -o prog1.o prog1.s [guill@leffe] $ file prog1.o prog1.o: ELF 32-bit LSB relocatable, Intel 80386, version 1, not stripped L'édition de liens est un peu plus compliquée puisqu'il faut indiquer ou se trouve les différentes librairies, le symbole de départ, etc... Si vous voulez absolument le faire nous vous conseillons de regarder une trace de compilation (strace). Bien sûr, lorsque nous compilons un programme il n'est pas nécessaire de faire ces étapes manuellement puisque le compilateur de GNU (GCC) s'occupe d'ordonnancer ces appels avec les bons paramètres. Si nous avons expliqué tout ça c'est pour bien comprendre les programmes que nous devons recompiler afin de mettre en oeuvre notre plateforme de développement. Les outils "as" et "ld" utilisés dans les étapes 3 et 4 sont fournis par le paquetage binutils [2]. Le compilateur GCC [3] fournit les outils "cpp", "cc" et "collect2" utilisés lors des étapes 1, 2 et 4. De plus, nous allons avoir besoin de recompiler la librairie C. Nous avons choisi la glibc [4] mais il existe d'autres librairies plus compactes comme par exemple la newlib [5] qui est surtout utilisée dans les systèmes embarqués. Pour récapituler, nous allons avoir besoin de recompiler au minimum trois choses : binutils, gcc et la libc. L'ordre de compilation est important car comme nous l'avons vu le programme gcc s'occupe d'organiser les différentes étapes de la compilation et notamment. Il a donc besoin de connaître les emplacements de certains programmes comme par exemple le gestionnaire d'archives ar fournit par binutil. Donc, nous allons commencer par compiler binutils, ensuite nous compilerons gcc et enfin la librairie C. Si vous souhaitez compiler g++, il faudra le faire après la compilation de la libc puisque celle-ci est nécessaire à sa compilation. Vous devrez donc en premier lieu compiler gcc avec uniquement le support du C après les binutils et après la compilation des librairies C vous pourrez ajouter le support pour C++, Objective C ou autre. 2 - Mise en place du cross-compiler =================================== La première chose que nous faisons est la création d'un espace de travail. Nous avons créé un répertoire de travail dans le /home/guill que nous avons appelé cross-compiler. Nous avons voulu éviter d'être administrateur pour la suite de l'installation (ce qui évite d'écraser par erreur sa libc-i386 par la libc-ppc que nous verrons plus tard). Le répertoire accueillant notre cross-compiler sera /home/guill/cross-compiler/ppc. Nous allons aussi créer un répertoire src et obj qui contiendront respectivement les sources et les fichiers de compilations des différentes parties de notre environnement. [guill@leffe:cross-compiler] $ pwd /home/guill/cross-compiler [guill@leffe:cross-compiler] $ mkdir ppc [guill@leffe:cross-compiler] $ mkdir src [guill@leffe:cross-compiler] $ mkdir obj 2.1 - Compilation de binutils ----------------------------- Nous avons utilisé la version 2.12 de binutils. [guill@leffe:src] $ tar zxvf binutils-2.12.tar.gz [guill@leffe:src] $ cd ../obj && mkdir binutils && cd binutils [guill@leffe:binutils] $ ../../src/binutils-2.12/configure \ > --prefix=/home/guill/cross-compiler/ppc \ > --target=powerpc-linux && make && make install Pour binutils nous voyons que les options sont simples. L'option "prefix" indique l'endroit ou nous installerons les programmes et l'option "target" précise l'architecture cible. Nous n'avons pas précisé l'architecture de la machine hôte car celle-ce sera détectée par le programme de configuration. Si la machine hôte est différente de celle sur utilisée pour la mise en place de l'environnement de cross-compiling alors vous devrez utiliser l'option "host". Les programmes de binutils et de gcc ne sont pas forcement synchronisés et la dernière version de binutils ne fonctionnera peut-être pas avec la dernière version de gcc. Malheureusement pour le savoir la seule solution est d'essayer. Donc, si vous souhaitez compiler votre propre environnement de "cross-compiling" regardez sur le web ce qui marche ou ne marche pas. Lorsque la compilation et l'installation sont terminés vous devriez obtenir ça : [guill@leffe:binutils] $ cd ../../ppc [guill@leffe:ppc] $ ls -l total 28 drwxr-xr-x 2 guill users 4096 May 9 16:14 bin drwxr-xr-x 2 guill users 4096 May 9 16:14 include drwxr-xr-x 2 guill users 4096 May 9 16:14 info drwxr-xr-x 2 guill users 4096 May 9 16:14 lib drwxr-xr-x 3 guill users 4096 May 9 16:14 man drwxr-xr-x 4 guill users 4096 May 9 16:14 powerpc-linux drwxr-xr-x 3 guill users 4096 May 9 16:14 share [guill@leffe:ppc] $ ls -l bin/ total 17384 -rwxr-xr-x 1 guill users 1398590 May 9 16:14 powerpc-linux-addr2line -rwxr-xr-x 2 guill users 1279900 May 9 16:14 powerpc-linux-ar -rwxr-xr-x 2 guill users 1823567 May 9 16:14 powerpc-linux-as -rwxr-xr-x 1 guill users 177872 May 9 16:14 powerpc-linux-c++filt -rwxr-xr-x 1 guill users 213991 May 9 16:14 powerpc-linux-gasp -rwxr-xr-x 2 guill users 1858507 May 9 16:14 powerpc-linux-ld -rwxr-xr-x 2 guill users 1378635 May 9 16:14 powerpc-linux-nm -rwxr-xr-x 1 guill users 1732441 May 9 16:14 powerpc-linux-objcopy -rwxr-xr-x 1 guill users 1887398 May 9 16:14 powerpc-linux-objdump -rwxr-xr-x 2 guill users 1281171 May 9 16:14 powerpc-linux-ranlib -rwxr-xr-x 1 guill users 477333 May 9 16:14 powerpc-linux-readelf -rwxr-xr-x 1 guill users 1224513 May 9 16:14 powerpc-linux-size -rwxr-xr-x 1 guill users 1253702 May 9 16:14 powerpc-linux-strings -rwxr-xr-x 2 guill users 1732440 May 9 16:14 powerpc-linux-strip 2.2 - Compilation de GCC ------------------------ Pour la compilation de gcc nous avons utilisé gcc-2.95.3. Si vous souhaitez compiler g++, ne le faites pas maintenant car comme nous l'avons dit, nous avons besoin des librairies pour l'architecture PowerPC qui n'est pas encore compilée. Pour la compiler, nous avons besoin du cross-compiler donc, la première chose est de compiler gcc avec le support C uniquement. [guill@leffe:ppc] $ cd ../src [guill@leffe:src] $ tar zxvf gcc-core-2.95.3.tar.gz [guill@leffe:src] $ cd ../obj && mkdir gcc && cd gcc Attention, ici gcc va avoir besoin de connaître l'emplacement des programmes de binutils s'exécutant sur i686 mais ayant pour cible l'architecture ppc. Donc il faut ajouter l'emplacement de ces programmes dans le "path" (utiliser la commande export ou setenv ou ce qui va bien) [guill@leffe:gcc] $ export PATH=/home/guill/cross-compiler/ppc/bin:$PATH [guill@leffe:gcc] $ ../../src/gcc-2.95.3/configure \ > --prefix=/home/guill/cross-compiler/ppc \ > --target=powerpc-linux \ > --enable-languages=c \ > --with-newlib && > make && make install Si tout c'est bien passé vous devriez avoir dans le répertoire de cross-compiling les fichiers et dossiers suivants : [guill@leffe:gcc] $ cd ../../ppc/bin [guill@leffe:bin] $ ls -l total 18192 -rwxr-xr-x 1 guill users 210440 May 9 16:49 cpp -rwxr-xr-x 1 guill users 74488 May 9 16:49 gcov -rwxr-xr-x 1 guill users 1398590 May 9 16:14 powerpc-linux-addr2line -rwxr-xr-x 2 guill users 1279900 May 9 16:14 powerpc-linux-ar -rwxr-xr-x 2 guill users 1823567 May 9 16:14 powerpc-linux-as -rwxr-xr-x 1 guill users 177872 May 9 16:14 powerpc-linux-c++filt -rwxr-xr-x 1 guill users 213991 May 9 16:14 powerpc-linux-gasp -rwxr-xr-x 1 guill users 207173 May 9 16:49 powerpc-linux-gcc -rwxr-xr-x 2 guill users 1858507 May 9 16:14 powerpc-linux-ld -rwxr-xr-x 2 guill users 1378635 May 9 16:14 powerpc-linux-nm -rwxr-xr-x 1 guill users 1732441 May 9 16:14 powerpc-linux-objcopy -rwxr-xr-x 1 guill users 1887398 May 9 16:14 powerpc-linux-objdump -rwxr-xr-x 1 guill users 156101 May 9 16:49 powerpc-linux-protoize -rwxr-xr-x 2 guill users 1281171 May 9 16:14 powerpc-linux-ranlib -rwxr-xr-x 1 guill users 477333 May 9 16:14 powerpc-linux-readelf -rwxr-xr-x 1 guill users 1224513 May 9 16:14 powerpc-linux-size -rwxr-xr-x 1 guill users 1253702 May 9 16:14 powerpc-linux-strings -rwxr-xr-x 2 guill users 1732440 May 9 16:14 powerpc-linux-strip -rwxr-xr-x 1 guill users 145921 May 9 16:49 powerpc-linux-unprotoize 2-3 Compilation de la Glibc --------------------------- Il reste la glibc. Nous avons utilisé la glibc-2.2.5 avec les threads que l'on retrouve dans glibc-linuxthreads-2.2.5.tar.gz. Les fichiers incluent dans la librairie C utilise certains fichiers d'en-têtes de linux. Avant de compiler il faut donc créer un lien entre les fichiers include de l'architecture cible dans l'arborescence de linux avec notre environnement de dévelopement. [guill@leffe:bin] $ cd ../powerpc-linux/include [guill@leffe:include] $ ln -s src/linux-2.4.18/include/asm-ppc asm [guill@leffe:include] $ ln -s src/linux-2.4.18/include/linux linux La compilation peut maintenant commencer (elle nécessiter un certain temps) [guill@leffe:include] $ cd ../../src [guill@leffe:src] $ tar zxvf glibc-2.2.5.tar.gz [guill@leffe:src] $ cd glibc-2.2.5 [guill@leffe:glibc-2.2.5] $ tar zxvf glibc-linuxthreads-2.2.5.tar.gz [guill@leffe:glibc-2.2.5] $ cd ../../obj/ && mkdir libc && cd libc [guill@leffe:libc] $ CC=powerpc-linux-gcc AR=powerpc-linux-ar \ > RANLIB=powerpc-linux-ranlib \ > ../../src/glibc-2.2.5/configure \ > --host=powerpc-linux --enable-add-ons \ > --with-headers=/home/guill/cross-compiler/ppc/powerpc-linux/include \ > --prefix=/home/guill/cross-compiler/ppc/powerpc-linux \ > make && make install !!! ATTENTION !!! Si vous avez choisi de faire tout ce qui précède en root et si vous ne faites pas attention aux chemins d'installation vous pourriez au moment du "make install" détruire tout votre système car si vous vous trompez vous pourriez écraser l'ancienne librairie C par celle que nous venons de compiler et bien évidemment plus rien ne fonctionnerait sur votre machine... Si vous avez bien suivi nos instructions vous n'aurez aucun problèmes et vous disposez maintenant d'un environnement minimum sur votre i686 pour développer des applications s'exécutant sur un PowerPC 2.4 - Compilation supplémentaires -------------------------------- L'environnement que nous avons mis en place est le minimum pour pouvoir faire du développement. A partir de maintenant vous pourriez recompiler d'autres choses comme par exemple le compilateur g++ ou un déboggeur. 3 - Test ======== Notre programme de test est très simple et utilise notre nouvelle libc. Il consiste à afficher les informations concernant la machine sur laquelle il s'exécute. [guill@leffe:test] $ vim prog.c #include#include int main() { struct utsname buf; int res; res = uname(&buf); if (res != 0) return -1; fprintf(stderr, "\nInformation about the current kernel:\n\n"); fprintf(stderr, "\tsysname = %s\n", buf.sysname); fprintf(stderr, "\tnodename = %s\n", buf.nodename); fprintf(stderr, "\trelease = %s\n", buf.release); fprintf(stderr, "\tversion = %s\n", buf.version); fprintf(stderr, "\tmachine = %s\n", buf.machine); fprintf(stderr, "\n"); return 0; } ~ ~ "prog.c" 22L, 504C [guill@leffe:test] $ gcc -o prog prog.c [guill@leffe:test] $ file prog prog: ELF 32-bit LSB executable, Intel 80386, version 1, dynamically linked (uses shared libs), not stripped [guill@leffe:test] $ ./prog Information about the current kernel: sysname = Linux nodename = leffe release = 2.4.17 version = #1 Sat Dec 22 11:04:17 EST 2001 machine = i686 Maintenant le cross-compiling. Il ne faut pas oublier d'ajouter les binaires dans le chemin des exécutables (si ce n'est déjà fait). [guill@leffe:test] $ export PATH=/home/guill/cross-compiler/ppc/bin/:$PATH [guill@leffe:test] $ powerpc-linux-gcc \ -I/home/guill/cross-compiler/ppc/powerpc-linux/include \ -L/home/guill/cross-compiler/ppc/powerpc-linux/lib \ -o prog prog.c [guill@leffe:test] $ file prog prog: ELF 32-bit MSB executable, PowerPC or cisco 4500, version 1 (SYSV), dynamically linked (uses shared libs), not stripped Pour tester si l'exécutable produit fonctionne sur un power pc nous le copions sur une machine IBM RS/6000 avec un processeur PPC. guill@usf-cf-ppc-linux-1:~$ ./prog Information about the current kernel: sysname = Linux nodename = usf-cf-ppc-linux-1 release = 2.4.19-pre3-ben0 version = #2 Tue Mar 26 17:09:37 PST 2002 machine = ppc 4 - Conclusion ============== Tout fonctionne donc parfaitement :). Nous avons donc compilé un programme sur notre machine, une architecture i686, et ce programme s'exécute sur une autre machine qui elle possède un processeur PPC. 5 - Références ============== CrossGCC Frequently Asked Questions http://www.sthoward.com/CrossGCC/ penguinppc.org - The home of the linux/ppc port http://penguinppc.org/embedded/cross-compiling [1] ELF: Executable and Linking Format http://www.cs.ucdavis.edu/~haungs/paper/node10.html [2] ftp://ftp.kernel.org/pub/linux/devel/binutils/ [3] GNU C Compiler http://gcc.gnu.org/ [4] GNU C Library ftp://ftp.gnu.org/pub/gnu/glibc/ [5] Newlib C library http://sources.redhat.com/newlib/