Fink

Porting - 2. Gemeinsam benutzter Code

2.1 Gemeinsam benutzte Bibliotheken vs. Ladbare Module

Ein Mach-O Feature erwischt viele ganz kalt: Die strikte Unterscheidung zwischen gemeinsam genutzten Bibliotheken und dynamisch ladbaren Modulen. Auf ELF-Systemen sind die beiden gleich; jeder gemeinsam benutzter Code kann als Bibliothek und als ladbares Mudol genutzt werden. Benutzen sie das Kommando otool -hv some_file, um den Dateityp der Datei some_file heraus zu finden.

Gemeinsam genutzte Mach-O Bibliotheken haben den Dateityp MH_DYLIB und die Dateiendung .dylib. Gegen sie kann ein programm mit den üblichen Linkeroptionen gelinkt werden, also -lfoo für libfoo.dylib. Sie können jedoch nicht als Modul geladen werden. (Randnotiz: Gemeinsam genutzte Bibliotheken können dynamisch über eine API geladen werden. Aber die API unterscheidet sich von der API für Bundles und die Semantik machen sie nutzlos for eine Emulation von dlopen(). Vor allem können gemeinsam genutzte Bibliotheken nicht wieder ausgeladen werden.)

Ladbare Module heißen in Mach-O-Sprech Bundles. Ihr Dateityp ist MH_BUNDLE. Da sich keine Komponente darum kümmert, ist die Dateierweiterung beliebig wählbar. Die Erweiterung .bundle wird von Apple empfohlen, aber die meisten portierten Programme benutzen aus Kompatibilitätsgründen .so. Bundles können dynamisch über die dyld API geladen und wieder ausgeladen werden. Ein Wrapper um diese API emuliert dlopen(). Gegen Bundles kann man nicht linken wie wenn sie gemeinsame genutzte Bibliotheken wären. Aber ein Bundle kann gegen vorhandene, gemeinsam genutzte Bibliotheken gelinkt werden; diese werden dann automatisch mit dem Bundle geladen.

2.2 Versionsnummerierung

Auf Elf-Systemen wird normalerweise die Versionsnummer am Ende des Dateinamens der gemeinsame genutzten Bibliothek nach der Erweiterung angehängt, z. B. libqt.so.2.3.0. Bei Darwin steht die Versionsnummer zwischen Bibliotheksnamen und Erweiterung, also libqt.2.3.0.dylib. Beachten sie, dass sie deshalb beim Linken eine bestimmte Version der Bibliothek angeben können, im obigen Beispiel mit -lqt.2.3.0.

Bei der Erstellung einer gemeinsam genutzten Bibliothek kann man einen Namen vergeben, mit dem zur Laufzeit nach der Bibliothek gesucht werden kann. Dies ist gängige Praxis und ermöglicht es, dass mehrere Haupt-Versionen einer Bibliothek gleichzeitig installiert sind. AUf ELF-Systemen nennt man diesen Namen soname. Unter Darwin kommt dazu, dass man den vollständigen Pfad zu der Bibliothek angeben kann und auch sollte. Die "rpath"-Optionen und das ldconfig/ld.so.cache-System werden dadurch überflüssig. Soll eine Bibliothek benutzt werden, die noch nicht installiert ist, kann man die Umgebungsvariable DYLD_LIBRARY_PATH. Details dazu stehen in der man-Seite von dyld.

Im Gegensatz zu ELF-Systemen erlaubt das Mach-O Format erlaubt auch die tatsächliche Überprüfung der Nebenversion. Jede Mach-O Bibliothek hat zwei Versionsnummern, die "aktuelle" Version und die "kompatible". Beide Nummern bestehen aus drei durch Punkte getrennte Zahlen. z. B. 1.4.2. Die erste Zahl darf nicht Null sein. Die zweite und dritte Zahl können ausgelassen werden und werden dann als Null angenommen. Ist überhaupt keine Zahl angegeben, wird die Version auf 0.0.0 gesetzt. Dies ist quasi ein Joker und passt auf alles.

