通过 FFI 调用 Rust 函数时访问冲突

Posted

技术标签:

【中文标题】通过 FFI 调用 Rust 函数时访问冲突【英文标题】:Access violation when calling Rust function through FFI 【发布时间】:2016-01-19 19:13:33 【问题描述】:

正如标题所述,当我尝试在 Python 中调用以下 Rust 代码时遇到访问冲突。

这是 Rust 代码:

#![crate_type = "dylib"]

extern crate libc;

use libc::c_char;
use std::ffi::CStr;
use std::str;

#[repr(C)]
pub struct AdditionalDetail 
    swis: String,
    sbl: String,
    school_code: String,
    land_assessed_value: u32,
    deed_book: String,
    deed_page: String,


#[no_mangle]
pub extern fn parse_details(l: *const c_char) -> AdditionalDetail
    let _line = unsafe 
        assert!(!l.is_null());
        CStr::from_ptr(l)
    ;
    let line = str::from_utf8(_line.to_bytes()).unwrap();
    let _swis = line[52..58].to_owned();
    let _sbl = line[58..78].to_owned();
    let _school_code = line[371..377].to_owned();
    let _land_assessed_value = line[824..836].parse::<u32>().ok().expect("Couldn't convert to an int");
    let _deed_book = line[814..819].to_owned();
    let _deed_page = line[819..824].to_owned();
    AdditionalDetailswis: _swis, sbl: _sbl, school_code: _school_code, deed_page: _deed_page,
                     land_assessed_value: _land_assessed_value, deed_book: _deed_book

以及我用来调用它的 Python 代码:

from ctypes import cdll, c_uint32, Structure, c_char_p


class TaxDetail(Structure):
    _fields_ = [('swis', c_char_p),
                ('sbl', c_char_p),
                ('school_code', c_char_p),
                ('land_assessed_value', c_uint32),
                ('deed_book', c_char_p),
                ('deed_page', c_char_p), ]

    def __str__(self):
        return str(self.swis)


lib = cdll.LoadLibrary(r"C:\Rust Workspace\embed\target\release\embed.dll")
lib.parse_details.restype = TaxDetail
lib.parse_details.argtype = (c_char_p,)
result = lib.parse_details(b"1346011          63 WAP WEST  LLC    00000101       13460100615800142703690000  63 Wap West  LLC              10 Fair Oaks Dr               Poughkeepsie, NY 12603                                                                                                                                                                                            000500000150000000017135601       14270369   411      000001 1        4-6Church St                            0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006158-14-270369-0000      058369006127002200002074000000052500000000286000N    0000000028600000000000000000000000000000000000Y")
print(result)

我在我的 Rust 代码中添加了println! 调用,当它尝试创建和返回结构时似乎发生了访问冲突。我收到的具体错误消息是Process finished with exit code -1073741819 (0xC0000005)

这发生在 64 位 Windows 10 上的 32 位 Rust 和 Python 上。

【问题讨论】:

您确定该错误不是因为print(result) 而发生的吗? @Shepmaster 是的,我在我的 Python 代码中在该行之前设置了一个断点,它甚至从未到达该行。我只是把它拿出来,得到了同样的结果。 【参考方案1】:

我不确定问题的全部范围,但我知道这个问题不会好:您不能通过 FFI 返回 String

Rust String 在概念上由 3 个部分组成:指向一块内存的指针、该内存的长度以及该内存中有多少是有效字符串。

将其与 C 字符串进行比较。 C 字符串只是一个指向内存的指针。你不知道有多少内存,你只能通过遍历每个字节直到到达 NUL 字节来知道有效长度。

更重要的是,String 并未标记为#[repr(C)],因此String 结构的实际布局取决于 Rust 编译器。

我怀疑发生错误是因为 Python 看到您返回的是 c_char_p(我假设是 char *)。然后它尝试读取一个指针的数据值,然后移动到下一个指针。它读取的“指针”可能是String 的指针长度容量,一旦它读取第二个指针,它就会在某处杂草丛生。

相反,您需要找出处理此字符串的替代方法。一些想法:

    操作传入的字符串以在断点处添加 NUL 字节,然后将指针返回到该大块中。在释放原始字符串后,您需要小心不要使用任何子字符串。此外,原始字符串现在看起来更短,因为它嵌入了 NUL 字节。我也不知道 Python 什么时候会释放字符串。 Return an object 持有 CString 并具有返回 as_ptr 的结果的方法。

类似的逻辑适用于&amp;str,它在概念上是指向一块内存的指针,以及该内存中有多少是有效的。

【讨论】:

你链接到我的例子不起作用。我完全复制了代码,但我在 Python 中得到了ctypes.ArgumentError: argument 2: &lt;class 'TypeError'&gt;: wrong type @MorganThrapp Omnibus 使用 Python 2;我猜 Python 3 有一些不同... :-( 是的,这似乎是问题所在。它在 2 中运行良好。 @MorganThrapp 我会在 Github repo 上为 Omnibus 提交一个问题,我会看看有一天我能不能解决它;-)

以上是关于通过 FFI 调用 Rust 函数时访问冲突的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Rust 函数和 FFI C++ 函数以相反的顺序执行?

如何将 C 字符串转换为 Rust 字符串并通过 FFI 转换回来?

从 Rust 调用动态链接的 Haskell 代码

如何创建 Rust 回调函数以传递给 FFI 函数?

为啥我的 FFI 函数的第二次调用无法匹配字符串比较?

如何从 NodeJS 中的 Rust FFI 函数返回字符串值?