ctypes:获取c函数的实际地址

问题描述 投票:0回答:2

这很棘手(至少对我来说:-),也许不可行。但我试着问你。

我有这个c共享库:

#include <stdio.h>
#include <stdlib.h>

static int variable = -666;

int get_value() {
    return variable;
}

void print_pointer_to_get_value() {
    printf("pointer_to_get_value: %p\n", &get_value);
}

以这种方式编译(在 Linux 上):

gcc -fPIC -c -O2 shared.c && gcc -shared -o shared.so shared.o

现在我加载库并调用 print_pointer_to_get_value():

>>> import ctypes
>>> so = ctypes.cdll.LoadLibrary('./shared.so')
>>> so.print_pointer_to_get_value()
pointer_to_get_value: 0x7f46e178f700

我想从 ctypes 获取由 print_pointer_to_get_value() 打印的 get_value 函数的实际地址(整数)。 我的最终目标是将该地址移动到 Cython 模块并在“nogil”Cython 函数内调用该函数。我需要在运行时加载 .so 库,因此我无法编译将其链接到库的 Cython 模块。

谢谢1000。

python cython ctypes
2个回答
2
投票

这是一个令人讨厌的多步骤过程,不容易优雅地完成:

一些 Cython 代码:

ctypedef double (*math_function_t)(double) nogil

import ctypes

def call_f(f, double x):
    cdef math_function_t cy_f_ptr = (<math_function_t*><size_t>ctypes.addressof(f))[0]

    cdef double res
    with nogil:
        res = cy_f_ptr(x)
    return res

这里我向 Cython 传递一个 Ctypes 函数类型(

f
)并获取 Cython 中的地址。我认为在Python中不可能获得地址。作为如何初始化
f
的示例,在 Linux 上您可以这样做:

lib = ctypes.cdll.LoadLibrary("libm.so.6")
f = lib.sin

call_f(f,0.5) # returns sin(0.5)

(使用标准库

sin
函数)。

Cython 系列

cdef math_function_t cy_f_ptr = (<math_function_t*><size_t>ctypes.addressof(f))[0]
可以细分如下:

  1. ctypes.addressof(f)
    获取
    ctypes
    变量
    f
    所在的地址。__这不是您要查找的值_ - 这是存储您要查找的值的位置。
  2. 首先将其转换为
    size_t
    整数,然后转换为指向
    cdef
    函数指针类型的指针。 Cython 需要两步转换。
  3. [0]
    取消引用您的
    math_function_t*
    以获得
    math_function_t
    。这是函数指针(即你想要的值)

此答案的信息来自此新闻组线程(我当前无法访问)


0
投票

比赛已经很晚了,但你开始了(一句台词):

函数地址 = (ctypes.cast(my_c_library.my_c_function, ctypes.c_void_p)).value

哪里

my_c_library = ctypes.CDLL('my_c_library.dll')
#or, on unix-like:
my_c_library = ctypes.CDLL.LoadLibrary('my_c_library.so')

和 my_c_library.c:

int __cdecl my_c_function() { return true; }

在 Windows 10、x64 上进行了测试,但我不明白为什么这在类 UNIX 中不起作用。 x64,Python 3.11.4。
请放心:我无法判断这是否是设计允许的,我只能告诉它它有效。
仅适用于 64 位系统,因为

ctypes.c_void_p

代表64位整数。在 x64 32 位系统上将其更改为 32 位整数。


工作示例:
my_c_library.c:

#include <my_c_library.h>
#include <Windows.h>
#include <stdio.h>
int __cdecl my_c_function() { return true; }

test_python_script.py:

import ctypes
#load C library via ctypes (Windows DLL in this case, modify appropriately):
my_c_library = ctypes.CDLL('my_c_library.dll')
#set C function return type (int)
my_c_library.my_c_function.restype  = ctypes.c_int
#set C function argument type (none in this example)
my_c_library.my_c_function.argtypes = []
#get address of function in library
address_of_function = (ctypes.cast(my_c_library.my_c_function, ctypes.c_void_p)).value
#print address
print(f"address_of_function: {address_of_function}")
#--
#to prove, let's build a function from given address using ctypes:
#create function prototype
proven_function_prototype = ctypes.WINFUNCTYPE(ctypes.c_int)
#create callable function from prototype at given address:
proven_function = proven_function_prototype(address_of_function)
#call the function we built:
returnvar = proven_function()
print(f"proven_function returned {returnvar}")
#--
#returns: proven_function returned 1

本杰明

© www.soinside.com 2019 - 2024. All rights reserved.