web-dev-qa-db-fra.com

ld: Utilisation de -rpath, $ Origin dans une bibliothèque partagée (récursive)

Je viens de faire un exemple simple d'utilisation de l'option -rpath de ld avec $Originici (voir la 2e réponse pour une version opérationnelle). J'essaie de créer un exemple où main.run est lié à foo.so, qui à son tour est lié à bar.so, tous en utilisant rpath et $Origin.

La structure de fichier au moment de l'exécution est:

  • projet/
    • lib /
      • dir /
        • sous/
          • bar.so
        • foo.so
    • courir/
      • main.run (échec de la construction)

Je construis foo.so en utilisant: 

g++ -c -o obj/foo.o src/foo.cpp -fPIC
g++ -shared -o lib/dir/foo.so obj/foo.o -Wl,-soname,foo.so -Wl,-rpath,'$Origin/sub' -Llib/dir/sub -l:bar.so

Qui construit bien. ldd lib/dir/foo.so peut même trouver bar.so.

Cependant, lorsque j'essaie de relier main.run à foo.so, foo.so ne trouve pas bar.so.

Je construis main.so en utilisant:

g++ -c -o obj/main.o src/main.cpp
g++ -o run/main.run obj/main.o -Wl,-rpath,'$Origin/../lib/dir' -Llib/dir -l:foo.so

Cela fonctionne correctement si une autre version de foo.so est utilisée sans lien récursif. (Décommentez les lignes dans make.sh, dans le projet ci-dessous, pour tester). 

Cependant, en utilisant le foo.so normal, je reçois cette erreur lors de la construction de main.run:

