Crosscompiler – Entwickeln für andere Systeme

Aus LinuxUser 02/2011

Crosscompiler – Entwickeln für andere Systeme

© Spectral, 123rf.com

Fremde Welten

Der große Vorteil freier Software ist die Zugänglichkeit des Quellcodes. Mithilfe eines Crosscompilers bringen Sie so viele Programme auch auf fremde Betriebssysteme oder andere Hardwareplattformen.

Wer manchmal selbst programmiert oder Software kompiliert, der kennt die üblichen Prozeduren, um ein einfaches C-Programm zu compilieren und auszuführen, wie

gcc -Wall -o helloworld helloworld.c ; ./helloworld

oder auch den Dreisatz ./configure; make; make install, um Programme einzurichten, die das GNU-Automake-System verwenden. Daneben gibt es noch etliche andere Build-Systeme wie etwa Cmake, Scons und Konsorten. Üblicherweise liegt der Software eine Datei INSTALL bei, die (hoffentlich gut) dokumentiert, wie man das Programm übersetzt und auf dem System einrichtet.

Was haben alle diese Prozeduren und Anleitungen – so verschieden sie erscheinen mögen – gemeinsam? Klar: Man kompiliert das Programm üblicherweise auf dem selben System, auf dem man es später auch ausführt. Freilich lässt sich ein fertig übersetztes Programm auch auf andere Rechner transferieren oder auch bereits als Binary herunterladen – aber das Betriebssystem und die CPU-Architektur der Zielplattform muss passen. Linux-Software läuft auf Windows gar nicht, und auch umgekehrt geht es per Wine nur recht mühsam. Von anderen Systemen, wie OpenSolaris oder Irix schweigt des Sängers Höflichkeit.

Crosscompiler

Hier kommen Crosscompiler in Spiel, also Compiler, die Programme für ein anderes Zielsystem erzeugen. Dabei können sich alle Komponenten des sogenannten Targets unterscheiden, sei es das Betriebssystem, die Registerbreite (von 8 bis 64 Bit) oder sogar der CPU-Typ an sich. Die Zielplattform muss nicht einmal real existieren: So portierte etwa das Projekt Trillian [1] den Linux-Kernel schon auf die Intel-IA64-Plattform (“Itanium”), bevor es eine entsprechende CPU überhaupt gab – der Linux-Port lief vorher nur in einem Simulator.

Portierungen auf eine noch gar nicht existierendes Target dürften freilich eher die Ausnahme darstellen. Doch selbst, wenn die Zielplattform bereits physikalisch existiert, gibt es anfangs in der Regel weder ein Betriebssystem noch einen Compiler darauf. Beides gilt es also auf einem anderen System für das neue Target zu übersetzen – das nennt der Fachmann Bootstrapping.

Gelegentlich kommt es auch vor, dass die Zielplattform zwar existiert, man aber keinen Zugriff darauf hat. Möglicherweise läuft darauf auch ein Closed-Source-Betriebssystem, das man als Open-Source-Verfechter nicht anfassen will – oder schlicht die Lizenzgebühren für Betriebssystem und Entwicklungstools nicht zahlen kann oder möchte. In vielen Fällen ziehen es Entwickler aber auch schlicht vor, die gewohnten und bewährten Entwicklungstools zu nutzen, um Executables für andere Plattformen zur Verfügung zu stellen.

Ein gängiges Einsatzfeld für Crosscompiling stellen daneben Embedded-Systeme dar: kleine und kleinste Rechner mit mickrigen CPUs, wenig RAM und kaum Massenspeicher. Selbst wenn die Ressourcen eines solchen Systems ausnahmsweise einmal für den Compiler und dessen Infrastruktur ausreichen, kann man in aller Regel durch Crosscompiling auf einem leistungsfähigen Rechner sehr viel Zeit sparen.

Möglicherweise haben auch Sie schon einmal mit einem Crosscompiler gearbeitet, ohne sich dessen bewusst zu sein: Auf 64-Bit-Linux-Systemen ist der GNU-Compiler gcc so installiert, dass er sowohl 32- als auch 64-Bit-Executables erzeugen kann. Es handelt sich also um einen sogenannten Bi-Arch-Compiler: In der Vorgabe übersetzt er Programme wie das typische helloworld.c aus Listing 1 für ein 64-Bit-System; über die Option -m32 weisen Sie ihn im Bedarfsfall an, ein 32-Bit-Binary zu erzeugen (Listing 2).

