如何调用动态分配 python 方法到从共享库导出的 C 指针函数

how to call assign dynamically a python method to a C pointer function exported from a shared library

我想解决以下问题。共享库中的某些 C 代码将调用指针函数,该函数是共享库的导出符号

#include <stdio.h>

typedef void (*pCallBack)(const char *);

pCallBack one_pointer;

void errorPrint(const char * str)
{
   printf("my address = %p", one_pointer);
   fflush(stdout);
   (*one_pointer)(str);
}

让我们将该库编译为共享库

gcc -fPIC -g -shared -o  call_back.so call_back.c

现在我想从 Python 调用它,动态重新定义 pCallBack 指针的实现

import ctypes
import sys

def myCallBack(str):
    print("inside python" % str)
    sys.stdout.flush()

libcallback = ctypes.CDLL("./call_back.so")
pointer_CallBack = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_char_p)
libcallback.one_pointer = pointer_CallBack(myCallBack)
libcallback.errorPrint(b"one_string")

不幸的是,导出的数据指针似乎无法重新定义。我也尝试了 ctypes 中的 in_dll 函数,但它没有解决问题

当尝试 运行 我的 Python 脚本时,我得到以下内容

LD_PRELOAD="./call_back.so" python3 error.py
my address = (nil)[1]    2871869 segmentation fault (core dumped) 

有人知道这个问题吗?

清单[Python.Docs]: ctypes - A foreign function library for Python.

使用ct.in_dll修改.dll中的变量适用于简单类型(int).我不知道如何(干净地)为(函数)指针执行此操作。因此,您需要一个简单设置回调的新函数。

还有一些错误:

  • NULL 签入 errorPrint
  • argtypesrestype 未设置导出函数。有关
  • 的更多详细信息
  • 回调函数返回 void(不是 void*

这是一个工作示例。

dll00.c:

#include <stdio.h>

#if defined(_WIN32)
#  define DLL00_EXPORT_API __declspec(dllexport)
#else
#  define DLL00_EXPORT_API
#endif

typedef void (*CallbackFuncPtr)(const char*);

#if defined(__cplusplus)
extern "C" {
#endif

DLL00_EXPORT_API void setCallback(CallbackFuncPtr cb);
DLL00_EXPORT_API void errorPrint(const char *str);

#if defined(__cplusplus)
}
#endif


CallbackFuncPtr callback = NULL;


void setCallback(CallbackFuncPtr cb) {
    callback = cb;
}

void errorPrint(const char * str) {
    printf("C - Callback: %p\n", callback);
    fflush(stdout);
    if (callback) {
        (*callback)(str);
    }
    printf("C - done\n");
}

code00.py

#!/usr/bin/env python

import sys
import ctypes as ct


DLL_NAME = "./dll00.so"

CallBackType = ct.CFUNCTYPE(None, ct.c_char_p)


def callback_func(s):
    print("PY - callback arg: {:}".format(s))
    sys.stdout.flush()


def main(*argv):
    dll00 = ct.CDLL(DLL_NAME)
    setCallback = dll00.setCallback
    setCallback.argtypes = (CallBackType,)
    setCallback.restype = None
    errorPrint = dll00.errorPrint
    errorPrint.argtypes = (ct.c_char_p,)
    errorPrint.restype = None

    cb = CallBackType.in_dll(dll00, "callback")
    print("Original callback: {:}".format(cb))

    callback = CallBackType(callback_func)
    print("New callback: {:}\n".format(callback))

    errorPrint(b"Before setting callback")
    setCallback(callback)
    errorPrint(b"After setting callback")


if __name__ == "__main__":
    print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    rc = main(*sys.argv[1:])
    print("\nDone.")
    sys.exit(rc)

输出:

[cfati@cfati-5510-0:/mnt/e/Work/Dev/Whosebug/q067079630]> ~/sopr.sh
*** Set shorter prompt to better fit when pasted in Whosebug (or other) pages ***

[064bit prompt]> ls
code00.py  dll00.c
[064bit prompt]> gcc -shared -fPIC -o dll00.so dll00.c
[064bit prompt]> ls
code00.py  dll00.c  dll00.so
[064bit prompt]>
[064bit prompt]> python code00.py
Python 3.8.5 (default, Jan 27 2021, 15:41:15) [GCC 9.3.0] 64bit on linux

Original callback: <CFunctionType object at 0x7f07ef740ac0>
New callback: <CFunctionType object at 0x7f07ef740e80>

C - Callback: (nil)
C - done
C - Callback: 0x7f07efffc010
PY - callback arg: b'After setting callback'
C - done

Done.