Portage - 2. Code partagé
2.1 Librairies partagées ou modules chargeables
Une caractéristique de Mach-O, qui surprend beaucoup de gens, est la distinction stricte entre les librairies partagées et les modules chargeables dynamiquement. Sur les systèmes ELF, cette distinction n'existe pas ; n'importe quelle partie de code partagé peut être utilisée comme librairie ou chargée dynamiquement. Utilisez otool -hv un_certain_fichier
pour connaître le type de fichier de un_certain_fichier
.
Les librairies partagées de Mach-O ont un type de fichier MH_DYLIB et une extension .dylib
. Elles peuvent être liées avec les options statiques habituelles de l'éditeur de liens, par exemple -lfoo
pour libfoo.dylib. Toutefois, elles ne peuvent pas être chargées en tant que modules. (Note subsidiaire : les librairies partagées peuvent être chargées dynamiquement par le biais d'une API. Néanmoins, cette API est différente de l'API pour les lots et sa sémantique la rend inutilisable pour une émulation dlopen()
. En particulier, les librairies partagées ne peuvent être déchargées).
Les modules chargeables sont appelés "lots" dans le jargon Mach-O. Ils ont un type de fichier MH_BUNDLE. Ils peuvent avoir n'importe quelle extension, car aucun des éléments concernés n'en tient compte. Apple recommande l'extension .bundle
, mais la plupart des logiciels portés utilisent .so
au nom de la compatibilité. Les lots peuvent être chargés dynamiquement et déchargés via les API dyld, et il existe une interface qui émule dlopen()
au sommet de l'API. Il est impossible de lier des lots comme s'ils étaient des librairies partagées. Toutefois, il est possible de lier un lot avec des librairies partagées réelles ; celles-ci sont automatiquement chargées lorsque le lot est chargé.
2.2 Numérotation de version
Sur un système ELF, les numéros de versions sont, en général, ajoutés au nom de la librairie partagée, après l'extension, par exemple : libqt.so.2.3.0
. Sur Darwin, les numéros de version sont placés entre le nom de la librairie et l'extension, par exemple : libqt.2.3.0.dylib
. Notez que cela vous permet de demander une version spécifique de librairie à l'édition de liens, en utilisant -lqt.2.3.0
dans l'exemple ci-dessus.
Lorsque vous créez une librairie partagée, vous pouvez indiquer un nom à utiliser lors de la recherche de la librairie à l'exécution. C'est une pratique usuelle et cela permet d'installer plusieurs versions majeures d'une librairie en même temps. C'est ce qu'on appelle le soname
sur les systèmes ELF. Sur Darwin, la différence est que vous pouvez (et devez) indiquer le chemin complet en même temps que le nom du fichier. Ceci élimine la nécessité des options "rpath" et du système de cache ldconfig/ld.so.cache. Pour utiliser une librairie qui n'est pas encore installée, vous pouvez définir la valeur de la variable d'environnement DYLD_LIBRARY_PATH ; voir la man page dyld pour de plus amples informations.
Le format Mach-O permet aussi un vrai contrôle du numéro de version mineure, inconnu sur les systèmes ELF. Chaque librairie Mach-O portent deux numéros de versions : une version "courante" et une version "compatible". Les deux numéros de versions sont constitués de trois chiffres séparés par des points, par exemple 1.4.2. Le premier chiffre ne peut être nul. Les second et troisième peuvent être omis, ils ont alors la valeur zéro. Quand aucune version n'est spécifiée, le numéro par défaut est 0.0.0, ce qui est une sorte de valeur passe-partout.
La version "courante" n'est donnée qu'à titre d'information. La version "compatible" sert au contrôle décrit ci-après. Quand un exécutable est lié, le numéro de version de la librairie est copié dans l'exécutable. Lorsque l'exécutable est lancé, le numéro de version copié dans l'exécutable est comparé à celui de la librairie utilisée. dyld génère une erreur d'exécution et arrête l'exécution du programme, sauf si le numéro de version de la librairie utilisée est égal ou supérieur à celui utilisé durant l'édition de liens.
2.3 Options de compilation
Par défaut, Darwin génère du code indépendant de la position (PIC). En fait, le code PowerPC est indépendant de la position par construction, si bien qu'il n'y a ni perte de place, ni dégradation de performance. Vous n'avez donc pas besoin de spécifier l'option PIC lors de la compilation d'une librairie partagée ou d'un module. Toutefois, l'éditeur de liens n'admet pas les symboles "common" dans les librairies partagées, si bien que vous devez utiliser l'option de compilation -fno-common
.
2.4 Construction d'une librairie partagée
Pour construire une librairie partagée, vous invoquez le compilateur avec l'option -dynamiclib
. Voici un exemple qui permettra de mieux comprendre le processus. Nous allons construire une librairie appelée libfoo, composée des fichiers sources source.c
et code.c
. Le numéro de version est 2.4.5, où 2 est le numéro de révision majeure (changement d'API incompatible), 4 est le numéro de révision mineure (compatible avec un retour à une API antérieure) et 5 est le compteur de révision des bogues (on l'appelle parfois une "mini-révision", elle indique des changements totalement compatibles avec l'API). La librairie ne dépend d'aucune autre librairie partagée et sera installée dans /usr/local/lib
.
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
Observez quelles sont les parties du numéro de version qui sont utilisées et où elles le sont. Lors de l'édition de lien avec cette librairie, on utilise normalement le drapeau -lfoo
, qui donne accès au lien symbolique libfoo.dylib
. Quel que soit le lien symbolique ou le fichier utilisé, c'est le nom_d'installation
qui est écrit dans le programme. Cela signifie que l'on peut supprimer le lien symbolique "de plus haut niveau" (celui qui est le moins spécifique d'une version donnée) libfoo.dylib
après compilation. Lors d'une mise à jour mineure, on change juste la cible du lien symbolique libfoo.2.dylib
utilisé par l'éditeur de lien dynamique.
2.5 Construction d'un module
Pour construire un module chargeable, vous invoquez le compilateur avec l'option -bundle
. Si le module utilise des symboles provenant du programme hôte, vous aurez à spécifier -undefined suppress
pour autoriser des symboles non définis, ainsi que -flat_namespace
, indispensable avec le nouvel éditeur de liens de Mac OS X 10.1. Petit exemple explicatif :
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
Remarquez qu'aucun numéro de version n'est utilisé. Il est possible, en théorie, d'en utiliser un, mais, en pratique, cela ne présente aucun intérêt. Notez aussi qu'il n'y a pas de restriction de nom pour les lots. Quelques paquets placent un "lib" avant le nom, parce que certains systèmes l'exigent ; cela ne présente aucun risque, car le programme utilise le nom de fichier complet lors du chargement du module.
Suite: 3. GNU libtool