Die "aktuelle" Version dient nur zur Information; während die "kompatible" Version für die Überprüfung wie folgt verwendet wird: Wird ein Program gelinkt, wird die Versionsinformation der Bibliothek in das Programm kopiert. Wird das Programm ausgeführt, wird die Version im Programm mit der aus der Bibliothek verglichen, die geladen wird. Die Version der Bibliothek muss mit der aus dem Programm übereinstimmen oder höher sein. Ist dies nicht der Fall, bricht dyld das Programm ab und erzeugt einen Laufzeit-Fehler.

2.3 Compiler-Optionen

Auf Darwin ist die Voreinstellung so, dass positionsunabhängiger Code (PIC) erzeugt wird. Tatsächlich ist PowerPC-Code so entworfen, dass er von vorne herein positionsunabhängig ist und damit keine Leistungs- oder Speicherplatz-Nachteil verbunden ist. Man muss deshalb nicht extra eine Option PIC angeben, wenn man eine gemeinsam genutzte Bibliothek oder ein Modul compiliert. Allerdings erlaubt der Linker keine "common" Symbole in gemeinsam genutzten Bibliotheken. Man muss also die Compiler-Option -fno-common angeben.

2.4 Eine gemeinsam genutzte Bibliothek erzeugen

Will man eine gemeinsam genutzte Bibliothek erzeugen, ruft man den Compilertreiber mit der Option -dynamiclib auf. Am besten versteht man dies an einem ausführlichen Beispiel. Es wird eine Bibliotheknamen libfoo aus den Quellcode-Dateien source.c und code.c erzeugt. Die Versionsnummer ist 2.4.5, mit 2 als der Hauptversionsnummer (wegen inkompatiblen Änderungen der API), 4 die Nebenversionsnummer (wegen aufwärtskompatiblen Änderungen der API) und 5 ist die Revisionsnummer für die Behebung von Fehlern (manchmal wird dies auch die "teeny" Revisionsnummer genannt, für vollkompatible Änderungen.). Die Bibliothek hängt von keiner anderen ab und wird in /usr/local/lib installiert.

cc -fno-common -c source.c
cc -fno-common -c code.c
cc -dynamiclib -install_name /usr/local/lib/libfoo.2.dylib \
-compatibility_version 2.4 -current_version 2.4.5 \
-o libfoo.2.4.5.dylib source.o code.o
rm -f libfoo.2.dylib libfoo.dylib
ln -s libfoo.2.4.5.dylib libfoo.2.dylib
ln -s libfoo.2.4.5.dylib libfoo.dylib

Beachten sie bitte, welche Teile der Versionsnummer an welcher Stelle verwendet werden. Linkt man gegen die Bibliothek, verwendet man normalerweise die Option -lfoo, die auf den Symlink libfoo.dylib zugreift. Unabhängig welcher Symlink oder welche tatsächliche Datei angegeben wird, wird der install_name in das Programm eingetragen. Dies bedeutet, dass der der "höhere" (weniger versionsspezifische) Symlink libfoo.dylib nach dem Kompilieren gelöscht werden kann. In einer Aktualisierung der Bibliothek auf dem Niveau der Nebenversion, muss man nur das Ziel des Symlinks libfoo.2.dylib ändern, dass von dynamischen Laufzeitlinker benutzt wird.

2.5 Ein Modul erzeugen

Will man ein ladbares Modul erzeugen, ruft man den Compilertreiber mit der Option -bundle auf. Benutzt das Modul Symbole des Wirtprogramms muss auch die Option -undefined suppress angegeben werden, damit undefinierte Symbole erlaubt sind und auch die Option -flat_namespace, damit der neue Linker ab Mac OS X 10.1 zufrieden ist. Ein ausführliches Beispiel:

cc -fno-common -c source.c
cc -fno-common -c code.c
cc -bundle -flat_namespace -undefined suppress \
-o mymodule.so source.o code.o

Beachten sie, dass es keine Versionsnummerierung gibt. Theoretisch kann dies gemacht werden, in der Praxis ist das aber unbedeutend. Beachten sie außerdem, dass es keine Einschränkungen für den Namen des Bundle gibt. Einige Pakete ziehen es vor, dem Namen ein "lib" voran zu stellen, weil das z. B. auf anderen Systemen verlangt wird. Dies ist alles unkritisch, weil ein Programm den vollständigen Namen benutzt, wenn das Modul geladen wird.

Weiter: 3. GNU libtool