Listing 1

#include <stdio.h>
int main()
{
  printf("Hello World\n");
  return 0;
}

Listing 2

$ gcc -Wall -o helloworld helloworld.c
$ file helloworld
helloworld: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.8, not stripped
$ gcc -Wall -m32 -o helloworld helloworld.c
$ file helloworld 
helloworld: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.8, not stripped

Windows als Target

Warum sollte jemand unter Linux ausgerechnet für Microsoft Windows entwickeln wollen? Ganz einfach: Eine Windows-Version der eigenen Software verbreitert das Zielpublikum deutlich, und das sogar, ohne dass man dafür ein Windows-System benötigt. Zudem erspart das Crosscompiling hier die Pflege verschiedener Build-Systeme – etwa Autoconf/Automake für Linux und diverse andere Unix-Varianten sowie zusätzlich Visual-C-Projekte für Windows.

Entsprechende Windows-Crosscompiler bringen die meisten Distribution bereits in ihren Repositories mit. Unter Debian/Ubuntu genügt beispielsweise der Befehl apt-get install mingw32, um den Crosscompiler zu installieren. Das Kürzel Mingw steht für “Minimalist GNU for Windows”, eine minimale Portierung der GNU-Entwicklungstools auf Windows. Im Gegensatz zu Cygwin, einer umfassenden Portierung der GNU-Umgebung auf Windows, braucht man hier keine zusätzliche DLLs als Emulationsschicht.

Bei Opensuse 11.3 fehlt ein Windows-Crosscompiler im Standard-Repository, lässt sich jedoch recht einfach vom Opensuse-Server nachziehen. Dazu fügen Sie in YaST unter Software | Software-Repositories das Software-Repository:

http://download.opensuse.org/repositories/CrossToolchain:/mingw/openSUSE_11.3/

hinzu und installieren anschließend das Paket cross-mingw-gcc über Yast2 nach. Ist der Crosscompiler schließlich im System installiert, übersetzen Sie beispielsweise das helloworld.c aus Listing 1) kurzerhand als Windows-Programm (Listing 3).

Listing 3

$ /opt/cross/bin/i386-mingw32msvc-gcc -Wall -o helloworld.exe helloworld.c
$ file helloworld.exe
helloworld.exe: PE32 executable for MS Windows (console) Intel 80386 32-bit

Das resultierende Programm helloworld.exe, hergestellt nur mit freier Software, lauft nur auf Windows (bitte testen, wenn vorhanden) – oder unter Linux mit Hilfe von Wine.

Unter Debian/Ubuntu funktioniert das Ganze nahezu identisch, nur heißt der Compiler in diesem Fall i586-mingw32msvc-gcc und liegt direkt unter /usr/bin. Zum Übersetzen des kleinen Windows-Programms genügt also der Befehl

$ i586-mingw32msvc-gcc -Wall -o helloworld.exe helloworld.c

Lassen Sie die Option -o mit dem Namen der Ausgabedatei (-o helloworld.exe) weg, lautet der Standard-Dateiname des Binaries unter Unix a.out. Unter Windows ist die Dateierweiterung relevant, der Vorgabename lautet hier daher a.exe.

Umfangreichere Programme

Unser helloworld.exe ist ja ganz nett, aber noch nicht wirklich anspruchsvoll. Aber auch komplexere Programme stellen kein wirkliches Problem dar, allerdings setzt eine erfolgreiche Kompilierung das Vorhandensein gewisser DLLs, Headerfiles und Hilfsprogramme auch unter Linux voraus.

Um beispielsweise Anwendungen zu übersetzen, welche die LibSDL verwenden (eine bekannte plattformübergreifende Grafikbibliothek, häufig eingesetzt in Spielen), müssen Sie von der SDL-Homepage [2] erst die entsprechenden Windows-Pakete herunterladen und unter /opt/cross einrichten (Listing 4). Erst dann klappt das Kompilieren eines lauffähigen SDL-Programms.

Listing 4

$ wget http://www.libsdl.org/release/SDL-devel-1.2.14-mingw32.tar.gz
$ tar xvf SDL-devel-1.2.14-mingw32.tar.gz
$ cd SDL-devel-1.2.14
$ sudo make cross CROSS_PATH=/opt/cross
$ /opt/cross/bin/i386-mingw32msvc-gcc -Wall -o sdlhelloworld.exe sdlhelloworld.c $(/opt/cross/bin/sdl-config --libs --cflags)

