Perché cross compilare quando si può compilare nativamente?

Lavorando con architetture diverse mi trovo spesso a dover utilzzare uno stesso pacchetto software su più piattaforme, nulla di speciale se si usano distribuzioni che lo supportano, ma la cosa diventa complicata se la distribuzione che abbiano non lo fa!

Molto spesso i sistemi embedded che si trovano in commercio vengono venduti con distribuzioni ad hoc (cioè ritagliate per l’hardware) le quali però non sempre supportano tutto il software che ci serve. In questo caso, quello che si può fare, è provare a cross compilare il software che ci serve per la nostra architettura; cosa però irta di difficoltà!

Questo perché le toolchain che ci vengono fornite sono spesso incomplete e, se anche non lo fossero, molto spesso succede che il pacchetto software che ci serve non è pensato per essere cross compilato. Tutti questi fattori rendono, alle volte, quasi impossibile la cross compilazione.

Ecco perché suggerisco ai miei clienti di abbandonare questa strada e di compilare nativamente; vediamo come e quali problematiche comporta.

Uno dei primi problemi della compilazione nativa è data dal fatto che l’hardware di cui disponiamo non sempre è in grado di supportare questo tipo di operazione; questo per due ragioni principali:

  1. La memoria di massa del sistema è troppo piccola per contenere una distrubuzione completa (seppur installata in modalità minimale).
  2. La memoria del sistema è troppo poca per eseguire gli strumenti di compilazione.

Di solito per poter compilare in maniera agevole un pacchetto software dobbiamo usare una distribuzione abbastanza completa (io uso sempre Debian poichè non solo è la più completa ma è anche quella che supporta il maggior numero di architetture e pacchetti software), il che implica però la necessità di una discreta quantità di memoria di massa per contenerla (per una Debian basilare ci vogliono almeno 512MB di disco).

Questo problema, comunque, si supera abbastanza facilmente se abbiamo a disposizione una scheda di rete (praticamente tutti i sistemi moderni ce l’hanno) ed utilizzando un rootfs di tipo NFS. In questo modo il disco del sistema è su di un server esterno il quale può darci tutta la memoria di massa di cui abbiamo bisogno. Risolvere il secondo problema è invece un po’ più complicato, infatti, o il nostro sistema ha abbastanza memoria (di solito 512MB di RAM) oppure non è che ci sia molto da fare…

In realtà però esiste anche una terza via che risolve entrambi in nostri due problemi in un colpo solo: usare un emulatore.

Con un emulatore possiamo utilizzare come memoria di massa il disco del sistema host e, per quanto riguarda la RAM, anch’essa ci viene fornita dall’host; insomma sembra proprio la soluzione ideale… è in un certo senso lo è davvero! Vediamo allora un esempio pratico.

Installare una Debian PPC

In questi giorni dovevo cross compilare questo programma per una piattaforma PowerPC. La compilazione nativa è abbastanza semplice, basta dare il solito comando ./configure && make; sulla mia Ubuntu ottengo infatti:

$ ./configure && make
checking build system type… x86_64-unknown-linux-gnu
checking host system type… x86_64-unknown-linux-gnu
checking for gcc… gcc
checking for C compiler default output file name… a.out
checking whether the C compiler works… yes
checking whether we are cross compiling… no
checking for suffix of executables…
checking for suffix of object files… o
checking whether we are using the GNU C compiler… yes
checking whether gcc accepts -g… yes
checking for gcc option to accept ISO C89… none needed

e la compilazione va a buon fine (anche se ho dovuto installare un po’ di pacchetti che mancavano).

La cross compilazione invece, pur disponendo una una buona toolchain, non sono riuscito a farla terminare con successo! Ci sono diverse problematiche legate al fatto che il pacchetto ha bisogno di diverse dipendenze, tra cui Python. La soluzione allora è compilare nativamente.

Per fare questo però ci vorrebbe una Debian PPC (o PowerPC) e, sopratutto, una macchina per farla funzionare… io però la macchina PowerPC non ce l’ho e quindi uso QEMU per emularla. Installare QEMU su Ubuntu è abbastanza semplice, basta installare i seguenti pacchetti con il comando:

