26 votes

Comprendre le fonctionnement de la liaison dynamique sous UNIX

Considérons la situation suivante :

  • un programme nommé program qui dépend dynamiquement de libfoo.so
  • libfoo.so qui ne dépend de rien (enfin, cela dépend de libstdc++ et d'autres choses, mais je suppose que nous pouvons omettre cela)

program fonctionne parfaitement.

Tout à coup, libfoo les codes changent, et certaines fonctions utilisent désormais en interne func_bar() une fonction qui est fournie par une autre bibliothèque libbar.so .

libfoo.so est recompilé et dépend maintenant de libbar.so . program reste inchangé, il ne dépend toujours que de libfoo.so .

Maintenant, quand j'exécute program il se plaint qu'il ne peut pas trouver func_bar() .

Voici mes questions :

  • libfoo.so n'a pas changé, seule sa mise en œuvre a changé. Pourquoi est-ce que program doivent explicitement lien avec libbar.so ?
  • L'arbre de dépendance n'est-il pas récursif ? J'aurais pensé que puisque libfoo.so dépend de libbar.so , libbar.so aurait été automatiquement ajouté à la liste des dépendances de l'application program , sans recompilation. Cependant, ldd program montre que ce n'est pas le cas.

Il semble bizarre que l'on doive recompiler (relink) chaque binaire qui dépend d'une bibliothèque à chaque fois que les dépendances de cette bibliothèque changent. Quelles solutions ai-je pour éviter cela ?

18voto

caf Points 114951

Le problème se pose lorsque vous n'avez pas lié libfoo.so contre libbar . Lorsque vous compilez un exécutable, par défaut, l'éditeur de liens ne vous laissera pas laisser de références non définies. Cependant, lorsque vous compilez une bibliothèque partagée, il se - et il s'attendra à ce qu'ils soient satisfaits au moment du lien. Ceci afin que libfoo peut utiliser les fonctions exportées par program lui-même - quand vous essayez de l'exécuter, le linker dynamique attend func_bar() à fournir par program . Le problème est illustré comme suit :

( foo.c est autonome)

export LD_RUN_PATH=`pwd`
gcc -Wall -shared foo.c -o libfoo.so
gcc -Wall -L. p.c -lfoo -o p

A ce stade, ./p fonctionne correctement, comme on peut s'y attendre. Nous créons ensuite libbar.so et modifier foo.c pour l'utiliser :

gcc -Wall -shared bar.c -o libbar.so
gcc -Wall -shared foo.c -o libfoo.so

A ce stade, ./p donne l'erreur que vous décrivez. Si nous vérifions ldd libfoo.so on remarque qu'il ne no ont une dépendance à l'égard de libbar.so - voici l'erreur. Pour corriger l'erreur, nous devons relier libfoo.so correctement :

gcc -Wall -L. -lbar -shared foo.c -o libfoo.so

A ce stade, ./p fonctionne à nouveau correctement, et ldd libfoo.so montre une dépendance à l'égard de libbar.so .

9voto

Tobias Points 415

Sur Fedora, la liaison dynamique est effectuée par ld-linux.so.2. L'éditeur de liens dynamiques utilise /etc/ld.so.cache et /etc/ld.so.preload pour trouver les fichiers de bibliothèque.

Lancez ldconfig pour indiquer au système où libfoo doit chercher libbar.

ldconfig cherche dans /lib, /usr/lib et tout répertoire listé dans /etc/ld.so.conf. Vous pouvez vérifier quelles bibliothèques un programme utilise avec ldd.

Plus de détails sont disponibles sur les pages de manuel de chaque commande.

Voici un exemple d'application utilisant des bibliothèques partagées.
Programme.cc

#include "foo.h"
#include <iostream>

int main(int argc, char *argv[])
{
    for (int i = 0; i < argc; ++i) {
        std::cout << func_foo(argv[i]) << std::endl;
    }
}

foo.h

#ifndef FOO_H
#define FOO_H
#include <string>
std::string func_foo(std::string const &);
#endif

foo.cc

#include "foo.h"

std::string func_foo(std::string const &arg)
{
    return arg + "|" + __func__;
}

bar.h

#ifndef BAR_H
#define BAR_H
#include <string>
std::string func_bar();
#endif

bar.cc

#include "bar.h"

std::string func_bar()
{
    return __func__;
}

Construire avec libfoo.so comme bibliothèque partagée.
g++ -Wall -Wextra -fPIC -shared foo.cc -o libfoo.so
g++ -lfoo -L./ -Wall -Wextra program.cc foo.h -o program
programme ldd
...
libfoo.so => non trouvé

Mise à jour de /etc/ld.so.cache
sudo ldconfig /home/tobias/projects/stubs/so/

ldd montre que l'éditeur de liens dynamiques trouve libfoo.so
programme ldd
...
libfoo.so => /home/tobias/projets/stubs/so/libfoo.so (0x00007f0bb9f15000)

Ajouter un appel à libbar.so dans libfoo.so
Nouveau foo.cc

#include "foo.h"
#include "bar.h"

std::string func_foo(std::string const &arg)
{
    return arg + "|" + __func__ + "|" + func_bar();
}

Construire libbar.so et reconstruire libfoo.so
g++ -Wall -Wextra -fPIC -shared bar.cc -o libbar.so
g++ -Wall -Wextra -fPIC -shared libbar.so foo.cc -o libfoo.so
ldd libfoo.so
...
libbar.so => non trouvé

programme ldd
...
libfoo.so => /home/tobias/projets/stubs/so/libfoo.so (0x00007f49236c7000)
libbar.so => non trouvé
Ceci montre que l'éditeur de liens dynamiques trouve toujours libfoo.so mais pas libbar.so.
Mettez à nouveau à jour /etc/ld.so.cache et revérifiez.
sudo ldconfig /home/tobias/projects/stubs/so/
ldd libfoo.so
...
libbar.so => /home/tobias/projets/stubs/so/libbar.so (0x00007f935e0bd000)

programme ldd
...
libfoo.so => /home/tobias/projets/stubs/so/libfoo.so (0x00007f2be4f11000)
libbar.so => /home/tobias/projets/stubs/so/libbar.so (0x00007f2be4d0e000)

On trouve à la fois libfoo.so et libbar.so.

Notez que cette dernière étape n'a aucun effet sur le programme d'application. Si vous êtes vraiment strict, exécuter ldconfig est une sorte de relier. Bizarre ou pas, l'éditeur de liens a besoin de connaître les dépendances des bibliothèques qu'il lie. Il y a beaucoup d'autres façons d'implémenter ceci mais c'est celle qui a été choisie.

2voto

MarcH Points 1868

Vous n'avez pas donné d'informations sur le système, utilisez-vous la glibc ? Si oui, quelle est la sortie de cette commande ?

LD_DEBUG=files du programme

Vérifiez également "Comment écrire des bibliothèques partagées (ELF)" (pdf) (que vous utilisiez la glibc ou non)

1voto

vitaut Points 10255

Votre programme n'a pas besoin d'être lié à libbar.so.

Je pense que le problème est dû au fait que l'on a omis de spécifier libbar.so comme une dépendance de libfoo.so lors de la construction de ce dernier. Je ne suis pas sûr du système de construction que vous utilisez mais dans CMake cela peut être fait comme suit :

add_library(bar SHARED bar.c)

add_library(foo SHARED foo.c)
target_link_libraries(foo bar)

add_executable(program program.c)
target_link_libraries(program foo)

Comme vous pouvez le constater program n'est lié qu'à foo ( libfoo.so ) et foo seulement avec bar ( libbar.so ).

Ou il se peut que libbar.so est introuvable. Essayez de spécifier le chemin d'accès à son répertoire dans LD_LIBRARY_PATH variable d'environnement.

1voto

Brad Points 6004

Cela ne devrait pas être le cas, sauf si quelque chose dans la fonction bar_func symbole changé. Utilisez la commande "nm" pour obtenir un dump des symboles de votre programme et de l'objet partagé - voyez s'il y a un décalage et pourquoi.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X