Capitolo 3 : Compilatore incrociato
Un compilatore in grado di produrre codice eseguibile su una piattaforma diversa da quella su cui opera viene definito compilatore incrociato. Un esempio di cross-compilatore è un compilatore progettato per essere eseguito su un personal computer, ma che genera anche codice che può essere eseguito su dispositivi Android.
Per compilare codice per piattaforme diverse da un singolo host di sviluppo, un cross-compilatore è uno strumento estremamente utile. La compilazione diretta sulla piattaforma di destinazione potrebbe non essere possibile, ad esempio su dispositivi embedded che dispongono di una quantità limitata di risorse della CPU.
I confronti tra compilatori source-to-source e cross-compilatori non sono gli stessi. La differenza tra un compilatore source-to-source e un cross-compilatore è che il primo viene utilizzato per generare codice macchina per applicazioni multipiattaforma, mentre il secondo viene utilizzato per tradurre il codice di testo da un linguaggio di programmazione a un altro. I due sono entrambi strumenti per la programmazione.
Una delle applicazioni più fondamentali di un cross-compilatore consiste nell'abilitare la separazione tra l'ambiente di destinazione e l'ambiente di compilazione. Ciò è utile in diverse situazioni:
Uno dei motivi per cui sono stati sviluppati i cross-compilatori è stato quello di superare alcuni dei problemi causati dall'uso di macchine virtuali, come la JVM di Java. Il paradigma della macchina virtuale consente di utilizzare lo stesso output del compilatore in molti sistemi di destinazione. Tuttavia, questa non è sempre l'opzione migliore perché le macchine virtuali sono in genere più lente di altri tipi di computer e il programma compilato può essere eseguito solo su computer che dispongono di tale macchina virtuale.
Tuttavia, la cross-compilazione può essere utilizzata anche quando solo l'ambiente del sistema operativo è diverso, come quando si compila un programma FreeBSD sotto Linux, o anche solo la libreria di sistema, come quando si compilano programmi con uClibc su un host glibc. Nella maggior parte dei casi, l'architettura hardware è diversa (ad esempio, quando si codifica un programma destinato all'architettura MIPS in un computer x86).
Un metodo per costruire compilatori incrociati per macchine diverse è noto come Canadian Cross. Questo metodo viene utilizzato in situazioni in cui il computer di origine è significativamente più lento o meno comodo del computer di destinazione. Un compilatore incrociato viene costruito utilizzando il computer A (ad esempio, l'esecuzione di Windows XP su un processore IA-32) per sviluppare un compilatore incrociato che viene eseguito sul computer B (ad esempio, l'esecuzione di macOS su un processore x86-64) per creare eseguibili per il computer C (ad esempio, l'esecuzione di Android su un processore ARM). Un compilatore incrociato viene utilizzato per creare eseguibili per la macchina C. La macchina A è lenta ma ha un compilatore proprietario, la macchina B è veloce ma non ha alcun compilatore e la macchina C è poco più lenta da utilizzare per la compilazione. Il vantaggio pratico in questo esempio è che il computer A dispone di un compilatore specifico che non è disponibile per altri computer.
Quando la croce canadese viene utilizzata insieme a GCC, come illustrato in questo esempio, potrebbero essere disponibili un totale di quattro compilatori.
Anche se il compilatore incrociato del risultato finale (4) non sarà in grado di essere eseguito sul computer di compilazione A, sarà in grado di essere eseguito sul computer B per compilare un'applicazione in codice eseguibile. Questo codice verrebbe quindi copiato sulla macchina C ed eseguito sulla macchina C.
Ad esempio, NetBSD ha uno script di shell Unix POSIX chiamato build.sh. Questo script costruirà inizialmente la propria toolchain utilizzando il compilatore dell'host. Questa toolchain verrà quindi utilizzata per costruire il compilatore incrociato, che verrà utilizzato per costruire l'intero sistema.
Poiché c'erano tre partiti politici nazionali in Canada all'epoca in cui queste preoccupazioni venivano discusse, il termine "Croce canadese" è venuto alla luce.
Il GCC, che è una raccolta di compilatori di software libero, può essere configurato per eseguire la cross-compilazione. Molte lingue e piattaforme diverse sono supportate da esso.
Una versione compilata di binutils deve essere accessibile per ogni piattaforma di destinazione, poiché questo è un requisito di GCC. In particolare, l'assembler GNU è di grande importanza. Pertanto, il primo passo è assicurarsi che binutils venga compilato correttamente inviando l'opzione --target=some-target allo script di configurazione. Inoltre, l'opzione --target deve essere specificata ogni volta che viene utilizzato GCC. Dopodiché, GCC può essere eseguito normalmente, supponendo che gli strumenti generati da binutils siano presenti nel percorso. Ciò può essere ottenuto utilizzando quanto segue (su sistemi operativi simili a UNIX e che utilizzano bash):
Quando si utilizza GCC per la compilazione incrociata, è necessario che la piattaforma host abbia accesso a un sottoinsieme della libreria standard C utilizzata dalla piattaforma di destinazione. C'è la possibilità che il programmatore decida di compilare l'intera libreria C; Tuttavia, questa decisione potrebbe non essere affidabile. L'alternativa è quella di utilizzare newlib, che è una libreria C compatta che include solo i componenti assolutamente necessari per la compilazione del codice sorgente C.
È possibile utilizzare i concetti di una piattaforma di compilazione, di una piattaforma host e di una piattaforma di destinazione mentre si lavora con i pacchetti GNU Autotools, che includono autoconf, automake e libtool. Durante il processo di compilazione, il compilatore viene effettivamente compilato sulla piattaforma di compilazione. Si raccomanda di lasciare la compilazione non definita nella maggior parte delle situazioni (sarà predefinita dall'host). È sempre la piattaforma host che sarà responsabile dell'esecuzione degli artefatti di output dal compilatore, indipendentemente dal fatto che l'output sia prodotto anche da un altro compilatore o meno. Quando si compilano in modo incrociato i compilatori incrociati, viene utilizzata la piattaforma di destinazione; è una rappresentazione del tipo di codice oggetto che il pacchetto genererà; In altri casi, l'opzione della piattaforma di destinazione è inutile. Si consideri, ad esempio, la possibilità di cross-compilare un videogioco in modo tale che possa essere giocato su un Dreamcast. Mentre il Dreamcast funge da piattaforma host, la piattaforma di build si riferisce al sistema responsabile della compilazione del gioco. A seconda del compilatore utilizzato, i nomi host e target sono correlati tra loro e possono essere modificati come figlio e nipote.
La combinazione di compilatori GCC con sandbox specifiche come Scratchbox e Scratchbox 2, o PRoot, è un altro modo che viene spesso utilizzato dagli sviluppatori che lavorano con Linux embedded. L'uso di questi strumenti si traduce nella creazione di una sandbox "chrooted", che consente al programmatore di costruire strumenti critici, libc e librerie senza la necessità di definire percorsi aggiuntivi. Inoltre, vengono fornite strutture per "ingannare" il runtime in modo che "creda" di operare realmente sulla CPU di destinazione prevista (ad esempio, un'architettura ARM). In questo modo gli script di configurazione e altri script simili vengono eseguiti senza errori. Rispetto agli approcci "non chrooted", Scratchbox viene eseguito più lentamente e, affinché la maggior parte degli strumenti che sono ora installati sull'host funzionino, devono essere riposizionati all'interno di Scratchbox.
A partire dagli anni '80, Manx Software Systems, con sede a Shrewsbury, nel New Jersey, iniziò a produrre compilatori C con l'intenzione di soddisfare gli sviluppatori professionisti per una vasta gamma di piattaforme, tra cui PC IBM compatibili e Mac.
Il linguaggio di programmazione Aztec C è stato reso disponibile per l'uso su un certo numero di sistemi diversi, come MS-DOS, Apple II, DOS 3.3 e ProDOS, Commodore 64, Mac 68k e Amiga.
La versione MS-DOS di Aztec C era disponibile per l'uso come compilatore in modalità nativa e come cross-compilatore per altre piattaforme con processori diversi, come il Commodore 64 e l'Apple II, a partire dagli anni '80 e continuando per tutti gli anni '90 fino a quando la Manx Software Systems ha cessato l'attività. Le distribuzioni Internet per Aztec C, inclusi i loro compilatori incrociati basati su MS-DOS, sono ancora disponibili oggi. Il loro uso continua ancora oggi.
Oltre ad essere un compilatore MS-DOS 8086 in modalità nativa, l'Aztec C86 di Manx utilizzava capacità di cross-compilation. Anche se non generava codice per una CPU diversa, come i compilatori incrociati Aztec C65 6502 per il Commodore 64 e l'Apple II, costruiva eseguibili binari per sistemi operativi che all'epoca erano considerati legacy. Questi eseguibili sono stati progettati per la famiglia di processori 8086 a 16 bit.
Quando il Personal Computer IBM ha debuttato per la prima volta, è stato offerto con una selezione di sistemi operativi, tra cui CP/M-86 e PC DOS, che erano due delle opzioni disponibili. Le librerie di collegamento sono state rese disponibili ad Aztec C86 in modo che potesse generare codice per entrambi i sistemi operativi utilizzati dai PC IBM. Con il progredire degli anni '80, ulteriori versioni di Aztec C86 (3.xx, 4.xx e 5.xx) ottennero il supporto per le versioni "transitorie" 1 e 2 di MS-DOS. Queste versioni erano meno robuste...