$ sudo aptitude install qemu qemu-system qemu-kvm-extras kvm-pxe

Da notare che mi sono anche dovuto installare un pacchetto Debian che Ubunti non supporta ma che è necessario a QEMU per emulare una macchina PowerPC:

$ wget http://ftp.us.debian.org/debian/pool/main/o/openbios-ppc/openbios-ppc_1.0+svn1018-1_all.deb
$ sudo dpkg -i openbios-ppc_1.0+svn1018-1_all.deb

A questo punto mi manca solo il disco di installazione della Debian PPC che trovo sul sito di Debian.

Creiamo quindi il disco del nostro sistema:

$ qemu-img create -f qcow2 debian-ppc.qcow2 1G

e quindi eseguire QEMU con il comando seguente per procedere all’installazione del sistema:

$ qemu-system-ppc -m 1024 -bios /usr/share/openbios/openbios-ppc -option-rom /usr/share/qemu/pxe-ne2k_pci.bin -boot d -cdrom debian-testing-powerpc-netinst.iso debian-ppc.qcow2

Se tutto è andato bene vedrete la schermata classica di installazione di Debian! A questo punto il tutto è abbastanza semplice, non ho fatto altro che procere all’installazione del sistema (che mi ha preso un bel po’ di tempo…) e di tutti i pacchetti software necessari e quindi sono passato alla compilazione nativa del programma:

$ ./configure
checking build system type… powerpc-unknown-linux-gnu
checking host system type… powerpc-unknown-linux-gnu
checking for gcc… gcc
checking for C compiler default output file name… a.out
checking whether the C compiler works… yes
checking whether we are cross compiling… no
checking for suffix of executables…
checking for suffix of object files… o
checking whether we are using the GNU C compiler… yes
checking whether gcc accepts -g… yes
checking for gcc option to accept ISO C89… none needed

Come si vede la compilazione si comporta esattamente come quella su PC solo che il risultato è per una macchina PowerPC, cioè esattamente quello che volevamo.

Conclusioni

Questa soluzione permette di compilare un pacchetto che di per se non è pensato per essere cross compilato in maniera molto agevole ma, naturalmente, anch’essa può avere degli svantaggi.

Innanzi tutto occorre avere una distribuzione Debian della stessa architettura del vostro sistema embedded, il che è quasi sempre vero a parte architetture esotiche; poi ci potrebbero essere alcuni problemi legati alle librerie dinamiche, infatti se la vostra distribuzione embedded è basta su versioni incompatibili della libc, gli eseguibili compilati sulla vostra Debian potrebbero non funzionare bene una volta trasferiti sul sistema embedded. In quest’ultimo caso si può pensare allora di compilare gli applicativi staticamente (se possibile) oppure fare in modo che questi carichino la versione della libc giusta magari trasportando i file necessari direttamente sul sistema embedded… ma queste cose vanno viste caso per caso.

Su Rodolfo Giometti

Ingegnere informatico libero professionista ed esperto GNU/Linux offre supporto per: - device drivers; - sistemi embedded; - sviluppo applicazioni industriali per controllo automatico e monitoraggio remoto; - corsi di formazione dedicati. Manutentore del progetto LinuxPPS (il sottosistema Pulse Per Second di Linux) contribuisce attivamente allo sviluppo del kernel Linux con diverse patch riguardanti varie applicazioni del kernel e dispositivi (switch, fisici di rete, RTC, USB, I2C, network, ecc.). Nei 15+ anni di esperienza su Linux ha lavorato con le piattaforme x86, ARM, MIPS & PowerPC.

Lascia un commento

Utilizzando il sito, accetti l'utilizzo dei cookie da parte nostra. maggiori informazioni

Questo sito utilizza i cookie per fonire la migliore esperienza di navigazione possibile. Continuando a utilizzare questo sito senza modificare le impostazioni dei cookie o clicchi su "Accetta" permetti al loro utilizzo.

Chiudi