使用 ctypes 列出从 dll 导出的函数
Posted
技术标签:
【中文标题】使用 ctypes 列出从 dll 导出的函数【英文标题】:list exported functions from dll with ctypes 【发布时间】:2011-02-22 21:43:42 【问题描述】:有没有办法通过python外部函数库ctypes
知道dll
导出了哪些函数?
如果可能的话,可以通过 ctypes
了解导出函数的详细信息。
如果是,有人可以提供一个sn-p的代码吗?
【问题讨论】:
【参考方案1】:我不认为 ctypes 提供这个功能。在带有 Visual Studio 的 Windows 上:
DUMPBIN -EXPORTS XXX.DLL
或者对于 windows 上的 mingw:
objdump -p XXX.dll
【讨论】:
【参考方案2】:如果您在 Linux 上,有一个方便的实用程序 nm
可以列出共享库的内容(Linux 上总是有一个方便的实用程序,尤其是 C 语言)。
Here is the question about it.
您将它与-D
标志一起使用:nm -D ./libMyLib.so
【讨论】:
【参考方案3】:一般来说,这是不可能的,因为一般来说,动态加载的库不携带您需要的元信息。在某些特殊情况下,可以通过系统特定的方式获取该信息,但ctypes
本身并不获取该信息。您可以通过ctypes
记录此类信息(参见例如函数指针的restype 和argtypes
属性),但前提是您必须通过不同的方式获得它。
【讨论】:
【参考方案4】:以下方法适用于 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 脚本查找test.dll
的哪些函数可以被 Python 使用。
#!/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
类的对象。
【讨论】:
【参考方案5】:@Mark 的回答使用 Visual Studio 工具。
在windows上你也可以使用Dependency Walker来获取dll导出的函数名。
有时名称会被破坏,不能用作有效的 Python 函数名称。
您可以使用getattr
来获取损坏函数的句柄,例如:
mylib = ctypes.cdll('mylib.dll')
my_func = getattr(mylib, '_my_func@0')
my_func()
【讨论】:
【参考方案6】:如果您也有上述库的源代码,并且您正在寻找一种完全自动化的全 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) 中的更多信息
【讨论】:
是的;应该可以,相信它们是相同的,只是扩展名不同。 测试过了,好像没有【参考方案7】:是的!有一种非常聪明的本地方法可以做到这一点。
假设您正在使用 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 包装器,而不必在每次发生变化时修改十个不同的东西。
【讨论】:
【参考方案8】:ctypes 内部使用动态链接库提供的函数(unix 上为 dlopen/dlsym,windows 上为 LoadLibrary/GetProcAddress)加载库并查找函数名指定的函数地址;然后使用cffi库动态传递参数。
问题是ctypes所依赖的动态链接库不包含从共享库中列出符号的函数,这就是为什么你不能按ctypes列出符号的原因。
为此,您必须使用特定工具来转储 elf 文件(在 unix 上为 readelf)和为 dll 转储 pe 文件(在 Windows 上为转储)。
【讨论】:
这真的很有趣...我使用 Wumpbin 来查找库中的内容...我现在如何使用它?我需要知道它是否可以使用参数...以上是关于使用 ctypes 列出从 dll 导出的函数的主要内容,如果未能解决你的问题,请参考以下文章