dll
からpython外部関数ライブラリctypes
を介してどの関数がエクスポートされるかを知る方法はありますか?
そして、可能であれば、c types
を介してエクスポートされた関数の詳細を知ることができます。
はいの場合、誰かがコードのスニペットを提供できますか?
Ctypesがこの機能を提供するとは思いません。 Visual Studioを備えたWindowsの場合:
DUMPBIN -EXPORTS XXX.DLL
または、Windowsのmingwの場合:
objdump -p XXX.dll
Linuxを使用している場合は、共有ライブラリの内容を一覧表示する便利なユーティリティnm
があります(特にCのものについては、Linuxには常に便利なユーティリティがあります)。
-D
フラグと一緒に使用します:nm -D ./libMyLib.so
一般に、これも不可能です。これも、一般的に、動的に読み込まれるライブラリには必要なメタ情報が含まれていないためです。特定の特殊なケースでは、システム固有の方法でその情報を取得できる場合がありますが、ctypes
自体はその情報を取得しません。 ctypes
を介してそのような情報をrecordできます(例 restype およびargtypes
属性を参照)関数ポインタの))が、別の方法で取得した後にのみ。
@Markの答えはVisual Studioツールを使用しています。
Windowsでは、 Dependency Walker を使用して、dllエクスポートの関数名を取得することもできます。
ときどき名前が変形され、有効なpython関数名として使用できない場合があります。
getattr
を使用して、破損した関数のハンドルを取得できます。例:
mylib = ctypes.cdll('mylib.dll')
my_func = getattr(mylib, '_my_func@0')
my_func()
以下のアプローチは、WindowsとUbuntuの両方で機能しました。 Windowsの場合、 Cygwin が必要です。
以下のようなc
ファイルがあり、名前はtest.c
であるとします。
int func1(int a, int b){
return a + b;
}
int func2(int a, int b){
return a - b;
}
上記のcコードは、以下のコマンドを使用してtest.dll
ファイルにコンパイルされました。
gcc -shared -Wl,-soname,adder -o test.dll -fPIC test.c
そして、以下のPythonスクリプトは、Pythonで使用できるtest.dll
の関数を見つけます。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from subprocess import Popen, PIPE
out = Popen(
args="nm ./test.dll",
Shell=True,
stdout=PIPE
).communicate()[0].decode("utf-8")
attrs = [
i.split(" ")[-1].replace("\r", "")
for i in out.split("\n") if " T " in i
]
from ctypes import CDLL
functions = [i for i in attrs if hasattr(CDLL("./test.dll"), i)]
print(functions)
私がWindowsで得た出力は次のとおりです:
['func1', 'func2']
私がUbuntuで得た出力は以下の通りです:
['_fini', 'func1', 'func2', '_init']
上記のリストの項目は、_FuncPtr
クラスのオブジェクトです。
上記のライブラリのソースも入手していて、完全に自動化された全Pythonの方法を探している場合は、pycparser
を使用できます。
ファイルの場合:_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);
}
_
gcc
でコンパイル
_gcc -I. -E ./prog.c > prog-preproc.c
_
前処理されたcファイルを提供します:_prog-preproc.c
_次に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)
_
収量
_found function: add
found function: passthrough
_
さらに掘り下げるには、パラメーターと戻り値の型を取得することもできます。抽象構文ツリー(AST)内の詳細については、node.show()
のコメントを外してください
はい!それを行うには非常に巧妙なネイティブメソッドがあります。
Python ctypesを使用しているとしましょう。Cコードに次のように記述します。
1)Cコードで:
#define PYEXPORT extern "C" __declspec(dllexport)
次に、エクスポートする関数の上にPYEXPORTを置きます。
PYEXPORT
int myfunc(params){
2)コンパイル後、Pythonに戻って.cファイルを開き、次のように解析します。
source1_ = open(cfile_name + '.c')
source1 = source1_.read()
source1_.close()
fn = source1.split('PYEXPORT')[-1].split('(')[0].split(' ')[1]
シェル入力:fn
シェル出力: 'myfunc'
3)これが賢い部分です:文字列で新しい関数を定義します:
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
ここでexec(a3)を実行すると、その文字列が使用可能な関数に変わります。
4)いつものようにする:
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] )
そして、何かが変更されるたびに10の異なるものを変更する必要なしに、Cスクリプトのpythonラッパーがあります。
内部的にctypesは、ダイナミックリンクライブラリ(UNIXではdlopen/dlsym、WindowsではLoadLibrary/GetProcAddress)によって提供される関数を使用して、ライブラリをロードし、関数名で指定された関数のアドレスを見つけます。次に、cffiライブラリを使用してパラメーターを動的に渡します。
問題は、ctypesが依存するダイナミックリンクライブラリに、共有ライブラリからシンボルをリストする関数が含まれていないことです。そのため、ctypesでシンボルをリストできません。
これを行うには、特定のツールを使用してelfファイル(UNIXではreadelf)を、dllのpeファイル(Windowsではdumpbin)をダンプする必要があります。