有没有办法知道从dll
到python外部函数库ctypes
导出哪些函数?
如果可能,通过c types
了解有关导出函数的详细信息。
如果是,有人可以提供一段代码吗?
答案 0 :(得分:15)
我不认为ctypes提供此功能。在Windows上使用visual studio:
DUMPBIN -EXPORTS XXX.DLL
或者对于Windows上的mingw:
objdump -p XXX.dll
答案 1 :(得分:9)
如果你在Linux上,有一个方便的实用程序nm
来列出共享库的内容(在Linux上总是有一个方便的实用程序,特别是对于C的东西)。
Here is the question about it.
您将其与-D
标记一起使用:nm -D ./libMyLib.so
答案 2 :(得分:7)
通常,这是不可能的,因为通常,动态加载的库不会携带您需要的元信息。有可能通过特定于系统的方式在某些特殊情况下获取该信息,但ctypes
本身不会获取该信息。你可以通过ctypes
记录这样的信息(参见例如函数指针的restype和argtypes
属性),但只有在你通过不同的方式获得它之后。
答案 3 :(得分:5)
@ Mark的回答使用Visual Studio工具。
在Windows上,您还可以使用Dependency Walker获取dll导出的函数名称。
有时名称被破坏,不能用作有效的python函数名。
您可以使用getattr
来处理损坏的函数,例如:
mylib = ctypes.cdll('mylib.dll')
my_func = getattr(mylib, '_my_func@0')
my_func()
答案 4 :(得分:1)
如果您还获得了所述库的源代码,并且您正在寻找全自动的全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
要进一步挖掘,您还可以获取参数和返回类型。
取消注释node.show()
以获取抽象语法树(AST)中的更多信息
我将很快发布一个图书馆(我会记得回来并删除一个链接)
答案 5 :(得分:0)
内部ctypes使用动态链接库(unix上的dlopen / dlsym,windows上的LoadLibrary / GetProcAddress)提供的函数来加载库并查找函数名指定的函数的地址;然后使用cffi库动态传递参数。
问题是ctypes所依赖的动态链接库不包含从共享库中列出符号的功能,这就是为什么你不能通过ctypes列出符号的原因。
要做到这一点,你必须使用特定的工具来转储elf文件(在unix上的readelf)和用于dll的pe文件(在windows上的dumpbin)。
答案 6 :(得分:0)
YES!有一个非常聪明的本地方法来做到这一点。
假设您正在使用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]
shell输入:fn
shell输出:'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] )
并且你有一个C脚本的python包装器,每次更改时都不需要修改十个不同的东西。
答案 7 :(得分:0)
以下方法适用于Windows和Ubuntu。
假设有一个c
文件,如下所示,其名称为test.c
。
#include <stdio.h>
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脚本查找test.dll
的哪些功能可以被Python使用。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from subprocess import Popen, PIPE
from ctypes import CDLL
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:
]
library = CDLL("./test.dll")
functions = []
for attr in attrs:
try:
eval(f"library.{attr}")
functions.append(attr)
except AttributeError:
pass
print(functions)
# ['func1', 'func2']
我在Ubuntu中获得的输出如下:
# ['_fini', 'func1', 'func2', '_init']
上面列出的项目是_FuncPtr
类的对象。