Se sei uno sviluppatore che crea pacchetti binari, come un RPM, DEB, Flatpak o Snap, devi compilare il codice per una varietà di piattaforme di destinazione diverse. Gli obiettivi tipici includono x86 e ARM a 32 bit e 64 bit. Potresti fare le tue build su diverse macchine fisiche o virtuali, ma ciò significa mantenere diversi sistemi. Invece, puoi utilizzare GNU Compiler Collection (GCC) per eseguire la cross-compilazione, producendo binari per diverse architetture da un'unica macchina di compilazione.
Supponiamo di avere un semplice gioco di lancio di dadi che vuoi compilare in modo incrociato. Qualcosa scritto in C è relativamente facile sulla maggior parte dei sistemi, quindi per aggiungere complessità per motivi di realismo, ho scritto questo esempio in C++, quindi il programma dipende da qualcosa non presente in C (iostream , in particolare).
#include <iostream>
#include <cstdlib>
using namespace std;
void lose (int c);
void win (int c);
void draw ();
int main() {
int i;
do {
cout << "Pick a number between 1 and 20: \n";
cin >> i;
int c = rand ( ) % 21;
if (i > 20) lose (c);
else if (i < c ) lose (c);
else if (i > c ) win (c);
else draw ();
}
while (1==1);
}
void lose (int c )
{
cout << "You lose! Computer rolled " << c << "\n";
}
void win (int c )
{
cout << "You win!! Computer rolled " << c << "\n";
}
void draw ( )
{
cout << "What are the chances. You tied. Try again, I dare you! \n";
}
Compilalo sul tuo sistema usando g++ comando:
$ g++ dice.cpp -o dice
Quindi eseguilo per confermare che funziona:
$ ./dice
Pick a number between 1 and 20:
[...]
Puoi vedere che tipo di file binario hai appena prodotto con il file comando:
$ file ./dice
dice: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically
linked (uses shared libs), for GNU/Linux 5.1.15, not stripped
E altrettanto importante, a quali librerie si collega con ldd :
$ ldd dice
linux-vdso.so.1 => (0x00007ffe0d1dc000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6
(0x00007fce8410e000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6
(0x00007fce83d4f000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6
(0x00007fce83a52000)
/lib64/ld-linux-x86-64.so.2 (0x00007fce84449000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1
(0x00007fce8383c000)
Hai confermato due cose da questi test:il binario che hai appena eseguito è a 64 bit ed è collegato a librerie a 64 bit.
Ciò significa che, per eseguire la compilazione incrociata per 32 bit, devi dire a g++ a:
- Produci un binario a 32 bit
- Collegamento a librerie a 32 bit anziché alle librerie a 64 bit predefinite
Configurazione del tuo ambiente di sviluppo
Per compilare a 32 bit, sono necessarie librerie e intestazioni a 32 bit installate sul sistema. Se esegui un sistema a 64 bit puro, non hai librerie o intestazioni a 32 bit e devi installare un set di base. Per lo meno, hai bisogno delle librerie C e C++ (glibc e libstdc++ ) insieme alla versione a 32 bit delle librerie GCC (libgcc ). I nomi di questi pacchetti possono variare da distribuzione a distribuzione. Su Slackware, è disponibile una distribuzione a 64 bit pura con compatibilità a 32 bit da multilib pacchetti forniti da Alien BOB. Su Fedora, CentOS e RHEL:
$ yum install libstdc++-*.i686
$ yum install glibc-*.i686
$ yum install libgcc.i686
Indipendentemente dal sistema che stai utilizzando, devi anche installare tutte le librerie a 32 bit utilizzate dal tuo progetto. Ad esempio, se includi yaml-cpp nel tuo progetto, devi installare la versione a 32 bit di yaml-cpp o, su molti sistemi, il pacchetto di sviluppo per yaml-cpp (ad esempio, yaml-cpp-devel su Fedora) prima di compilarlo.
Una volta risolto, la compilazione è abbastanza semplice:
$ g++ -m32 dice.cpp -o dice32 -L /usr/lib -march=i686
Il -m32 flag dice a GCC di compilare in modalità a 32 bit. Il -march=i686 l'opzione definisce ulteriormente il tipo di ottimizzazione da utilizzare (fare riferimento a info gcc per un elenco di opzioni). La -L flag imposta il percorso delle librerie a cui desideri collegare GCC. Di solito è /usr/lib per 32 bit, anche se, a seconda di come è configurato il tuo sistema, potrebbe essere /usr/lib32 o anche /opt/usr/lib o in qualsiasi luogo in cui sai di conservare le tue librerie a 32 bit.
Dopo la compilazione del codice, guarda la prova della tua build:
$ file ./dice32
dice: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV),
dynamically linked (uses shared libs) [...]
E, naturalmente, ldd ./dice32 punta alle tue librerie a 32 bit.
Diverse architetture
Il terminale Linux
- I 7 migliori emulatori di terminale per Linux
- 10 strumenti da riga di comando per l'analisi dei dati in Linux
- Scarica ora:cheat sheet SSH
- Cheat sheet sui comandi avanzati di Linux
- Esercitazioni sulla riga di comando di Linux
La compilazione a 32 bit su 64 bit per la stessa famiglia di processori consente a GCC di fare molte ipotesi su come compilare il codice. Se devi compilare per un processore completamente diverso, devi installare le utilità GCC cross-build appropriate. L'utilità che installi dipende da cosa stai compilando. Questo processo è un po' più complesso della compilazione per la stessa famiglia di CPU.
Quando esegui la compilazione incrociata per la stessa famiglia, puoi aspettarti di trovare lo stesso set di librerie a 32 bit delle librerie a 64 bit, perché la tua distribuzione Linux le mantiene entrambe. Quando si compila per un'architettura completamente diversa, potrebbe essere necessario cercare le librerie richieste dal codice. Le versioni di cui hai bisogno potrebbero non essere nei repository della tua distribuzione perché la tua distribuzione potrebbe non fornire pacchetti per il tuo sistema di destinazione o potrebbe non rispecchiare tutti i pacchetti in una posizione conveniente. Se il codice che stai compilando è tuo, probabilmente hai una buona idea di quali siano le sue dipendenze e possibilmente dove trovarle. Se il codice è qualcosa che hai scaricato e devi compilare, probabilmente non hai familiarità con i suoi requisiti. In tal caso, esamina ciò che il codice richiede per essere compilato correttamente (di solito sono elencati nei file README o INSTALL e sicuramente nel codice sorgente stesso), quindi vai a raccogliere i componenti.
Ad esempio, se devi compilare il codice C per ARM, devi prima installare gcc-arm-linux-gnu (32 bit) o gcc-aarch64-linux-gnu (64-bit) su Fedora o RHEL, o arm-linux-gnueabi-gcc e binutils-arm-linux-gnueabi su Ubuntu. Questo fornisce i comandi e le librerie necessarie per costruire (almeno) un semplice programma C. Inoltre, hai bisogno di tutte le librerie utilizzate dal tuo codice. Puoi posizionare i file di intestazione nella posizione normale (/usr/include sulla maggior parte dei sistemi), oppure puoi inserirli in una directory a tua scelta e puntare su GCC con il -I opzione.
Durante la compilazione, non utilizzare lo standard gcc o g++ comando. Invece, usa l'utilità GCC che hai installato. Ad esempio:
$ arm-linux-gnu-g++ dice.cpp \
-I/home/seth/src/crossbuild/arm/cpp \
-o armdice.bin
Verifica cosa hai costruito:
$ file armdice.bin
armdice.bin: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV) [...]
Biblioteche e prodotti finali
Questo è stato un semplice esempio di come utilizzare la compilazione incrociata. Nella vita reale, il tuo codice sorgente può produrre più di un singolo binario. Sebbene tu possa gestirlo manualmente, probabilmente non ci sono buone ragioni per farlo. Nel prossimo articolo mostrerò GNU Autotools, che fa la maggior parte del lavoro necessario per rendere portabile il tuo codice.