web-dev-qa-db-ja.com

ctypesを使用してDLLからエクスポートされた関数をリストする

dllからpython外部関数ライブラリctypesを介してどの関数がエクスポートされるかを知る方法はありますか?

そして、可能であれば、c typesを介してエクスポートされた関数の詳細を知ることができます。

はいの場合、誰かがコードのスニペットを提供できますか?

37
Tom

Ctypesがこの機能を提供するとは思いません。 Visual Studioを備えたWindowsの場合:

DUMPBIN -EXPORTS XXX.DLL

または、Windowsのmingwの場合:

objdump -p XXX.dll
15
Mark

Linuxを使用している場合は、共有ライブラリの内容を一覧表示する便利なユーティリティnmがあります(特にCのものについては、Linuxには常に便利なユーティリティがあります)。

これについての質問はこちらです

-Dフラグと一緒に使用します:nm -D ./libMyLib.so

13
xealits

一般に、これも不可能です。これも、一般的に、動的に読み込まれるライブラリには必要なメタ情報が含まれていないためです。特定の特殊なケースでは、システム固有の方法でその情報を取得できる場合がありますが、ctypes自体はその情報を取得しません。 ctypesを介してそのような情報をrecordできます(例 restype およびargtypes属性を参照)関数ポインタの))が、別の方法で取得した後にのみ。

7
Alex Martelli

@Markの答えはVisual Studioツールを使用しています。

Windowsでは、 Dependency Walker を使用して、dllエクスポートの関数名を取得することもできます。

ときどき名前が変形され、有効なpython関数名として使用できない場合があります。

getattr を使用して、破損した関数のハンドルを取得できます。例:

mylib = ctypes.cdll('mylib.dll')
my_func = getattr(mylib, '_my_func@0')

my_func()
4
Peter Wood

以下のアプローチは、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クラスのオブジェクトです。

3
dildeolupbiten

上記のライブラリのソースも入手していて、完全に自動化された全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()のコメントを外してください

2
FraggaMuffin

はい!それを行うには非常に巧妙なネイティブメソッドがあります。

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ラッパーがあります。

1
Chant

内部的にctypesは、ダイナミックリンクライブラリ(UNIXではdlopen/dlsym、WindowsではLoadLibrary/GetProcAddress)によって提供される関数を使用して、ライブラリをロードし、関数名で指定された関数のアドレスを見つけます。次に、cffiライブラリを使用してパラメーターを動的に渡します。

問題は、ctypesが依存するダイナミックリンクライブラリに、共有ライブラリからシンボルをリストする関数が含まれていないことです。そのため、ctypesでシンボルをリストできません。

これを行うには、特定のツールを使用してelfファイル(UNIXではreadelf)を、dllのpeファイル(Windowsではdumpbin)をダンプする必要があります。

0
Sam Liao