Construire un exemple simple (hello-world-esque) d’utilisation de l’option -rpath avec $ ORIGIN

Note: Exemple de travail complet maintenant ci-dessous. La question originale est la suivante:

J’ai des problèmes avec le paramètre -rpath de ld avec $ORIGIN .
Comme je ne pouvais pas trouver un exemple complet, je pensais que j’essaierais d’en écrire un moi-même, afin que moi et d’autres puissions l’utiliser plus tard. Une fois que je le fais fonctionner, je vais le ranger.

J’ai demandé à ce sujet avant , mais je pense que mon message était un peu déroutant.

L’exemple de projet construit une bibliothèque partagée et un exécutable qui est lié à ladite bibliothèque.
C’est très petit (3 fichiers, 22 lignes incl
Vous pouvez télécharger le projet à partir d’ ici


Structure du fichier (avant la construction):

  • project/
    • src/
      • foo.cpp
      • main.cpp
    • make.sh

project/src/foo.cpp

 int foo() { return 3; } 

project/src/main.cpp

 int foo(); #include  int main() { std::cout << foo() << std::endl; return 0; } 

project/make.sh

 # Make directories: mkdir -p -v obj mkdir -p -v lib mkdir -p -v run # Build the library: g++ -c -o obj/foo.o src/foo.cpp -fPIC g++ -shared -o lib/foo.sh obj/foo.o # Build the executable: g++ -c -o obj/main.o src/main.cpp g++ -o run/main.run obj/main.o -Wl,-rpath,'$ORIGIN/../../lib' -Llib -l:foo.sh 

Depuis le répertoire du project , exécutez make.sh (assurez-vous qu’il est exécutable).


Structure du fichier (après construction):

  • project/
    • src/
      • foo.cpp
      • main.cpp
    • obj/
      • foo.o
      • main.o
    • lib/
      • foo.so
    • run/
      • main.run
    • make.sh

run/main.run devrait maintenant charger lib/foo.sh à l’exécution, de n’importe où.

Problèmes

Actuellement, cela ne fonctionne que partiellement.
Les fichiers comstacknt et lient OK, mais ils ne parviennent pas à se lier lorsqu’ils sont exécutés à partir d’un répertoire, à l’exception de project (ce qui est le sharepoint l’exercice).

Inspection de main.run avec readelf -d montre:
0x0000000000000001 (NEEDED) Shared library: [lib/foo.sh]
0x000000000000000f (RPATH) Library rpath: [$ORIGIN/../../lib] Qui a l’air proche (j’aurais plutôt [foo.sh] que [lib/foo.sh] mais je le réparerai plus tard).

AFAICT le $ORIGIN dans -Wl,-rpath,'$ORIGIN/../../lib' signifie project/run/main.run donc ce rpath devrait devenir project/lib .

J’ai essayé $ORIGIN/.. , $ORIGIN/../lib , $ORIGIN/../.. , $ORIGIN/../../lib en vain.

Note: J’utilise -l: qui requirejs le nom complet de la bibliothèque (entre autres raisons, il est plus facile de scripter avec des variables lorsque toutes les fonctions prennent le même nom).

Est-ce que quelqu’un sait pourquoi cela ne fonctionne pas?
Ou alternativement, est-ce que quelqu’un a ou connaît un exemple de travail complet?

(Je préférerais avoir [foo.sh] que [lib/foo.sh] mais je le réparerai plus tard).

Il y a la plupart de vos problèmes: le / dans le nom empêche l’éditeur de liens dynamic de faire la magie des chemins.

(Votre rpath est également faux. Pensez-y: à partir du shell, si vous étiez actuellement dans le répertoire où se trouve votre exécutable, comment pourriez-vous accéder au répertoire où se trouve votre bibliothèque? cd ../lib . Donc votre rpath devrait être $ORIGIN/../lib .)

Si vous avez construit votre object comme libfoo.so et lié avec -Llib -lfoo , l’éditeur de liens travaillera sur ce que vous vouliez et fera ce qu’il faut. Mais si vous utilisez des conventions de nommage inhabituelles, vous devrez les aider:

  1. Modifiez la ligne de lien pour que la bibliothèque définisse explicitement SONAME pour votre bibliothèque sur foo.sh :

    g++ -shared -Wl,-soname,foo.sh -o lib/foo.sh obj/foo.o

  2. Fixe le chemin:

    g++ -o run/main.run obj/main.o -Wl,-rpath,'$ORIGIN/../lib' -Llib -l:foo.sh

Il est utile de lancer ldd main/main.run pour voir ce qui se passe. Dans votre cas original, vous verrez quelque chose comme:

  lib/foo.sh (0xNNNNNNNN) 

(l’absence de tout => /some/resolved/path indiquant que le => /some/resolved/path n’a pas été résolu). Dans le cas fixé, vous verrez quelque chose comme:

  foo.sh => /your/path/to/run/../lib/foo.sh (0xNNNNNNNN) 

Ceci est un exemple de lien relatif (avec ld) en utilisant $ORIGIN dans un rpath .

rpath est un chemin (ou un ensemble de chemins) incorporé dans des fichiers binarys (bibliothèques partagées (.so) et exécutables).
Ces chemins sont les principaux chemins de recherche pour les bibliothèques partagées auxquelles le binary doit être lié lors de l’exécution.

$ ORIGIN est un répertoire de démarrage potentiel pour un chemin rpath. Il se résout dans le répertoire contenant le fichier en cours d’exécution. (ex: $ORIGIN/lib )

L’exemple de projet construit une bibliothèque partagée et un exécutable qui est lié à ladite bibliothèque à l’aide de rpath et de $ORIGIN .
Vous pouvez télécharger le projet à partir d’ ici .


Structure du fichier (avant la construction):

  • project/
    • src/
      • foo.cpp
      • main.cpp
    • make.sh

project/src/foo.cpp

 int foo() { return 3; } 

project/src/main.cpp

 int foo(); #include  int main() { std::cout << foo() << std::endl; return 0; } 

project/make.sh

 # Make directories: mkdir -p -v obj mkdir -p -v lib/dir mkdir -p -v run # Build the library: g++ -c -o obj/foo.o src/foo.cpp -fPIC g++ -shared -o lib/dir/foo.so -Wl,-soname,foo.so obj/foo.o # Build the executable: 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 

À partir du répertoire du project , exécutez make.sh (s'il ne s'exécute pas, assurez- make.sh que make.sh dispose des droits d'exécution).

Si tout s'est bien passé, main.run devrait maintenant charger lib/dir/foo.so à l'exécution, quel que soit le chemin absolu du project (vous pouvez le déplacer n'importe où), quel que soit le répertoire de travail actuel (vous pouvez l'exécuter depuis nulle part).


Remarques:

  • -fPIC demande au compilateur de créer des fichiers objects relogeables (les fichiers objects construits dans les bibliothèques partagées doivent être relocalisables).
  • -Wl,-soname, incorpore dans la bibliothèque générée. Cela doit correspondre au nom que vous indiquez pour les options -l ou -l: lors de la liaison avec cette bibliothèque.
  • -Wl,-rpath,'' incorpore dans la bibliothèque générée en tant que chemin de recherche (ou rpath - voir ci-dessus).
  • -L ajoute un chemin d'access à la liste du chemin de recherche de la bibliothèque à la génération . (Remarque: rpath n'est pas pertinent au moment de la construction, -L n'est pas pertinent au moment de l'exécution).
  • -l: ajoute le nom de fichier (sans chemin d'access) d'une bibliothèque à associer. (Similaire à -l , sauf -l: nécessite le nom de fichier complet.

Structure du fichier (après construction):

  • project/
    • src/
      • foo.cpp
      • main.cpp
    • obj/
      • foo.o
      • main.o
    • lib/
      • dir/
        • foo.so
    • run/
      • main.run
    • make.sh

Note: J'utilise -l: qui requirejs le nom complet de la bibliothèque (entre autres raisons, il est plus facile de scripter avec des variables lorsque toutes les fonctions prennent le même nom).
Il est plus courant d'utiliser -l , où -l désigne lib.so.

Limites

Autant que je sache (corrigez-moi si je me trompe), il n'y a aucun moyen d'append une bibliothèque dans un sous-répertoire d'un chemin de recherche (sauf pour l'append en tant que sous-chemin). Cela est vrai pour les chemins de recherche à la fois ( -L ) et d'exécution ( -rpath ).

Donc, si vous avez deux bibliothèques avec le même nom mais des emplacements différents, vous ne pourrez pas les lier toutes les deux. (J'espère que je me trompe ou que cela est corrigé).