list exported functions from dll with ctypes

2019-03-11 16:13发布

问题:

Is there any way to know which functions are exported from the dll through python foreign function library ctypes?

And if possible to know details about the exported functions through ctypes.

If yes, could someone provide a snippet of code?

回答1:

I don't think ctypes offers this functionality. On Windows with visual studio:

DUMPBIN -EXPORTS XXX.DLL

Or for mingw on windows:

objdump -p XXX.dll


回答2:

If you are on Linux, there is a handy utility nm to list the content of a shared library (there is always a handy utility on Linux, especially for C stuff).

Here is the question about it.

You use it with the -D flag: nm -D ./libMyLib.so



回答3:

In general, this is not possible, because, again in general, dynamically loaded libraries do not carry the meta-information you require. It may be possible to obtain that information in certain special cases through system-specific ways, but ctypes itself does not fetch that information. You can record such info via ctypes (see e.g. the restype and argtypes attributes of function pointers), but only after you have obtained it by different means.



回答4:

@Mark's answer uses Visual Studio tools.

On windows you can also use Dependency Walker to get the function names of dll exports.

Sometimes names are mangled and can't be used as a valid python function name.

You can use getattr to get a handle to mangled functions, e.g:

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

my_func()


回答5:

If you've also got the source for said library, and you're looking for a fully automated all-python way, you could use pycparser

for the file: 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);
}

compiling with gcc

gcc -I. -E ./prog.c > prog-preproc.c

gives us the pre-processed c file: prog-preproc.c then in 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)

yields

found function: add
found function: passthrough

To dig further you can also get parameter and return types. Uncomment node.show() for more information from within the Abstract Syntax Tree (AST)

I'll be releasing a library for this soon (I'll try to remember to come back and drop a link)



回答6:

Internally ctypes uses functions provided by dynamic link library (dlopen/dlsym on unix, LoadLibrary/GetProcAddress on windows) to load library and find address of function specified by function name; and then use cffi library to pass parameter dynamically.

Problem is that the dynamic link library that ctypes depends on doesn't include function to list symbol from the shared library, that's why you can't list symbol by ctypes.

To do that, you have to use specific tools to dump elf file (readelf on unix) and pe file for dll (dumpbin on windows).



回答7:

YES! there is a very clever native method to do it.

let's say you are using Python ctypes. put something like this in your C code:

1) in your C code:

#define PYEXPORT extern "C" __declspec(dllexport)

now put PYEXPORT above the function you want to export:

PYEXPORT
int myfunc(params){

2) After compiling, go back into Python and open your .c file, and parse it similar to this:

source1_ = open(cfile_name + '.c')
source1 = source1_.read()
source1_.close()
fn = source1.split('PYEXPORT')[-1].split('(')[0].split(' ')[1]

shell input: fn

shell output: 'myfunc'

3) Now here's the clever part: define a new function in a string:

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

NOW do exec(a3) and it will turn that string into a function that you can use.

4) do the usual:

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] )

and there you have a python wrapper for a C script without having to modify ten different things every time something changes.