简体   繁体   English

用 ctypes 替换共享库中的 function 指针

[英]Replace a function pointer in a shared library with ctypes

I am trying to replace an existing function pointer in a shared library with a callback defined in Python, through ctypes.我正在尝试通过 ctypes 用 Python 中定义的回调替换共享库中现有的 function 指针。

The source of the shared library in C: C中共享库的来源:

#include <assert.h>
#include <stdio.h>

void (*plot)();

int c_main(int argc, void** argv) {
  printf("plot is %p\n", (void*)plot);
  assert(plot != NULL);
  plot();
  return 0;
}

The source of the Python script: Python脚本的来源:

from sys import platform
from pathlib import Path
import ctypes
import _ctypes


FUNCTYPE = ctypes.WINFUNCTYPE if platform == 'win32' else ctypes.CFUNCTYPE


def dlclose(obj):
    if platform == "win32":
        _ctypes.FreeLibrary(obj._handle)
    else:
        _ctypes.dlclose(obj._handle)


def enc_args(args):
    C_ARGS = (ctypes.POINTER(ctypes.c_char) * len(args))()
    for idx, arg in enumerate(args):
        C_ARGS[idx] = ctypes.create_string_buffer(arg.encode("utf-8"))
    return C_ARGS


@FUNCTYPE(None)
def plotxy():
    print("plotxy")


C_ARGS = enc_args([str(Path(__file__))])
CAUXDLL = ctypes.CDLL("./test.so")

print(plotxy)

print(CAUXDLL.plot)

CAUXDLL.plot = plotxy

print(CAUXDLL.plot)

print(CAUXDLL.c_main(len(C_ARGS), C_ARGS))

The script to test it:测试它的脚本:

gcc -fPIC -shared -o test.so test.c
python3 test.py

The output I get: output 我得到:

# ./test.sh
<CFunctionType object at 0x7fb1f0abb1c0>
<_FuncPtr object at 0x7fb1f0abb640>
<CFunctionType object at 0x7fb1f0abb1c0>
plot is (nil)
python3: test.c:8: c_main: Assertion `plot != NULL' failed.
./test.sh: line 3: 21171 Aborted                 python3 test.py

So, it seems that the function defined in Python (plotxy) is of type CFunctionType , while the function pointer defined in C is of type _FuncPtr . So, it seems that the function defined in Python (plotxy) is of type CFunctionType , while the function pointer defined in C is of type _FuncPtr . Although the replacement in CAUXDLL is applied, it seems to have no effect when the main function is called.虽然应用了CAUXDLL中的替换,但在调用主 function 时似乎没有效果。

Apart from reading https://docs.python.org/3/library/ctypes.html#module-ctypes , I found other questions (eg How to use typedef in ctypes or python cytpes creating callback function - Segmentation fault (core dumped) ), but I cannot find how to convert CFunctionType (plotxy) to _FuncPtr . Apart from reading https://docs.python.org/3/library/ctypes.html#module-ctypes , I found other questions (eg How to use typedef in ctypes or python cytpes creating callback function - Segmentation fault (core dumped) ),但我找不到如何将CFunctionType (plotxy) 转换为_FuncPtr

EDIT编辑

I believe this might not be an issue with the regular usage of ctypes.我相信这对于 ctypes 的常规使用可能不是问题。 That's something I have successfully achieved and which is sufficiently explained in the docs.这是我成功实现的目标,并且在文档中得到了充分解释。 This question goes beyond.这个问题超越了。 I don't want to execute the C function.我不想执行 C function。 I want Python to replace an existing function pointer with a callback written in Python.我希望 Python 用用 Python 编写的回调替换现有的 function 指针。 Note that it is possible to do it by using a helper C function (see https://github.com/ghdl/ghdl-cosim/blob/master/vhpidirect/shared/pycb/caux.c#L32-L40 ). Note that it is possible to do it by using a helper C function (see https://github.com/ghdl/ghdl-cosim/blob/master/vhpidirect/shared/pycb/caux.c#L32-L40 ). Hence this question is about how to achieve it without that helper function (if possible).因此,这个问题是关于如何在没有该助手 function (如果可能的话)的情况下实现它。

The way to access global variables from ctypes is to use in_dll , but there doesn't seem to be an exposed way to change a function pointer.ctypes访问全局变量的方法是使用in_dll ,但似乎没有公开的方法来更改 function 指针。 I was only able to read it and call it, so I don't think it is possible without a helper function.我只能阅读并调用它,所以我认为没有助手 function 是不可能的。

The example below alters an int global variable, but CFUNCTYPE instances don't have a value member to alter it.下面的示例更改了一个int全局变量,但CFUNCTYPE实例没有value成员来更改它。 I added a C helper to set the global to work around the issue and a default value of the callback to verify it was accessed correctly before changing it.我添加了一个 C 帮助程序来设置全局以解决该问题,并添加回调的默认值以在更改之前验证它是否被正确访问。

test.c:测试.c:

#include <stdio.h>

#define API __declspec(dllexport)

typedef void (*CB)();

void dllplot() {
    printf("default\n");
}

API CB plot = dllplot;
API int x = 5;

API int c_main() {
  printf("x=%d (from C)\n",x);
  plot();
  return 0;
}

API void set_cb(CB cb) {
    plot = cb;
}

test.py:测试.py:

from ctypes import *

PLOT = CFUNCTYPE(None)

dll = CDLL('./test')
dll.c_main.argtypes = ()
dll.c_main.restype = c_int
dll.set_cb.argtypes = PLOT,
dll.set_cb.restype = None

@PLOT
def plotxy():
    print("plotxy")

x = c_int.in_dll(dll,'x')
plot = PLOT.in_dll(dll,'plot')
print(f'x={x.value} (from Python)')
x.value = 7
print('calling plot from Python directly:')
plot()
print('calling c_main():')
dll.c_main()
dll.set_cb(plotxy)
print('calling plot from Python after setting callback:')
plot()
print('calling plot from C after setting callback:')
dll.c_main()

Output: Output:

x=5 (from Python)
calling plot from Python directly:
default
calling c_main():
x=7 (from C)
default
calling plot from Python after setting callback:
plotxy
calling plot from C after setting callback:
x=7 (from C)
plotxy

Note that global pointers use .contents to access their value, so I experimented with using a POINTER(CFUNCTYPE(None)) and using plot.contents = plotxy but that doesn't assign the global variable correctly and C crashed.请注意,全局指针使用.contents来访问它们的值,因此我尝试使用POINTER(CFUNCTYPE(None))并使用plot.contents = plotxy但这并没有正确分配全局变量并且 C 崩溃了。

I even tried adding an actual global pointer to function pointer:我什至尝试向 function 指针添加一个实际的全局指针:

API CB plot = dllplot;
API CB* pplot = &plot;

and then using:然后使用:

PLOT = CFUNCTYPE(None)
PPLOT = POINTER(PLOT)
plot = PPLOT.in_dll(dll,'pplot')
plot.contents = plotxy

That let me assign the function via .contents , but c_main still called the default plot value.这让我通过.contents分配 function ,但c_main仍然调用默认的 plot 值。 So the functionality of using CFUNCTYPE as anything but a function parameter doesn't appear to be implemented.因此,除了 function 参数之外,似乎没有实现使用CFUNCTYPE的功能。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM