web-dev-qa-db-fra.com

Modification de LD_LIBRARY_PATH lors de l'exécution pour les ctypes

Comment mettre à jour cette variable d'environnement au moment de l'exécution afin que ctypes puisse charger une bibliothèque n'importe où? J'ai essayé ce qui suit et aucun ne semble fonctionner.

from ctypes import *
os.environ['LD_LIBRARY_PATH'] = "/home/starlon/Projects/pyCFA635/lib"  
os.putenv('LD_LIBRARY_PATH', "/home/starlon/Projects/pyCFA635/lib")  
lib = CDLL("libevaluator.so")
37
Scott

Au moment où un programme tel que Python est en cours d'exécution, le chargeur dynamique (ld.so.1 ou quelque chose de similaire) a déjà lu LD_LIBRARY_PATH et ne remarquera aucun changement par la suite. Donc, à moins que le Python lui-même évalue LD_LIBRARY_PATH et l'utilise pour créer le chemin d'accès possible de la bibliothèque pour dlopen() ou une fonction équivalente à utiliser, la définition de la variable dans le script n'aura pas effet.

Étant donné que vous dites que cela ne fonctionne pas, il semble plausible de supposer que Python ne construit pas et n'essaye pas tous les noms de bibliothèques possibles; il repose probablement uniquement sur LD_LIBRARY_PATH.

41
Jonathan Leffler

Même si vous donnez un chemin complet vers CDLL ou cdll.LoadLibrary (), vous devrez peut-être toujours définir LD_LIBRARY_PATH avant d'appeler Python. Si la bibliothèque partagée que vous chargez fait explicitement référence à une autre bibliothèque partagée et qu'aucun "rpath" n'est défini dans le .so pour cette bibliothèque, alors elle ne sera pas trouvée, même si elle a déjà été chargée. Un rpath dans une bibliothèque spécifie un chemin de recherche à utiliser pour rechercher d'autres bibliothèques nécessaires à cette bibliothèque

Par exemple, j'ai un cas d'un ensemble de bibliothèques tierces interdépendantes que je n'ai pas produites. b.so fait référence a.so. Même si je charge a.so à l'avance:

ctypes.cdll.LoadLibrary('/abs/path/to/a.so')
ctypes.cdll.LoadLibrary('/abs/path/to/b.so')

J'obtiens une erreur sur le deuxième chargement, car b.so se réfère simplement à 'a.so', sans rpath, et donc b.so ne sait pas que c'est le bon a.so. Je dois donc définir LD_LIBRARY_PATH à l'avance pour inclure '/ abs/path/to'.

Pour éviter d'avoir à définir LD_LIBRARY_PATH, vous modifiez l'entrée rpath dans les fichiers .so. Sur Linux, j'ai trouvé deux utilitaires qui font cela: chrpath et patchelf . chrpath est disponible dans les référentiels Ubuntu. Il ne peut pas changer de chemin sur les .so qui n'en ont jamais eu. patchelf est plus flexible.

30
Dan Halbert

CDLL peut recevoir un nom de chemin complet, donc par exemple j'utilise ce qui suit dans l'un de mes scripts où le .so se trouve dans le même répertoire que le script python.

import os
path = os.path.dirname(os.path.realpath(__file__))
dll = CDLL("%s/iface.so"%path)

Dans votre cas, ce qui suit devrait suffire.

from ctypes import *
lib = CDLL("/home/starlon/Projects/pyCFA635/lib/libevaluator.so")
16
Gordon Wrigley

Compilez votre binaire avec un rpath relatif au répertoire de travail actuel comme:

gcc -shared -o yourbinary.so yoursource.c otherbinary.so \
    -Wl,-rpath='.',-rpath='./another/relative/rpath' -fpic

Ensuite, vous pouvez changer le répertoire de travail dans python au moment de l'exécution avec:

import os
os.chdir('/path/to/your/binaries')

Comme ça, le chargeur trouve également d'autres bibliothèques dynamiques comme otherbinary.so

3
Simon Ramstedt