Der Aufruf von /opt/cross/bin/sdl-config am Ende der Compiler-Zeile von Listing 4 liefert Pfade und Libraries für die Windows-Umgebung – für ein Linux-Programm würde man stattdessen sdl-config heranziehen.

Um das Programm unter Linux (Wine) oder Windows zu testen, benötigen Sie dann noch die SDL.dll aus SDL-1.2.14-win32.zip. Auch andere Bibliotheken gilt es gegebenenfalls für Windows nachzuinstallieren. Hier verwenden Sie, sofern vorhanden, die Entwicklerpakete für Windows oder müssen anderenfalls die Bibliotheken selbst crosskompilieren.

Installationspakete für Windows

Ein rein mit freier Software erzeugtes meinprogramm.exe ist ja ganz nett, und jeder Linux-User weiß auch, wie er so etwas startet – Windows-User dagegen tun sich damit oft schwer. Sie suchen nach einem Installationsprogramm namens setup.exe, das auf dem ohnehin chronisch überfüllten Desktop noch ein Icon und einen zusätzlichen Eintrag im Startmenü erzeugt. Auch so etwa lässt sich unter Linux realisieren: Beim Nullsoft Scriptable Install System (NSIS, [4]) handelt es sich um freie Software, die auch nativ unter Linux läuft.

Spezial-Crosscompiler

Der bislang in diesem Artikel verwendete GCC unterstützt als Crosscompiler zwar eine beeindruckende Menge an Targets, doch gibt es auch viele mehr oder weniger exotische Plattformen, die er nicht ansteuert. Für diese existieren oft Spezial-Crosscompiler, die meist ausschließlich Code für die Zielplattform erzeugen.

Ein typisches Beispiel für eine solche exotische Plattform ist der Commodore 64 (C64) aus den 80er Jahren des letzten Jahrhunderts (siehe Kasten “Der Brotkorb”). Solche Heimcomputer wurden entweder in Basic (eingebaut, langsam) oder in Assembler programmiert. Sprachen wie Logo, Forth oder Pascal standen zwar auch zur Auswahl, wurden aber selten benutzt. Ein Compiler für C – heute die führende Programmiersprache – existierte 1982 noch nicht. Heute gibt es ihn.

Der Brotkorb

Viele ältere Leser dürften ihre ersten Erfahrungen mit Rechnern in den 1980ern mit einem der damals verbreiteten sogenannten Heimcomputer gemacht haben.

Das mit Abstand erfolgreichste Modell dieser Gattung war der Commodore 64 (Abbildung 1), der von 1982 bis 1994 gebaut wurde. Seine 8-Bit-CPU des Typs MOS Technology 6510 war mit 1 MHz getaktet, als Arbeitsspeicher brachte er 64 KByte RAM mit. Daneben gab es 20 KByte ROM, in denen unter anderem das Betriebssystem und der eingebaute Basic-Interpreter lagerten.

Der Grafikchip VIC-II brachte es auf eine Auflösung von 320×200 Pixeln bei 16 Farben und steuerte bis zu acht Hardware-Sprites an. Als Soundchip kam ein 3-stimmiger SID 6581 zum Einsatz. Die Daten speicherte man auf externen Massenspeichern. Dazu fand zunächst die “Datasette” Verwendung, ein Bandlaufwerk mit Musikkassetten. Diese löste später eine 5¼-Zoll-Floppydisk ab, die 170 KByte Kapazität bot.

Abbildung 1: Seine unverkennbare Form trug dem Commodore 64 den liebvollen Spitznamen "Brotkorb" ein.

Abbildung 1: Seine unverkennbare Form trug dem Commodore 64 den liebvollen Spitznamen “Brotkorb” ein.

6502-Crosscompiler

Das freie Softwareprojekt CC65 [3] stellt einen C-Crosscompiler für die MOS-6502-CPU-Familie zur Verfügung, wie sie nicht nur im Commodore 64, sondern auch in Heimcomputern wie dem VC20, C16, Apple II oder Atari verwendet wurde. Damit lassen sich C64-Programme auch in C programmieren.

Mit der Befehlsfolge Listing 5 übersetzen Sie den Compiler recht einfach aus dem Quellcode selbst und installieren Ihn nach /usr/local. Alternativ finden Sie auf der Homepage auch RPM-Pakete sowie ein Debian-Repository.

