Racine Index Rechercher Liens Qui sommes nous ?
[LinuxFocus Image]
[Navegation Bar]
  Nouveautés   Archives

Construire des bibliothèques partagées

par Luis Colorado


Introduction

Historique

Bibliothèque

Statique ou partagée

Edition de liens

link

soname

ldconfig

Bibliothèque partagéee

Bibliothèque statique

A l'heure du choix

1.- La création d'un programme. Introduction.

La création d'un programme et les outils de développement actuels résultent de l'évolution des habitudes et de l'expérience des programmeurs et créateurs. 

La création d'un programme implique les étapes suivantes: 
 

Ecriture du code source dans un langage de haut niveau avec un éditeur de texte. Les programmes importants sont difficiles à gérer s'ils résident dans un unique fichier. C'est pourquoi  le code source est souvent divisé en modules fonctionnels, formés d'un ou plusieurs fichiers de code source. Remarquons que ces modules peuvent être écrits dans des langages différents, de façon à s'adapter à la tâche a réaliser. 
 
Après écriture des fichiers de code source du programme, l'étape de traduction permet d'obtenir des instructions exécutables par la machine. C'est le code objet, qui réalise les mêmes opérations que le code source, mais dans un langage directement exploitable par la machine. Cette étape de traduction de code source en code objet est connue sous le terme de compilation. Une session de compilation n'implique généralement qu'une partie du programme, c'est à dire un ou quelques fichiers source. Le code objet résultant peut contenir un programme, un sous-programme, des variables, etc. -- de façon  plus générale des éléments de programmes traduits et prêts pour le traitement suivant. 

Lorsque tous les fichiers de code objet du programme sont générés, l'étape suivante consiste à lier -regrouper- tous les morceaux du puzzle. L'éditeur de lien est l'utilitaire responsable de cette tâche. En particulier, toutes les références entre les différents modules de code sont résolues (comme les appels de sous-programmes ou les références à des variables définies dans d'autres modules). Le résultat est un programme qui peut normalement être chargé et exécuté directement. 

L'exécution d'un programme est réalisée par une partie essentielle du système d'exploitation ; pour Linux, il s'agitde l'appel à la fonction système exec(). Cette fonction cherche le fichier, alloue de la mémoire au processus, charge certaines parties particulières du fichier (celles qui contiennent le code et certaines variables), et finalement transfère le contrôle du CPU au début du programme (généralement repéré dans le fichier exécutable lui-même).

 

2.- Rapide historique des méthodes de création de programmes.

La manière de créer des programmes a constamment évolué afin de parvenir à une exécution plus efficace ou à une meilleure utilisation des ressources du système. 

A l'origine, les programmes étaient directement écrits en code machine. Plus tard, il est apparu que l'écriture dans un langage de haut niveau suivie d'une traduction en code machine pouvait être automatisée du fait de la nature systématique du procédé de traduction. Cette méthode a grandement amélioré la productivité du développement des logiciels. 

A l'avènement de la compilation (j'ai volontairement simplifié l'évolution de la compilation, qui en réalité a été une étape difficile à cause de la grande complexité de la tâche), la création d'un programme consistait donc à générer un fichier de code source, à le compiler puis finalement à l'exécuter. 

Il fut néanmoins rapidement évident que la compilation était gourmande en terme de ressources, en particulier en temps CPU ; il apparut aussi que beaucoup de fonctions étaient communes à de nombreux programmes. D'autre part, une modification même mineure du code source impliquait la recompilation de l'ensemble du code source, c'est à dire recompiler des parties de code inchangées pour obtenir le programme modifié. 

Ceci justifia l'introduction de la compilation par modules. Le principe consistait à séparer le programme principal des fonctions utilitaires fréquemment utilisées, qui étaient alors compilées et archivées dans un fichier spécial (le précurseur des bibliothèques). 

