Chi realizza sistemi embedded utilizza per forza di cose un compilatore. In questo articolo si vuole affrontare un tema decisamente interessante: quali sono le attività da svolgere per realizzare un proprio cross-compilatore partendo da GCC.
La cross-compilazione (cross-compilation) è una tecnica nella quale un generico compilatore, ospitato su un ambiente host, ad esempio un desktop o notebook, produce codice eseguibile per una piattaforma differente (target). In applicazioni di questo tipo, il progettista ha il totale controllo della macchina target, dalle risorse fisiche della scheda al controllo totale del processore; inoltre, a volte può capitare che le risorse dell’intera board siano mediate da un’applicazione intermedia, un sistema operativo. Questo non rappresenta però un limite, ma consente di sfruttare la risorsa hardware in maniera più efficiente possibile e fornisce le dell’intero sistema. Di solito, chi si occupa di sistemi dedicati, ad esempio applicazioni di telefonia mobile o per generici dispostivi di largo consumo, ha la necessità di disporre di un ambiente di questo tipo. Sul mercato esistono differenti prodotti in grado di rispondere a diverse fasce di prezzo proposte da aziende europee o americane. A volte, per ragioni le alla nostra applicazione software/firmware un insieme di funzioni, librerie in grado di incidere sul carico prestaziona utilizzare un ambiente di tipo free, libero da qualsiasi vincolo commerciale. Nel campo open source l’ambiente di lavoro che probabilmente incontra il maggior favore di pubblico è senz’altro la suite GCC. Esistono innumerevoli versioni del compilatore C, o C++, di GCC e per differenti piattaforme. Inoltre, pensare di portare GCC su un processore differente tra quelli supportati non presenta particolari problemi o difficoltà.
Porting di GCC - crosstool
Una volta, alcuni anni fa, per realizzare una distribuzione di GCC per una differente architettura, o realizzare una distribuzione target, era necessario intervenire direttamente sulla parte di configurazione. In un approccio del genere era necessario, tuttavia, conoscere non solo la distribuzione ufficiale, ma anche quelle accessorie. Il listato 1 mostra i diversi passi da compiere per ottenere una distribuzione target.
$ mkdir -p /src $ cd /src $ bunzip2 < nome_distribuzione.tar.bz2 | tar xvf – $ patch -p0 < gcc-3.2.1-arm-multilib.patch $ PATH=/gnutools/bin:$PATH ; export PATH $ mkdir -p /tmp/build/gcc $ cd /tmp/build/gcc /src/gcc-3.2.1/configure —target=powerpc-eabi \ —prefix=/gnutools —enable-languages=c,c++ \ —with-gnu-as —with-gnu-ld —with-newlib \ —with-gxx-include-dir=/gnutools/powerpc-eabi/include \ -v 2>&1 | tee configure.out $ make -w all install 2>&1 | tee make.out
Listato 1 - Sequenza per PowerPc |
Con l’aumento della complessità e con le proliferazioni di versioni o patch differenti, un approccio del genere è sconsigliabile. Oggi, infatti, l’approccio è decisamente differente, e di solito si preferiscono utilizzare degli strumenti automatici che minimizzano l’approccio diretto del programmatore. In realtà, questo non vuol dire che la costruzione di un cross compilatore, partendo da GCC, sia diventata un’attività “difficile”, ma al contrario, grazie all’uso di strumenti automatici e trasparenti, si fornisce al programmatore uno strumento in grado di alleggerire il lavoro di definizione e di generazione degli eseguibili finali. Oggi esistono diversi tool che permettono di svolgere questo lavoro; i più famosi e utilizzati sono sicuramente crosstool e buildroot. Crosstool è un insieme di utility utilizzate nella definizione e costruzione del cross-compilatore. Sono presenti due definizioni: il platform configuration file e il package specific configuration file, utilizzati nel processo di generazione. Oltre al GCC, il processo di generazione del cross-compilatore ha l’esigenza di generare le versioni cross di diverse utility presenti, per esempio quelle di binutils (per esempio assembler e linker). Nella distribuzione ufficiale di GCC è presente solo la versione desktop del compilatore, non la versione cross. In questo articolo mostreremo come utilizzare crosstool per costruire la nostra versione cross di GCC. Crosstool è un pacchetto che contiene, tra l’altro, una serie di comandi, mediante dei comuni file script, e definizioni per differenti piattaforme target. Crosstool automatizza il processo di generazione dell’ambiente cross, e in questo senso, offre uno strumento veramente flessibile per costruire la nostra applicazione perché l’intero processo è guidato da diverse variabili di ambiente inserite in due file di configurazione:
» Package configuration file che descrive la versione del package che si vuole costruire correlando tutte le informazioni possibili in base all’architettura scelta;
» Platform configuration file che definisce 67 prefissi utilizzati dal cross-compiler per dere i binari prodotti compatibili con le impostazioni di configurazione. In altre parole, attraverso opportuni variabili si generano versioni del compilatore o dell’assemblatore compatibili con l’ambiente target scelto. Crosstool permette la definizione delle varianti di GCC per differenti ambienti target. Oggi crosstool è uno strumento utilizzato da diversi sviluppatori per costruire la propria versione target del processore in uso. Crosstool offre una serie di file di configurazione per un’enorme varietà di piattaforme, purtroppo, tuttavia, insieme ai diversi file sorgenti indispensabili per concludere correttamente il lavoro, esistono anche un insieme variegato di patch per ogni piattaforma e versione, ma non solo, anche per le differenti versioni di binutils e Glibc. La gestione di tutte queste informazioni può comportare un carico di lavoro eccessivo per crosstool in termini di manutenzione e verifica. Per questa ragione crosstool non fornisce, nella sua distribuzione, tutte le differenti combinazioni possibili. Per fortuna, chi ha definito e realizzato crosstool ha inserito nella distribuzione un matrice che pone in relazione le piattaforme con le differenti versioni dei package. In questo modo, l'utilizzatore può ricavare le corrette relazioni supportate. Per i casi non contemplati è necessario armarsi di pazienza per condurre in proprio il lavoro senza l’ausilio di crosstool. Una versione rilasciata di crosstool è la 0.43. Oltre a definire correttamente una serie di parametri nel nostro sistema, su Linux o con l’ottimo Cygwin in ambiente Microsoft Windows, utilizzati dai file di configurazione, è necessario impostare tre variabili di ambiente all’interno della nostra shell.
Queste variabili sono utilizzate dai vari comandi, script, lanciati da crosstool:
» GCC_LANGUAGE: è una stringa che contiene, utilizzando delle virgole per separare i vari campi, la lista dei linguaggi per i quali si vuole costruire l’ambiente di cross-compilazione. Ai fini del lavoro finale, è la stessa cosa se decidessimo di utilizzare l’opzione –enabel-language (vedi listato 1). Per esempio con GCC_LANGAUGES="c,c++,java" si manifesta l'intenzione di utilizzare il linguaggio C, C++ e Java;
» RESULT_TOP: si identifica la struttura gerarchica dell’albero delle cartelle, vale a dire la posizione dove si vuole installare il cross-compilatore con tutte le librerie, tools e file include. Un esempio potrebbe essere RESULT_TOP=/tools/cross-compilers;
» TARBALLS_DIR: identifica il nome della cartella temporanea dove crosstool posizionerà il codice sorgente dei file estratti durante il processo di generazione del crosscompilatore. Così: TARBALLS_DIR=/tmp/crosstooldownload.
Costruire un default cross-compiler
La costruire di un cross-compilatore passa attraverso la definizione di tre variabili d’ambiente che abbiamo già descritto precedentemente: occorre identificare il package e la configurazione da utilizzare per la piattaforma che si vuole realizzare. In seguito occorre eseguire tutti i comandi associati per mezzo dei file script. I passi necessari sono così brevemente riassunti:
» assicurarsi di lavorare nella cartella che è stata creata dal processo di estrazione dei file con la versione corrente di crosstool, la cartella è “crosstool-0.43”;
» creare, utilizzando gli opportuni comandi, la cartella nella quale si intende installare il nostro cross-compilatore (se questa non esistesse già). Occorre, inoltre, verificare gli opportuni permessi di accesso alla cartella;
» definire la variabile d’ambiente RESULT_TOP, per esempio: $ export RESULT_TOP=/tools/cross-compilers;
» definire la variabile GCC_LANGUAGE, così:$ export GCC_LANGUAGES=”c,c++,java”;
» a questo punto occorre creare una cartella (directory) che aiuta a identificare le varie patch e i diversi package che costituiscono la distribuzione ufficiale. Successivamente, occorre definire la variabile che contiene il nome di questa cartella:
$ mkdir /tmp/crosstool-download $ export TARBALLS_DIR=/tmp/crosstooldownload
» a questo punto possiamo iniziare il processo di generazione utilizzando la piattaforma e il package appropriato, così come ricavato precedentemente:
$ eval ‘cat arm-xscale.dat gcc-3.4.5-glibc-2.3.6.dat’ all.sh –notest
Il comando sopra mostrato si occuperà di costruire l’ambiente target identificato da arm-xscale, utilizzando la versione del compilatore gcc 3.4.5 e la libreria Glibc 2.3.6. In questo modo, i vari script di crosstool si attivano, in rapida successione, per leggere i vari package richiesti per lo specifico cross-compilatore; inoltre, compila tutti i vari sorgenti inclusi e, al termine del processo di compilazione e di linking, installa i file che rappresentano il risultato del processo di costruzione nelle sottocartelle /tools/cross-compilers. Le sottocartelle sono identificate con una particolare nomenclatura: nome del package e versione relativa. Per esempio, i vari file bin, lib, include, share dell’ambiente arm-xscale risultano localizzati nella cartella:
/tools/cross-compilers/gcc-3.4.5-glibc-2.3.6/arm-xscale-linux-gnu
Costruire un custom cross-compiler
A volte può capitare di dover utilizzare una specifica versione della libreria binutils, GCC o Glibc, allora è necessario creare un proprio package e un file di configurazione appositamente definito proprio perché, magari, le diverse scelte fatte, non corrispondono a uno specifico ambiente fornito da crosstool. Per esempio, supponiamo di volere utilizzare la versione 3.4.5 di GCC e la 2.3.6 della Glibc. A questo riguardo occorrerà aggiornare il package configuration file come riportato nel listato 3.
BINUTILS_DIR=binutils-2.15 GCC_DIR=gcc-3.4.5 GLIBC_DIR=glibc-2.3.6 LINUX_DIR=linux-2.6.8 LINUX_SANITIZED_HEADER_DIR=linux-libc-headers-2.6.12.0 GLIBCTHREADS_FILENAME=glibc-linuxthreads-2.3.6
Listato 3 - Target definition file |
Il procedimento risulta abbastanza semplice. Si potrebbe copiare un package configuration file in un altro file con il nome che rifletta il nuovo package, e quindi la nuova definizione, e, successivamente, una volta aperto il file, occorrerà modificare conseguentemente le informazioni in esso contenute. Supponiamo, per esempio, di costruire una nuova versione di arm-xscale utilizzando i riferimenti contenuti nel listato 3 utilizzando la versione 4.2 di GCC. A questo punto, copiamo per prima cosa le informazioni del file “gcc-3.4.5-glibc-2.3.6.dat” in un altro appropriato file con il nome “gcc-4.2.0-glibc-2.3.6.dat” e solo successivamente modifichiamo la variabile GCC_DIR al suo nuovo riferimento, 4.2.0. Mostriamo il passaggio nel listato 4.
BINUTILS_DIR=binutils-2.15 GCC_DIR=gcc-4.2.0 GLIBC_DIR=glibc-2.3.6 LINUX_DIR=linux-2.6.8 LINUX_SANITIZED_HEADER_DIR=linux-libc-headers-2.6.12.0 GLIBCTHREADS_FILENAME=glibc-linuxthreads-2.3.6
Listato 4 - gcc-4.2.0-glibc-2.3.6.dat |
È, inoltre, possibile con crosstool applicare le eventuali patch note delle componenti utilizzate nel processo di configurazione oppure possiamo anche costruire altri tipi di cross-compilatori, oltre a quelli di base forniti e supportati da crosstool. In questo caso, il tutto passa attraverso la definizione del platform configuration file. Il listato 2 mostra il contenuto di un semplice configuration file per arm-xscale (arm-xscale.dat). In questo caso è necessario sostituire tutti i riferimenti ad arm-xscale al diverso processore. Come vediamo, in questo caso occorre intervenire sulla variabile KERNELCONFIG.
KERNELCONFIG=`pwd`/arm.config TARGET=arm-xscale-linux-gnu TARGET_CFLAGS=”-O” GCC_EXTRA_CONFIG=”—with-cpu=xscale —enable-cxx-flags=-mcpu=xscale”
Listato 2 - Platform configuration file |
Gcc è oggi uno dei compilatori maggiormente utilizzati sui sistemi desktop ed embedded. Del resto, lo stesso Arduino IDE (uno degli ambienti più conosciuti e apprezzati dai maker) si basa su questo compilatore.