Listing 5

$ make -f make/gcc.mak
$ su -c "make -f make/gcc.mak install"

Nach der Installation stehen neben dem C-Compiler cc65 unter anderem ein Assembler (ca65), ein Disassembler (da65) und ein Linker (cl65) zur Verfügung. Als besonders praktisch erweist sich das Compile-and-Link-Dienstprogramm cl65: Es erkennt die Inputfiles (Assembler, Object-File, C-File) automatisch anhand der Endung und generiert ein ausführbares Programm. Das Kompilieren und Ausführen unseres kleinen Helloworld-Programms (Listing 1) klappt daher ohne großen Aufwand:

$ cl65 helloworld.c
$ x64 -autoload helloworld

TIPP

Falls Sie früher einmal mühsam 6502-Assembler gelernt haben und schnell wieder einsteigen wollen: Der Befehl cl65 --listing helloworld.c erzeugt ein Assembler-Listing aus dem C-Code.

In der zweiten Zeile lädt und startet der C64-Emulator x64 (sie Kasten “Commodore 64 emulieren”) das Programm helloworld von der virtuellen Floppy. Mit dem Basic-Befehl LIST lassen Sie sich das Programm anzeigen – ein Basic-Programm, das nur aus einem Befehl besteht: SYS 2061. Das bedeutet: Rufe das Assemblerprogramm an der Speicherstelle 2061 auf (dort steht unser compilierter Code). Nun starten Sie das Programm mit RUN (Abbildung 2) und erhalten die Ausgabe Hello world. Ersetzen Sie im Befehl -autoload durch -autostart, startet das Programm sofort.

Abbildung 2: Das erste Helloworld-Programm in C auf dem (virtuellen) Commodore 64.

Abbildung 2: Das erste Helloworld-Programm in C auf dem (virtuellen) Commodore 64.

Commodore 64 emulieren

Das Emulator-Paket Vice [5] für den Commodore 64 und seine Verwandten installiert sich unter Debian/Ubuntu fast von selbst, es genügt der Aufruf apt-get install vice. Unter Opensuse müssen Sie erst das Repository http://download.opensuse.org/repositories/Emulators/openSUSE_11.3/ in YaST hinzufügen, um Vice einzurichten.

Nach der Installation rufen Sie mit x64 den C64-Emulator auf (C128: x128, VC20: xvic, …). Er bietet unzählige Kommandozeilenoptionen, um Einstellungen zu ändern (x64 -h). Drücken Sie im Emulatorfenster die rechte Maustaste, können Sie Einstellungen zur Hardware treffen (Abbildung 3). Ein Linksklick fördert das Menü zur Behandlung von Disk- und Tape-Images zutage (Abbildung 4).

Abbildung 3: Die Hardware-Einstellungen von <code srcset=

x64 (rechte Maustaste).” width=”300″ height=”300″ /> Abbildung 3: Die Hardware-Einstellungen von x64 (rechte Maustaste).

Abbildung 4: Die Image-Einstellungen von <code srcset=

x64 (linke Maustaste).” width=”171″ height=”300″ /> Abbildung 4: Die Image-Einstellungen von x64 (linke Maustaste).

Programmatisches

Abgesehen von einigen wenigen, meist hardwarebedingten Einschränkungen im Sprachumfang unterstützt der CC65-Crosscompiler die C-Programmierung für den Commodore 64 recht problemlos. Was noch fehlt, sind Graphik und Sound. Der C64 hatte für damalige Verhältnisse sehr leistungsfähige Grafik und Sound-Chips.

Diese steuerte man meist direkt über Register. Unter Basic schrieb man mit dem Befehl POKE Zelle,Wert einen Wert direkt in eine Speicherstelle, mit Variable = PEEK(Zelle) wurde er wieder ausgelesen. Echte Fans kannten damals die Position und Bedeutung der Register auswendig. PEEK und POKE lassen sich in C auf mehrere Weisen emulierten. Dem routinierten C-Programmierer fällt für POKE sofort die Funktion memset() (string.h) ein. Mit memset((void*)53280U,1,1); ließe sich etwa der Wert der Speicherstelle 53280 auf 1 und damit die Farbe des Bildschirmrahmens auf Weiß ändern.