Il était alors possible de développer des programmes utilisant ces fonctions sans l'effort inutile de recopier leur code encore et toujours. La méthode demeurait néanmoins complexe car il était nécessaire à l'édition de liens de regrouper tous les morceaux, que le programmeur devait identifier (cela impliquait éventuellement d'inclure des fonctions inconnues, utilisées ou appelées par les fonctions choisies). 

3.- Qu'est-ce qu'une bibliothèque ?

Les problèmes rencontrés ont conduit à la création des bibliothèques. Il ne s'agit en fait que d'un autre type de fichier (plus exactement une archive, de type tar(1)ou cpio(1)) avec la caractéristique que l'éditeur de lien  reconnaît son format. Quand on spécifie une bibliothèque archive, L'EDITEUR DE LIEN NE SELECTIONNE QUE LES MODULES REELLEMENT UTILISES PAR LE PROGRAMME, et exclue le reste. Un nouvel avantage apparaissait donc. Il était maintenant possible de développer des programmes utilisant des bibliothèques comportant un grand nombre de fonctions, sans que le programmeur ait à connaître les dépendances entre les fonctions de la bibliothèque. 

La bibliothèque telle que nous venons de la présenter a peu évolué. Simplement, le fichier comporte une zone supplémentaire qui apparaît au début de l'archive et contient une description des modules et identifieurs permettant à l'éditeur de liens de résoudre les références correspondantes sans avoir à parcourir toute la bibliothèque. Cette zone supplémentaire, appelée table des symboles de la bibliothèque archive, est ajouté par la commande ranlib(1) de Linux. Les bibliothèques décrites jusqu'ici sont dénommées BIBLIOTHEQUES STATIQUES. 
 
Un progrès significatif est apparu avec les premiers systèmes multitâches: le partage du code. Si, sur un même système, deux copies d'un code identique doivent être exécutées simultanément, il apparaît intéressant que les deux processus puissent partager le code, puisque normalement un programme ne modifie pas son code. Cette idée élimine les copies multiples en mémoire, et donc économise des quantités de mémoire importantes, en particulier sur les gros systèmes multi-utilisateurs. 

Poussant plus avant cette innovation, quelqu'un (je ne sais qui il/elle était, mais son idée était excellente ;-) réalisa que très souvent, plusieurs programmes utilisent la même bibliothèque, mais puisque les programmes sont différents, les portions de la bibliothèque utilisées peuvent être différentes. De plus, le code principal est différent (puisque ce sont deux programmes distincts). L'idée était que des programmes distincts utilisant une même bibliothèque puissent partager le code de cette bibliothèque, économisant ainsi encore plus de mémoire. Maintenant, différents programmes partagent le code de la bibliothèque, sans avoir le même code programme. 

Cependant la démarche est maintenant plus compliquée. En effet, l'édition des liens du code exécutable du programme ne peut plus être complètement réalisée, et les références aux identifieurs de la bibliothèque doivent être résolues de façon dynamique lors du chargement du programme. L'éditeur de liens (ld(1) sous Linux) reconnaît qu'il s'agit d'une bibliothèque partagée et n'inclut pas le code correspondant dans le programme. Le système lui-même, le noyau, lorsqu'il lance exec() reconnaît qu'il lance un programme utilisant des bibliothèques partagées, et exécute un code spécial pour charger les bibliothèques partagées (allocation de mémoire partagée pour les instructions, et de mémoire privée pour les variables, etc.). Ce traitement est maintenant effectué pour chaque chargement de code exécutable, rendant la procédure beaucoup plus complexe. 

Bien entendu, lorsque l'éditeur de liens rencontre une bibliothèque simple, il exécute les mêmes traitements qu'avant. 

La bibliothèque partagée n'est pas une archive de fichiers contenant du code exécutable, mais ressemble plutôt à un fichier de code objet. Lors de l'édition des liens d'un programme avec une bibliothèque partagée, l'éditeur de liens ne regarde pas dans la bibliothèque pour vérifier si tel module doit être ajouté et tel autre ignoré. Il vérifie simplement que les références peuvent être résolues, et repère lesquelles doivent être ajoutées par l'inclusion de la bibliothèque. Il serait possible de réaliser une bibliothèque archive ar(1) de toutes les bibliothèques partagées,mais c'est rarement fait car une bibliothèque partagée est souvent le résultat du lien de plusieurs modules en sorte que la bibliothèque est utilisée plus tard, lors de l'exécution. Le nom de bibliothèque partagée est peut être mal choisi, il serait plus juste de parler d'objets partagés (néanmoins nous conserverons le terme universellement utilisé pour faciliter la compréhension). 
 

4.- Types de bibliothèques.

Comme nous l'avons vu, sous Linux il existe deux type de bibliothèques : statiques et partagées. Les bibliothèques statiques sont des collections de modules regroupées dans une archive avec l'utilitaire ar(1) et indexées par ranlib(1). Ces modules sont généralement stockés dans un fichier nommé *.a par convention (je n'utilise pas le terme d'extension car il n'a pas de signification sous Linux).  L'éditeur de liens reconnait la terminaison .a, effectue la sélection des modules nécessaires au programme et ajoute le code correspondant, permettant de résoudre toutes les références indéterminées. 

A l'opposé, les bibliothèques partagées ne sont pas des archives mais des objets relogeables, identifiés en tant que bibliothèques partagées par un code special. Comme indiqué, l'éditeur de liens ld(1) n'ajoute pas les modules dans le code du programme, mais marque les identificateurs fournis par la bibliothèque comme résolus, ajoute les identificateurs nécessaires à la bibliothèque elle-même, et continue sans ajouter de code, comme si le code concerné existait dans le code principal. L'éditeur de liens ld(1) reconnaît une bibliothèque partagée par sa terminaison .so (et non pas .so.xxx.yyy, nous reviendrons sur ce point plus loin). 
 

 

5.- L'édition de liens sous Linux

Tous les programmes sont constitués de modules liés pour former un exécutable. L'éditeur de liens sous Linux est l'utilitaire ld(1)

ld(1) peut être invoqué avec de nombreuses options qui modifient son fonctionnement ; nous nous restreindrons ici aux options qui concernent les bibliothèques d'une manière générale. ld(1) n'est pas appelé directement par l'utilisateur, mais par le compilateur gcc(1). Une connaissance élémentaire de son modus operandis nous aidera à mieux comprendre l'utilisation des bibliothèques sous Linux. 

ld(1) maintient une liste d'objets qui doivent être liés au programme. Ces objets peuvent être donnés et appelés dans n'importe quel ordre (*) tant que la convention précédente est repectée, c'est à dire que le nom des bibliothèques statiques se termine par .a et celui des bibliothèques partagées par .so (pas .so.xxx.yyy). Bien entendu, le nom des fichiers objets simples se termine toujours par .o

(*) Ce n'est pas tout à fait exact. ld(1)inclue uniquement les modules qui résolvent les références au moment de l'inclusion, donc il pourrait demeurer une référence produite par un module inclu ultérieurement qui, puisqu'il n'apparaît pas encore lors de l'inclusion, peut rendre significatif l'ordre d'inclusion des bibliothèques. 

D'autre part, ld(1) autorise l'ajout de bibliothèques standards grâce aux options -l et -L

Mais qu'entendons-nous par "bibliothèque standard", quelle est la différence ? Aucune. Simplement, ld(1) recherche les bibliothèques standard dans des endroits particuliers, alors quele code objet est recherché à partir du nom de fichier. 

Les bibliothèques sont recherchées par défaut dans les répertoires /lib et /usr/lib (j'ai néanmoins entendu dire que selon la version ou l'implémentation de  ld(1), la recherche pouvait s'effectuer dans des répertoires supplémentaires). L'option -L permet d'ajouter des répertoires au chemin de recherche par défaut. On l'utilise en ajoutant -L repertoire pour chaque repertoire concerné. Les bibliothèques standard sont indiquées par l'option -l Nom (ou Nom précise la bibliothèque à charger) et ld(1)cherchera, dans l'ordre, dans les répertoires correspondants, le fichier libNom.so. Si ce fichier n'existe pas, ld(1)essaiera libNom.a, c'est à dire la version statique. 

Selon que ld(1) trouve libNom.so ou libNom.a, l'édition des liens est effectuée en tant que bibliothèque partagée ou statique. 

6.- L'édition dynamique des liens et le chargement des bibliothèques partagées

L'édition dynamique des liens est effectuée au chargement de l'exécutable par un module spécial (d'ailleurs ce module est lui-même une bibliothèque partagée), appelé /lib/ld-linux.so

En réalité, il y a deux modules responsables du chargement des bibliothèques partagées : /lib/ld.so (pour les bibliothèques à l'ancien format a.out), et /lib/ld-linux.so (pour les bibliothèques au nouveau format ELF). 

Ces modules sont particuliers, car ils doivent être chargés à chaque fois qu'un programme utilisant une bibliothèque partagée est lancé. Leurs noms sont pré-définis (ils ne doivent donc pas être renommés ou déplacés du répertoire /lib). Si quelqu'un modifiait le nom /lib/ld-linux.so, le système deviendrait incapable d'exécuter tous les programmes faisant appel à des bibliothèques partagées car ce module prend en charge la résolution de toutes les références indéterminées au moment de l'exécution. 

Ce module est secondé par le fichier /etc/ld.so.cache, qui indique pour chaque bibliothèque le fichier exécutable le plus approprié. Nous reviendrons à ce problème plus loin. 

7.- soname. Versions de bibliothèques partagées. Compatibilité.

Nous atteignons ici le point le plus délicat concernant les bibliothèques partagées. 

Qui n'a jamais recu un message du type "library libX11.so.3 notfound" ? Il est parfaitement possible de disposer de la bibliothèque libX11.so.6, mais néanmoins de ne rien pouvoir faire. Comment se fait-il que ld.so(8) reconnaisse comme interchangeables les bibliothèques libfoo.so.45.0.1 et libtoto.so.45.22.8 et ne reconnaisse pas libfoo.46.5.7

Sous Linux (et tous les systèmes d'exploitation implémentant le format exécutable ELF), les bibliothèques sont identifiées par une chaîne de caractères qui les distinguent les unes des autres : le soname

Le soname est incorporé dans la bibliothèque elle-même et la chaîne est déterminée à l'édition des liens des objets formant la bibliothèque. A la création de la bibliothèque partagée, il faut utiliser l'option -soname pour donner une valeur à la chaîne de caractères. 

La chaîne de caractères est utilisée au chargement pour identifier la bibliothèque partagée à charger et l'exécutable correspondant. Le déroulement est le suivant: ld-linux.so détecte que le programme nécessite une bibliothèque et détermine son soname. Intervient ensuite/etc/ld.so.cache qui fournit le nom de fichier.  Finalement, il compare le soname au nom contenu dans la bibliothèque, et s'ils sont identiques c'est fini! Sinon, la recherche continue. En cas d'échec, une erreur est renvoyée. 

Le soname permet d'assurer que la bibliothèque correspond bien car ld-linux.so vérifie sa concordance avec le fichier utilisé. En cas de non concordance, nous obtenons le fameux message"libXXX.so.Y not found". Ce que ld-linux.so recherche est le soname et c'est à lui que fait référence le message d'erreur. 

Cela porte à confusion lorsqu'on change le nom de fichier dela bibliothèque, et que l'erreur demeure. Mais ce serait une mauvaise idée de modifier le soname, car il existe une convention dans la communauté Linux pour l'assigner. 

Par convention, le soname d'une bibliothèque doit identifier une bibliothèque et son INTERFACE. Si des modifications sont apportées qui n'affectent que le fonctionnement interne de la bibliothèque, et donc que l'interface entière demeure intacte (nombre de fonctions, variables, paramètres et valeur renvoyée pour les fonctions), alors les deux bibliothèques sont interchangeables, et les modifications apportées sont qualifiées de mineures (les bibliothèques sont compatibles et peuvent être utilisées indifféremment). Généralement dans ce cas, le numéro mineur de version de bibliothèque (qui n'apparaît pas dans soname) est modifié, et une bibliothèque peut à priori etre remplacée par l'autre sans problème majeur. 

Par contre, si l'on ajoute ou supprime des fonctions, ou d'une manière plus générale si on MODIFIE L'INTERFACE de la bibliothèque, alors il est impossible de maintenir la compatibilité entre les deux versions (par exemple, l'évolution de X11R5 à X11R6 se traduit par le passage de libX11.so.3 à libX11.so.6, qui définit de nouvelle fonctions et donc modifie l'interface). Le changement de X11R6-v3.1.2 à X11R6-v3.1.3 n'apportera probablement pas de modification de l'interface, et la bibliothèque portera le même soname -bien que pour éviter d'écraser l'ancienne bibliothèque, la nouvelle portera un autre nom (pour cette raison, le numéro de version complet apparaît dans le nom de la bibliothèque, mais seul le numéro majeur est utilisé dans le soname). 
 

8.- ldconfig(8)

Comme indiqué plus haut, /etc/ld.so.cache permet à ld-linux.so de retrouver les bibliothèques. Ce fichier au format binaire pour plus d'efficacité est créé par ldconfig(8)

Pour toutes les bibliothèques partagées trouvées dans les répertoires spécifiés /etc/ld.so.conf,ldconfig(8) génère un lien symbolique appelé par le soname de la librairie. Il réalise cela de sorte que lorsque ld-linux.so demande un nom de fichier, il sélectionne dans la liste des répertoires un fichier avec le soname recherché. Ainsi, il est inutile d'exécuter ldconfig(8) à chaque ajout d'une bibliothèque partagée ; on ne le lance que lorsqu'un répertoire est ajouté à la liste. 

9.- Créer une bibliothèque partagée

Avant de créer une bibliothèque dynamique, il faut s'interroger sur l'utilité réelle. Il faut savoir que l'utilisation de bibliothèques dynamiques génère une surcharge du système pour différentes raisons :  Le chargement du programme s'effectue en plusieurs étapes ; une première pour le chargement du programme principal, et une autre pour chaque bibliothèque dynamique que le programme utilise. 

Les bibliothèques dynamiques doivent contenir du code relogeable, puisque l'adresse réellement affectée à l'intérieur de l'espace des adresses virtuelles n'est connue qu'au chargement. Le compilateur est obligé de réserver un registre pour mémoriser la position de la bibliothèque, ce qui diminue d'autant le nombre de registres disponibles pour l'exécution du code. C'est un moindre mal, et la surcharge correspondante est estimée à moins de 5% dans la plupart des cas.

Une bibliothèque dynamique est adaptée lorsqu'elle est utilisée la plupart du temps par un ou plusieurs programmes (ce qui évite le rechargement de la bibliothèque à la fin du programme l'ayant appelée, puisqu'elle demeure en mémoire tant qu'un programme l'utilise). 

La bibliothèque partagée est totalement chargée en mémoire (et non pas uniquement les modules nécessaires),ce qui implique qu'elle doit être utilisée dans sa totalité. Lepire exemple serait une bibliothèque partagée dont seule une fonction serait utilisée, et dont 90% du code ne servirait presque jamais. 

Un bon exemple de bibliothèque dynamique est la bibliotheque C standard (utilisée par tous les programmes écrits en C). En moyenne, toutes les fonctions sont utilisées ici ou là. 

Dans une bibliothèque statique, il est inutile d'inclure les fonctions rarement utilisées ; il suffit de placer ces fonctions dans leur propre module, et elles ne seront pas liées dans les programmes qui ne les référencent pas. 

9.1.- Compilation du source

La compilation du code source est effectuée comme d'habitude, excepté l'option '-f PIC' (Position Independant Code, code relogeable) pour produire un code objet qui pourra être chargé à différentes positions dans l'espace des adresses virtuelles du programme. 

Cette étape est fondamentale car pour un  programme lié statiquement, la position des objets des bibliothèques est déterminée à l'édition des liens, c'est à dire une fois pour toutes. L'ancien format d'exécutable a.out ne permettait pas de réaliser ce traitement, en conséquence chaque bibliothèque partagée etait située à une adresse fixe de l'espace des adresses virtuelles. Il en résultait des conflits quand un programme essayait d'utiliser deux bibliothèques partagées et les chargeait à des régions non disjointes de la mémoire virtuelle. Cela obligeait à maintenir une liste indiquant pour chaque bibliothèque dynamique l'étendue des adresses occupées afin que personne d'autre ne l'utilise aussi. 

Comme nous l'avons déjà mentionné, l'inscription d'une bibliothèque dynamique dans une liste officielle est inutile car la position réellement affectée est donnée dynamiquement au chargement. D'où la nécessité d'un code relogeable. 

9.2.- Edition des liens

Après compilation de tous les objets, il faut les lier en invoquant une option permettant de produire un objet susceptible d'être chargé dynamiquement. 

gcc -shared -o libName.so.xxx.yyy.zzz -Wl, -soname, libName.so.xxx 

Comme le lecteur peut le constater , l'édition de liens est classique, excepté l'ajout d'une série d'options qui justement conduisent à produire une bibliothèque partagée. Détaillons les maintenant : 

-shared
cette option indique à l'éditeur de liens qu'il doit produire une bibliothèque partagée, et donc qu'il y aura un type particulier d'exécutable dans le fichier de sortie correspondant à la bibliothèque. 

-o libName.so.xxx.yyy.zzz
est le nom du fichier de sortie. La convention de nom n'est pas obligatoire, mais est néanmoins conseillée en particulier si cette librairie est destinée à devenir standard. 

-Wl, -soname, libName.so.xxx, 
l'option -Wl indique au compilateur que les options suivantes (séparées par des virgules) sont destinées à l'éditeur de liens. C'est le mécanisme utilisé par gcc(1) pour passer des options à ld(1). Ci-dessus nous passons les options suivantes à l'éditeur de lien : 
 

-soname libName.so.xxx. Cette option fixe le soname de la bibliothèque, de facon qu'il puisse être appelé par les programmes la requérant.

9.3.- Installation de la bibliothèque

Nous disposons désormais du code exécutable de la bibliothèque. Il faut maintenant l'installer à un endroit approprié afin de pouvoir l'utiliser. 

Pour compiler un programme utilisant notre nouvelle librairie, la ligne de commande serait : 

gcc -o program libName.so.xxx.yyy.zzz 

ou plus simplement, si la bibliothèque a été installée à la bonne place (/usr/lib): 

gcc -o program -lName 

(si la librairie avait été installée dans /usr/local/lib, il aurait suffit d'ajouter l'option -L/usr/local/lib). Les étapes suivantes permettent d'installer la bibliothèque : 

Copier la bibliothèque dans le répertoire /lib ou /usr/lib. Si vous décidez de l'installer dans un répertoire différent (par exemple /usr/local/lib), l'éditeur de lien ld(1) ne la trouvera peut être pas automatiquement. 

Exécuter ldconfig(1) pour créer le lien symboliquede libName.so.xxx.yyy.zzz vers libName.so.xxx. Cette étape permettra également de vérifier que les étapes précédentes ont été réalisées avec succès,et que la bibliothèque est reconnue comme bibliothèque dynamique. La facon dont les programmes sont liés à l'exécution n'est pas touchée par cette étape, seul le chargement des bibliothèques est affecté. 

Créer un lien symbolique de libName.so.xxx.yyyy.zzz(ou libName.so.xxx le soname) vers libName.so de sorte que l'éditeur de lien trouve la bibliothèque avec l'option -l. pour que ce mécanisme fonctionne correctement, il est nécessaire que le nom de la bibliothèque soit de la forme libName.so.

10.- Créer une bibliothèque statique

Si au contraire vous souhaitez créer une bibliothèque statique (ou si les deux versions sont nécessaires afin de pouvoir offrir des programmes liés statiquement), nous allons maintenant décrire les étapes à suivre. 

Remarque : l'éditeur de liens commence à chercher la bibliotheque Nom dans un fichier libNom.so puis ensuite libNom.a. Si les deux versions (statique et dynamique) de la librairie portent le même nom, il ne sera généralement pas possible de déterminer laquelle a été utilisée par l'éditeur de liens

Pour cette raison, quand les deux versions d'une même bibliothèque sont nécessaires, il est recommandé d'utiliser le nom libName_s.a pour la version statique, et libName.so pour la version partagée.  Ainsi, la version statique sera produite en invoquant: 

gcc -o program -lName_s, 

tandis que la version partagée sera obtenue par : 

gcc -o program -lName. 
 

10.1.- Compilation du source

Aucune mesure spéciale n'est à prendre pour compiler le(s) code(s) source. Puisque la position des objets est déterminée à l'édition des liens, il n'est pas nécessaire d'utiliser l'option -f PIC (néanmoins, conserver cette option ne causera pas d'erreur). 

10.2.- Edition des liens

Pour les bibliothèques statiques, il n'y a pas d'édition des liens. Tous les codes objets sont archivés dans un fichier par l'utilitaire ar(1). Ensuite, afin d'accélérer la résolution des références, il est conseillé d'exécuter ranlib(1) sur la bibliothèque. Ne pas exécuter cette commande peut dans certains cas conduire à la perte des liens d'un module de  l'exécutable. En effet, quand le module est traité par l'éditeur de liens lors de la construction de la bibliothèque, toutes les références indirectes ne sont pas résolues immédiatement ; si le module est utilisé par un autre module de la bibliothèque, il faudra plusieurs passages sur la bibliothèque pour résoudre toutes les références. 

10.3.- Installation de la bibliothèque

La bibliothèque statique est nommée libName.a si cette seule version suffit. Dans le cas où deux versions sont nécessaires, je recommande de différencier les noms en appelant libName_s.a la bibliothèque statique. Il sera ainsi plus facile de contrôler quelle version de bibliothèque est effectivement utilisée à l'édition de liens. 

L'édition des liens permet d'inclure l'option -static. Cette option contrôle le chargement du module /lib/ld-linux.so, et n'affecte pas l'ordre de recherche des librairies. En effet, si l'option -static est utilisée et  si ld(1) trouve une librairie partagée, il l'utilisera (au lieu de continuer à chercher la librairie statique). Cela conduit à des erreurs à l'exécution à cause de l'appel de routines qui n'appartiennent pas au code exécutable -le module pour le chargement automatique n'est pas lié et donc ce traitement ne peut intervenir. 

11.- Liens statiques ou dynamiques ?

Supposons que l'on souhaite distribuer un programme qui utilise une librairie que l'on est autorisé à distribuer seulement si elle est inclue statiquement dans le programme, et pas sous une autre forme (c'est par exemple le cas pour les applications developpées avec Motif). 

A priori, deux options sont possibles pour distribuer ce type de logiciel. La première consiste à créer un exécutable lié statiquement (en utilisant des bibliothèques .a). Lesprogrammes de ce type sont chargés en une seule fois et ne repose sur aucune bibliothèque du système (pas même /lib/ld-linux.so). Cependant, ils ont l'inconvénient d'être des programmes très volumineux puisque le fichier doit contenir absolument tout le code nécessaire au fonctionnement du programme. La deuxième option consiste à réaliser un programme lié dynamiquement, où l'environnement d'exécution doit fournir toutes les bibliothèques dynamiques nécessaires. Le fichier exécutable peut alors avoir une très petite taille, avec cependant le risque que certaines bibliothèques ne soient pas installées (par exemple tout le monde ne dispose pas de Motif). 

En fait il existe une autre option, à mi chemin entre les deux précédentes. Certaines librairies peuvent être liées statiquement, les autres dynamiquement. Il suffit alors de lier statiquement les bibliothèques posant problème, et dynamiquement toutes les autres. Cette dernière solution est une façon très efficace de distribuer des logiciels. 

Par exemple, on peut compiler trois versions différentes d'un programme de la manière suivante : 
 

gcc -static -o program.static program.o 
-lm_s -lXm_s -lXt_s -lX11_s\
-lXmu_s -lXpm_s

gcc -o program.dynamic program.o 
-lm -lXm -lXt -lX11 -lXmu -lXpm

gcc -o program.mixed program.o 
-lm -lXm_s -lXt -lX11 -lXmu -lXpm
Dans le troisième cas seul la librairie Motif (-lXm_s)est liée statiquement, les autres sont liées dynamiquement. L'environnement d'exécution doit fournir les versions appropriées des bibliothèques libm.so.xx libXt.so.xx libX11.so.xx libXmu.so.xxy libXpm.so.xx
 
 

Traduit par Jean-Denis Girard


Pour en savoir plus:
  • Consultez ELF-HOWTO. 


© 1998 Autor
Ce site web est maintenu par Miguel A Sepulveda.