Kapitel 3 : Fremdcompilierer
Ein Compiler, der in der Lage ist, Code zu erzeugen, der auf einer Plattform ausführbar ist, die sich von der Plattform unterscheidet, auf der der Compiler gerade ausgeführt wird, wird als Cross-Compiler bezeichnet. Ein Beispiel für einen Cross-Compiler ist ein Compiler, der für die Ausführung auf einem PC konzipiert ist, aber auch Code generiert, der auf Android-Geräten ausgeführt werden kann.
Um Code für verschiedene Plattformen von einem einzigen Entwicklungshost aus zu kompilieren, ist ein Cross-Compiler ein äußerst nützliches Werkzeug. Eine direkte Kompilierung auf der Zielplattform ist möglicherweise nicht möglich, z. B. auf Embedded-Geräten, die über eine begrenzte Menge an CPU-Ressourcen verfügen.
Vergleiche zwischen Source-to-Source-Compilern und Cross-Compilern sind nicht identisch. Der Unterschied zwischen einem Source-to-Source-Compiler und einem Cross-Compiler besteht darin, dass ersterer zum Generieren von Maschinencode für plattformübergreifende Anwendungen verwendet wird, während letzterer zum Übersetzen von Textcode von einer Programmiersprache in eine andere verwendet wird. Die beiden sind beides Werkzeuge zum Programmieren.
Eine der grundlegendsten Anwendungen eines Cross-Compilers besteht darin, die Trennung von Zielumgebung und Build-Umgebung zu ermöglichen. Dies ist in einer Reihe von verschiedenen Situationen hilfreich:
Einer der Gründe, warum Cross-Compiler entwickelt wurden, bestand darin, einige der Probleme zu überwinden, die durch die Verwendung von virtuellen Maschinen, wie z. B. der JVM von Java, verursacht wurden. Das Paradigma der virtuellen Maschine ermöglicht es, die gleiche Compilerausgabe auf vielen Zielsystemen zu verwenden. Dies ist jedoch nicht immer die beste Option, da virtuelle Computer in der Regel langsamer sind als andere Computertypen und das kompilierte Programm nur auf Computern ausgeführt werden kann, die über diesen virtuellen Computer verfügen.
Die Kreuzkompilierung kann jedoch auch verwendet werden, wenn nur die Betriebssystemumgebung unterschiedlich ist, z. B. beim Kompilieren eines FreeBSD-Programms unter Linux, oder sogar nur die Systembibliothek, z. B. beim Kompilieren von Programmen mit uClibc auf einem glibc-Host. In den meisten Fällen ist die Hardwarearchitektur unterschiedlich (z. B. beim Codieren eines Programms, das für die MIPS-Architektur auf einem x86-Computer vorgesehen ist).
Eine Methode zum Erstellen von Cross-Compilern für verschiedene Computer ist als Canadian Cross bekannt. Diese Methode wird in Situationen verwendet, in denen der ursprüngliche Computer erheblich langsamer oder weniger komfortabel ist als der Zielcomputer. Ein Kreuzcompiler wird erstellt, indem Computer A (z. B. Windows XP auf einem IA-32-Prozessor) verwendet wird, um einen Kreuzcompiler zu entwickeln, der auf Computer B ausgeführt wird (z. B. unter macOS auf einem x86-64-Prozessor), um ausführbare Dateien für Computer C zu erstellen (z. B. unter Android auf einem ARM-Prozessor). Ein Cross-Compiler wird verwendet, um ausführbare Dateien für Computer C zu erstellen. Maschine A ist langsam, verfügt aber über einen proprietären Compiler, Maschine B ist schnell, hat aber überhaupt keinen Compiler, und Maschine C ist unpraktisch langsam, um sie für die Kompilierung zu verwenden. Der praktische Vorteil in diesem Beispiel besteht darin, dass Maschine A über einen bestimmten Compiler verfügt, der anderen Computern nicht zur Verfügung steht.
Wenn das Kanadische Kreuz in Verbindung mit GCC verwendet wird, wie in diesem Beispiel zu sehen ist, stehen möglicherweise insgesamt vier Compiler zur Verfügung.
Obwohl der Endergebnis-Kreuzcompiler (4) nicht auf Buildmaschine A ausgeführt werden kann, kann er auf Maschine B ausgeführt werden, um eine Anwendung in ausführbaren Code zu kompilieren. Dieser Code wird dann auf Computer C kopiert und auf Computer C ausgeführt.
Zum Beispiel hat NetBSD ein POSIX-Unix-Shell-Skript, das build.sh heißt. Dieses Skript erstellt zunächst eine eigene Toolchain, indem es den Compiler des Hosts verwendet. Diese Toolchain wird dann verwendet, um den Cross-Compiler zu erstellen, der wiederum zum Aufbau des gesamten Systems verwendet wird.
Da es zu der Zeit, als diese Bedenken diskutiert wurden, in Kanada drei nationale politische Parteien gab, entstand der Begriff "Kanadisches Kreuz".
Der GCC, bei dem es sich um eine Sammlung von Freie-Software-Compilern handelt, kann so konfiguriert werden, dass er eine Kreuzkompilierung durchführt. Viele verschiedene Sprachen und Plattformen werden davon unterstützt.
Eine kompilierte Version von binutils muss für jede Zielplattform zugänglich sein, da dies eine Anforderung von GCC ist. Insbesondere der GNU Assembler ist von großer Bedeutung. Daher besteht der erste Schritt darin, sicherzustellen, dass binutils erfolgreich kompiliert wird, indem der Schalter --target=some-target an das configure-Skript gesendet wird. Darüber hinaus muss die Option --target angegeben werden, wenn GCC verwendet wird. Danach kann GCC normal ausgeführt werden, vorausgesetzt, dass die von binutils generierten Tools im Pfad vorhanden sind. Dies kann mit den folgenden Mitteln erreicht werden (auf Betriebssystemen, die UNIX ähneln und bash verwenden):
Bei der Verwendung von GCC für die Kreuzkompilierung ist es erforderlich, dass die Hostplattform Zugriff auf eine Teilmenge der C-Standardbibliothek hat, die von der Zielplattform verwendet wird. Es besteht die Möglichkeit, dass der Programmierer sich entscheidet, die gesamte C-Bibliothek zu kompilieren; Diese Entscheidung ist jedoch möglicherweise nicht zuverlässig. Die Alternative besteht darin, newlib zu verwenden, eine kompakte C-Bibliothek, die nur die Komponenten enthält, die für die Kompilierung von C-Quellcode unbedingt erforderlich sind.
Es ist möglich, die Konzepte einer Build-Plattform, einer Host-Plattform und einer Zielplattform zu verwenden, während mit den GNU Autotools-Paketen gearbeitet wird, zu denen autoconf, automake und libtool gehören. Während des Kompilierungsprozesses wird der Compiler wirklich auf der Build-Plattform kompiliert. Es wird empfohlen, den Build in den meisten Situationen undefiniert zu lassen (er wird standardmäßig vom Host übernommen). Es ist immer die Hostplattform, die für die Ausführung der Ausgabeartefakte vom Compiler verantwortlich ist, unabhängig davon, ob die Ausgabe auch von einem anderen Compiler erzeugt wird oder nicht. Beim Cross-Compilieren von Cross-Compilern wird die Zielplattform verwendet. Es handelt sich um eine Darstellung des Typs des Objektcodes, den das Paket generiert. In anderen Fällen ist die Option "Zielplattform" nutzlos. Betrachten Sie zum Beispiel die Möglichkeit, ein Videospiel so zu kompilieren, dass es auf einer Dreamcast gespielt werden kann. Während die Dreamcast als Host-Plattform dient, bezieht sich die Build-Plattform auf das System, das für die Kompilierung des Spiels verantwortlich ist. Je nach verwendetem Compiler sind die Namen Host und Ziel miteinander verwandt und können wie Sohn und Enkel geändert werden.
Die Kombination von GCC-Compilern mit speziellen Sandboxes wie Scratchbox und Scratchbox 2 oder PRoot ist eine weitere Möglichkeit, die häufig von Entwicklern genutzt wird, die mit Embedded Linux arbeiten. Die Verwendung dieser Werkzeuge führt zur Erstellung einer "chrooted" Sandbox, die es dem Programmierer ermöglicht, kritische Werkzeuge, libc und Bibliotheken zu erstellen, ohne zusätzliche Pfade definieren zu müssen. Darüber hinaus werden Funktionen bereitgestellt, um die Laufzeit so zu "täuschen", dass sie "glaubt", dass sie tatsächlich auf der beabsichtigten Ziel-CPU (z. B. einer ARM-Architektur) arbeitet. Auf diese Weise können Konfigurationsskripte und andere ähnliche Skripte fehlerfrei ausgeführt werden. Im Vergleich zu "nicht-chrooted" Ansätzen wird Scratchbox langsamer ausgeführt, und damit die meisten Tools, die jetzt auf dem Host installiert sind, funktionieren, müssen sie innerhalb von Scratchbox verschoben werden.
Ab den 1980er Jahren begann Manx Software Systems mit Sitz in Shrewsbury, New Jersey, mit der Produktion von C-Compilern mit der Absicht, professionelle Entwickler für eine breite Palette von Plattformen, einschließlich IBM PC-kompatiblen und Macs, zu bedienen.
Die Programmiersprache Aztec C wurde für den Einsatz auf einer Reihe verschiedener Systeme zur Verfügung gestellt, wie z. B. MS-DOS, Apple II, DOS 3.3 und ProDOS, Commodore 64, Mac 68k und Amiga.
Die MS-DOS-Version von Aztec C war ab den 1980er Jahren und in den 1990er Jahren bis zur Schließung von Manx Software Systems als Native-Mode-Compiler und als Cross-Compiler für andere Plattformen mit anderen Prozessoren, wie dem Commodore 64 und dem Apple II, verfügbar. Internet-Distributionen für Aztec C, einschließlich ihrer MS-DOS-basierten Cross-Compiler, sind auch heute noch verfügbar. Sie werden bis heute verwendet.
Aztec C86 von Manx war nicht nur ein 8086 MS-DOS-Compiler im nativen Modus, sondern nutzte auch Cross-Compilation-Funktionen. Auch wenn es keinen Code für eine andere CPU generierte, wie z. B. die Aztec C65 6502 Cross-Compiler für den Commodore 64 und Apple II, erstellte es binäre ausführbare Dateien für Betriebssysteme, die zu dieser Zeit als veraltet galten. Diese ausführbaren Dateien wurden für die 16-Bit-Prozessorfamilie 8086 entwickelt.
Als der IBM Personal Computer zum ersten Mal vorgestellt wurde, wurde er mit einer Auswahl von Betriebssystemen angeboten, darunter CP/M-86 und PC DOS, die zwei der verfügbaren Optionen waren. Aztec C86 wurde Link-Bibliotheken zur Verfügung gestellt, damit es Code für beide Computer-Betriebssysteme generieren...