Cross Compiler
Unter einem Cross-Compiler versteht man einen Compiler, der auf einem bestimmten System (auch Hostplattform genannt) läuft, aber Kompilate (Objektdateien oder ausführbare Programme) für andere Systeme erzeugt. Diese Ziel-Systeme können andere Betriebssysteme, andere Prozessoren oder eine Kombination der beiden sein. Ein konkretes Beispiel wäre ein Compiler, der auf einem Intel-basierten Windows-System läuft und Programme für PowerPC-basierte Linux-Systeme erzeugt. Handelt es sich bei der Zielplattform um ein eingebettetes System, das selbst nicht für Entwicklung und Übersetzung geeignet ist, spricht man auch von einem Target-Compiler.
Vorgestellt wird hier ein Cross-Compiler, der auf der auf unseren ARM-Boxen läuft und Code für Mips-Boxen erstellt. Motivation dazu ist, dass unsere 4k Boxen genügend Flash-Speicher und Hauptspeicher bereitstellen, dass ein Cross-Compiler für Mips problemlos neben einem ARM-Compiler auf den Boxen erstellt und installiert werden kann. Und natürlich, dass wir als Entwickler gerne beide von Vu+ bereit gestellten Architekturen unterstützen möchten, auch wenn wir selber nicht beide Architekturen im Zugriff haben.
Beispielhaft zeigt dieser Artikel die Installation und Benutzung des Cross-Compilers.
Inhaltsverzeichnis
Installation
In der Database haben wir einen Mips-Cross-Compiler für ARM-Boxen zur Verfügung gestellt. Dieser besteht aus mehreren Paketen :
- Binutils
- Compiler
- Grundstock an Include-Dateien und Libraries ("Sysroot").
Um Binaries für Mips-Boxen auf unseren ARM-Boxen zu erstellen, müssen alle drei Pakete installiert sein.
Nach dem Download der Pakete auf eine ARM-Box können die drei Pakete mit opkg install <Paket.ipk> wie jedes andere Paket auch installiert werden. Nach der Installation befinden sich unter /usr/bin diverse Programme mit dem Präfix mipsel-oe-linux- und unterhalb von /usr/mipsel-oe-linux das "Sysroot" mit Include-Dateien und Bibliotheken, um Mipsel-Binaries zu compilieren.
Benutzung
Beispielhaft wird jetzt die Benutzung des Cross-Compilers anhand eines Pakets beschrieben, welches ich oft und viel benutze: "grep".
Schritt 1: Sourcen besorgen
Die Sourcen für grep befinden sich auf dem GNU-FTP-Server unter https://ftp.gnu.org/gnu/grep/ - die momentan aktuelle Version ist grep-3.8. Die Sourcen werden von dort heruntergeladen und irgendwo auf der Festplatte der Box entpackt, z.B. unter /media/hdd/build. Ausgepackt wird in der Konsole (z.B. Putty) mit dem tar-Befehl: tar xvf grep-3.8.tar.gz
Schritt 2: Sourcen konfigurieren
Mit cd grep-3.8
wird in den ausgpackten Source-Tree gewechselt. Die meisten der heute benutzten Software-Pakete werden mit einem configure
konfiguriert; dieses schaut, ob alle benötigten Komponenten für ein erfolgreiches übersetzen vorhanden sind, und generiert die Makefiles, um das Programm anschließend zu übersetzen.
Um Makefiles benutzen zu können, benötigt man das make
-Programm. Ein fertig übersetztes Programm befindet sich in der Database beim aktuellen GCC für die ARM-Boxen.
Ein erster Versuch, den Cross-Compiler zu benutzen, ist immer, den Aufruf von configure
mit der CC
-Variablen zu testen; es gibt eine Reihe von Umgebungsvariablen, die man auf diese Weise (nicht nur) an das configure-Script weiterreichen kann. Ein paar weitere sind: CXX (c++-Compiler), CFLAGS (Flags für den C-Compiler), CXXFLAGS (Flags für den C++-Compiler)
CC=mipsel-oe-linux-gcc ./configure --prefix=/usr
Das funktioniert bei grep
nicht, die Fehlermeldung sagt, dass man den Compiler über den Switch --host=mipsel-oe-linux
angeben soll. Das funktioniert. Also zweiter Versuch:
./configure --host=mipsel-oe-linux --prefix=/usr
Der Switch --prefix=/usr
sagt der Umgebung, dass das fertig übersetzte Programm später nach /usr
installiert werden soll; das bedeutet, dass das Binary unter /usr/bin/grep
installiert wird. Ohne diesen Switch wird normalerweise /usr/local
als Ziel benutzt, und das Binary findet sich später unter /usr/local/bin/grep
.
Schritt 3: Compilieren
Als nächstes soll das Programm übersetzt werden; dazu wird make
aufgerufen:
make
Meistens dauert der Configure-Schritt genauso lange wie das eigentliche Übersetzen des Programms. Wenn beim Übersetzen keine Fehler passiert sind, kann das Programm installiert werden.
Schritt 4: Installation
Ich kontrolliere gerne vor dem installieren, was installiert werden soll. Fast alle Makefiles kennen das "Target" "install", viele können auch mit dem Target "install-strip" umgehen, und viele kennen auch eine Möglichkeit, das Programm in einem anderen Verzeichnis zu installieren. In der Regel wird das mit der Variablen DESTDIR
erreicht. Wir führen also aus:
make install-strip DESTDIR=/media/hdd/install
Das installiert das übersetzte Programm unterhalb von /media/hdd/install
, wo wir noch einmal schauen können, was wir mitnehmen möchten, und was nicht.
Schritt 5: Programm einpacken und testen
Der nächste Schritt ist bei mir immer, die Dateien als IPK einzupacken. Wie das gemacht werden kann, soll hier nicht gezeigt werden; dazu habe ich unter Paketmanager opkg schon einiges geschrieben.
Wir kontrollieren aber noch einmal, dass der Cross-Compiler ein Mipsel-Binary erstellt hat. Dazu wechseln wir in der Konsole mit "cd" nach /media/hdd/install/usr/bin
. Ein ls -l
zeigt hier drei Dateien an, egrep
, fgrep
und grep
. egrep und fgrep sind Shell-Scripten, das eigentliche Binary ist grep
. Wir können uns den Inhalt mit einigen Tools anschauen. Als erstes:
file grep
: Ausgabe sollte sein: grep: ELF 32-bit LSB executable, MIPS, MIPS-I version 1 (SYSV), dynamically linked, interpreter /lib/ld.so.1, for GNU/Linux 2.6.32, stripped
Schauen wir etwas genauer in das Binary:
mipsel-oe-linux-objdump -p grep
Hier sehen wir alle interessanten Informationen, zum Beispiel welche Shared-Libraries das Programm in welcher Version auf der Mips-Maschine benötigt.
Ausgabe von objdump grep
: bitte aufklappen.
grep: file format elf32-little Program Header: PHDR off 0x00000034 vaddr 0x00400034 paddr 0x00400034 align 2**2 filesz 0x00000140 memsz 0x00000140 flags r-- INTERP off 0x00000174 vaddr 0x00400174 paddr 0x00400174 align 2**0 filesz 0x0000000d memsz 0x0000000d flags r-- 0x70000003 off 0x000001a8 vaddr 0x004001a8 paddr 0x004001a8 align 2**3 filesz 0x00000018 memsz 0x00000018 flags r-- 0x70000000 off 0x000001c0 vaddr 0x004001c0 paddr 0x004001c0 align 2**2 filesz 0x00000018 memsz 0x00000018 flags r-- LOAD off 0x00000000 vaddr 0x00400000 paddr 0x00400000 align 2**16 filesz 0x00040280 memsz 0x00040280 flags r-x LOAD off 0x00040280 vaddr 0x00450280 paddr 0x00450280 align 2**16 filesz 0x00000ab8 memsz 0x00011580 flags rw- DYNAMIC off 0x000001d8 vaddr 0x004001d8 paddr 0x004001d8 align 2**2 filesz 0x00000118 memsz 0x00000118 flags r-- NOTE off 0x00000184 vaddr 0x00400184 paddr 0x00400184 align 2**2 filesz 0x00000020 memsz 0x00000020 flags r-- EH_FRAME off 0x00040240 vaddr 0x00440240 paddr 0x00440240 align 2**2 filesz 0x00000014 memsz 0x00000014 flags r-- NULL off 0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**2 filesz 0x00000000 memsz 0x00000000 flags --- Dynamic Section: NEEDED libc.so.6 INIT 0x00402cf4 FINI 0x0043ca60 INIT_ARRAY 0x00450280 INIT_ARRAYSZ 0x00000004 FINI_ARRAY 0x00450284 FINI_ARRAYSZ 0x00000004 HASH 0x004002f0 STRTAB 0x00401d6c SYMTAB 0x00400b8c STRSZ 0x00000c84 SYMENT 0x00000010 0x70000016 0x004508b0 0x70000035 0x00050670 DEBUG 0x00000000 PLTGOT 0x004508c0 REL 0x00402c8c RELSZ 0x00000068 RELENT 0x00000008 0x70000001 0x00000001 0x70000005 0x00000002 0x70000006 0x00400000 0x7000000a 0x00000097 0x70000011 0x0000011e 0x70000012 0x00000029 0x70000013 0x00000098 VERNEED 0x00402c2c VERNEEDNUM 0x00000001 VERSYM 0x004029f0 Version References: required from libc.so.6: 0x0d696914 0x00 06 GLIBC_2.4 0x0d696915 0x00 05 GLIBC_2.5 0x0d696912 0x00 04 GLIBC_2.2 0x0d696910 0x00 03 GLIBC_2.0 0x0d696913 0x00 02 GLIBC_2.3 root@vusolo4k:/media/hdd/install/tut/grep-3.8/src# ^Cjdump -p grep root@vusolo4k:/media/hdd/install/tut/grep-3.8/src# mipsel-oe-linux-objdump -p grep grep: file format elf32-tradlittlemips Program Header: PHDR off 0x00000034 vaddr 0x00400034 paddr 0x00400034 align 2**2 filesz 0x00000140 memsz 0x00000140 flags r-- INTERP off 0x00000174 vaddr 0x00400174 paddr 0x00400174 align 2**0 filesz 0x0000000d memsz 0x0000000d flags r-- 0x70000003 off 0x000001a8 vaddr 0x004001a8 paddr 0x004001a8 align 2**3 filesz 0x00000018 memsz 0x00000018 flags r-- 0x70000000 off 0x000001c0 vaddr 0x004001c0 paddr 0x004001c0 align 2**2 filesz 0x00000018 memsz 0x00000018 flags r-- LOAD off 0x00000000 vaddr 0x00400000 paddr 0x00400000 align 2**16 filesz 0x00040280 memsz 0x00040280 flags r-x LOAD off 0x00040280 vaddr 0x00450280 paddr 0x00450280 align 2**16 filesz 0x00000ab8 memsz 0x00011580 flags rw- DYNAMIC off 0x000001d8 vaddr 0x004001d8 paddr 0x004001d8 align 2**2 filesz 0x00000118 memsz 0x00000118 flags r-- NOTE off 0x00000184 vaddr 0x00400184 paddr 0x00400184 align 2**2 filesz 0x00000020 memsz 0x00000020 flags r-- EH_FRAME off 0x00040240 vaddr 0x00440240 paddr 0x00440240 align 2**2 filesz 0x00000014 memsz 0x00000014 flags r-- NULL off 0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**2 filesz 0x00000000 memsz 0x00000000 flags --- Dynamic Section: NEEDED libc.so.6 INIT 0x00402cf4 FINI 0x0043ca60 INIT_ARRAY 0x00450280 INIT_ARRAYSZ 0x00000004 FINI_ARRAY 0x00450284 FINI_ARRAYSZ 0x00000004 HASH 0x004002f0 STRTAB 0x00401d6c SYMTAB 0x00400b8c STRSZ 0x00000c84 SYMENT 0x00000010 MIPS_RLD_MAP 0x004508b0 MIPS_RLD_MAP_REL 0x00050670 DEBUG 0x00000000 PLTGOT 0x004508c0 REL 0x00402c8c RELSZ 0x00000068 RELENT 0x00000008 MIPS_RLD_VERSION 0x00000001 MIPS_FLAGS 0x00000002 MIPS_BASE_ADDRESS 0x00400000 MIPS_LOCAL_GOTNO 0x00000097 MIPS_SYMTABNO 0x0000011e MIPS_UNREFEXTNO 0x00000029 MIPS_GOTSYM 0x00000098 VERNEED 0x00402c2c VERNEEDNUM 0x00000001 VERSYM 0x004029f0 Version References: required from libc.so.6: 0x0d696914 0x00 06 GLIBC_2.4 0x0d696915 0x00 05 GLIBC_2.5 0x0d696912 0x00 04 GLIBC_2.2 0x0d696910 0x00 03 GLIBC_2.0 0x0d696913 0x00 02 GLIBC_2.3 private flags = 1007: [abi=O32] [mips1] [not 32bitmode] [noreorder] [PIC] [CPIC] MIPS ABI Flags Version: 0 ISA: MIPS1 GPR size: 32 CPR1 size: 32 CPR2 size: 0 FP ABI: Hard float (double precision) ISA Extension: None ASEs: None FLAGS 1: 00000000 FLAGS 2: 00000000
Das Binary kann jetzt auf eine Mips-Box kopiert werden, um endgültig sicherzustellen, dass wir hier keinen Murks produziert haben.