-1

I've written a wrapper for a camera library in Rust that commands and operates a 16-bit camera, and also saves an image to file, using bindgen. Once I command an exposure to start (basically telling the camera to take an image), I can grab the image using a function of the form:

pub fn GetQHYCCDSingleFrame(
handle: *mut qhyccd_handle,
w: *mut u32,
...,
imgdata: *mut u16,) -> u32 //(u32 is a retval)

In C++, this function was:

uint32_t STDCALL GetQHYCCDSingleFrame(qhyccd_handle: *handle, ..., uint8_t *imgdata)

In C++, I can pass in a buffer of the form imgdata = new unsigned char[length_buffer] and the function would fill the buffer with image data from the camera.

In Rust, similarly, I can pass in a mut_ptr of a buffer in the form of a Vec: let mut buffer: Vec<u16> = vec![0u16; length_buffer].

Currently, the way I have structured the code is that there is a main struct, with settings such as the width and height of image, the camera handle, and others, including the image buffer. The struct has been initialized as a mut as:

let mut main_settings = MainSettings {
width: 9600,
...,
buffer: vec![0u16; length_buffer],
}

And when passing it to the GetQHYCCDSingleFrame function, I pass it accordingly:

let retval = unsafe { GetQHYCCDSingleFrame(main_settings.cam_handle, ..., main_settings.image_buffer.as_mut_ptr()) };

This code works perfectly when I build the program with cargo build or cargo build --release and run using cargo run or cargo run --release. The program starts the exposure, waits until the exposure is done, and then "gets" the frame and populates the buffer with image data correctly.

However, initially when I was writing the program, I had mistakenly declared the GetQHYCCDSingleFrame function as:

pub fn GetQHYCCDSingleFrame(
handle: *mut qhyccd_handle,
w: *mut u32,
...,
imgdata: &mut [u16],) -> u32 //(u32 is a retval)

(Note the change in imgdata type from *mut u16 to &mut [u16]).

When I passed in the same previous buffer as a slice and not a pointer:

let retval = unsafe { GetQHYCCDSingleFrame(main_settings.cam_handle, ..., &mut main_settings.image_buffer) };

the program worked perfectly when I built it with cargo build and ran using cargo run (dev build with 0 optimizations). The function and call worked, and the image buffer populated. However, the program segfaulted at GetQHYCCDSingleFrame when I built it using cargo build --release or ran it using cargo run --release (release build with opt-level = 3).

Using rust-gdb, the segfault error was:

Thread 1 "qsc" received signal SIGSEGV, Segmentation fault.
_memmove_sse2_unaligned_erms()
at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:538
538
../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S: No such file or directory.

I haven't had much experience with Rust optimizations, and don't quite get what optimizations Rust is doing to cause the program to segfault when it runs perfectly with no optimizations. I do understand the correct thing to do is to send in a mut_ptr as I do now, and everything works, but looking back, I would like to understand why the previous erroneous program worked but only in dev build and not release build? Is there a possibility it might be understanding the context in the C++ code the requirement there? Or is it something else going on?

If any more information might help understand, I'd be more than happy to provide that as well. Thanks!

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
EmaadP
  • 160
  • 1
  • 1
  • 10
  • 3
    With behavior like the one you describe (works in debug mode, fails in release mode), it's almost always because your code has [undefined behavior](https://doc.rust-lang.org/reference/behavior-considered-undefined.html). The Rust compiler (as well as C and C++ compilers) assumes that your code has no undefined behavior when performing optimizations, which means that some of the optimizations may cause weird behavior and crashes if there in fact _is_ undefined behavior. – Frxstrem Nov 12 '21 at 15:19

1 Answers1

2

Your FFI-function signature is invalid because you use invalid ABI:

pub fn GetQHYCCDSingleFrame(
handle: *mut qhyccd_handle,
w: *mut u32,
...,
imgdata: *mut u16,) -> u32 //(u32 is a retval)

You should read about FFI first.

https://doc.rust-lang.org/nomicon/ffi.html

https://doc.rust-lang.org/reference/items/external-blocks.html

By default, Rust uses "C" ABI for external functions but you function should use stdcall ABI (assuming by your C++ code). You can read more about this.

E.g.

extern "stdcall" {
 pub fn GetQHYCCDSingleFrame(
   handle: *mut qhyccd_handle,
   w: *mut u32,
   ...,
   imgdata: *mut u16,) -> u32;
}