通过 FFI 调用 Rust 函数时发生访问冲突
Access violation when calling Rust function through FFI
如标题所述,当我尝试在 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();
AdditionalDetail{swis: _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)
我已经添加了 println!
对我的 Rust 代码的调用,当它尝试创建和 return 结构时似乎发生了访问冲突。我收到的具体错误消息是 Process finished with exit code -1073741819 (0xC0000005)
.
这发生在 32 位 Rust 和 Python 64 位 Windows 10.
我不确定问题的全部程度,但我知道这个问题不会很好:你 不能 return String
通过 FFI。
Rust String
在概念上由 3 个部分组成:指向内存块的指针、该内存的长度以及该内存中有多少是有效字符串。
将其与 C 字符串进行比较。 C 字符串只是指向内存的指针。你不知道有多少内存,你只能通过遍历每个字节直到到达 NUL 字节来知道有效长度。
更重要的是,String
未标记为 #[repr(C)]
,因此 String
结构的实际布局取决于 Rust 编译器。
我怀疑发生错误是因为 Python 看到您正在 returning a c_char_p
(我假设是 char *
)。然后它尝试读取指针的数据值,然后移动到下一个指针。它读取的"pointer"可能是String
的指针or长度or的容量,一读取第二个它在某处的杂草中脱落。
相反,您将需要找出处理此字符串的替代方法。一些想法:
- 操作 passed-in 字符串以在断点处添加 NUL 字节,然后 return 指向该大块的指针。在释放原始字符串后,您需要注意不要使用任何子字符串。此外,原始字符串现在看起来更短,因为它嵌入了 NUL 字节。我也不知道什么时候 Python 会释放字符串。
- Return an object 持有
CString
并具有 return as_ptr
. 结果的方法
类似的逻辑适用于 &str
,它在概念上是指向内存块的指针以及该内存块中有多少是有效的。
如标题所述,当我尝试在 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();
AdditionalDetail{swis: _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)
我已经添加了 println!
对我的 Rust 代码的调用,当它尝试创建和 return 结构时似乎发生了访问冲突。我收到的具体错误消息是 Process finished with exit code -1073741819 (0xC0000005)
.
这发生在 32 位 Rust 和 Python 64 位 Windows 10.
我不确定问题的全部程度,但我知道这个问题不会很好:你 不能 return String
通过 FFI。
Rust String
在概念上由 3 个部分组成:指向内存块的指针、该内存的长度以及该内存中有多少是有效字符串。
将其与 C 字符串进行比较。 C 字符串只是指向内存的指针。你不知道有多少内存,你只能通过遍历每个字节直到到达 NUL 字节来知道有效长度。
更重要的是,String
未标记为 #[repr(C)]
,因此 String
结构的实际布局取决于 Rust 编译器。
我怀疑发生错误是因为 Python 看到您正在 returning a c_char_p
(我假设是 char *
)。然后它尝试读取指针的数据值,然后移动到下一个指针。它读取的"pointer"可能是String
的指针or长度or的容量,一读取第二个它在某处的杂草中脱落。
相反,您将需要找出处理此字符串的替代方法。一些想法:
- 操作 passed-in 字符串以在断点处添加 NUL 字节,然后 return 指向该大块的指针。在释放原始字符串后,您需要注意不要使用任何子字符串。此外,原始字符串现在看起来更短,因为它嵌入了 NUL 字节。我也不知道什么时候 Python 会释放字符串。
- Return an object 持有
CString
并具有 returnas_ptr
. 结果的方法
类似的逻辑适用于 &str
,它在概念上是指向内存块的指针以及该内存块中有多少是有效的。