Wenn Sie ein Entwickler sind, der Binärpakete wie RPM, DEB, Flatpak oder Snap erstellt, müssen Sie Code für eine Vielzahl unterschiedlicher Zielplattformen kompilieren. Typische Ziele sind 32-Bit- und 64-Bit-x86 und ARM. Sie könnten Ihre Builds auf verschiedenen physischen oder virtuellen Maschinen erstellen, aber das bedeutet, dass Sie mehrere Systeme verwalten müssen. Stattdessen können Sie die GNU Compiler Collection (GCC) zum Cross-Compilieren verwenden und Binärdateien für mehrere verschiedene Architekturen von einer einzigen Build-Maschine erzeugen.
Angenommen, Sie haben ein einfaches Würfelspiel, das Sie crosskompilieren möchten. Etwas, das in C geschrieben ist, ist auf den meisten Systemen relativ einfach, also habe ich dieses Beispiel, um es realistischer zu machen, in C++ geschrieben, also hängt das Programm von etwas ab, das in C nicht vorhanden ist (iostream , speziell).
#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";
}
Kompilieren Sie es auf Ihrem System mit g++ Befehl:
$ g++ dice.cpp -o dice
Führen Sie es dann aus, um zu bestätigen, dass es funktioniert:
$ ./dice
Pick a number between 1 and 20:
[...]
Sie können sehen, welche Art von Binärdatei Sie gerade mit der Datei erstellt haben Befehl:
$ 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
Und ebenso wichtig, welche Bibliotheken es mit ldd verknüpft :
$ 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)
Sie haben bei diesen Tests zwei Dinge bestätigt:Die Binärdatei, die Sie gerade ausgeführt haben, ist 64-Bit und mit 64-Bit-Bibliotheken verknüpft.
Das bedeutet, dass Sie, um für 32-Bit zu kompilieren, g++ mitteilen müssen zu:
- Erzeuge eine 32-Bit-Binärdatei
- Link zu 32-Bit-Bibliotheken anstelle der standardmäßigen 64-Bit-Bibliotheken
Einrichten Ihrer Entwicklungsumgebung
Um auf 32-Bit zu kompilieren, benötigen Sie 32-Bit-Bibliotheken und -Header, die auf Ihrem System installiert sind. Wenn Sie ein reines 64-Bit-System ausführen, haben Sie keine 32-Bit-Bibliotheken oder -Header und müssen ein Basisset installieren. Sie benötigen zumindest die C- und C++-Bibliotheken (glibc und libstdc++ ) zusammen mit der 32-Bit-Version der GCC-Bibliotheken (libgcc ). Die Namen dieser Pakete können von Distribution zu Distribution variieren. Auf Slackware ist eine reine 64-Bit-Distribution mit 32-Bit-Kompatibilität von multilib verfügbar von Alien BOB bereitgestellte Pakete. Unter Fedora, CentOS und RHEL:
$ yum install libstdc++-*.i686
$ yum install glibc-*.i686
$ yum install libgcc.i686
Unabhängig vom verwendeten System müssen Sie auch alle 32-Bit-Bibliotheken installieren, die Ihr Projekt verwendet. Wenn Sie beispielsweise yaml-cpp einschließen in Ihrem Projekt, dann müssen Sie die 32-Bit-Version von yaml-cpp installieren oder auf vielen Systemen das Entwicklungspaket für yaml-cpp (zum Beispiel yaml-cpp-devel auf Fedora), bevor Sie es kompilieren.
Sobald das erledigt ist, ist die Kompilierung ziemlich einfach:
$ g++ -m32 dice.cpp -o dice32 -L /usr/lib -march=i686
Das -m32 Flag weist GCC an, im 32-Bit-Modus zu kompilieren. Der -march=i686 Option definiert weiter, welche Art von Optimierungen verwendet werden sollen (siehe info gcc für eine Liste der Optionen). Das -L Flag legt den Pfad zu den Bibliotheken fest, zu denen GCC verlinken soll. Dies ist normalerweise /usr/lib für 32-Bit, obwohl es je nach Konfiguration Ihres Systems /usr/lib32 sein könnte oder sogar /opt/usr/lib oder jeder Ort, von dem Sie wissen, dass Sie Ihre 32-Bit-Bibliotheken aufbewahren.
Nachdem der Code kompiliert wurde, sehen Sie sich den Beweis Ihres Builds an:
$ file ./dice32
dice: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV),
dynamically linked (uses shared libs) [...]
Und natürlich ldd ./dice32 verweist auf Ihre 32-Bit-Bibliotheken.
Unterschiedliche Architekturen
Das Linux-Terminal
- Die 7 besten Terminalemulatoren für Linux
- 10 Befehlszeilentools für die Datenanalyse unter Linux
- Jetzt herunterladen:SSH-Spickzettel
- Spickzettel für fortgeschrittene Linux-Befehle
- Linux-Befehlszeilen-Tutorials
Durch das Kompilieren von 32-Bit auf 64-Bit für dieselbe Prozessorfamilie kann GCC viele Annahmen darüber treffen, wie der Code zu kompilieren ist. Wenn Sie für einen völlig anderen Prozessor kompilieren müssen, müssen Sie die entsprechenden Build-übergreifenden GCC-Dienstprogramme installieren. Welches Dienstprogramm Sie installieren, hängt davon ab, was Sie kompilieren. Dieser Vorgang ist etwas komplexer als das Kompilieren für dieselbe CPU-Familie.
Wenn Sie für dieselbe Familie crosskompilieren, können Sie damit rechnen, denselben Satz von 32-Bit-Bibliotheken wie 64-Bit-Bibliotheken zu finden, da Ihre Linux-Distribution beide verwaltet. Wenn Sie für eine völlig andere Architektur kompilieren, müssen Sie möglicherweise nach Bibliotheken suchen, die für Ihren Code erforderlich sind. Die Versionen, die Sie benötigen, befinden sich möglicherweise nicht in den Repositories Ihrer Distribution, da Ihre Distribution möglicherweise keine Pakete für Ihr Zielsystem bereitstellt oder nicht alle Pakete an einem geeigneten Ort spiegelt. Wenn der Code, den Sie kompilieren, Ihnen gehört, haben Sie wahrscheinlich eine gute Vorstellung davon, was seine Abhängigkeiten sind und wo Sie sie möglicherweise finden. Wenn Sie den Code heruntergeladen haben und kompilieren müssen, sind Sie wahrscheinlich mit den Anforderungen nicht so vertraut. Untersuchen Sie in diesem Fall, was der Code benötigt, um korrekt zu bauen (sie sind normalerweise in den README- oder INSTALL-Dateien und sicherlich im Quellcode selbst aufgeführt), und sammeln Sie dann die Komponenten.
Wenn Sie beispielsweise C-Code für ARM kompilieren müssen, müssen Sie zuerst gcc-arm-linux-gnu installieren (32-Bit) oder gcc-aarch64-linux-gnu (64-Bit) auf Fedora oder RHEL oder arm-linux-gnueabi-gcc und binutils-arm-linux-gnueabi auf Ubuntu. Dies stellt die Befehle und Bibliotheken bereit, die Sie benötigen, um (mindestens) ein einfaches C-Programm zu erstellen. Darüber hinaus benötigen Sie alle Bibliotheken, die Ihr Code verwendet. Sie können Header-Dateien am üblichen Speicherort ablegen (/usr/include auf den meisten Systemen), oder Sie können sie in einem Verzeichnis Ihrer Wahl ablegen und GCC mit dem -I darauf verweisen Option.
Verwenden Sie beim Kompilieren nicht den Standard-gcc oder g++ Befehl. Verwenden Sie stattdessen das von Ihnen installierte GCC-Dienstprogramm. Zum Beispiel:
$ arm-linux-gnu-g++ dice.cpp \
-I/home/seth/src/crossbuild/arm/cpp \
-o armdice.bin
Überprüfen Sie, was Sie erstellt haben:
$ file armdice.bin
armdice.bin: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV) [...]
Bibliotheken und Leistungen
Dies war ein einfaches Beispiel für die Verwendung von Cross-Compiling. Im wirklichen Leben kann Ihr Quellcode mehr als nur eine einzige Binärdatei erzeugen. Sie können dies zwar manuell verwalten, aber es gibt wahrscheinlich keinen guten Grund dafür. In meinem nächsten Artikel werde ich GNU Autotools demonstrieren, das die meiste Arbeit erledigt, die erforderlich ist, um Ihren Code portabel zu machen.