Есть ли способ узнать, какие функции экспортируются из dll
через библиотеку внешних функций python ctypes
?
И если возможно узнать подробности об экспортируемых функциях через c types
.
Если да, может ли кто-нибудь предоставить фрагмент кода?
Есть ли способ узнать, какие функции экспортируются из dll
через библиотеку внешних функций python ctypes
?
И если возможно узнать подробности об экспортируемых функциях через c types
.
Если да, может ли кто-нибудь предоставить фрагмент кода?
Я не думаю, что ctypes предлагает эту функцию. В Windows с визуальной студией:
DUMPBIN -EXPORTS XXX.DLL
Или для включения в окна:
objdump -p XXX.dll
Если вы находитесь в Linux, есть удобная утилита nm
, чтобы перечислить содержимое разделяемой библиотеки (в Linux всегда есть удобная утилита, особенно для C файлов).
Вы используете его с флагом -D
: nm -D ./libMyLib.so
В общем, это невозможно, потому что, как правило, динамически загружаемые библиотеки не несут требуемую метаинформацию. Возможно, эта информация может быть получена в определенных особых случаях с помощью системных способов, но сама ctypes
не получает эту информацию. Вы можете записать эту информацию через ctypes
(см., Например, restype и argtypes
атрибуты указателей функций), но только после того, получил его различными способами.
@Mark answer использует инструменты Visual Studio.
На окнах вы также можете использовать Dependency Walker, чтобы получить имена функций экспорта DLL.
Иногда имена искажены и не могут использоваться в качестве допустимого имени функции Python.
Вы можете использовать getattr
чтобы получить getattr
на искаженные функции, например:
mylib = ctypes.cdll('mylib.dll')
my_func = getattr(mylib, '[email protected]')
my_func()
Если у вас также есть источник для указанной библиотеки, и вы ищете полностью автоматизированный полностью 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
затем в питоне:
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)
Приведенный ниже подход работал как для 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
.
ДА! есть очень умный нативный способ сделать это.
допустим, вы используете 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]
ввод оболочки: fn
вывод оболочки: '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] )
и там у вас есть оболочка Python для сценария C без необходимости изменять десять разных вещей каждый раз, когда что-то меняется.
Внутренне ctypes использует функции, предоставляемые библиотекой динамических ссылок (dlopen/dlsym для unix, LoadLibrary/GetProcAddress для Windows) для загрузки библиотеки и поиска адреса функции, указанной именем функции; а затем используйте библиотеку cffi для динамического прохождения параметра.
Проблема в том, что динамическая библиотека ссылок, в которой ctypes зависит, не включает функцию для отображения символа из общей библиотеки, поэтому вы не можете перечислить символ ctypes.
Для этого вам нужно использовать специальные инструменты для удаления файла эльфа (readelf on unix) и файла pe для dll (dumpbin на windows).