Wesentlich vorbildnäher klappt es, wenn Sie mit #include <peekpoke.h> die entsprechende Header-Datei ins Programm einbinden. Sie definiert die Makros POKE(addr,val), POKEW(addr,val), PEEK(addr) und PEEKW(addr) um Speicherzellen zu beschreiben und auszulesen. POKEW und PEEKW schreiben beziehungsweise lesen gleich ein ganzes Wort, also 16 Bit. Die ebenfalls 16 Bit langen Adressen müssen als Unsigned angeben (65535U statt 65535), sonst gibt der Compiler eine Warnung aus.

Die zusätzliche Include-Datei c64.h bildet die Hardwarefähigkeiten des Systems gut ab: Dort gibt es #defines für die Farben, die Register des Grafik- und des Sound-Chips sind als Structs definiert. Daneben setzt c64.h noch die Basisadresse des Grafikchips und stellt sie als VIC-Struktur zur Verfügung.

Ein Programm, um die Rahmenfarbe auf Grün und die Hintergrundfarbe auf Schwarz zu setzen, könnte dann so aussehen wie in Listing 6. Das lässt sich wesentlich leichter lesen als in Form von PEEKs und POKEs. Auch für die anderen Chips (den Soundchip SID und die IO-Chips CIA1 und CIA2) gibt es solche Strukturen. Zur Ansteuerung des Textbildschirms stellt die Headerdatei conio.h einige interessante Funktionen zur Verfügung.

Listing 6

#include <c64.h>
int main()
{
  VIC.bordercolor = COLOR_GREEN;
  VIC.bgcolor0 = COLOR_BLACK;
  return(0);
}

Ausblick

Die Website des CC65-Projekts [3] liefert zahlreiche konkrete Tipps zur Code-Optimierung. So sollten Sie etwa statt Postinkrement (i++) stets Präinkrement verwenden (++i) und die Deklaration sowie Initialisierung von Variablen in einem Aufwasch erledigen (int i=1; statt int i; i=1;). Da es sich beim MOS 6502 um eine 8-Bit-CPU handelt, empfiehlt es sich, grundsätzlich Unsigned-Datentypen zu verwenden, möglichst char statt int einzusetzen und long tunlichst zu vermeiden: Längere Datentypen verbrauchen mehr Speicher und Rechenzeit.

Derlei Ratschläge zeigen auch deutlich, dass das Übersetzen von Software für andere Plattformen oft wesentlich mehr bedeutet, als nur einfach ein gegebenes Programm durch einen Crosscompiler laufen zu lassen. Gerade, wenn sich die Hardware des Targets deutlich von jener der Ursprungsplattform unterscheidet, verlangt ein erfolgreiches Übertragen dem Entwickler grundlegende Kenntnisse der Eigenschaften der Zielplattform sowie ein intelligentes Anpassen des Codes an die Gegebenheiten ab. Genau das macht aber auch den Reiz der Aufgabe aus, ein Stück Software auf einem anderen System zum Laufen zu bringen. 

Glossar

Hardware-Sprites

Die Sprites waren kleine Grafiken im Format 24 x 21 Pixel. Die Hardware unterstützte wichtige Features wie das Positionieren auf dem Bildschirm und die Kollisionserkennung direkt. Das vereinfachte die insbesondere die Spieleprogrammierung enorm, bei der die Sprites etwa als Spielfigur oder Opponent Verwendung fanden.

Infos

[1] Linux auf Itanium: http://en.wikipedia.org/wiki/Project_Trillian

[2] Simple DirectMedia Layer: http://www.libsdl.org/download-1.2.php

[3] CC65-Projekt: http://www.cc65.org/

[4] Nullsoft Scriptable Install System: http://nsis.sourceforge.net/

[5] Emulator Vice: http://www.viceteam.org

Der Autor

Der Systemadministrator Wolfgang Dautermann begann seine Computerkarriere mit einem Commodore VC20, der bald von einem Commodore 64 abgelöst wurde. Neben vielen Linuxen auch schon diverse Unixe wie Solaris, Irix oder Tru64 gebändigt. Heute zählt er zu den Organisatoren der Grazer Linux-Tage.

LinuxUser 02/2011 KAUFEN
EINZELNE AUSGABE
ABONNEMENTS
TABLET & SMARTPHONE APPS
E-Mail Benachrichtigung
Benachrichtige mich zu:

Hinweis: Dieser Artikel ist älter als ein Jahr, enthaltene Informationen sind möglicherweise veraltet.

0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben