web-dev-qa-db-fra.com

A quoi sert l'option 'soname' pour la construction de bibliothèques partagées?

J'ai appris le "Programme Library HOWTO". Il est mentionné que l'utilisation de soname pour gérer la version est la suivante.

gcc -shared -fPIC -Wl,-soname,libfoo.so.1  -o libfoo.so.1.0.0 foo.c
ln -s libfoo.so.1.0.0  libfoo.so.1
ln -s libfoo.so.1 libfoo.so

Et je reçois l'information que si la soname n'est pas définie. ce sera égal à libfoo.so.1.0.0, voir la réponse de ici .

Et je trouve que ça peut aussi marcher sans soname, comme suivre 

 gcc -shared -fPIC -o libfoo.so.1.0.0 foo.c
 ln -s libfoo.so.1.0.0  libfoo.so.1
 ln -s libfoo.so.1 libfoo.so

Je pense donc que le seul point utile est que l'option soname peut vous indiquer la version de la bibliothèque partagée lorsque vous utilisez la commande readelf -d libfoo.so pour la vérifier. 

Que puis-je faire d'autre?

39
Samuel

soname est utilisé pour indiquer quelle compatibilité API binaire votre bibliothèque prend en charge.

SONAME est utilisé au moment de la compilation par l'éditeur de liens pour déterminer à partir du fichier de bibliothèque quelle version de bibliothèque cible réelle. gcc -lNAME cherchera un lien ou un fichier libNAME.so, puis capturera son nom SONAME, qui sera certainement plus spécifique (ex libnuke.so renvoie à libnuke.so.0.1.4 contenant SONAME libnuke.so.0).

Au moment de l'exécution, le lien avec celui-ci est défini dans la section dynamique ELF NEEDED, puis une bibliothèque portant ce nom (ou un lien vers celui-ci) doit exister . À l'exécution, la variable SONAME est ignorée. Par conséquent, seul le lien ou le fichier l'existence suffit.

Remarque: SONAME est appliqué uniquement au moment de la création du lien et non au moment de l'exécution.

'SONAME' de la bibliothèque peut être vu avec 'objdump -p fichier | grep SONAME' . 'BESOIN' des fichiers binaires peut être vu avec 'objdump -p fichier | grep NEEDED'.

[EDIT] WARNING Voici une remarque d'ordre général, pas celle déployée sous Linux. Voir à la fin.

Supposons que vous avez une bibliothèque avec le nom libnuke.so.1.2 et que vous développez une nouvelle bibliothèque libnuke:

  • si votre nouvelle bibliothèque est un correctif de précédente sans modification de l'API, vous devez simplement conserver le même nom, augmenter la version du nom de fichier. le fichier sera libnuke.so.1.2.1 mais soname sera toujours libnuke.so.1.2. 
  • si vous avez une nouvelle bibliothèque qui n’a ajouté que de nouvelles fonctions, mais qui n’a pas cédé la fonctionnalité et qui est toujours compatible avec les versions précédentes, vous souhaitez utiliser le même nom de fichier que la version précédente, ainsi qu’un nouveau suffixe tel que .1. c'est-à-dire que le fichier et le nom seront libnuke.so.1.2.1. Tout programme lié à libnuke.1.2 fonctionnera toujours avec celui-ci. Les nouveaux programmes liés à libnuke.1.2.1 ne fonctionneront qu’avec celui-ci (jusqu’à ce que la nouvelle Subversion arrive comme libnuke.1.2.1.1).
  • si votre nouvelle bibliothèque n'est compatible avec aucun des libnuke: libnuke.so.2
  • si votre nouvelle bibliothèque est compatible avec l'ancienne version nue: libnuke.so.1.3 [c.-à-d. toujours compatible avec libnuke.so.1]

[EDIT] à compléter: cas linux.

Dans Linux real life, SONAME sous une forme spécifique: Lib [NOM] [API-VERSION] .so. [Version majeure] Version majeure n'est qu'une valeur entière qui augmente à chaque changement majeur de bibliothèque API-VERSION est vide par défaut

ex libnuke.so.0

Ensuite, le nom de fichier réel inclut les versions mineures et les subversions ex: libnuke.so.0.1.5

Je pense que ne pas fournir de nom de fichier est une mauvaise pratique car renommer un fichier changera son comportement.

43
philippe lhardy

Supposons que libA.so dépend de libB.so et qu'ils se trouvent tous dans un répertoire (bien entendu, le répertoire ne peut pas être trouvé par l'éditeur de liens dynamique). Si vous n'avez pas défini soname, alors dlopen ne fonctionne pas:

auto pB = dlopen("./libB.so", RTLD_LAZY | RTLD_GLOBAL);
auto pA = dlopen("./libA.so", RTLD_LAZY | RTLD_GLOBAL);

Comme l'éditeur de liens au moment de l'exécution ne peut pas trouver libB.so, donc pA est défini sur NULL

Dans ce cas, soname vous sauvera de l'enfer ...

1
Lw Cui

Un autre aspect: Au moins sous Linux, l’entrée SONAME fournit un indice pour le système d’éditeur de liens à la compilation sur la manière de créer les liens appropriés dans/lib,/lib64, etc L’exécution de la commande ldconfig tente de créer un lien symbolique nommé avec SONAME qui est également pris dans le cache de l'éditeur de liens au moment de l'exécution. La plus récente des librairies marquant le même SONAME remporte la course par la liaison . Si certains logiciels reposent sur un SONAME spécifique et que vous souhaitez renouveler une bibliothèque, vous devez fournir à ce SONAME le nom de ldconfig stick sur cette nouvelle bibliothèque (si ldconfig est utilisé pour reconstruire le cache et les liens). Par exemple. libssl.so.6 et libcrypto.so.6 sont de tels cas.

0
Johann Klasek

Voici un exemple à l'appui de La réponse de Johann Klasek .

Dans un mot, SONAME est nécessaire au moment de l'exécution. au moment de la compilation, seuls linker name ou real name sont nécessaires (par exemple, g ++ main.cpp -L. -ladd ou g ++ main.cpp -L. -l:libadd.so.1.1). Les définitions de linker name et real name suivent Bibliothèque de programmes HOWTO: 3. Bibliothèques partagées

arbre source:

├── add.cpp
├── add.h
├── main.cpp
└── Makefile

Makefile: 

SOURCE_FILE=add.cpp
# main.cpp include `add.h` whose implementation is `add.cpp`
MAIN_FILE=main.cpp
SONAME=libadd.so.1
REAL_NAME=libadd.so.1.1
LINKER_NAME=libadd.so
OUTPUT_FILE=a.out

all:
   g++ -shared -fPIC -Wl,-soname,${SONAME} -o ${REAL_NAME} ${SOURCE_FILE}
   ln -s ${REAL_NAME} ${LINKER_NAME}
   g++ main.cpp -I. -L. -ladd -o ${OUTPUT_FILE} 
   # same with `ldconfig -n .` create a soft symbolic link
   ln -s ${REAL_NAME} ${SONAME}
   #./a.out: error while loading shared libraries: libadd.so.1: cannot open 
   # shared object file: No such file or directory
   LD_LIBRARY_PATH=. ./${OUTPUT_FILE}
clean:
   rm ${SONAME} ${REAL_NAME} ${LINKER_NAME} ${OUTPUT_FILE}
0
BugKiller