簡體   English   中英

ACCESS_VIOLATION 從 Rust 調用 Btrieve BTRCALL 函數

[英]ACCESS_VIOLATION calling Btrieve BTRCALL function from Rust

我試圖從 Rust 調用 Btrieve(一個非常古老的數據庫引擎)。 這有點長,但這是我第一次從 Rust 嘗試 FFI,我想描述我所做的一切。

Btrieve 引擎是在一個 DLL w3btrv7.dll 中實現的,它是一個 32 位 DLL。 我使用 32 位 MSVC 工具為它制作了一個導入庫(它沒有官方工具):

lib /Def:w3btrv7.def /Out:w3btrv7.lib /Machine:x86

然后我安裝了 32 位 Rust 工具鏈stable-i686-pc-windows-msvc並將其設置為我的默認值。 Bindgen 在官方 Btrieve 標頭上顯示錯誤,所以我必須自己制作。 幸運的是,我們只需要包裝一個函數BTRCALL

我的 wrapper.h 中有這個:

short int BTRCALL(
    unsigned short  operation,
    void*           posBlock,
    void*           dataBuffer,
    unsigned short* dataLength,
    void*           keyBuffer,
    unsigned char   keyLength,
    char            ckeynum);

我鏈接為:

println!("cargo:rustc-link-lib=./src/pervasive/w3btrv7");

這似乎有效:該程序運行,是一個 32 位 exe,我可以在 Process Explorer 中看到它已加載w3btrv7.dll

當我通過 bindgen 發送標頭時,我得到:

extern "C" {
    pub fn BTRCALL(
        operation: ::std::os::raw::c_ushort,
        posBlock: *mut ::std::os::raw::c_void,
        dataBuffer: *mut ::std::os::raw::c_void,
        dataLength: *mut ::std::os::raw::c_ushort,
        keyBuffer: *mut ::std::os::raw::c_void,
        keyLength: ::std::os::raw::c_uchar,
        ckeynum: ::std::os::raw::c_char,
    ) -> ::std::os::raw::c_short;
}

類型和大小似乎都正確匹配,它們與我從 C# 應用程序中獲得的 DllImport 相匹配,該應用程序完美運行:

[DllImport("w3btrv7.dll", CharSet = CharSet.Ansi)]
private static extern short BTRCALL(
    ushort operation, // In C#, ushort = UInt16.
    [MarshalAs(UnmanagedType.LPArray, SizeConst = 128)] byte[] posBlock,
    [MarshalAs(UnmanagedType.LPArray)] byte[] dataBuffer,
    ref ushort dataLength,
    [MarshalAs(UnmanagedType.LPArray)] byte[] keyBuffer,
    byte keyLength,     // unsigned byte
    char keyNumber);    // 2 byte char

keyNumber略有不同,但我嘗試了有符號和無符號變體中的字節和短褲,但它仍然不起作用。

不幸的是,當我運行我的程序時,它在第一次調用 BTRCALL 后就崩潰了。 (嗯,實際上是這個調用所在的函數返回時)。 我已經將所有參數提取到局部變量中並檢查了它們的類型,並且看起來都是正確的:

let op: u16 = 0;
let mut pos_block: [u8; 128] = self.pos_block.clone();
let pos_block_ptr: *mut std::ffi::c_void = pos_block.as_mut_ptr() as *mut _;
let mut data_buffer: [u8; 32768] = self.data_buffer.clone();
let data_buffer_ptr: *mut std::ffi::c_void = data_buffer.as_mut_ptr() as *mut _;
let mut data_length: u16 = data_buffer.len() as u16;
let mut key_buffer: [u8; 256] = self.key_buffer.clone();
let key_buffer_ptr: *mut std::ffi::c_void = key_buffer.as_mut_ptr() as *mut _;
let key_length: u8 = 255; //self.key_length;
let key_number: i8 = self.key_number.try_into().unwrap();

let status: i16 = BTRCALL(
    op,
    pos_block_ptr,
    data_buffer_ptr,
    &mut data_length,
    key_buffer_ptr,
    key_length,
    key_number
);

它使程序崩潰

error: process didn't exit successfully: `target\debug\blah.exe` (exit code: 0xc0000005, STATUS_ACCESS_VIOLATION)

根據我的閱讀,這可能是由於地址訪問不當造成的。

確實,當我進行一些跟蹤以檢查變量時,有一些非常有趣的行為,因為我的按值傳遞的局部變量似乎被覆蓋了。 此處的日志僅轉儲緩沖區的前 30 個字節,因為其余部分只是零:

pos_block = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
pos_block_ptr = 0xad6524
data_buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
data_buffer_ptr = 0xad65a8
data_length = 32768
key_buffer = [34, 67, 58, 92, 116, 101, 109, 112, 92, 99, 115, 115, 92, 120, 100, 98, 92, 67, 65, 83, 69, 46, 68, 66, 34, 0, 0, 0, 0, 0]
key_buffer_ptr = 0xade5b0
key_length = 255
key_number = 0

>>>>>>>>>>>>>>> AFTER THE CALL TO BTRCALL:

pos_block = [0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 203, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0]
pos_block_ptr = 0x0
data_buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
data_buffer_ptr = 0x42442e45
data_length = 0
key_buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
key_buffer_ptr = 0x0
key_length = 173
key_number = 0
BTRCALL() returned B_NO_ERROR

請注意pos_block_ptr已設置為 0 等。 相比之下,從 C# 代碼成功執行完全相同的調用只是將一些數據寫入pos_block的前 18 個字節,而不會更改任何其他變量。

就好像它有點發瘋了,剛剛開始覆蓋內存......

在這一點上,我不知道接下來要嘗試什么。

將聲明從extern "C"更改為extern "stdcall"有效:

extern "stdcall" {
    pub fn BTRCALL(
        operation: ::std::os::raw::c_ushort,
        posBlock: *mut ::std::os::raw::c_void,
        dataBuffer: *mut ::std::os::raw::c_void,
        dataLength: *mut ::std::os::raw::c_ushort,
        keyBuffer: *mut ::std::os::raw::c_void,
        keyLength: ::std::os::raw::c_uchar,
        ckeynum: ::std::os::raw::c_char,
    ) -> ::std::os::raw::c_short;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM