Existe-t-il un moyen de savoir quelles fonctions sont exportées de la bibliothèque de fonctions étrangères dll
à python ctypes
?
Et, si possible, connaître les détails des fonctions exportées via ctypes
.
Si oui, quelqu'un pourrait-il fournir un extrait de code?
Je ne pense pas que ctypes offre cette fonctionnalité. Sous Windows avec Visual Studio:
DUMPBIN -EXPORTS XXX.DLL
Ou pour les fenêtres:
objdump -p XXX.dll
Si vous êtes sous Linux, il existe un utilitaire pratique nm
permettant de répertorier le contenu d'une bibliothèque partagée (il existe toujours un utilitaire pratique sous Linux, en particulier pour les éléments C).
Vous l'utilisez avec l'indicateur -D
: nm -D ./libMyLib.so
En général, cela n’est pas possible car, toujours en général, les bibliothèques à chargement dynamique ne contiennent pas les méta-informations requises. Il est peut-être possible d'obtenir ces informations dans certains cas particuliers par des moyens spécifiques au système, mais ctypes
ne lui-même ne les récupère pas. Vous pouvez enregistrer ces informations via ctypes
(voir, par exemple, les attributs restype et argtypes
des pointeurs de fonction), mais seulement après que vous les ayez obtenues par différents moyens.
La réponse de @ Mark utilise les outils Visual Studio.
Sous Windows, vous pouvez également utiliser Dependency Walker pour obtenir les noms de fonction des exportations de DLL.
Parfois, les noms sont mutilés et ne peuvent pas être utilisés comme nom de fonction python valide.
Vous pouvez utiliser getattr
pour obtenir un descripteur des fonctions modifiées, par exemple:
mylib = ctypes.cdll('mylib.dll')
my_func = getattr(mylib, '_my_func@0')
my_func()
Si vous avez également le code source de ladite bibliothèque et que vous recherchez une méthode entièrement automatisée entièrement en python, vous pouvez utiliser pycparser
pour le fichier: prog.c
typedef short int ret_t;
typedef short int param_t;
ret_t add(param_t a, param_t b) {
return (ret_t)(a + b);
}
ret_t passthrough(ret_t (* func)(param_t a, param_t b), param_t a, param_t b) {
// parameter intentionally altered.
// if this isn't done, compiler will deem entire function redundant
return func(a, b + 1);
}
compiler avec gcc
gcc -I. -E ./prog.c > prog-preproc.c
nous donne le fichier c prétraité: prog-preproc.c
puis en python:
import pycparser
parser = pycparser.c_parser.CParser()
with open('prog-preproc.c', 'r') as fh:
ast = parser.parse(fh.read())
class FunctionVisitor(pycparser.c_ast.NodeVisitor):
def visit_FuncDef(self, node):
print("found function: %s" % node.decl.name)
#node.show()
FunctionVisitor().visit(ast)
les rendements
found function: add
found function: passthrough
Pour continuer à creuser, vous pouvez également obtenir les types de paramètre et de retour. Annuler le commentaire node.show()
pour plus d'informations à partir de l'arbre de syntaxe abstraite (AST)
Je publierai bientôt une bibliothèque pour cela (je vais essayer de me rappeler de revenir et de déposer un lien)
OUI! il existe une méthode native très intelligente pour le faire.
disons que vous utilisez des types Python. mettez quelque chose comme ceci dans votre code C:
1) dans votre code C:
#define PYEXPORT extern "C" __declspec(dllexport)
placez maintenant PYEXPORT au-dessus de la fonction que vous souhaitez exporter:
PYEXPORT
int myfunc(params){
2) Après la compilation, retournez dans Python et ouvrez votre fichier .c, puis analysez-le comme suit:
source1_ = open(cfile_name + '.c')
source1 = source1_.read()
source1_.close()
fn = source1.split('PYEXPORT')[-1].split('(')[0].split(' ')[1]
Entrée shell: fn
Sortie du shell: 'myfunc'
3) Maintenant, voici la partie intelligente: définissez une nouvelle fonction dans une chaîne:
a1 = """
global get_c_fn
def get_c_fn(dll):
func = dll."""
a2 = """
return func"""
a3 = a1 + fn + a2
print(a3)
global get_c_fn
def get_c_fn(dll):
func = dll.myfunc
return func
MAINTENANT exécutez exec (a3) et il transformera cette chaîne en une fonction que vous pouvez utiliser.
4) faire comme d'habitude:
mydll = ctypes.CDLL(cfile_name + '.dll')
c_fn = get_cuda_fn(mydll)
c_fn.argtypes = func_params (an array of C-converted inputs you need)
c_fn( *[params] )
et là vous avez un wrapper Python pour un script C sans avoir à modifier dix choses différentes à chaque fois que quelque chose change.
En interne, ctypes utilise les fonctions fournies par la bibliothèque de liens dynamiques (dlopen/dlsym sous unix, LoadLibrary/GetProcAddress sous windows) pour charger la bibliothèque et trouver l’adresse de la fonction spécifiée par le nom de la fonction; puis utilisez la bibliothèque cffi pour passer le paramètre de manière dynamique.
Le problème est que la bibliothèque de liens dynamiques dont dépend ctypes n'inclut pas la fonction permettant de répertorier le symbole de la bibliothèque partagée. C'est pourquoi vous ne pouvez pas répertorier symbole par ctypes.
Pour ce faire, vous devez utiliser des outils spécifiques pour dump le fichier elf (readelf sous unix) et le fichier pe pour dll (dumpbin sous windows).