Que sont les bibliothèques partagées en p. Tutoriel illustré sur Macromedia Flash MX. Organiser des objets dans une bibliothèque

Bibliothèque partagée ou bibliotheque publique est un fichier destiné à être partagé entre . Les modules utilisés par le programme sont chargés à partir d'objets partagés distincts dans la mémoire, plutôt que d'être copiés par l'éditeur de liens lorsqu'il copie un seul exécutable pour le programme.

Les bibliothèques partagées peuvent être liées statiquement, ce qui signifie que les références aux modules de bibliothèque sont résolues et que la mémoire est allouée aux modules lors de la création de l'exécutable. Mais souvent, la liaison des bibliothèques partagées est retardée jusqu'à leur chargement.

Certains systèmes plus anciens, par ex. MCP de Burroughs Les multics n'ont également qu'un seul format pour les exécutables, qu'ils soient génériques ou non. Ils ont partagé des fichiers de bibliothèque au même format que les fichiers exécutables. Cela présente deux avantages principaux : premièrement, chacun d’eux ne nécessite qu’un seul chargeur de démarrage, au lieu de deux (avoir un chargeur de démarrage séparé introduit une complexité supplémentaire). Deuxièmement, il permet également d'utiliser les exécutables comme bibliothèques partagées s'ils disposent d'une table de symboles. Les formats typiques combinés d'exécutables et de bibliothèques partagées sont ELF et Mach-O (tous deux sous Unix) et (Windows).

Dans certains environnements plus anciens tels que Windows 16 bits ou MPE Pour HP 3000, le code de la bibliothèque partagée n'autorisait que les données basées sur la pile (locales), ou d'autres restrictions importantes étaient placées sur le code de la bibliothèque partagée.

La memoire partagée

Le code de la bibliothèque peut être partagé en mémoire avec les processus, ainsi que sur disque. Si de la mémoire virtuelle est utilisée, les processus s'exécuteront dans une page physique de RAM mappée à différents espaces d'adressage de processus. Cela a ses avantages. Par exemple, dans le système OpenStep, les applications ne font souvent que quelques centaines de kilo-octets et se chargent rapidement ; la plupart de leur code se trouvait dans des bibliothèques déjà chargées par le système d'exploitation à d'autres fins.

Les programmes peuvent partager la RAM en utilisant du code indépendant, comme sous Unix, ce qui donne lieu à une architecture complexe mais flexible. Cela garantit que, grâce à diverses techniques telles que le prémapping de l'espace d'adressage et les réservations de pages, chaque bibliothèque partagée a une plus grande probabilité d'être partagée. La troisième option est stockage à un seul niveau, utilisé Système IBM/38 et ses successeurs.

Dans certains cas, différentes versions de bibliothèques partagées peuvent poser des problèmes, notamment lorsque les bibliothèques de différentes versions portent les mêmes noms de fichiers et sont utilisées pour différentes applications installées sur le système, chacune nécessitant une version spécifique. Ce scénario est connu sous le nom

Gcc -fPIC -c helloworld.c -o helloworld.o gcc -shared -Wl,-soname,libhelloworld.so.1 -o libhelloworld.so.1.0.1 helloworld.o

La première commande compile la source helloworld.c dans le fichier objet helloworld.o, la seconde crée la bibliothèque partagée helloworld.so.1.0.1 à partir du fichier objet. Vous devez faire attention aux choses suivantes.

  • Les objets doivent être compilés avec l'option -fPIC (code indépendant de la position).
  • Par conséquent, vous devez lier une bibliothèque partagée avec l'option -shared.
  • L'option -Wl vous permet de spécifier les options de l'éditeur de liens, dans ce cas soname.
  • soname est le nom connu du programme qui utilisera la bibliothèque. Il doit être préfixé par « lib » et se terminer par « .so.X », où X est le numéro de version qui change lorsque l'interface de la bibliothèque change.
  • L'option -o spécifie le nom du fichier de sortie, qui doit être soname suivi d'un numéro de version mineure en pointillés et d'un numéro de version.
  • Toutes les bibliothèques partagées sous Linux ont un troisième nom : le nom utilisé par le compilateur. C'est soname sans numéro de version.

Comment installer la bibliothèque créée sous Linux

Lib_name = libhelloworld.so.1 lib_full_name = libhelloworld.so.1.0.1 lib_short_name = libhelloworld.so lib_install_path = /usr/lib lib_include_path = /usr/local/include install : donc sudo install -m 0644 $(lib_full_name) $(lib_install_path ) sudo ln -sf $(lib_install_path)/$(lib_full_name) $(lib_install_path)/$(lib_short_name) sudo ldconfig -n $(lib_install_path) sudo cp helloworld.h $(lib_include_path) .PHONY : installer

