【问题标题】:list exported functions from dll with ctypes使用 ctypes 列出从 dll 导出的函数
【发布时间】:2011-02-22 21:43:42
【问题描述】:

有没有办法通过python外部函数库ctypes知道dll导出了哪些函数?

如果可能的话,可以通过 ctypes 了解导出函数的详细信息。

如果是,有人可以提供一个sn-p的代码吗?

【问题讨论】:

    标签: python dll shared-libraries ctypes


    【解决方案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记录此类信息(参见例如函数指针的restypeargtypes 属性),但前提是您必须通过不同的方式获得它。

        【讨论】:

          【解决方案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 来查找库中的内容...我现在如何使用它?我需要知道它是否可以使用参数...
                猜你喜欢
                • 2021-11-17
                • 1970-01-01
                • 2021-09-17
                • 1970-01-01
                • 1970-01-01
                • 2021-11-03
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多