使用ctypes从dll列出导出的函数

时间:2010-05-23 11:22:13

标签: python dll shared-libraries ctypes

有没有办法知道从dll到python外部函数库ctypes导出哪些函数?

如果可能,通过c types了解有关导出函数的详细信息。

如果是,有人可以提供一段代码吗?

8 个答案:

答案 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 记录这样的信息(参见例如函数指针的restypeargtypes属性),但只有在你通过不同的方式获得它之后。

答案 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类的对象。