Ce makefile ajoute une cible d'installation, ce qui signifie que vous pouvez maintenant exécuter make install et la bibliothèque sera installée dans /usr/lib.

  • La commande "install" copie le fichier de bibliothèque donné dans le chemin indiqué et crée un lien symbolique vers celui-ci avec le nom donné dans soname. L'option -m vous permet également de définir les droits d'accès au fichier (644 - autorisation d'écriture et de lecture pour le propriétaire du fichier, lecture pour les utilisateurs du même groupe, lecture pour les autres utilisateurs).
  • La commande "sudo ln -sf" crée un lien symbolique, écrasant celui existant (s'il existe déjà) vers le fichier de la bibliothèque nouvellement installée avec le nom du compilateur (soname sans version). Après cette commande, le répertoire donné contient les fichiers de bibliothèque portant les trois noms précédemment répertoriés.
  • La commande "sudo ldconfig -n" relit le répertoire spécifié (option -n) et met à jour le cache. L'éditeur de liens et d'autres programmes peuvent alors demander la bibliothèque par son nom (par exemple, par soname). Vous pouvez lister toutes les bibliothèques chargées à l'aide de la commande "ldconfig -p".
  • Ensuite, si nécessaire, vous pouvez copier les fichiers d'en-tête de la bibliothèque.
  • La cible "install" doit être créée comme

Une bibliothèque partagée permet aux symboles qu'elle contient d'être utilisés dans plusieurs films sans avoir à copier ces symboles dans les bibliothèques de films. Pour cette raison, les objets de bibliothèque partagée sont appelés ressources(Actifs). Dans ce cas, la bibliothèque partagée est utilisée comme fichier externe et n'est pas ajoutée au film créé (ou modifié).

Le recours à des bibliothèques partagées est conseillé par exemple dans les cas suivants :

  • lors de l'utilisation d'une même bande sonore sur plusieurs pages du site ;
  • lors du partage de caractères de police de texte sur plusieurs pages du site ;
  • lorsque vous devez fournir une source unique pour les éléments d'animation utilisés dans plusieurs scènes d'un film ou dans plusieurs films ;
  • lorsque vous souhaitez disposer d’une bibliothèque centrale de ressources pour faciliter le contrôle des modifications.

Flash MX prend en charge deux types de bibliothèques partagées :

  • Durée- bibliothèque d'exécution partagée ; les symboles inclus dans une telle bibliothèque peuvent être partagés par plusieurs films, cependant, ces symboles ne peuvent être modifiés que directement dans le film source ;
  • Temps d'auteur- bibliothèque partagée pendant le développement ; Les symboles inclus dans une telle bibliothèque peuvent être partagés entre plusieurs films, et le contenu de la bibliothèque peut être édité dans n'importe quel film en copropriété.

Pour que les ressources partagées de la bibliothèque soient disponibles dans les films hébergés sur un site distant, le fichier Flash de la bibliothèque doit être exporté au format SWF et téléchargé sur le site Web.

Commentaire
La version précédente de Flash ne prenait en charge que la bibliothèque partagée d'exécution.
.

Pour créer une bibliothèque partagée comme Durée, nécessaire:

  1. Déterminez ses ressources (les personnages qui y sont inclus) dans un film séparé.
  2. Autoriser l'exportation des caractères partagés.
  3. Précisez l'URL du site où la bibliothèque sera hébergée.
  4. Exportez un fichier Flash avec cette bibliothèque au format SWF et téléchargez-le sur un site Web.

Pour pouvoir utiliser les symboles de la bibliothèque partagée Durée dans d'autres films (« copropriétaires »), il faut créer un lien vers les symboles partagés dans chacun d'eux.

Examinons maintenant plus en détail les étapes répertoriées ci-dessus.

Après avoir créé une bibliothèque partagée, vous devez spécifier les symboles qu'elle contient peuvent être exportés vers d'autres films. Pour ce faire, vous devez procéder comme suit :

  1. Sélectionnez le symbole que vous souhaitez rendre « partagé » dans la liste.
  2. Dans le menu contextuel du symbole, sélectionnez la commande Mise en relation(Obligatoire).
  3. Dans la boîte de dialogue qui s'ouvre Propriétés de liaison de symboles(Paramètres de liaison de symboles), fig. 10.12, cochez la case Exporter pour le partage d'exécution(Autoriser l'exportation au moment de l'exécution).
  4. Dans un champ de texte Identifiant saisir le nom (identifiant) du symbole sous lequel il sera exporté vers le film copropriétaire ; Bien que le champ indique par défaut le nom de la bibliothèque du symbole, s'il contient des espaces, supprimez-les.
  5. Dans un champ de texte URL Saisissez l'adresse Internet du film source (c'est-à-dire le fichier SWF de la bibliothèque partagée).
  6. Si le symbole exporté doit être utilisé directement à partir de la première image du film copropriétaire, sélectionnez le Exporter dans la première image.
  7. Si vous souhaitez que le symbole exporté soit disponible dans un script ActionScript, sélectionnez l'option Exporter pour ActionScript.

Riz. 10.12. Boîte de dialogue Paramètres des symboles de la bibliothèque partagée

Pour utiliser les ressources de la bibliothèque partagée Durée dans un film en copropriété, vous devez procéder comme suit :

  1. Ouvrez la bibliothèque de ce film en sélectionnant Fenêtreéquipe Bibliothèque.
  2. Dans le menu déroulant de la bibliothèque, sélectionnez la commande Nouveau symbole ; En conséquence, une boîte de dialogue apparaîtra à l'écran Créer un nouveau symbole(Créer un nouveau symbole), dont la partie centrale est de format similaire à la boîte de dialogue Propriétés de liaison de symboles(Fig. 10.13).
  3. Dans un champ de texte Identifiant Saisissez le nom du symbole que vous souhaitez importer dans l'animation du copropriétaire.
  4. Dans un champ de texte URL Saisissez l'adresse Internet du film source.


Riz. 10.13. Boîte de dialogue pour définir les paramètres de caractères divisés

Saviez-vous, Qu'est-ce qu'une expérience de pensée, une expérience gedanken ?
Il s’agit d’une pratique inexistante, d’une expérience d’un autre monde, d’une imagination de quelque chose qui n’existe pas réellement. Les expériences de pensée sont comme des rêves éveillés. Ils donnent naissance à des monstres. Contrairement à une expérience physique, qui est un test expérimental d'hypothèses, une « expérience de pensée » remplace comme par magie les tests expérimentaux par des conclusions souhaitées qui n'ont pas été testées dans la pratique, en manipulant des constructions logiques qui violent en réalité la logique elle-même en utilisant des prémisses non prouvées comme des prémisses prouvées, qui c'est, par substitution. Ainsi, la tâche principale des candidats aux « expériences de pensée » est de tromper l'auditeur ou le lecteur en remplaçant une véritable expérience physique par sa « poupée » - un raisonnement fictif sur parole sans la vérification physique elle-même.
Remplir la physique d’« expériences de pensée » imaginaires a conduit à l’émergence d’une image absurde, surréaliste et confuse du monde. Un vrai chercheur doit distinguer ces « emballages de bonbons » des valeurs réelles.

Les relativistes et les positivistes soutiennent que les « expériences de pensée » sont un outil très utile pour tester la cohérence des théories (également nées dans notre esprit). En cela, ils trompent les gens, puisque toute vérification ne peut être effectuée que par une source indépendante de l'objet de la vérification. Le demandeur de l'hypothèse lui-même ne peut pas tester sa propre déclaration, puisque la raison même de cette déclaration est l'absence de contradictions dans la déclaration visibles par le demandeur.

Nous le voyons dans l’exemple du SRT et du GTR, qui sont devenus une sorte de religion qui contrôle la science et l’opinion publique. Aucun nombre de faits qui les contredisent ne peut vaincre la formule d'Einstein : « Si un fait ne correspond pas à la théorie, changez le fait » (Dans une autre version, « Le fait ne correspond-il pas à la théorie ? - Tant pis pour le fait »).

Le maximum auquel une « expérience de pensée » peut prétendre est seulement la cohérence interne de l’hypothèse dans le cadre de la logique propre du candidat, souvent loin d’être vraie. Cela ne vérifie pas le respect de la pratique. Une véritable vérification ne peut avoir lieu que dans le cadre d’une véritable expérience physique.

Une expérience est une expérience car elle n’est pas un raffinement de la pensée, mais un test de la pensée. Une pensée cohérente ne peut pas se vérifier. Cela a été prouvé par Kurt Gödel.

Les grands programmes nécessitent souvent des bibliothèques prêtes à l'emploi. Une bibliothèque est un ensemble de fonctions déjà compilées (par exemple, une bibliothèque mathématique). La bibliothèque est demandée lors de la liaison, lorsque le fichier exe est généré. Cela se fait en utilisant l'option -l : cc -o badmath badmath.o -lm Si vous devez spécifier le chemin d'accès à une bibliothèque non standard : cc -o badmath badmath.o -lm -L/usr/junk/lib -lcrud Si le nom de la bibliothèque se termine par .a, il s'agit d'une bibliothèque statique. Lorsqu'une bibliothèque statique est liée, l'éditeur de liens en copie simplement des morceaux entiers dans l'exe généré, qui peut ainsi fonctionner de manière autonome sans lui. Si l'exécutable utilise une bibliothèque partagée, le code n'est chargé que si nécessaire. Généralement, les bibliothèques se trouvent dans les répertoires /lib et /usr/lib. Le répertoire /lib ne doit pas contenir de bibliothèques statiques. Le nom de la bibliothèque partagée inclut le préfixe .so . Pour déterminer quelles bibliothèques le programme utilise, vous devez exécuter la commande : ldd prog Le chargement des bibliothèques partagées est effectué par le chargeur ld.so. Le chemin d'accès à la bibliothèque peut se trouver dans le fichier exe. S'il n'y est pas, le chargeur de démarrage regarde le fichier /etc/ld.so.cache, qui répertorie les répertoires. Il existe également un fichier /etc/ld.so.conf. Si vous avez ajouté manuellement le chemin du fichier /etc/ld.so.cache, vous devrez alors exécuter la commande : ldconfig -v Il existe également une variable système LD_LIBRARY_PATH. Ajouter des chemins au fichier /etc/ld.so.conf n'est pas une bonne chose. Les bibliothèques partagées se retrouvent dans le cache système et des problèmes peuvent survenir. Il est préférable d'intégrer le chemin d'accès à la bibliothèque dans le fichier exe : cc -o monprog monprog.o -Wl,-rpath=/opt/obscure/lib -L/opt/obscure/lib -lweird Lorsque les bibliothèques partagées se développent, des problèmes peuvent survenir en raison de chevauchements, etc. L'utilisation de LD_LIBRARY_PATH pose également des problèmes. Vous pouvez utiliser le script à la place : LD_LIBRARY_PATH=/opt/crummy/lib export LD_LIBRARY_PATH exec /opt/crummy/bin/my_prog $@ Le principal outil de compilation sous Unix est make. Sa description la plus détaillée est donnée dans L'environnement de programmation UNIX ou dans Gérer des projets avec make. make a toujours une cible, qui peut être soit un fichier, soit une étiquette. L'objectif peut être primaire ou secondaire. Pour construire une cible, recherchez des règles. Par exemple: OBJS=aux.o main.o # fichiers objets tous : monprog monprog : $(OBJS) $(CC) -o monprog $(OBJS) Le premier objectif – tout – est le principal par défaut. La règle est myprog , qui peut être une autre règle, une autre cible ou un fichier. OBJS - macro pour construire un objectif. Ce makefile produira le résultat suivant : cc -c -o aux.o aux.c cc -c -o main.o main.c cc -o monprog aux.o main.o make peut être exécuté avec des options de commande, par exemple : make aux.o Options Make : -n -f Macros couramment utilisées : CFLAGS - options du compilateur LDFLAGS - options de l'éditeur de liens make a plusieurs objectifs standard : clean distclean install test depend Les bibliothèques sont généralement spécifié au début du Makefile et comprend, par exemple : X_INCLUDES=-I/usr/X11R6/include X_LIB=-L/usr/X11R6/lib -lX11 -Xt NG_INCLUDES=-I/usr/local/include PNG_LIB=-L/usr/local/lib -lpng Viennent ensuite les options du compilateur et de l'éditeur de liens. CFLAGS=$(CFLAGS) $(X_INCLUDES) $(PNG_INCLUDES) LDFLAGS=$(LDFLAGS) $(X_LIB) $(PNG_LIB) Ce qui suit peut être une liste de sources : UTIL_OBJS=util.o BORING_OBJS=$(UTIL_OBJS) ennuyeux.o TRITE_OBJS=$(UTIL_OBJS) trite.o PROGS=ennuyeux banal Et ce n'est que maintenant que viennent les objectifs et les règles : tous : $(PROGS) ennuyeux : $(BORING_OBJS) $(CC) -o $@ $(BORING_OBJS) $(LDFLAGS) banal : $(TRITE_OBJS) $(CC) -o $@ $(TRITE_OBJS) $(LDFLAGS) Les bibliothèques partagées sont un composant fondamental d'un système Unix. La bibliothèque C standard, par exemple sur Suse 9.1, occupe 1,3 Mo. Une copie de cette bibliothèque pour tous les programmes qui l'utilisent dans /usr/bin occupera plus d'un gigaoctet. De telles bibliothèques nécessitent non seulement de l'espace disque, mais également de la mémoire. Le noyau est conçu pour stocker une copie d'une bibliothèque partagée en mémoire. Il faut se rappeler qu'avec la liaison statique, le code de la bibliothèque est ajouté statiquement au code du fichier exécutable, et lorsqu'il est lancé, nous n'avons pas besoin de la bibliothèque - son code est déjà lié lors de la compilation au corps de l'exécutable. déposer. Avec la liaison dynamique, la bibliothèque partagée est incluse lors de l'exécution de ce fichier.
La liaison dynamique est le type prédominant de liaison de bibliothèque. La bibliothèque standard comprend à elle seule plus d’un millier d’appels système. Pour les programmes réellement fonctionnels, ce qu'on appelle mécanisme Procedure Linkage Table (PLT) - une table composée d'appels à des fonctions appelées à partir de bibliothèques partagées. Le premier problème qui peut survenir est un problème de compatibilité. Avec la compilation statique, ce problème est résolu au stade de la compilation. Avec les liens dynamiques, cette garantie n'existe pas, car nous pouvons mettre à jour la bibliothèque partagée après la compilation. Dans ce cas, la vérification est le numéro de version - lorsque l'éditeur de liens dynamique tente de lier une bibliothèque, il vérifie le numéro de version, et si le numéro ne correspond pas, alors elle n'est pas liée. Le numéro de version se compose de 2 parties : un numéro majeur et un numéro secondaire. Si le numéro principal correspond, en règle générale, il ne devrait y avoir aucun problème lors du chargement de la bibliothèque. Si nous avons une bibliothèque libexample.so, alors elle aura un lien vers la version libexample.so.N où N est le numéro de version majeure, qui à son tour aura un lien vers libexample.so.N.M où M est le numéro de version majeur. numéro de version secondaire. Dans ce cas, le programme recherchera exactement la version N dont il a besoin.
Dans les bibliothèques statiques, le code se trouve dans des fichiers avec l'extension .a. Pour les bibliothèques dynamiques, les fichiers ont l'extension .so . Le format de la bibliothèque statique est généré à l'aide de l'utilitaire ar et le format est similaire à celui généré par l'utilitaire tar. Pour les bibliothèques dynamiques sur les dernières versions de Linux, ce format est généralement binaire ELF. Il se compose d’un en-tête et de segments divisés en sections. Lors de la liaison, le code des bibliothèques statiques est directement ajouté au fichier exécutable du programme, et les liens des bibliothèques dynamiques sont extraits et ajoutés au PLT. Lors du chargement, le programme charge un éditeur de liens dynamique qui charge les bibliothèques requises. Lors de la liaison dynamique d'un programme, vous pouvez ajouter des chemins pour rechercher des bibliothèques au stade de la compilation. Pour gcc, cette syntaxe ressemble à -Wl,-R/path Si le programme est déjà lié, vous pouvez utiliser la variable LD_LIBRARY_PATH. Vous pouvez également exécuter un script wrapper spécial avant de démarrer le programme, qui configure spécifiquement les répertoires de la bibliothèque, tel quel effectué, par exemple, au démarrage de Mozilla. L'éditeur de liens dynamique accède à la configuration /etc/ld.so.conf, qui comprend une liste de répertoires. Mais d'abord, l'éditeur de liens accède à LD_LIBRARY_PATH . ldd (lister les dépendances dynamiques)- utilitaire de débogage des bibliothèques partagées. Il affiche une liste de bibliothèques partagées pour un module donné, par exemple : ldd /bin/sh linux-gate.so.1 => (0xffffe000) libreadline.so.4 => /lib/libreadline.so.4 (0x40036000) libhistory.so.4 => /lib/libhistory.so.4 (0x40062000) libncurses.so.5 => /lib/libncurses.so.5 (0x40069000) libdl.so.2 => /lib/libdl.so.2 (0x400af000) libc.so.6 => /lib/tls /libc.so.6 (0x400b2000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
  • Pour les débutants : article du Linux Journal "Linkers and Loaders" par Sandeep Grover
  • Réservez le manuscrit "Linkers and Loaders" en ligne.
  • L'ouvrage de 2002 d'Ulrich Drepper intitulé "Comment écrire des bibliothèques partagées" (pdf) est également recommandé. Diapositives (pdf) et un script mentionné dans les diapositives

Construire une bibliothèque partagée

Écrivons une petite bibliothèque qui ne fait rien, presque rien, à part "hello world" . Premier objet :

/* print1.c */ #include void print1(void) ( printf("1\n"); )

Similaire au deuxième objet :

/* print2.c */ #include void print2(void) ( printf("2\n"); )

Compilons :

gcc -Wall -g -c -fpic print1.c gcc -Wall -g -c -fpic print2.c

Créez une bibliothèque partagée :

gcc -shared -Wlsoname=libprint.so.1 -g \ -o libprint.so.1.0.1 print1.o print2.o

Nous faisons un lien vers la bibliothèque :

ln -sf libprint.so.1.0.1 libprint.so

Pour que d'autres puissent utiliser cette bibliothèque, vous devez écrire un en-tête :

/* print.h */ #ifndef PRINT_H #define PRINT_H void print1(void); void print2(vide); #fin si

Écrivons un programme de test :

/* print.c */ #include "print.h" int main() ( print1(); print2(); return 0; )

Compilons-le :

gcc -Wall -g -L. -lprint -o imprimer imprimer.c

Pour exécuter le programme, l'éditeur de liens doit savoir où se trouve la bibliothèque :

Surcharge de fonctions

Avant de charger une bibliothèque, nous pouvons toujours indiquer au chargeur par où commencer la recherche et la charger.

Ajoutons une nouvelle fonction à la bibliothèque que nous venons d'écrire. Cette fonction est nommée fopen.

/* fopen.c */ #include FILE* fopen(const char* fn, const char* mode) ( printf("fopen appelé\n"); return NULL; )

Compilons cette fonction et mettons-la dans la bibliothèque :

gcc -Wall -g -c -fpic fopen.c gcc -shared -Wlsoname=libprint.so.1 -g \ -o libprint.so.1.0.2 print1.o print2.o fopen.o ln -sf libprint.so .1.0.2 libprint.so

Écrivons un programme pour tester :

/* print-2.c */ #include int main() ( FILE* f; f = fopen("abc", "r"); if(f == NULL) ( printf("1"); ) else ( printf("2"); fclose(f); ) return 0; )

Compilons ce programme :

gcc -Wall -g -o print print.c

Il faut le lancer avec le préfixe suivant :

LD_PRELOAD=./libprint.so ./print

Si quelque chose n'est pas fait correctement, la ligne sera imprimée : fopen appelé

Le processus de création d'un programme.

Traduction en russe : Vladimir Popov

Aujourd'hui, dans un environnement en constante évolution, le processus de création de programmes est le fruit de l'évolution des compétences et de l'expérience développées par les programmeurs et les développeurs.

Ce processus comprend les étapes suivantes :

    Création de code source dans un langage de haut niveau dans un éditeur de texte. Si nous essayons de regrouper un très gros programme dans un seul fichier, cela sera difficile à gérer. Pour cette raison, le code source est divisé en modules fonctionnels, créés à partir d'un ou plusieurs fichiers de code source. Le code source de ces modules n’est pas nécessairement écrit dans le même langage, puisque certains langages sont mieux adaptés que d’autres pour résoudre un problème particulier.

    Une fois les fichiers de code source du programme créés, ils doivent être traduits en blocs de code que la machine peut exécuter. Ce code est généralement appelé code objet. Ce code fait la même chose que le code source, sauf qu'il est écrit dans un langage spécial qui peut être directement exécuté par une machine. Le processus de traduction du code source en code objet est connu sous le nom de compilation. La compilation se fait en plusieurs parties et compile (selon le compilateur) une partie du programme et généralement un ou plusieurs fichiers à la fois. Le code objet compilé contient le programme, le sous-programme, les variables, etc. -- des parties de programmes qui ont été traduites et sont prêtes pour l'étape suivante.

    Une fois que tous les fichiers du programme de code machine ont été créés, ils doivent être connectés ou liés à l'aide d'une opération effectuée par un utilitaire spécial appelé éditeur de liens. A ce stade, toutes les références que le code du module fait au code appartenant à un autre module sont "résolues" (comme les appels de sous-programmes ou les références à des variables possédées ou définies dans un autre module). Le produit résultant est un programme qui peut être directement téléchargé et exécuté.

    L'exécution du programme est effectuée par un type spécial de logiciel qui constitue une partie essentielle du système d'exploitation, dans le cas de Linux, il s'agit de l'appel système exec(). Cette fonction trouve le fichier, alloue de la mémoire au processus, charge des parties spécifiques du contenu du fichier (contenant le code et les valeurs initiales des variables) et transfère le contrôle au processeur au point "texte" du programme, qui pointe généralement vers l'exécutable. fichier lui-même.

2.- Un bref historique du processus de création du programme.

Le processus de création de programmes est en constante évolution, il est nécessaire d'obtenir l'exécution la plus efficace des programmes ou la meilleure utilisation des ressources système.

Au début, les programmes étaient écrits directement en code machine. Plus tard, il est devenu clair qu'il était possible d'écrire des programmes dans un langage de niveau supérieur, puisque la traduction ultérieure en code machine pouvait être automatisée en raison de la nature systématique de la traduction. Cela a augmenté les performances du programme.

Après avoir appris à compiler des programmes (j'ai simplifié l'évolution de la compilation, c'était en fait une étape très difficile car c'est un processus très complexe), le processus de création de programmes a commencé à consister à créer un fichier avec le code source du programme. , en le compilant et finalement en l'exécutant.

On a vite remarqué que le processus de compilation était très laborieux et consommateur de ressources, y compris du temps informatique, et que bon nombre des fonctions incluses dans ces programmes étaient utilisées encore et encore dans différents programmes. De plus, lorsque quelqu'un modifiait une partie du programme, compiler le code inséré signifiait une nouvelle compilation de tout le code source, y compris une nouvelle traduction de tout le code inchangé.

Pour cette raison, la compilation modulaire a commencé à être utilisée. Son but était de diviser le programme entre, d'une part, le programme principal et, d'autre part, les fonctions qui étaient souvent utilisées encore et encore et qui avaient déjà été compilées et stockées dans un endroit spécial (nous appellerons cela la bibliothèque précédente).

Il est alors devenu possible de développer des programmes prenant en charge ces fonctions sans consacrer d'efforts supplémentaires à créer leur code encore et encore. Même alors, le processus était compliqué du fait que lors de la liaison des programmes, il était nécessaire de combiner toutes les parties et qu'elles devaient être connues du programmeur (cela introduisait le coût supplémentaire du test de l'utilisabilité d'une fonction connue qui utilise/nécessite autres fonctions inconnues).

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

Le problème décrit ci-dessus a conduit à la création de bibliothèques. Ce n'est rien de plus qu'un type particulier de fichier (une archive, tar(1) ou cpio(1) pour être précis) avec des paramètres spéciaux, dont le linker comprend le format, et quand on lui indique une archive de bibliothèque, le LINKER SÉLECTIONNE UNIQUEMENT LES MODULES NÉCESSAIRES AU PROGRAMME et exclut tout le reste. Un nouvel avantage est apparu. Il était désormais possible de développer des programmes utilisant de grandes bibliothèques de fonctions, sans que le programmeur ait besoin de connaître toutes les dépendances des fonctions de cette bibliothèque.

Les bibliothèques dont nous avons discuté jusqu'à présent ne sont pas allées plus loin dans leur développement. Ils sont uniquement ajoutés à un fichier, souvent au début de l'archive, contenant des descriptions de modules et des identifiants que l'éditeur de liens doit résoudre sans lire la bibliothèque entière (éliminant ainsi le besoin de lire la bibliothèque plusieurs fois). Ce processus (ajout d'une table de symboles à l'archive de la bibliothèque) sous Linux est effectué avec la commande ranlib(1). Les bibliothèques décrites jusqu'à présent sont connues sous le nom de BIBLIOTHÈQUES STATIQUES.

Des progrès ont eu lieu après l'apparition du premier système multitâche : Partage de Code. Si deux copies du même code s'exécutaient sur le même système, il serait souhaitable que les deux processus puissent partager le même code, puisqu'un programme ne modifie généralement pas son propre code. Cette idée élimine le besoin de placer plusieurs copies en mémoire, libérant ainsi de grandes quantités de mémoire dans d'énormes systèmes multi-utilisateurs.

En poussant cette dernière innovation un peu plus loin, quelqu'un (je ne sais pas de qui il s'agissait, mais c'était une excellente idée ;-) a pensé que très souvent de nombreux programmes utilisent la même bibliothèque, mais, étant des programmes différents, les parties utilisées des bibliothèques étaient pas nécessairement les mêmes pièces utilisées par un autre programme. De plus, le code principal était différent (ce sont aussi des programmes différents), donc leurs textes n'étaient pas séparables. Eh bien, notre héros pensait que si différents programmes utilisant la même bibliothèque pouvaient partager cette bibliothèque entre eux, nous pourrions alors gagner de la mémoire. Désormais, différents programmes, ayant un texte différent, fonctionnent avec le même code de bibliothèque.

Cependant, le processus est désormais devenu plus compliqué. Le programme exécutable n'est pas entièrement lié, mais la résolution des références aux identifiants de bibliothèque est différée jusqu'au chargement du programme. L'éditeur de liens (dans le cas de Linux, il s'agit de ld(1)) reconnaît les appels aux bibliothèques partagées et n'inclut pas leur code dans le programme. Le système lui-même, le noyau, lors de l'exécution de exec(), reconnaît le lancement d'un programme qui utilise des bibliothèques partagées et exécute un code spécial qui charge les bibliothèques partagées (en allouant de la mémoire partagée pour leur texte, en allouant de la mémoire privée pour les valeurs de bibliothèque, etc.). Désormais, ce processus est effectué lors du téléchargement du fichier exécutable et l'ensemble de la procédure est devenu beaucoup plus compliqué.

