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