/ usr/bin/ld: warning: bar.so, requis par lib/dir/foo.so, introuvable (essayez d'utiliser -rpath ou -rpath-link)

Donc mes questions sont: 

  1. Est-ce que $Origin dans foo.so se résout en project/lib/dir (où foo.so est) ou project/run (où main.run (l'exécutable le reliant) est)?
    ldd semble indiquer que c'est project/lib/dir, ce qui semble être la meilleure solution (bien que j'aie essayé de supposer les deux).
  2. Comment puis-je obtenir ces liens (tout en préservant la transférabilité) - de préférence sans utiliser -rpath-link.

Vous pouvez télécharger le projet ici . C'est aussi simple que je peux le faire. 4 sources courtes et un script.
Après l'extraction, lancez simplement ./make.sh à partir de project/.

Note: J'utilise -l:. Cela ne devrait rien changer si ce n'est que les bibliothèques portent le nom foo.so au lieu de libfoo.so, et lunk avec -l:foo.so au lieu de -lfoo.

27
Simon

Eh bien, j'ai quelque chose qui fonctionne. Mais je ne comprends pas vraiment pourquoi ça marche. Cela ressemble à un bug dans le LD pour moi.

J'ai exécuté strace -f -o /var/tmp/strace.out -- g++ ... pour la compilation main.run. L'éditeur de liens statique tente en fait d'ouvrir des fichiers dont le nom littéral ressemble à "$ Origin/lib/dir/sub/bar.so", entre 20-30 autres éléments. (En d’autres termes, il recherche un répertoire nommé $Origin. Sérieusement.)

Il semble également qu’il cherche dans le chemin -rpath-link le nom "lib/dir/sub/bar.so", pas seulement "bar.so". Je ne sais pas pourquoi.

Quoi qu’il en soit, c’est le lien pour main.run qui fonctionne pour moi:

g++ -o run/main.run obj/main.o -Wl,-rpath,'$Origin/../lib/dir' -Wl,-rpath-link,. -Llib/dir -l:foo.so

Il est identique au vôtre mais avec -Wl,-rpath-link,. inséré.

[Addenda]

OK je pense que je vois ce qui se passe. Tout d'abord, l'éditeur de liens statique (GNU ld) n'honore tout simplement pas $ Origin dans les bibliothèques avec lesquelles il est lié.

Deuxièmement, le comportement lorsque vous utilisez -lbar par rapport à -l:bar.so est très différent.

Exécutez readelf -a sur foo.so. Dans votre construction, cela montre une dépendance sur "lib/dir/sub/bar.so". C'est pourquoi placer le rpath-link à "." corrige la construction de main.run; il oblige l'éditeur de liens statique à rechercher "." pour "lib/dir/sub/bar.so", qu'il trouve.

Si vous renommez bar.so en libbar.so et reliez foo.so pour utiliser -lbar au lieu de -l:bar.so, le même readelf indique que foo.so dépend maintenant de "libbar.so" (sans composant de chemin). Avec ce foo.so, vous pouvez obtenir le lien main.run à l'aide de -Wl,-rpath-link,lib/dir/sub, comme vous vous en doutez, si vous saviez que l'éditeur de liens statique n'honorait tout simplement pas $ Origin.

En passant, je ne vois pas la syntaxe -l:bar.so documentée dans le manuel GNU ld. Par curiosité, comment l'avez-vous trouvé?

En supposant qu'il s'agisse d'une fonctionnalité prise en charge, cela ressemble un peu à un bogue (-l: bar.so créant une dépendance sur lib/dir/sous/bar.so au lieu de simplement bar.so). Vous pouvez soit gérer ce bogue en définissant rpath-link sur '.' pour main.run, ou vous pouvez renommer des choses de la manière habituelle (libxxx.so).

7
Nemo

De la ld-linux (8) manpage:

$ Origine et rpath

ld.so comprend la chaîne $ Origin (ou l'équivalent $ {Origin}) dans un fichier La spécification rpath (DT_RPATH ou DT_RUNPATH) désigne le répertoire contenant l'exécutable de l'application. Ainsi, une application située dans somedir/app peut être compilé avec gcc -Wl, -rpath, '$ Origin /../ lib' donc qu'il trouve une bibliothèque partagée associée dans somedir/lib, peu importe où somedir est situé dans la hiérarchie des répertoires. Cela facilite la création d'applications "clé en main" qui n'ont pas besoin d'être installé dans des répertoires spéciaux, mais peut être décompressé dans n'importe quel répertoire et toujours trouver leurs propres bibliothèques partagées.

Ainsi, en réponse à votre première question, il n'y a qu'une seule valeur pour $Origin: project/run.

Par conséquent, la réponse à votre deuxième question devrait être d'utiliser la commande suivante pour lier foo.so:

g++ -shared -o lib/dir/foo.so obj/foo.o -Wl,-soname,foo.so -Wl,-rpath,'$Origin/../lib/dir/sub' -Llib/dir/sub -l:bar.so
6
Rob Stewart

Premièrement, il existe des problèmes avec l’extension du signe $ qui pourraient poser problème. Je construis Python à partir des sources et je le fais:

export LDFLAGS='-Wl,-rpath,\$${Origin}/../lib -Wl,-rpath,\$${Origin}/../usr/lib -Wl,--enable-new-dtags'

avant d'exécuter make. Cela fonctionne bien et trouve les dépendances de premier niveau. Soyez prudent avec les guillemets simples et doubles lorsque vous traitez avec ce type de problème d'expansion de macro.

Deuxièmement, si vous exécutez objdump -x sur un binaire ou une bibliothèque, vous pouvez voir l’en-tête RPATH qu’il contient. Quand je lance objdump -x path/to/python |grep RPATH, il me montre ceci .RPATH $ {Origin} /../ lib: $ {Origin} /../ usr/lib`

Je vous suggère de vérifier vos fichiers binaires pour voir ce qui est réellement dans l'en-tête RPATH. Malheureusement, je ne pense pas que cela résoudra votre problème. C'est ce que je vois quand je lance ldd path/to/python:

libpython2.7.so.1.0 => /data1/python27/bin/../lib/libpython2.7.so.1.0 (0x00002ad369d4f000)
libpthread.so.0 => /lib/libpthread.so.0 (0x00002ad36a12f000)
libdl.so.2 => /lib/libdl.so.2 (0x00002ad36a34d000)
libutil.so.1 => /lib/libutil.so.1 (0x00002ad36a551000)
libm.so.6 => /lib/libm.so.6 (0x00002ad36a754000)
libc.so.6 => /lib/libc.so.6 (0x00002ad36a9d8000)
/lib64/ld-linux-x86-64.so.2 (0x00002ad369b2d000)

Comme vous pouvez le constater, la dépendance de premier niveau est correctement gérée par rpath, mais les dépendances de second niveau, c’est-à-dire les dépendances de libpython, reviennent aux bibliothèques système. Et oui, libpython a exactement le même en-tête RPATH dans son binaire. J'ai trouvé votre question en recherchant rpath recursive dans Google pour tenter de résoudre mon problème de création d'un paquetage indépendant de la distribution.

Ajouté plus tard Les en-têtes rpath ne modifient que le chemin FIRST recherché pour les bibliothèques. S'ils n'y sont pas trouvés, le chargeur continue à chercher aux endroits habituels. ldd répertorie uniquement le chemin réel de la bibliothèque qui a été trouvé à la suite de la recherche. Lorsque j'ai copié ces bibliothèques dans le répertoire rpath, tout a fonctionné. En gros, il n’existe aucun moyen simple de trouver toutes les dépendances et de les copier, il suffit de ldd -v path/to/python et de quelques analyses de cette sortie.

5
Michael Dillon

J'ai également étudié cette question et, autant que je sache, vous devez utiliser -rpath-link pour tout chemin qui utiliserait l'extension Origin. Par exemple:

CC -shared (other flags) -R'$Origin/../lib/' -o /buildpath/lib/libmylib1.so
CC -shared (other flags) -R'$Origin/../lib/' -lmylib1 -o /buildpath/lib/libmylib2.so
# This fails to link 'somebinary'
CC (various flags) -R'$Origin/../lib/' -lmylib2 -o /buildpath/bin/somebinary
# This works correctly
CC (various flags) -R'$Origin/../lib/' -Wl,-rpath-link,/buildpath/lib/mylib1 -lmylib2 -o /buildpath/bin/somebinary
# The text above the carets to the right is a typo: ------------------^^^^^^
# I'm fairly sure it should read like this (though it has been awhile since I wrote this):
# (...) -Wl,-rpath-link,/buildpath/lib -lmylib1 (...)

ld ne développera pas $Origin dans les chemins spécifiés à l'aide de -rpath-link ni dans les chemins extraits du RPATH d'une sous-dépendance. Dans l'exemple ci-dessus, mylib2 dépend de mylib1; en liant somebinary, ld tente de trouver mylib1 à l'aide de la chaîne littérale/inexpansée $Origin/../lib/ intégrée dans libmylib2.so. ld.so serait au moment de l'exécution, mais pas ld.

Il n'utilisera pas non plus les chemins spécifiés avec -L pour trouver la bibliothèque de sous-dépendances (y | ies).

1
Brian Vandenberg

Vérifiez ma version modifiée de votre script make . Fondamentalement, un -rpath-link sans $Origin supplémentaire devrait être utilisé, puisque ld ne comprend pas du tout $Origin.

Quant à vos questions.

  1. $Origin fonctionne uniquement pendant l'exécution, et c'est w.r.t. chaque bibliothèque. Donc, différentes bibliothèques partagées ont différents $Origin.
  2. Je crains que le meilleur moyen consiste à ajouter rpath-link, et cela n'affectera pas votre portabilité, car ils sont relatifs et n'existeront pas dans l'exécutable final, comme je l'ai montré dans ma version de make.sh

En outre, ceci est ma propre compréhension de tout ce qui concerne les liens . J'espère que ça aide.

1
zym1010

De ma compréhension, c’est un problème dans ld (c’est-à-dire binutils) et comment cela résout les "dépendances secondaires"

AFAIK a commencé à partir de binutils >= 2.30. Les chemins dans les dépendances sont ajoutés à la recherche . i.e. ld ... main.exe find foo.so, puis lisez le RPATH dans foo.so et retrouvez donc bar.so

Voici ma question de débordement de pile: Changement de dépendance secondaire de Binutils

Et voici mon enquête sur diverses distributions (dans le conteneur de menu fixe) pour tester diverses versions de binutils https://github.com/Mizux/SecondaryDependency

remarque: jetez un coup d'œil au journal travis-CI ...

0
Mizux