Bien entendu, lorsque l’éditeur de liens rencontre une bibliothèque standard, il continue de se comporter comme avant.

Une bibliothèque partagée n'est pas une archive contenant du code objet, mais plutôt un fichier contenant le code objet lui-même. Lors de la liaison d'un programme avec une bibliothèque partagée, l'éditeur de liens n'examine pas la bibliothèque pour déterminer quels modules doivent être ajoutés au programme et lesquels ne doivent pas l'être. Il s'assure uniquement que les références non résolues le sont et détermine ce qui doit être ajouté à la liste lors de l'inclusion de la bibliothèque. Il est possible de créer une bibliothèque ar(1) archivée de toutes les bibliothèques partagées, mais cela n'est souvent pas fait car les bibliothèques partagées sont souvent le résultat de la liaison de différents modules, donc la bibliothèque sera nécessaire plus tard au moment de l'exécution. Peut-être que le nom de bibliothèque partagée n'est pas le plus approprié et qu'il serait plus juste de l'appeler objet partagé (cependant, comme nous pouvons être mal compris, nous n'utilisons pas ce terme).

4.- Types de bibliothèques.

Comme nous l'avons déjà dit, il existe deux types de bibliothèques sous Linux : statiques et partagées. Les bibliothèques statiques sont un ensemble de modules combinés dans une archive à l'aide de l'utilitaire ar(1) et indexés par l'utilitaire ranlib(1). Ces modules sont souvent stockés dans un fichier se terminant par .a (je n'utilise pas le terme extension car Linux n'utilise pas le concept d'extension de fichier). L'éditeur de liens reconnaît la fin du fichier .a et commence à rechercher des modules comme s'il s'agissait d'une bibliothèque statique, en sélectionnant et en ajoutant au programme les modules qui résolvent les références non résolues.

Contrairement aux bibliothèques statiques, les bibliothèques partagées ne sont pas des archives, mais des objets déplaçables, désignés par un code spécial (qui les désigne comme bibliothèques partagées). L'éditeur de liens ld(1), comme déjà mentionné, n'ajoute pas de modules au code du programme, mais sélectionne les identifiants fournis par la bibliothèque comme autorisé, ajoute ceux qui sont nécessaires à la bibliothèque elle-même et continue sans ajouter de code, en supposant que le le code requis a déjà été ajouté au code principal. L'éditeur de liens ld(1) reconnaît les bibliothèques partagées en se terminant par .so (et non par .so.xxx.yyy, nous en discuterons plus tard).

5.- Processus de liaison sous Linux.

Chaque programme est constitué de modules objets compilés dans un fichier exécutable. Cette opération est effectuée par l'éditeur de liens ld(1) utilisé sous Linux.

ld(1) supporte diverses options qui modifient son comportement, mais nous nous limiterons ici à celles liées à l'utilisation des bibliothèques en général. ld(1) n'est pas appelé directement par l'utilisateur, mais par le compilateur gcc(1) lui-même dans sa phase finale. Un peu de connaissances à ce sujet mode opératoire nous aidera à comprendre la manière d'utiliser les bibliothèques sous Linux.

Pour que ld(1) fonctionne correctement, il nécessite une liste d'objets qui doivent être liés au programme. Ces objets peuvent être spécifiés dans n'importe quel ordre (*) tant que nous suivons la convention ci-dessus, comme indiqué, une bibliothèque partagée est reconnue par la terminaison .so (et non .so.xx.yy) et une bibliothèque statique par .a (et bien entendu, les fichiers objets simples sont ceux dont le nom se termine par .o).

(*) Ce n'est pas tout à fait vrai. ld(1) inclut uniquement les modules qui résolvent les références au moment où la bibliothèque est incluse, donc un module inclus ultérieurement peut toujours avoir une référence qui plus tard, parce qu'elle n'est pas apparente au moment où la bibliothèque est incluse, peut forcer une commande à inclure les bibliothèques requises.

D'autre part, ld(1) permet d'inclure des bibliothèques standards grâce aux options -l et -L.

Mais... Qu'entendons-nous par bibliothèque standard, quelle est la différence ? Non. Seulement, ld(1) recherche les bibliothèques standards à des endroits spécifiques, tandis que celles décrites comme objets dans la liste des paramètres sont recherchées par leurs noms de fichiers.

Par défaut, les bibliothèques sont recherchées dans les répertoires /lib et /usr/lib (même si j'ai entendu dire qu'il peut y avoir des répertoires supplémentaires en fonction de la version/implémentation de ld(1) ). -L nous permet d'ajouter des répertoires à ceux recherchés dans une recherche normale de bibliothèque. Il est utilisé en spécifiant -L directory pour chaque répertoire que nous souhaitons ajouter. Les bibliothèques standard sont spécifiées avec l'option -l Name (où Name spécifie la bibliothèque à charger) et ld(1) recherchera, dans cet ordre, dans les répertoires appropriés un fichier appelé libName.so. S'il n'est pas trouvé, une tentative sera faite pour trouver libName.a, sa version statique.

Si ld(1) trouve le fichier libName.so, il le liera en tant que bibliothèque partagée, tandis que s'il trouve le fichier libName.a, il liera les modules qui en dérivent s'ils résolvent les références non résolues.

La liaison dynamique est effectuée lorsque l'exécutable est chargé par un module spécial (en fait, ce module spécial est la bibliothèque partagée elle-même) appelé /lib/ld-linux.so.

Il existe en fait deux modules permettant de lier des bibliothèques dynamiques : /lib/ld.so (pour les bibliothèques utilisant l'ancien format a.out) et /lib/ld-linux.so (pour les bibliothèques utilisant le nouveau format ELF).

La particularité de ces modules est qu'ils doivent être chargés à chaque fois qu'un enchaînement dynamique de programmes se produit. Leurs noms sont standard (la raison est qu'ils ne peuvent pas être déplacés du répertoire /lib et que leurs noms ne peuvent pas être modifiés). Si nous remplaçons le nom /etc/ld-linux.so, nous arrêterons automatiquement l'exécution de tout programme utilisant des bibliothèques partagées, car ce module est responsable de la résolution de toutes les références qui ne sont pas résolues au moment de l'exécution.

Ce dernier module est aidé par l'existence d'un fichier /etc/ld.so.cache, qui pour chaque bibliothèque spécifie le fichier exécutable le plus approprié contenant cette bibliothèque. Nous reviendrons sur ce sujet plus tard.

7.- soname. Versions des bibliothèques exécutables. Compatibilité.

Nous arrivons au sujet le plus déroutant lié aux bibliothèques partagées : leurs versions.

Nous voyons souvent le message "bibliothèque libX11.so.3 introuvable", nous laissant perplexes : avec la bibliothèque libX11.so.6, nous ne pouvons rien faire. Comment est-il possible que ld.so(8) reconnaisse libpepe.so.45.0.1 et libpepe.so.45.22.3 comme interchangeables, mais ne reconnaisse pas libpepe.so.46.22.3 ?

Sous Linux (et tous les systèmes d'exploitation utilisant le format ELF), les bibliothèques sont identifiées par une séquence de caractères distinctive : soname.

soname est inclus dans la bibliothèque elle-même et cette séquence est déterminée lors de la liaison des objets qui forment la bibliothèque. Lors de la création d'une bibliothèque partagée, pour donner une valeur à cette chaîne de caractères il faut passer l'option ld(1) (-soname<имя_библиотеки>).

Cette séquence de caractères est utilisée par le chargeur dynamique pour identifier la bibliothèque partagée à charger et pour identifier le fichier exécutable. Cela ressemble à ceci :
Ld-linux.so détermine que le programme nécessite une bibliothèque et définit son nom. Ensuite, le nom est recherché dans /etc/ld.so.cache et le nom du fichier contenant cette bibliothèque est déterminé. Ensuite, le soname demandé est comparé au nom de la bibliothèque existante, et s'ils sont identiques, alors cela signifie que nous en avons besoin ! Sinon, la recherche se poursuivra jusqu'à ce qu'elle soit trouvée, ou si elle n'est pas trouvée, un message d'erreur sera émis.

Le nom son peut être utilisé pour déterminer si une bibliothèque est adaptée au chargement, car ld-linux.so vérifie si le nom son requis correspond au fichier requis. S'il y a une différence, on peut obtenir le fameux "libXXX.so.Y not found" . C'est soname qui est recherché et l'erreur générée est déterminée par soname.

Si nous changeons le nom de la bibliothèque, beaucoup de confusion peut survenir, mais le problème lui-même demeurera. Mais changer le soname n'est pas non plus une bonne idée, car dans la communauté Linux, il existe une convention pour attribuer le soname :

Le soname de la bibliothèque, par défaut, doit identifier la bibliothèque correspondante et l'INTERFACE de cette bibliothèque. Si nous apportons des modifications à la bibliothèque qui affectent uniquement les fonctionnalités internes, mais que l'interface reste inchangée (nombre de fonctions, variables, paramètres de fonction), alors les deux bibliothèques seront interchangeables et en général nous dirons que les modifications étaient mineures (les deux les bibliothèques sont compatibles et nous pouvons remplacer les unes par les autres). Si cela se produit, le numéro mineur (qui ne fait pas partie du soname) est souvent modifié et la bibliothèque peut être remplacée sans problème majeur.

Cependant, lorsque l'on ajoute des fonctions, supprime des fonctions et généralement MODIFIE l'INTERFACE de la bibliothèque, il n'est plus possible de dire que cette bibliothèque est interchangeable avec la précédente (par exemple, remplacer libX11.so.3 par libX11.so.6 fait partie de la transition de X11R5 vers X11R6, avec Cela introduit de nouvelles fonctions et change donc l'interface). Passer de X11R6-v3.1.2 à X11R6-v3.1.3 n'entraînera probablement aucune modification de l'interface et la bibliothèque portera toujours le même nom -- bien que pour conserver l'ancienne version, nous devrons lui donner un nom différent (par exemple c'est pour cette raison que le numéro de version termine le nom de la bibliothèque, alors que dans soname, seuls les numéros majeurs sont spécifiés).

8.- ldconfig(8)

Comme nous l'avons dit plus tôt, /etc/ld.so.cache permet à ld-linux.so de convertir le nom son du fichier contenu dans la bibliothèque. Il s'agit d'un fichier binaire (pour plus d'efficacité) créé par l'utilitaire ldconfig(8).
ldconfig(8) crée pour chaque bibliothèque dynamique trouvée dans les répertoires spécifiés dans /etc/ld.so.conf un lien symbolique avec le nom de la bibliothèque soname. Dans ce cas, lorsque ld.so souhaite obtenir le nom d'un fichier, il sélectionne le fichier portant le nom requis dans la liste des répertoires. Et donc il n'est pas nécessaire d'exécuter ldconfig(8) à chaque fois lors de l'ajout d'une bibliothèque. Nous n'exécutons ldconfig que lorsque nous ajoutons un répertoire à la liste.

9.- Je veux créer une bibliothèque dynamique.

Avant de commencer à créer une bibliothèque dynamique, nous devons nous demander si cela est vraiment nécessaire. Les bibliothèques dynamiques provoquent une surcharge du système pour plusieurs raisons :
    Le programme se télécharge en plusieurs étapes ; un pour charger le programme principal, le reste pour chaque bibliothèque dynamique utilisée par ce programme (nous considérerons cela pour la bibliothèque dynamique correspondante, puisque ce dernier point n'est plus une rareté et devient un avantage).

    Les bibliothèques dynamiques doivent contenir du code déplaçable car l'adresse allouée à un processus dans l'espace d'adressage virtuel est inconnue jusqu'à son chargement. Dans ce cas, le compilateur est obligé de réserver un registre pour stocker la position de chargement de la bibliothèque et, par conséquent, nous avons un registre de moins pour optimiser le code. Ce n'est pas un problème, puisque la surcharge qui en résulte dans la plupart des cas ne dépasse pas 5 % de surcharge.

Pour une bibliothèque dynamique, le cas le plus approprié est lorsqu'elle est constamment utilisée par un programme (cela permet d'éviter de décharger le texte de la bibliothèque après la fin du processus qui l'a chargé. Alors que d'autres processus utilisent des modules de bibliothèque, il reste en mémoire).

Une bibliothèque partagée est chargée en mémoire dans son intégralité (pas seulement les modules requis), elle doit donc être utilisée dans son intégralité pour être utile. Le pire exemple d’utilisation d’une bibliothèque dynamique est d’utiliser une seule fonction, et 90 % de la bibliothèque n’est presque jamais utilisée.

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

Il n'est pas nécessaire d'inclure des fonctions rarement utilisées dans une bibliothèque statique ; tant qu'ils sont contenus dans leur propre module, ils ne seront liés à aucun programme qui n'en a pas besoin.

9.1.- Compilation des codes sources
La compilation du code source est exactement la même qu'avec le code source normal, sauf que pour créer du code pouvant être chargé à différentes positions dans l'espace d'adressage virtuel du processus, nous utiliserons l'option "-f PIC" (code indépendant de la position).

Cette étape est fondamentale car dans une bibliothèque statique, la position des objets de la bibliothèque est résolue lors de la liaison et prend donc un temps fixe. Cette étape n'était pas possible dans les anciens exécutables a.out, ce qui entraînait que chaque bibliothèque partagée était placée dans une position fixe dans l'espace d'adressage virtuel. Et par conséquent, des conflits pourraient survenir à tout moment si le programme souhaitait utiliser deux bibliothèques et les chargeait dans des zones de mémoire virtuelle qui se chevauchent. Cela signifiait que vous étiez obligé de maintenir une liste dans laquelle toute personne souhaitant dynamiser la bibliothèque devait déclarer la plage d'adresses à utiliser afin que personne d'autre ne puisse l'utiliser.

Comme nous l'avons déjà noté, l'enregistrement d'une bibliothèque dynamique sur la liste officielle n'est pas nécessaire, puisque lorsque la bibliothèque est chargée, elle est placée dans la position actuellement définie, malgré le fait que le code doit être déplacé.

9.2.- Disposer les objets dans une bibliothèque
Une fois que tous les objets ont été compilés, ils doivent être liés en définissant une option qui crée un objet chargé dynamiquement. gcc -shared -o libName.so.xxx.yyy.zzz -Wl,-soname,libName.so.xxx Comme le lecteur peut le remarquer, ceci est similaire au processus de liaison normal, sauf que plusieurs options sont ajoutées qui provoqueront le bibliothèque partagée. Examinons chacun séparément :
    -partagé.
    Cela indique à l'éditeur de liens qu'il doit finalement produire une bibliothèque partagée et que, par conséquent, le fichier de sortie correspondant à la bibliothèque doit contenir du code exécutable.

    O libName.so.xxx.yyy.zzz .
    C'est le nom du fichier de sortie. Il n'est pas nécessaire de suivre la convention de dénomination, mais si nous voulons que cette bibliothèque devienne un standard pour les développements futurs, alors il vaut mieux les suivre.

    Wl,-soname,libName.so.xxx .
    L'option -Wl indique à gcc(1) que les options suivantes (séparées par des virgules) sont destinées à l'éditeur de liens. Ce mécanisme est utilisé par gcc(1) pour transmettre des options à ld(1) . Dans l'exemple, nous transmettons les options suivantes à l'éditeur de liens :

-soname libName.so.xxx Cette option modifie le soname de la bibliothèque afin que la bibliothèque soit chargée à la demande des programmes qui nécessitent le soname spécifié.
9.3.- Installation de la bibliothèque
Eh bien, nous avons déjà le fichier exécutable correspondant. Désormais, pour qu’il puisse être utilisé, il doit être installé à l’emplacement approprié.

Pour compiler un programme nécessitant notre nouvelle bibliothèque, vous devez utiliser la commande suivante :

Gcc -o program libName.so.xxx.yyy.zzz ou, si la bibliothèque a été installée dans le répertoire (/usr/lib), il suffira : gcc -o program -lName (si la bibliothèque est dans /usr/ local/lib, puis ajoutez simplement l'option "-L/usr/local/lib"). Pour installer la bibliothèque, procédez comme suit :

    Copiez la bibliothèque dans le répertoire /lib ou /usr/lib. Si vous décidez de le copier vers un autre emplacement (comme /usr/local/lib), vous ne pouvez pas être sûr que l'éditeur de liens ld(1) le trouvera automatiquement lorsque vous lierez vos programmes.

    Exécutez ldconfig(1) pour créer un lien symbolique libName.so.xxx.yyy.zzz vers libName.so.xxx. A cette étape, nous saurons si nous avons correctement réalisé toutes les étapes précédentes et si la bibliothèque est reconnue comme dynamique. Cette étape affecte uniquement le chargement de la bibliothèque au moment de l'exécution, pas la liaison du programme.

    Pour permettre à l'éditeur de liens de trouver la bibliothèque à l'aide de l'option -l, créez un lien symbolique de libName.so.xxx.yyy.zzz (ou libName.so.xxx , soname) vers libName.so . Pour que ce mécanisme fonctionne, le nom de la bibliothèque doit correspondre au modèle libName.so

10.- Création d'une bibliothèque statique

Si, par contre, vous souhaitez créer une bibliothèque statique (ou si vous avez besoin de deux versions pour pouvoir créer des copies liées statiquement), alors vous devez procéder comme suit :

Remarque : lors de la recherche de bibliothèques, l'éditeur de liens recherche d'abord un fichier nommé libName.so, puis seulement libName.a. Si nous appelons ces deux bibliothèques (versions statique et dynamique) avec le même nom, il sera généralement impossible de déterminer laquelle des deux sera liée dans chaque cas (la dynamique est toujours liée en premier, puisque l'éditeur de liens la détecte en premier). ).

Pour cette raison, si vous avez besoin de deux versions de la même bibliothèque, il est toujours recommandé de nommer la version statique libName_s.a et la dynamique libName.so . Ensuite lors de la composition vous devrez préciser :

Gcc -o program -lName_s pour faire le lien avec la version statique, alors que pour la version dynamique : gcc -o program -lName

10.1.- Compilation du code source
Vous n'avez pas besoin de prendre de mesures particulières pour compiler le code source. De même, la position des objets est décidée au stade du lien ; il n'est pas nécessaire de compiler avec l'option -f PIC (même s'il est possible de continuer à l'utiliser).
10.2.- Disposer les objets dans une bibliothèque
Pour les bibliothèques statiques, aucune liaison n'est effectuée. Tous les objets sont archivés dans un fichier bibliothèque à l'aide de la commande ar(1). Ensuite, pour résoudre rapidement les symboles, il est conseillé d'exécuter la commande ranlib(1) sur la bibliothèque. Bien que cela ne soit pas obligatoire, l'échec de l'exécution de cette commande peut dissocier les modules dans l'exécutable car lorsqu'un module est traité par l'éditeur de liens lors de la création de la bibliothèque, toutes les dépendances indirectes entre les modules ne sont pas résolues immédiatement : par exemple, un module à la fin de l'archive en nécessite un autre. module au début de l'archive, cela signifie que plusieurs passages dans la même bibliothèque sont nécessaires pour résoudre toutes les références.
10.3.- Installation de la bibliothèque
Il est conseillé de nommer les bibliothèques statiques au format libName.a uniquement si vous souhaitez n'avoir que des bibliothèques statiques. Dans le cas de deux types de bibliothèques, je recommanderais de les appeler libName_s.a afin qu'il soit plus facile de distinguer quand charger une bibliothèque statique et quand charger une bibliothèque dynamique.

Le processus de liaison autorise l'option -static. Cette option contrôle le chargement du module /lib/ld-linux.so et n'affecte pas l'ordre dans lequel les bibliothèques sont recherchées, donc si quelqu'un spécifie -static et que ld(1) trouve une bibliothèque dynamique, il fonctionnera avec elle ( plutôt que de continuer à chercher dans la bibliothèque statique). Cela entraînera des erreurs d'exécution dues à des appels de procédures dans une bibliothèque qui n'est pas incluse dans l'exécutable - le module de chargement dynamique automatique n'est pas lié et donc le processus ne peut pas être exécuté.

11.- Comparaison de la disposition statique et dynamique

Disons que nous voulons créer une distribution d'un programme qui utilise une bibliothèque que nous pouvons distribuer uniquement lorsqu'elle est incluse de manière statique dans le programme, et non sous aucune autre forme (un exemple de ce cas est celui des applications créées à l'aide de Motif).

Il existe deux manières de créer un tel programme. La première consiste à créer un exécutable lié statiquement (en utilisant uniquement les bibliothèques .a et sans utiliser de chargeur dynamique). Ce type de programme est chargé une seule fois et ne nécessite aucune bibliothèque du système (pas même /lib/ld-linux.so). Cependant, ils présentent l'inconvénient que tout ce dont vous avez besoin doit être contenu dans un seul fichier binaire et qu'il s'agit donc généralement de fichiers très volumineux. La deuxième option consiste à créer un programme lié dynamiquement, c'est-à-dire que l'environnement dans lequel notre application s'exécutera doit disposer de toutes les bibliothèques dynamiques correspondantes. Le fichier exécutable peut être très petit, même si parfois il est impossible d'avoir absolument toutes les bibliothèques (par exemple, certaines personnes n'ont pas Motif).

Il existe une troisième option, mixte, dans laquelle certaines bibliothèques sont liées dynamiquement et le reste statiquement. Dans ce cas, il serait logique de sélectionner la bibliothèque en conflit sous sa forme statique et toutes les autres sous leur forme dynamique. Cette option est une forme très courante de distribution de logiciels.

Par exemple, vous pouvez compiler trois versions différentes d'un programme comme suit :

Gcc -static -o programme.static programme.o -lm_s -lXm_s -lXt_s -lX11_s\ -lXmu_s -lXpm_s gcc -o programme.dynamic programme.o -lm -lXm -lXt -lX11 -lXmu -lXpm gcc -o programme. programme mixte.o -lm -lXm_s -lXt -lX11 -lXmu -lXpm Dans le troisième cas, seule la bibliothèque Motif (-lXm_s) est liée statiquement, et toutes les autres sont liées dynamiquement. L'environnement dans lequel le programme sera exécuté doit disposer des versions appropriées des bibliothèques libm.so.xx libXt.so.xx libX11.so.xx libXmu.so.xx et libXpm.so.xx