【问题标题】:CFUNCTYPE causes segmentation faultCFUNCTYPE 导致分段错误
【发布时间】:2022-01-12 04:51:07
【问题描述】:

我有一个简单的 C 代码,它接受一些数字并返回一个字符串。

const char * get_err_string(const uint8_t errcode) {

    switch (errcode) {
        case 0:
            return "No errors";
            break;
        case 1:
            return "Some error";
            break;
        default:
            return "There is no such error code";
            break;
    }
}

我正在尝试使用函数原型通过ctypes 执行它,但每次都会遇到分段错误。

import ctypes

libc = ctypes.cdll.LoadLibrary("lib.so")

get_err_string = ctypes.CFUNCTYPE(
    ctypes.c_char_p,  # restype
    ctypes.c_uint8  # 1st argument
)(libc.get_err_string)

get_err_string(ctypes.c_uint8(0))  # segmentation fault

令我惊讶的是,稍微不同的代码执行得很好。

import ctypes

libc = ctypes.cdll.LoadLibrary("genevo/c/bin/genevo.so")

get_err_string = libc.get_err_string
get_err_string.restype = ctypes.c_char_p
get_err_string.argtypes = [ctypes.c_uint8]

get_err_string(ctypes.c_uint8(0))  # b'No errors.'

那么,这是为什么呢?有什么我想念的吗?

【问题讨论】:

  • return "No errors" 后面的分号丢失。
  • @MarsZ'uh 是的,我的错。我试图给出最小的工作示例并忘记了它。
  • 猜猜这是 gdb 需要检查的东西。在两种情况下都设置一个b get_err_string,并比较regs,尤其是。 %rdi %rsi 和 %rax 因为它们在两个调用中可能不同。请参阅 x86_64 ABI(假设您正在运行最常见的拱门),例如在 p22 上:refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf

标签: python c ctypes


【解决方案1】:

CFUNCTYPE 未使用正确的参数调用。请参阅ctypes 文档(摘录)中的Function Prototypes

这些工厂函数创建的函数原型可以 以不同的方式实例化,具体取决于类型和数量 调用中的参数:

原型地址
返回指定地址的外部函数 必须是整数。

原型可调用
创建 C 可调用函数(回调函数) 来自 Python 可调用对象。

原型(func_spec[, paramflags])
返回导出的外部函数 通过共享库。 func_spec 必须是 2 元组 (name_or_ordinal, 图书馆)。第一项是导出函数的名称 字符串,或导出函数的序号为小整数。这 第二项是共享库实例。

在这种情况下使用上面的第 3 个版本:

import ctypes as ct

libc = ct.CDLL('./test')
prototype = ct.CFUNCTYPE(ct.c_char_p, ct.c_uint8)
get_err_string = prototype(('get_err_string',libc))
print(get_err_string(0))
// Complete test.c for reference (Windows DLL)
#include <stdint.h>

__declspec(dllexport)
const char * get_err_string(const uint8_t errcode) {
    switch (errcode) {
        case 0:
            return "No errors";
            break;
        case 1:
            return "Some error";
            break;
        default:
            return "There is no such error code";
    }
}

输出:

b'No errors'

请注意,第二种方式是调用函数的通常方式:

import ctypes as ct

libc = ct.CDLL('./test')
libc.get_err_string.argtypes = ct.c_uint8,
libc.get_err_string.restype = ct.c_char_p

print(libc.get_err_string(0))

输出:

b'No errors'

但第一种方式有一些优点,即指定哪些参数是输入/输出,并分配参数名称和默认值以使其更“Pythonic”。

【讨论】:

  • 好的,可以了,谢谢。虽然,我不明白为什么将libc.get_err_string 传递给原型是错误的。 get_err_string 也是 Python 可调用的。
  • @PaulLynn 是的,但是第二种用法使 Python 函数可由 C 调用,因此您可以将 Python 函数作为回调传递给 C,这不是您正在做的。第三种用法返回可由 Python 调用的外部函数(C 可调用)。您可以通过将.argtypes.restype 分配给libc.get_err_string,然后将其传递给CFUNCTYPE 来使第二次使用起作用。我让它工作,但在尝试它时收到了来自 ctypes 的内存泄漏警告。这是错误的做法。
猜你喜欢
  • 1970-01-01
  • 2011-11-06
  • 2020-12-31
  • 2019-07-21
  • 2018-07-28
  • 2014-04-25
  • 2011-07-18
  • 2013-02-26
  • 2014-09-10
相关资源
最近更新 更多